Full Code of 0326/iBookmark for AI

master f8334ce1636a cached
18 files
21.6 KB
7.1k tokens
11 symbols
1 requests
Download .txt
Repository: 0326/iBookmark
Branch: master
Commit: f8334ce1636a
Files: 18
Total size: 21.6 KB

Directory structure:
gitextract_qjheeu6_/

├── LICENSE
├── README.md
├── _locales/
│   ├── en/
│   │   └── messages.json
│   ├── en_US/
│   │   └── messages.json
│   ├── ja/
│   │   └── messages.json
│   ├── ko/
│   │   └── messages.json
│   ├── zh_CN/
│   │   └── messages.json
│   └── zh_TW/
│       └── messages.json
├── foreground.js
├── manifest.json
├── popup/
│   ├── popup.css
│   ├── popup.html
│   └── popup.js
├── service-worker-utils.js
├── service-worker.js
└── settings/
    ├── settings.css
    ├── settings.html
    └── settings.js

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 SimGus

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
<img width="400" src="https://lh3.googleusercontent.com/_oj_6Y4K4cjpguig23UpstgNdLh6qUvWfcS3LBE73U6p6f8FRr_QuqTIEWmGzq5MpRevAwzF=s1280-h800-e365-rw"></img>

# iBookmark
Chrome extension for manage bookmarks 一款简单好用的 Chrome 书签管理插件,项目工程非常简单,可供学习参考。

下载地址:https://chrome.google.com/webstore/detail/ibookmark/fnfchnalfnjbjbfeccpophocngdgapad

Wiki文档: https://github.com/0326/iBookmark/wiki

## 快速上手
先clone项目启动watch:
```shell
git clone git@github.com:0326/iBookmark.git
cd iBookmark
# install devDependencies
npm i 
# start watch
gulp   
```
然后chrome://extensions/ => 加载已解压 扩展程序 => 加载iBookmark文件夹:

注意插件ID信息,浏览器访问:
chrome-extension://{{插件ID}}/popup/popup.html

编码完成后把项目打包成zip即可上传应用市场。



## 已完成功能
- 所有书签分组展示
- 在当前分组新增书签
- 修改已有书签信息
- 删除书签
- 搜索书签功能
- 统计书签使用频率,新增常用书签一栏
- 支持 manifest.json V3

## 待完成功能
- 新增书签类别
- 修改书签类别名称
- 删除某个书签分类
- 支持书签拖拽,以及移动到其他分类
- 支持历史记录
- 支持插件配置功能,可配置主题,配置是否隐藏某些分类下的书签

## 协议
MIT.

## 更新日志
0.0.4版本: 支持 manifest V3 协议
0.0.2版本: 新增配置功能,是否在每个分类下展示添加网址链接按钮

================================================
FILE: _locales/en/messages.json
================================================
{
  "appName": { "message": "iBookmark" },
  "appDesc": { "message": "the best bookmark manager in Chrome" },
  "extErrTitle": { "message": "Title Required" },
  "extErrUrl": { "message": "Url Format Error<br>Please Start With 'http://' or 'https://'" },
  "extBtnCancel": { "message": "Cancel" },
  "extBtnDelete": { "message": "Delete" },
  "extBtnUpdate": { "message": "Update" },
  "extBtnSubmit": { "message": "Submit" },
  "extTitleSearchResult": { "message": "Search Result" },
  "extTxtNewSite": { "message": "Add New Site" },
  "extTitleNewest": { "message": "The Newest" },
  "extTitleViews": { "message": "The Most Views" }
}

================================================
FILE: _locales/en_US/messages.json
================================================
{
  "appName": { "message": "iBookmark" },
  "appDesc": { "message": "the best bookmark manager in Chrome" },
  "extErrTitle": { "message": "Title Required" },
  "extErrUrl": { "message": "Url Format Error<br>Please Start With 'http://' or 'https://'" },
  "extBtnCancel": { "message": "Cancel" },
  "extBtnDelete": { "message": "Delete" },
  "extBtnUpdate": { "message": "Update" },
  "extBtnSubmit": { "message": "Submit" },
  "extTitleSearchResult": { "message": "Search Result" },
  "extTxtNewSite": { "message": "Add New Site" },
  "extTitleNewest": { "message": "The Newest" },
  "extTitleViews": { "message": "The Most Views" }
}

================================================
FILE: _locales/ja/messages.json
================================================
{
  "appName": { "message": "iBookmark" },
  "appDesc": { "message": "Chromeで最高のブックマークマネージャー" },
  "extErrTitle": { "message": "タイトルを入力してください" },
  "extErrUrl": { "message": "URL形式エラー<br>「http://」または「https://」で始めてください" },
  "extBtnCancel": { "message": "キャンセル" },
  "extBtnDelete": { "message": "削除" },
  "extBtnUpdate": { "message": "更新" },
  "extBtnSubmit": { "message": "送信" },
  "extTitleSearchResult": { "message": "検索結果" },
  "extTxtNewSite": { "message": "新しいサイトを追加" },
  "extTitleNewest": { "message": "最新の追加" },
  "extTitleViews": { "message": "最も閲覧された" }
}

================================================
FILE: _locales/ko/messages.json
================================================
{
  "appName": { "message": "iBookmark" },
  "appDesc": { "message": "Chrome에서 최고의 북마크 관리자" },
  "extErrTitle": { "message": "제목을 입력하세요" },
  "extErrUrl": { "message": "URL 형식 오류<br>'http://' 또는 'https://'로 시작하세요" },
  "extBtnCancel": { "message": "취소" },
  "extBtnDelete": { "message": "삭제" },
  "extBtnUpdate": { "message": "수정" },
  "extBtnSubmit": { "message": "제출" },
  "extTitleSearchResult": { "message": "검색 결과" },
  "extTxtNewSite": { "message": "새 사이트 추가" },
  "extTitleNewest": { "message": "최신 추가" },
  "extTitleViews": { "message": "가장 많이 본" }
}

================================================
FILE: _locales/zh_CN/messages.json
================================================
{
  "appName": {
    "message": "iBookmark 收藏夹"
  },
  "appDesc": {
    "message": "做最好用的Chrome收藏夹"
  },
  "extErrTitle": {
    "message":"请输入标题"
  },
  "extErrUrl": {
    "message":"地址格式错误<br>请以http://或者https://开头"
  },
  "extBtnCancel": {
    "message":"取消"
  },
  "extBtnDelete": {
    "message":"删除"
  },
  "extBtnUpdate": {
    "message":"修改"
  },
  "extBtnSubmit": {
    "message":"提交"
  },
  "extTitleSearchResult": {
    "message":"搜索结果"
  },
  "extTxtNewSite": {
    "message":"添加新网址"
  },
  "extTitleNewest": {
    "message":"最新添加"
  },
  "extTitleViews": {
    "message":"最常访问"
  }
}

================================================
FILE: _locales/zh_TW/messages.json
================================================
{
  "appName": { "message": "iBookmark 收藏夾" },
  "appDesc": { "message": "做最好用的Chrome收藏夾" },
  "extErrTitle": { "message": "請輸入標題" },
  "extErrUrl": { "message": "地址格式錯誤<br>請以http://或https://開頭" },
  "extBtnCancel": { "message": "取消" },
  "extBtnDelete": { "message": "刪除" },
  "extBtnUpdate": { "message": "修改" },
  "extBtnSubmit": { "message": "提交" },
  "extTitleSearchResult": { "message": "搜尋結果" },
  "extTxtNewSite": { "message": "新增網址" },
  "extTitleNewest": { "message": "最新新增" },
  "extTitleViews": { "message": "最常訪問" }
}

================================================
FILE: foreground.js
================================================
// This script gets injected into any opened page
// whose URL matches the pattern defined in the manifest
// (see "content_script" key).
// Several foreground scripts can be declared
// and injected into the same or different pages.

console.log("This prints to the console of the page (injected only if the page url matched)")


================================================
FILE: manifest.json
================================================
{
    "manifest_version": 3,
    "name": "__MSG_appName__",
    "description": "__MSG_appDesc__",
    "default_locale": "en",
    "version": "0.0.4",
    "icons": {
        "16": "img/icon16.png",
        "48": "img/icon48.png",
        "128": "img/icon128.png"
    },
    "options_page": "settings/settings.html",
    "action": {
        "default_title": "iBookmark 收藏夹",
        "default_popup": "popup/popup.html"
    },
    "permissions": [
        "bookmarks",
        "favicon",
        "storage"
    ],
    "host_permissions": [
        "*://*/*"
    ],
    "web_accessible_resources": [
    {
      "resources": ["_favicon/*"],
      "matches": ["<all_urls>"],
      "extension_ids": ["*"]
    }
  ],
    "background": {
        "service_worker": "service-worker.js"
    },
    "content_scripts": [{
        "js": ["foreground.js"],
        "matches": ["https://github.com/*"]
    }]
}


================================================
FILE: popup/popup.css
================================================
html, body {
	 width: 800px;
	 height: 600px;
	 margin: 0;
	 padding: 0;
	 font-family: STHeiti, '微软雅黑', Arial, sans-serif;
}
 .clearfix:after {
	 content: ".";
	 display: block;
	 height: 0;
	 clear: both;
	 visibility: hidden;
}
 .show {
	 display: block !important;
}
 .icon {
	 display: block;
	 position: absolute;
	 width: 16px;
	 height: 16px;
	 background-repeat: no-repeat;
}
 .icon-search {
	 left: 2px;
	 top: 7px;
	 background-image: url('../img/icon-search16.png');
}
 .icon-close {
	 right: 2px;
	 top: 7px;
	 background-image: url('../img/icon-close16.png');
}
 .fix-header {
	 position: fixed;
	 top: 0;
	 width: 100%;
	 background: rgba(255,255,255,0.9);
	 box-shadow: 0 1px 4px #ccc;
	 z-index: 100;
}
 .fix-header ul {
	 margin: 0;
	 padding: 0;
	 height: 30px;
	 line-height: 30px;
}
 .fix-header li {
	 float: left;
	 width: 120px;
	 list-style: none;
	 text-align: center;
	 cursor: pointer;
}
 .fix-header li:hover {
	 background: #f2f7f2;
}
 .fix-header .search-bar {
	 width: 240px;
	 margin: 0 5px;
	 position: relative;
}
 .fix-header #J_SearchBookmark {
	 width: 100%;
	 height: 26px;
	 padding-left: 20px;
	 outline: none;
	 border: none;
	 background: transparent;
	 border-bottom: 1px solid #0d9572;
}
 #J_BookmarkHot, #J_BookmarkSearchList {
	 margin-top: 40px;
}
 .bookmark-ctr {
	 margin: 5px;
}
 .bookmark-ctr h2 {
	 position: relative;
	 padding: 0 0 5px 10px;
	 margin-bottom: 5px;
	 height: 20px;
	 font-size: 16px;
	 line-height: 20px;
	 color: #666;
	 border-bottom: 1px dashed #999;
}
 .bookmark-ctr h2::after {
	 display: block;
	 content: ' ';
	 position: absolute;
	 left: 0;
	 top: 0;
	 width: 5px;
	 height: 20px;
	 background: #0d9572;
}
 .bookmark-ctr ul {
	 padding: 0;
}
 .bookmark-ctr li {
	 position: relative;
	 float: left;
	 width: 170px;
	 height: 24px;
	 padding-left: 22px;
	 margin: 0 5px 5px 0;
	 font-size: 14px;
	 line-height: 24px;
	 cursor: pointer;
	 text-overflow: ellipsis;
	 white-space: nowrap;
	 overflow: hidden;
}
 .bookmark-ctr li:hover {
	 background-color: #f2f7f2;
	 text-decoration: underline;
}
 .bookmark-ctr a, .bookmark-ctr a:active, .bookmark-ctr a:visited {
	 text-decoration: none;
	 color: #0d9572;
	 outline: none;
}
 .bookmark-ctr a:hover {
	 opacity: .5;
}
 .bookmark-ctr i {
	 position: absolute;
	 display: block;
	 left: 4px;
	 top: 4px;
	 height: 16px;
	 width: 16px;
	 background-repeat: no-repeat;
	 background-size: contain;
}
 .add-bookmark-pop {
	 display: none;
	 position: fixed;
	 left: 0;
	 top: 0;
	 width: 100%;
	 height: 100%;
	 background: rgba(0,0,0,0.5);
	 z-index: 1000;
}
 .add-bookmark-pop .wrapper {
	 margin: 100px auto;
	 width: 240px;
}
 .add-bookmark-pop input, .add-bookmark-pop button {
	 display: block;
	 width: 100%;
	 height: 30px;
	 margin: 10px 0;
	 padding: 0;
	 line-height: 30px;
}
 .add-bookmark-pop input[type="text"] {
	 padding: 0 2px;
	 border: 1px solid #0d9572;
	 outline: #0d9572;
}
 .add-bookmark-pop input[type="button"], .add-bookmark-pop button {
	 border: 1px solid #fff;
	 background: #0d9572;
	 color: #fff;
	 cursor: pointer;
}
 .add-bookmark-pop input[type="button"]:hover, .add-bookmark-pop button:hover {
	 background: #f2f7f2;
	 border-color: #0d9572;
	 color: #0d9572;
}
 .add-bookmark-pop .tips {
	 margin: 10px;
	 text-align: center;
	 color: #FFFE00;
	 line-height: 20px;
}
 

================================================
FILE: popup/popup.html
================================================
<html>
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="popup.css" charset="utf-8">
</head>

<body>
  <header class="fix-header">
    <ul class="clearfix">
      <li class="search-bar">
        <i class="icon icon-search"></i>
        <input type="text" id="J_SearchBookmark" placeholder="search..."></input>
        <i id="J_SearchClose" class="icon icon-close"></i>
      </li>
    </ul>
  </header>

  <div id="J_BookmarkSearchList" class="bookmark-ctr">
    <!--搜索标签结果列表-->
  </div>

  <div id="J_BookmarkHot" class="bookmark-ctr">
    <!--最常使用-->
  </div>

  <div id="J_BookmarkRecent" class="bookmark-ctr">
    <!--最新添加-->
  </div>

  <div id="J_BookmarkCtr" class="bookmark-ctr">
    <!--书签列表-->
  </div>

  <div id="J_AddBookmarkPop" class="add-bookmark-pop">
    <div class="wrapper">
      <input type="text" name="title" value="" placeholder="title" autocomplete="off">
      <input type="text" name="url" value="" placeholder="url link" autocomplete="off">
      <input type="button" name="name" value="Add" class="J_SubmitAddBookmark">
      <input type="button" name="name" value="Update" class="J_UpdateAddBookmark">
      <input type="button" name="name" value="Delete" class="J_DeleteAddBookmark">
      <button type="button" name="button" class="J_CancelAddBookmark">Cancel</button>
      <p class="tips"></p>
    </div>
  </div>
  <script src="/lib/zepto.min.js"></script>
  <script src="popup.js"></script>
</body>

</html>

================================================
FILE: popup/popup.js
================================================
function faviconURL(u) {
  const url = new URL(chrome.runtime.getURL("/_favicon/"));
  url.searchParams.set("pageUrl", u);
  url.searchParams.set("size", "32");
  return url.toString();
}

class Bookmark {
  constructor() {
    let that = this

    that.bookmarkDict = {}

    that.initI18n()
    that.registerHotBookmark()
    that.registerSearchBar()

    chrome.bookmarks.getRecent(8, function(list) {
      let obj = {}
      obj[that.i18n.txtTitleNewest] = list
      that.renderBookmarks($('#J_BookmarkRecent'), obj)
    })

    // 获取书签数据
    chrome.bookmarks.getTree(function(tree) {
      // 获取用户配置
      chrome.storage.sync.get('hasNewBookmarkBtn',(obj)=>{
        let hasNew = obj.hasNewBookmarkBtn === false ? false : true
        that.recursiveTree(tree[0], '')
        that.renderBookmarks($('#J_BookmarkCtr'), that.bookmarkDict, hasNew)
        that.bindEvent()
      })
    })
  }

  initI18n() {
    let i18n = chrome.i18n
    this.i18n = {
      errTitle: i18n.getMessage('extErrTitle'),
      errUrl: i18n.getMessage('extErrUrl'),
      btnCancel: i18n.getMessage('extBtnCancel'),
      btnDelete: i18n.getMessage('extBtnDelete'),
      btnUpdate: i18n.getMessage('extBtnUpdate'),
      btnSubmit: i18n.getMessage('extBtnSubmit'),
      txtNewSite: i18n.getMessage('extTxtNewSite'),
      txtsearchResult: i18n.getMessage('extTitleSearchResult'),
      txtTitleNewest: i18n.getMessage('extTitleNewest'),
      txtTitleSearchResult: i18n.getMessage('extTitleSearchResult'),
      txtTitleViews: i18n.getMessage('extTitleViews')
    }

    $('.J_UpdateAddBookmark').val(this.i18n.btnUpdate)
    $('.J_DeleteAddBookmark').val(this.i18n.btnDelete)
    $('.J_SubmitAddBookmark').val(this.i18n.btnSubmit)
    $('.J_CancelAddBookmark').text(this.i18n.btnCancel)
  }

  registerSearchBar() {
    let that = this
    let inputPause = false
    let $searchBar = $('#J_SearchBookmark')
    let $searchResultList = $('#J_BookmarkSearchList')
    $searchBar.on('input', function(params) {
      if (inputPause) {
        return
      }

      inputPause = true

      setTimeout(function(params) {
        let val = $searchBar.val()
        if (val) {
          renderSearchBookmark(val)
        } else {
          $searchResultList.hide()
        }
        inputPause = false
      }, 1000)
    })

    $('#J_SearchClose').on('click', function() {
      $searchResultList.hide()
    })

    function renderSearchBookmark(val) {
      chrome.bookmarks.search(val, function(list) {
        let obj = {}
        obj[that.i18n.txtTitleSearchResult] = list
        that.renderBookmarks($searchResultList, obj)
        $searchResultList.show()
      })
    }
  }

  // 实现最热书签排行榜
  registerHotBookmark() {
    let that = this
    let CStorage = chrome.storage.local
    $('.bookmark-ctr').on('click', 'li', function(e) {
      //  e.preventDefault()
      if (e.target.href) {
        let bkid = e.currentTarget.dataset.id.toString()
        CStorage.get(bkid, function(obj) {
          let num = obj[bkid]
          let saveObj = {}

          saveObj[bkid] = 1
          if (num) {
            saveObj[bkid] = num + 1
          }
          CStorage.set(saveObj)
        })
      }
    })

    CStorage.get(null, function(obj) {
      let objArr = []
      let idList = []

      // obj=>arr
      for (let i in obj) {
        if (/^\d+$/.test(i)) {
          objArr.push({
            id: i,
            num: obj[i]
          })
        }
      }

      // 过滤出点击num最大的前8个数据
      objArr.sort(function(a, b) {
          return b.num - a.num
        })
        .slice(0, 8)
        .forEach(function(item) {
          idList.push(item.id)
        })

      chrome.bookmarks.get(idList, function(list) {
        let obj = {}
        obj[that.i18n.txtTitleViews] = list
        that.renderBookmarks($('#J_BookmarkHot'), obj)
      })
    })
  }

  // 遍历收藏夹,将叶子节点的父节点作为分类名归类
  recursiveTree(dad, dadName, self) {
    let that = this
    if (dad && dad.children) {
      if (dad.children.length) {
        dad.children.forEach(function(son) {
          let title = dadName
          if (son.children) {
            title = dadName ? dadName + '-' + son.title : son.title
          }
          that.recursiveTree(son, title)
        })
      } else {
        that.recursiveTree(null, dadName, dad)
      }

    } else if (that.bookmarkDict[dadName]) {
      that.bookmarkDict[dadName].push(dad)
    } else if (dadName) {
      that.bookmarkDict[dadName] = dad ? [dad] : self
    }
  }

  // 渲染收藏夹列表
  renderBookmarks($ctr, dict, hasNew) {
    let tpl = ''
    let txtNewSite = this.i18n.txtNewSite
    for (let key in dict) {
      tpl += '<section><h2>' + key + '</h2><ul class="clearfix">'
      if (dict[key].length) {
        dict[key].forEach(function(item) {
          tpl += '<li data-id="' + item.id + '" data-parentId="' + item.parentId + '" data-index="' + item.index + '" data-title="' + item.title + '" data-url="' + item.url + '"' + '><i class="J_BookmarkEdit" style="background-image:url(' + faviconURL(item.url) + ')"></i><a target="_blank" class="bm-item" href="' + item.url + '" alt="' + item.url + '">' + item.title + '</a></li>'
        })

        if (hasNew) {
          tpl += '<li class="J_BookmarkNew bookmark-new" data-parentId="' + dict[key][0].parentId + '" >' + txtNewSite + '</li></ul></section>'
        } else {
          tpl += '</ul></section>'
        }

      } else if (hasNew) {
        // 该分类下无数据,那么在该分类下建立的页面的parentId就是该分类本身的id
        tpl += '<li class="J_BookmarkNew bookmark-new" data-parentId="' + dict[key].id + '" >' + txtNewSite + '</li></ul></section>'
      } else {
        tpl += '</ul></section>'
      }

    }
    $ctr.html(tpl)
  }

  // 注册所有用户事件
  bindEvent() {
    let that = this
    let currentNewBookmarkParentId = 0
    $('.J_BookmarkNew').on('click', function(e) {
      let $target = $(e.target)
      currentNewBookmarkParentId = $target.attr('data-parentId')

      that.showAddBookmarkPop()
    })

    $('.J_BookmarkEdit').on('click', function(e) {
      let $father = $(e.target).parent()
      that.showAddBookmarkPop($father.attr('data-id'), $father.attr('data-title'), $father.attr('data-url'))
    })

    $('.J_SubmitAddBookmark').on('click', function(e) {
      let title = $('#J_AddBookmarkPop').find('input[name=title]').val()
      let url = $('#J_AddBookmarkPop').find('input[name=url]').val()
      if (!title.length) {
        $('#J_AddBookmarkPop').find('.tips').html(that.i18n.errTitle)
        return
      }
      if (!/(http|https):\/\/.+/g.test(url)) {
        $('#J_AddBookmarkPop').find('.tips').html(that.i18n.errUrl)
        return
      }

      chrome.bookmarks.create({
        parentId: currentNewBookmarkParentId,
        title: title,
        url: url
      }, function(e) {
        location.reload()
      });
    })

    $('.J_DeleteAddBookmark').on('click', function(e) {
      chrome.bookmarks.remove($('.J_DeleteAddBookmark').attr('data-id'), function(e) {
        location.reload()
      });
    })

    $('.J_UpdateAddBookmark').on('click', function(e) {
      let title = $('#J_AddBookmarkPop').find('input[name=title]').val()
      let url = $('#J_AddBookmarkPop').find('input[name=url]').val()
      if (!title.length) {
        $('#J_AddBookmarkPop').find('.tips').html(that.i18n.errTitle)
        return
      }
      if (!/(http|https):\/\/.+/g.test(url)) {
        $('#J_AddBookmarkPop').find('.tips').html(that.i18n.errUrl)
        return
      }

      chrome.bookmarks.update($('.J_UpdateAddBookmark').attr('data-id'), {
        title: title,
        url: url
      }, function(e) {
        location.reload()
      });
    })

    $('.J_CancelAddBookmark').on('click', function(e) {
      $('#J_AddBookmarkPop').removeClass('show')
    })
  }

  // 展示书签编辑框,根据参数来判断是更新还是新建或者删除
  showAddBookmarkPop(id, title, url) {
    $('#J_AddBookmarkPop').addClass('show')
    $('#J_AddBookmarkPop').find('input[type=button]').hide()

    if (id) {
      $('.J_UpdateAddBookmark').attr('data-id', id).show()
      $('.J_DeleteAddBookmark').attr('data-id', id).show()
      $('#J_AddBookmarkPop').find('input[name=title]').val(title)
      $('#J_AddBookmarkPop').find('input[name=url]').val(url)
    } else {
      $('.J_SubmitAddBookmark').show()
      $('#J_AddBookmarkPop').find('input[name=title]').val('')
      $('#J_AddBookmarkPop').find('input[name=url]').val('')
    }
  }
}

new Bookmark()

================================================
FILE: service-worker-utils.js
================================================
// This file can be imported inside the service worker,
// which means all of its functions and variables will be accessible
// inside the service worker.
// The importation is done in the file `service-worker.js`.

console.log("External file is also loaded!")


================================================
FILE: service-worker.js
================================================
// This is the service worker script, which executes in its own context
// when the extension is installed or refreshed (or when you access its console).
// It would correspond to the background script in chrome extensions v2.

console.log("This prints to the console of the service worker (background script)")

// Importing and using functionality from external files is also possible.
importScripts('service-worker-utils.js')

// If you want to import a file that is deeper in the file hierarchy of your
// extension, simply do `importScripts('path/to/file.js')`.
// The path should be relative to the file `manifest.json`.


================================================
FILE: settings/settings.css
================================================
body {
    background-color: #222;
    color: #eee;
}

.special-text {
    color: red;
}


================================================
FILE: settings/settings.html
================================================
<html>
<head>
<meta charset="utf-8">
  <title>iBookmark</title>
</head>
<style>
  section {
    width: 600px;
    margin: 50px auto;
    text-align: center;
  }
</style>

<body>
  <section>
    <img src="/img/icon128.png" alt="iBookmark">
    <h1>iBookmark 0.0.4</h1>
    <p><a href="https://github.com/0326/iBookmark">fork on GitHub:iBookmark</a></p>
  </section>
  <section>
    <h1>功能设置</h1>
    <form action="">
      <div>
        <label for="i_BookmarkNew">是否展示添加新网址链接</label>
        <input type="checkbox" name="bookmark-new" id="i_BookmarkNew" checked></input>
      </div>
    </form>
  </section>
  <script src="/lib/zepto.min.js"></script>
  <script src="settings.js"></script>
</body>

</html>

================================================
FILE: settings/settings.js
================================================

const $ = Zepto

chrome.storage.sync.get('hasNewBookmarkBtn',(obj)=>{
  let $opt = $('#i_BookmarkNew')[0]
  obj.hasNewBookmarkBtn === false ? $opt.checked = false : '默认为true'
})

function registerEvent() {
  $('#i_BookmarkNew').on('change', (e) => {
    chrome.storage.sync.set({'hasNewBookmarkBtn': e.target.checked},() => {
      // chrome.storage.sync.get('hasNewBookmarkBtn',(obj)=>{
      //   console.log(obj)
      // })
    }) 
  })
}

registerEvent()
Download .txt
gitextract_qjheeu6_/

├── LICENSE
├── README.md
├── _locales/
│   ├── en/
│   │   └── messages.json
│   ├── en_US/
│   │   └── messages.json
│   ├── ja/
│   │   └── messages.json
│   ├── ko/
│   │   └── messages.json
│   ├── zh_CN/
│   │   └── messages.json
│   └── zh_TW/
│       └── messages.json
├── foreground.js
├── manifest.json
├── popup/
│   ├── popup.css
│   ├── popup.html
│   └── popup.js
├── service-worker-utils.js
├── service-worker.js
└── settings/
    ├── settings.css
    ├── settings.html
    └── settings.js
Download .txt
SYMBOL INDEX (11 symbols across 2 files)

FILE: popup/popup.js
  function faviconURL (line 1) | function faviconURL(u) {
  class Bookmark (line 8) | class Bookmark {
    method constructor (line 9) | constructor() {
    method initI18n (line 36) | initI18n() {
    method registerSearchBar (line 58) | registerSearchBar() {
    method registerHotBookmark (line 96) | registerHotBookmark() {
    method recursiveTree (line 148) | recursiveTree(dad, dadName, self) {
    method renderBookmarks (line 171) | renderBookmarks($ctr, dict, hasNew) {
    method bindEvent (line 199) | bindEvent() {
    method showAddBookmarkPop (line 267) | showAddBookmarkPop(id, title, url) {

FILE: settings/settings.js
  function registerEvent (line 9) | function registerEvent() {
Condensed preview — 18 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (26K chars).
[
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "MIT License\n\nCopyright (c) 2021 SimGus\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
  },
  {
    "path": "README.md",
    "chars": 966,
    "preview": "<img width=\"400\" src=\"https://lh3.googleusercontent.com/_oj_6Y4K4cjpguig23UpstgNdLh6qUvWfcS3LBE73U6p6f8FRr_QuqTIEWmGzq5M"
  },
  {
    "path": "_locales/en/messages.json",
    "chars": 636,
    "preview": "{\n  \"appName\": { \"message\": \"iBookmark\" },\n  \"appDesc\": { \"message\": \"the best bookmark manager in Chrome\" },\n  \"extErrT"
  },
  {
    "path": "_locales/en_US/messages.json",
    "chars": 636,
    "preview": "{\n  \"appName\": { \"message\": \"iBookmark\" },\n  \"appDesc\": { \"message\": \"the best bookmark manager in Chrome\" },\n  \"extErrT"
  },
  {
    "path": "_locales/ja/messages.json",
    "chars": 566,
    "preview": "{\n  \"appName\": { \"message\": \"iBookmark\" },\n  \"appDesc\": { \"message\": \"Chromeで最高のブックマークマネージャー\" },\n  \"extErrTitle\": { \"mes"
  },
  {
    "path": "_locales/ko/messages.json",
    "chars": 558,
    "preview": "{\n  \"appName\": { \"message\": \"iBookmark\" },\n  \"appDesc\": { \"message\": \"Chrome에서 최고의 북마크 관리자\" },\n  \"extErrTitle\": { \"messa"
  },
  {
    "path": "_locales/zh_CN/messages.json",
    "chars": 594,
    "preview": "{\n  \"appName\": {\n    \"message\": \"iBookmark 收藏夹\"\n  },\n  \"appDesc\": {\n    \"message\": \"做最好用的Chrome收藏夹\"\n  },\n  \"extErrTitle\""
  },
  {
    "path": "_locales/zh_TW/messages.json",
    "chars": 530,
    "preview": "{\n  \"appName\": { \"message\": \"iBookmark 收藏夾\" },\n  \"appDesc\": { \"message\": \"做最好用的Chrome收藏夾\" },\n  \"extErrTitle\": { \"message"
  },
  {
    "path": "foreground.js",
    "chars": 329,
    "preview": "// This script gets injected into any opened page\n// whose URL matches the pattern defined in the manifest\n// (see \"cont"
  },
  {
    "path": "manifest.json",
    "chars": 894,
    "preview": "{\n    \"manifest_version\": 3,\n    \"name\": \"__MSG_appName__\",\n    \"description\": \"__MSG_appDesc__\",\n    \"default_locale\": "
  },
  {
    "path": "popup/popup.css",
    "chars": 3325,
    "preview": "html, body {\n\t width: 800px;\n\t height: 600px;\n\t margin: 0;\n\t padding: 0;\n\t font-family: STHeiti, '微软雅黑', Arial, sans-ser"
  },
  {
    "path": "popup/popup.html",
    "chars": 1457,
    "preview": "<html>\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"popup.css\" charset=\"utf-8\">\n</head>\n\n<body>\n  <hea"
  },
  {
    "path": "popup/popup.js",
    "chars": 8390,
    "preview": "function faviconURL(u) {\n  const url = new URL(chrome.runtime.getURL(\"/_favicon/\"));\n  url.searchParams.set(\"pageUrl\", u"
  },
  {
    "path": "service-worker-utils.js",
    "chars": 261,
    "preview": "// This file can be imported inside the service worker,\n// which means all of its functions and variables will be access"
  },
  {
    "path": "service-worker.js",
    "chars": 627,
    "preview": "// This is the service worker script, which executes in its own context\n// when the extension is installed or refreshed "
  },
  {
    "path": "settings/settings.css",
    "chars": 89,
    "preview": "body {\n    background-color: #222;\n    color: #eee;\n}\n\n.special-text {\n    color: red;\n}\n"
  },
  {
    "path": "settings/settings.html",
    "chars": 706,
    "preview": "<html>\n<head>\n<meta charset=\"utf-8\">\n  <title>iBookmark</title>\n</head>\n<style>\n  section {\n    width: 600px;\n    margin"
  },
  {
    "path": "settings/settings.js",
    "chars": 460,
    "preview": "\nconst $ = Zepto\n\nchrome.storage.sync.get('hasNewBookmarkBtn',(obj)=>{\n  let $opt = $('#i_BookmarkNew')[0]\n  obj.hasNewB"
  }
]

About this extraction

This page contains the full source code of the 0326/iBookmark GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 18 files (21.6 KB), approximately 7.1k tokens, and a symbol index with 11 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!