├── 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 ├── img ├── icon-close16.png ├── icon-search16.png ├── icon128.png ├── icon16.png ├── icon19.png ├── icon38.png └── icon48.png ├── lib └── zepto.min.js ├── manifest.json ├── popup ├── popup.css ├── popup.html └── popup.js ├── service-worker-utils.js ├── service-worker.js └── settings ├── settings.css ├── settings.html └── settings.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 SimGus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # iBookmark 4 | Chrome extension for manage bookmarks 一款简单好用的 Chrome 书签管理插件,项目工程非常简单,可供学习参考。 5 | 6 | 下载地址:https://chrome.google.com/webstore/detail/ibookmark/fnfchnalfnjbjbfeccpophocngdgapad 7 | 8 | Wiki文档: https://github.com/0326/iBookmark/wiki 9 | 10 | ## 快速上手 11 | 先clone项目启动watch: 12 | ```shell 13 | git clone git@github.com:0326/iBookmark.git 14 | cd iBookmark 15 | # install devDependencies 16 | npm i 17 | # start watch 18 | gulp 19 | ``` 20 | 然后chrome://extensions/ => 加载已解压 扩展程序 => 加载iBookmark文件夹: 21 | 22 | 注意插件ID信息,浏览器访问: 23 | chrome-extension://{{插件ID}}/popup/popup.html 24 | 25 | 编码完成后把项目打包成zip即可上传应用市场。 26 | 27 | 28 | 29 | ## 已完成功能 30 | - 所有书签分组展示 31 | - 在当前分组新增书签 32 | - 修改已有书签信息 33 | - 删除书签 34 | - 搜索书签功能 35 | - 统计书签使用频率,新增常用书签一栏 36 | - 支持 manifest.json V3 37 | 38 | ## 待完成功能 39 | - 新增书签类别 40 | - 修改书签类别名称 41 | - 删除某个书签分类 42 | - 支持书签拖拽,以及移动到其他分类 43 | - 支持历史记录 44 | - 支持插件配置功能,可配置主题,配置是否隐藏某些分类下的书签 45 | 46 | ## 协议 47 | MIT. 48 | 49 | ## 更新日志 50 | 0.0.4版本: 支持 manifest V3 协议 51 | 0.0.2版本: 新增配置功能,是否在每个分类下展示添加网址链接按钮 -------------------------------------------------------------------------------- /_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { "message": "iBookmark" }, 3 | "appDesc": { "message": "the best bookmark manager in Chrome" }, 4 | "extErrTitle": { "message": "Title Required" }, 5 | "extErrUrl": { "message": "Url Format Error
Please Start With 'http://' or 'https://'" }, 6 | "extBtnCancel": { "message": "Cancel" }, 7 | "extBtnDelete": { "message": "Delete" }, 8 | "extBtnUpdate": { "message": "Update" }, 9 | "extBtnSubmit": { "message": "Submit" }, 10 | "extTitleSearchResult": { "message": "Search Result" }, 11 | "extTxtNewSite": { "message": "Add New Site" }, 12 | "extTitleNewest": { "message": "The Newest" }, 13 | "extTitleViews": { "message": "The Most Views" } 14 | } -------------------------------------------------------------------------------- /_locales/en_US/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { "message": "iBookmark" }, 3 | "appDesc": { "message": "the best bookmark manager in Chrome" }, 4 | "extErrTitle": { "message": "Title Required" }, 5 | "extErrUrl": { "message": "Url Format Error
Please Start With 'http://' or 'https://'" }, 6 | "extBtnCancel": { "message": "Cancel" }, 7 | "extBtnDelete": { "message": "Delete" }, 8 | "extBtnUpdate": { "message": "Update" }, 9 | "extBtnSubmit": { "message": "Submit" }, 10 | "extTitleSearchResult": { "message": "Search Result" }, 11 | "extTxtNewSite": { "message": "Add New Site" }, 12 | "extTitleNewest": { "message": "The Newest" }, 13 | "extTitleViews": { "message": "The Most Views" } 14 | } -------------------------------------------------------------------------------- /_locales/ja/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { "message": "iBookmark" }, 3 | "appDesc": { "message": "Chromeで最高のブックマークマネージャー" }, 4 | "extErrTitle": { "message": "タイトルを入力してください" }, 5 | "extErrUrl": { "message": "URL形式エラー
「http://」または「https://」で始めてください" }, 6 | "extBtnCancel": { "message": "キャンセル" }, 7 | "extBtnDelete": { "message": "削除" }, 8 | "extBtnUpdate": { "message": "更新" }, 9 | "extBtnSubmit": { "message": "送信" }, 10 | "extTitleSearchResult": { "message": "検索結果" }, 11 | "extTxtNewSite": { "message": "新しいサイトを追加" }, 12 | "extTitleNewest": { "message": "最新の追加" }, 13 | "extTitleViews": { "message": "最も閲覧された" } 14 | } -------------------------------------------------------------------------------- /_locales/ko/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { "message": "iBookmark" }, 3 | "appDesc": { "message": "Chrome에서 최고의 북마크 관리자" }, 4 | "extErrTitle": { "message": "제목을 입력하세요" }, 5 | "extErrUrl": { "message": "URL 형식 오류
'http://' 또는 'https://'로 시작하세요" }, 6 | "extBtnCancel": { "message": "취소" }, 7 | "extBtnDelete": { "message": "삭제" }, 8 | "extBtnUpdate": { "message": "수정" }, 9 | "extBtnSubmit": { "message": "제출" }, 10 | "extTitleSearchResult": { "message": "검색 결과" }, 11 | "extTxtNewSite": { "message": "새 사이트 추가" }, 12 | "extTitleNewest": { "message": "최신 추가" }, 13 | "extTitleViews": { "message": "가장 많이 본" } 14 | } -------------------------------------------------------------------------------- /_locales/zh_CN/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "iBookmark 收藏夹" 4 | }, 5 | "appDesc": { 6 | "message": "做最好用的Chrome收藏夹" 7 | }, 8 | "extErrTitle": { 9 | "message":"请输入标题" 10 | }, 11 | "extErrUrl": { 12 | "message":"地址格式错误
请以http://或者https://开头" 13 | }, 14 | "extBtnCancel": { 15 | "message":"取消" 16 | }, 17 | "extBtnDelete": { 18 | "message":"删除" 19 | }, 20 | "extBtnUpdate": { 21 | "message":"修改" 22 | }, 23 | "extBtnSubmit": { 24 | "message":"提交" 25 | }, 26 | "extTitleSearchResult": { 27 | "message":"搜索结果" 28 | }, 29 | "extTxtNewSite": { 30 | "message":"添加新网址" 31 | }, 32 | "extTitleNewest": { 33 | "message":"最新添加" 34 | }, 35 | "extTitleViews": { 36 | "message":"最常访问" 37 | } 38 | } -------------------------------------------------------------------------------- /_locales/zh_TW/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { "message": "iBookmark 收藏夾" }, 3 | "appDesc": { "message": "做最好用的Chrome收藏夾" }, 4 | "extErrTitle": { "message": "請輸入標題" }, 5 | "extErrUrl": { "message": "地址格式錯誤
請以http://或https://開頭" }, 6 | "extBtnCancel": { "message": "取消" }, 7 | "extBtnDelete": { "message": "刪除" }, 8 | "extBtnUpdate": { "message": "修改" }, 9 | "extBtnSubmit": { "message": "提交" }, 10 | "extTitleSearchResult": { "message": "搜尋結果" }, 11 | "extTxtNewSite": { "message": "新增網址" }, 12 | "extTitleNewest": { "message": "最新新增" }, 13 | "extTitleViews": { "message": "最常訪問" } 14 | } -------------------------------------------------------------------------------- /foreground.js: -------------------------------------------------------------------------------- 1 | // This script gets injected into any opened page 2 | // whose URL matches the pattern defined in the manifest 3 | // (see "content_script" key). 4 | // Several foreground scripts can be declared 5 | // and injected into the same or different pages. 6 | 7 | console.log("This prints to the console of the page (injected only if the page url matched)") 8 | -------------------------------------------------------------------------------- /img/icon-close16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0326/iBookmark/f8334ce1636aabce17104f8b3cbb42e02cee4517/img/icon-close16.png -------------------------------------------------------------------------------- /img/icon-search16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0326/iBookmark/f8334ce1636aabce17104f8b3cbb42e02cee4517/img/icon-search16.png -------------------------------------------------------------------------------- /img/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0326/iBookmark/f8334ce1636aabce17104f8b3cbb42e02cee4517/img/icon128.png -------------------------------------------------------------------------------- /img/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0326/iBookmark/f8334ce1636aabce17104f8b3cbb42e02cee4517/img/icon16.png -------------------------------------------------------------------------------- /img/icon19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0326/iBookmark/f8334ce1636aabce17104f8b3cbb42e02cee4517/img/icon19.png -------------------------------------------------------------------------------- /img/icon38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0326/iBookmark/f8334ce1636aabce17104f8b3cbb42e02cee4517/img/icon38.png -------------------------------------------------------------------------------- /img/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0326/iBookmark/f8334ce1636aabce17104f8b3cbb42e02cee4517/img/icon48.png -------------------------------------------------------------------------------- /lib/zepto.min.js: -------------------------------------------------------------------------------- 1 | /* Zepto v1.0rc1 - polyfill zepto event detect fx ajax form touch - zeptojs.com/license */ 2 | (function(a){String.prototype.trim===a&&(String.prototype.trim=function(){return this.replace(/^\s+/,"").replace(/\s+$/,"")}),Array.prototype.reduce===a&&(Array.prototype.reduce=function(b){if(this===void 0||this===null)throw new TypeError;var c=Object(this),d=c.length>>>0,e=0,f;if(typeof b!="function")throw new TypeError;if(d==0&&arguments.length==1)throw new TypeError;if(arguments.length>=2)f=arguments[1];else do{if(e in c){f=c[e++];break}if(++e>=d)throw new TypeError}while(!0);while(e0?[].concat.apply([],a):a}function H(a){return a.replace(/::/g,"/").replace(/([A-Z]+)([A-Z][a-z])/g,"$1_$2").replace(/([a-z\d])([A-Z])/g,"$1_$2").replace(/_/g,"-").toLowerCase()}function I(a){return a in i?i[a]:i[a]=new RegExp("(^|\\s)"+a+"(\\s|$)")}function J(a,b){return typeof b=="number"&&!k[H(a)]?b+"px":b}function K(a){var b,c;return h[a]||(b=g.createElement(a),g.body.appendChild(b),c=j(b,"").getPropertyValue("display"),b.parentNode.removeChild(b),c=="none"&&(c="block"),h[a]=c),h[a]}function L(b,d){return d===a?c(b):c(b).filter(d)}function M(a,b,c,d){return A(b)?b.call(a,c,d):b}function N(a,b,d){var e=a%2?b:b.parentNode;e?e.insertBefore(d,a?a==1?e.firstChild:a==2?b:null:b.nextSibling):c(d).remove()}function O(a,b){b(a);for(var c in a.childNodes)O(a.childNodes[c],b)}var a,b,c,d,e=[],f=e.slice,g=window.document,h={},i={},j=g.defaultView.getComputedStyle,k={"column-count":1,columns:1,"font-weight":1,"line-height":1,opacity:1,"z-index":1,zoom:1},l=/^\s*<(\w+|!)[^>]*>/,m=[1,3,8,9,11],n=["after","prepend","before","append"],o=g.createElement("table"),p=g.createElement("tr"),q={tr:g.createElement("tbody"),tbody:o,thead:o,tfoot:o,td:p,th:p,"*":g.createElement("div")},r=/complete|loaded|interactive/,s=/^\.([\w-]+)$/,t=/^#([\w-]+)$/,u=/^[\w-]+$/,v={}.toString,w={},x,y,z=g.createElement("div");return w.matches=function(a,b){if(!a||a.nodeType!==1)return!1;var c=a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.matchesSelector;if(c)return c.call(a,b);var d,e=a.parentNode,f=!e;return f&&(e=z).appendChild(a),d=~w.qsa(e,b).indexOf(a),f&&z.removeChild(a),d},x=function(a){return a.replace(/-+(.)?/g,function(a,b){return b?b.toUpperCase():""})},y=function(a){return a.filter(function(b,c){return a.indexOf(b)==c})},w.fragment=function(b,d){d===a&&(d=l.test(b)&&RegExp.$1),d in q||(d="*");var e=q[d];return e.innerHTML=""+b,c.each(f.call(e.childNodes),function(){e.removeChild(this)})},w.Z=function(a,b){return a=a||[],a.__proto__=arguments.callee.prototype,a.selector=b||"",a},w.isZ=function(a){return a instanceof w.Z},w.init=function(b,d){if(!b)return w.Z();if(A(b))return c(g).ready(b);if(w.isZ(b))return b;var e;if(D(b))e=F(b);else if(C(b))e=[c.extend({},b)],b=null;else if(m.indexOf(b.nodeType)>=0||b===window)e=[b],b=null;else if(l.test(b))e=w.fragment(b.trim(),RegExp.$1),b=null;else{if(d!==a)return c(d).find(b);e=w.qsa(g,b)}return w.Z(e,b)},c=function(a,b){return w.init(a,b)},c.extend=function(c){return f.call(arguments,1).forEach(function(d){for(b in d)d[b]!==a&&(c[b]=d[b])}),c},w.qsa=function(a,b){var c;return a===g&&t.test(b)?(c=a.getElementById(RegExp.$1))?[c]:e:a.nodeType!==1&&a.nodeType!==9?e:f.call(s.test(b)?a.getElementsByClassName(RegExp.$1):u.test(b)?a.getElementsByTagName(b):a.querySelectorAll(b))},c.isFunction=A,c.isObject=B,c.isArray=D,c.isPlainObject=C,c.inArray=function(a,b,c){return e.indexOf.call(b,a,c)},c.trim=function(a){return a.trim()},c.uuid=0,c.map=function(a,b){var c,d=[],e,f;if(E(a))for(e=0;e0&&w.matches(this[0],a)},not:function(b){var d=[];if(A(b)&&b.call!==a)this.each(function(a){b.call(this,a)||d.push(this)});else{var e=typeof b=="string"?this.filter(b):E(b)&&A(b.item)?f.call(b):c(b);this.forEach(function(a){e.indexOf(a)<0&&d.push(a)})}return c(d)},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){var a=this[0];return a&&!B(a)?a:c(a)},last:function(){var a=this[this.length-1];return a&&!B(a)?a:c(a)},find:function(a){var b;return this.length==1?b=w.qsa(this[0],a):b=this.map(function(){return w.qsa(this,a)}),c(b)},closest:function(a,b){var d=this[0];while(d&&!w.matches(d,a))d=d!==b&&d!==g&&d.parentNode;return c(d)},parents:function(a){var b=[],d=this;while(d.length>0)d=c.map(d,function(a){if((a=a.parentNode)&&a!==g&&b.indexOf(a)<0)return b.push(a),a});return L(b,a)},parent:function(a){return L(y(this.pluck("parentNode")),a)},children:function(a){return L(this.map(function(){return f.call(this.children)}),a)},siblings:function(a){return L(this.map(function(a,b){return f.call(b.parentNode.children).filter(function(a){return a!==b})}),a)},empty:function(){return this.each(function(){this.innerHTML=""})},pluck:function(a){return this.map(function(){return this[a]})},show:function(){return this.each(function(){this.style.display=="none"&&(this.style.display=null),j(this,"").getPropertyValue("display")=="none"&&(this.style.display=K(this.nodeName))})},replaceWith:function(a){return this.before(a).remove()},wrap:function(a){return this.each(function(){c(this).wrapAll(c(a)[0].cloneNode(!1))})},wrapAll:function(a){return this[0]&&(c(this[0]).before(a=c(a)),a.append(this)),this},unwrap:function(){return this.parent().each(function(){c(this).replaceWith(c(this).children())}),this},clone:function(){return c(this.map(function(){return this.cloneNode(!0)}))},hide:function(){return this.css("display","none")},toggle:function(b){return(b===a?this.css("display")=="none":b)?this.show():this.hide()},prev:function(){return c(this.pluck("previousElementSibling"))},next:function(){return c(this.pluck("nextElementSibling"))},html:function(b){return b===a?this.length>0?this[0].innerHTML:null:this.each(function(a){var d=this.innerHTML;c(this).empty().append(M(this,b,a,d))})},text:function(b){return b===a?this.length>0?this[0].textContent:null:this.each(function(){this.textContent=b})},attr:function(c,d){var e;return typeof c=="string"&&d===a?this.length==0||this[0].nodeType!==1?a:c=="value"&&this[0].nodeName=="INPUT"?this.val():!(e=this[0].getAttribute(c))&&c in this[0]?this[0][c]:e:this.each(function(a){if(this.nodeType!==1)return;if(B(c))for(b in c)this.setAttribute(b,c[b]);else this.setAttribute(c,M(this,d,a,this.getAttribute(c)))})},removeAttr:function(a){return this.each(function(){this.nodeType===1&&this.removeAttribute(a)})},prop:function(b,c){return c===a?this[0]?this[0][b]:a:this.each(function(a){this[b]=M(this,c,a,this[b])})},data:function(b,c){var d=this.attr("data-"+H(b),c);return d!==null?d:a},val:function(b){return b===a?this.length>0?this[0].value:a:this.each(function(a){this.value=M(this,b,a,this.value)})},offset:function(){if(this.length==0)return null;var a=this[0].getBoundingClientRect();return{left:a.left+window.pageXOffset,top:a.top+window.pageYOffset,width:a.width,height:a.height}},css:function(c,d){if(d===a&&typeof c=="string")return this.length==0?a:this[0].style[x(c)]||j(this[0],"").getPropertyValue(c);var e="";for(b in c)typeof c[b]=="string"&&c[b]==""?this.each(function(){this.style.removeProperty(H(b))}):e+=H(b)+":"+J(b,c[b])+";";return typeof c=="string"&&(d==""?this.each(function(){this.style.removeProperty(H(c))}):e=H(c)+":"+J(c,d)),this.each(function(){this.style.cssText+=";"+e})},index:function(a){return a?this.indexOf(c(a)[0]):this.parent().children().indexOf(this[0])},hasClass:function(a){return this.length<1?!1:I(a).test(this[0].className)},addClass:function(a){return this.each(function(b){d=[];var e=this.className,f=M(this,a,b,e);f.split(/\s+/g).forEach(function(a){c(this).hasClass(a)||d.push(a)},this),d.length&&(this.className+=(e?" ":"")+d.join(" "))})},removeClass:function(b){return this.each(function(c){if(b===a)return this.className="";d=this.className,M(this,b,c,d).split(/\s+/g).forEach(function(a){d=d.replace(I(a)," ")}),this.className=d.trim()})},toggleClass:function(b,d){return this.each(function(e){var f=M(this,b,e,this.className);(d===a?!c(this).hasClass(f):d)?c(this).addClass(f):c(this).removeClass(f)})}},["width","height"].forEach(function(b){c.fn[b]=function(d){var e,f=b.replace(/./,function(a){return a[0].toUpperCase()});return d===a?this[0]==window?window["inner"+f]:this[0]==g?g.documentElement["offset"+f]:(e=this.offset())&&e[b]:this.each(function(a){var e=c(this);e.css(b,M(this,d,a,e[b]()))})}}),n.forEach(function(a,b){c.fn[a]=function(){var a=c.map(arguments,function(a){return B(a)?a:w.fragment(a)});if(a.length<1)return this;var d=this.length,e=d>1,f=b<2;return this.each(function(c,g){for(var h=0;h0&&this.bind(o,n),setTimeout(function(){m.css(i),e<=0&&setTimeout(function(){m.each(function(){n.call(this)})},0)},0),this},i=null}(Zepto),function($){function triggerAndReturn(a,b,c){var d=$.Event(b);return $(a).trigger(d,c),!d.defaultPrevented}function triggerGlobal(a,b,c,d){if(a.global)return triggerAndReturn(b||document,c,d)}function ajaxStart(a){a.global&&$.active++===0&&triggerGlobal(a,null,"ajaxStart")}function ajaxStop(a){a.global&&!--$.active&&triggerGlobal(a,null,"ajaxStop")}function ajaxBeforeSend(a,b){var c=b.context;if(b.beforeSend.call(c,a,b)===!1||triggerGlobal(b,c,"ajaxBeforeSend",[a,b])===!1)return!1;triggerGlobal(b,c,"ajaxSend",[a,b])}function ajaxSuccess(a,b,c){var d=c.context,e="success";c.success.call(d,a,e,b),triggerGlobal(c,d,"ajaxSuccess",[b,c,a]),ajaxComplete(e,b,c)}function ajaxError(a,b,c,d){var e=d.context;d.error.call(e,c,b,a),triggerGlobal(d,e,"ajaxError",[c,d,a]),ajaxComplete(b,c,d)}function ajaxComplete(a,b,c){var d=c.context;c.complete.call(d,b,a),triggerGlobal(c,d,"ajaxComplete",[b,c]),ajaxStop(c)}function empty(){}function mimeToDataType(a){return a&&(a==htmlType?"html":a==jsonType?"json":scriptTypeRE.test(a)?"script":xmlTypeRE.test(a)&&"xml")||"text"}function appendQuery(a,b){return(a+"&"+b).replace(/[&?]{1,2}/,"?")}function serializeData(a){isObject(a.data)&&(a.data=$.param(a.data)),a.data&&(!a.type||a.type.toUpperCase()=="GET")&&(a.url=appendQuery(a.url,a.data))}function serialize(a,b,c,d){var e=$.isArray(b);$.each(b,function(b,f){d&&(b=c?d:d+"["+(e?"":b)+"]"),!d&&e?a.add(f.name,f.value):(c?$.isArray(f):isObject(f))?serialize(a,f,c,b):a.add(b,f)})}var jsonpID=0,isObject=$.isObject,document=window.document,key,name,rscript=/)<[^<]*)*<\/script>/gi,scriptTypeRE=/^(?:text|application)\/javascript/i,xmlTypeRE=/^(?:text|application)\/xml/i,jsonType="application/json",htmlType="text/html",blankRE=/^\s*$/;$.active=0,$.ajaxJSONP=function(a){var b="jsonp"+ ++jsonpID,c=document.createElement("script"),d=function(){$(c).remove(),b in window&&(window[b]=empty),ajaxComplete("abort",e,a)},e={abort:d},f;return a.error&&(c.onerror=function(){e.abort(),a.error()}),window[b]=function(d){clearTimeout(f),$(c).remove(),delete window[b],ajaxSuccess(d,e,a)},serializeData(a),c.src=a.url.replace(/=\?/,"="+b),$("head").append(c),a.timeout>0&&(f=setTimeout(function(){e.abort(),ajaxComplete("timeout",e,a)},a.timeout)),e},$.ajaxSettings={type:"GET",beforeSend:empty,success:empty,error:empty,complete:empty,context:null,global:!0,xhr:function(){return new window.XMLHttpRequest},accepts:{script:"text/javascript, application/javascript",json:jsonType,xml:"application/xml, text/xml",html:htmlType,text:"text/plain"},crossDomain:!1,timeout:0},$.ajax=function(options){var settings=$.extend({},options||{});for(key in $.ajaxSettings)settings[key]===undefined&&(settings[key]=$.ajaxSettings[key]);ajaxStart(settings),settings.crossDomain||(settings.crossDomain=/^([\w-]+:)?\/\/([^\/]+)/.test(settings.url)&&RegExp.$2!=window.location.host);var dataType=settings.dataType,hasPlaceholder=/=\?/.test(settings.url);if(dataType=="jsonp"||hasPlaceholder)return hasPlaceholder||(settings.url=appendQuery(settings.url,"callback=?")),$.ajaxJSONP(settings);settings.url||(settings.url=window.location.toString()),serializeData(settings);var mime=settings.accepts[dataType],baseHeaders={},protocol=/^([\w-]+:)\/\//.test(settings.url)?RegExp.$1:window.location.protocol,xhr=$.ajaxSettings.xhr(),abortTimeout;settings.crossDomain||(baseHeaders["X-Requested-With"]="XMLHttpRequest"),mime&&(baseHeaders.Accept=mime,mime.indexOf(",")>-1&&(mime=mime.split(",",2)[0]),xhr.overrideMimeType&&xhr.overrideMimeType(mime));if(settings.contentType||settings.data&&settings.type.toUpperCase()!="GET")baseHeaders["Content-Type"]=settings.contentType||"application/x-www-form-urlencoded";settings.headers=$.extend(baseHeaders,settings.headers||{}),xhr.onreadystatechange=function(){if(xhr.readyState==4){clearTimeout(abortTimeout);var result,error=!1;if(xhr.status>=200&&xhr.status<300||xhr.status==304||xhr.status==0&&protocol=="file:"){dataType=dataType||mimeToDataType(xhr.getResponseHeader("content-type")),result=xhr.responseText;try{dataType=="script"?(1,eval)(result):dataType=="xml"?result=xhr.responseXML:dataType=="json"&&(result=blankRE.test(result)?null:JSON.parse(result))}catch(e){error=e}error?ajaxError(error,"parsererror",xhr,settings):ajaxSuccess(result,xhr,settings)}else ajaxError(null,"error",xhr,settings)}};var async="async"in settings?settings.async:!0;xhr.open(settings.type,settings.url,async);for(name in settings.headers)xhr.setRequestHeader(name,settings.headers[name]);return ajaxBeforeSend(xhr,settings)===!1?(xhr.abort(),!1):(settings.timeout>0&&(abortTimeout=setTimeout(function(){xhr.onreadystatechange=empty,xhr.abort(),ajaxError(null,"timeout",xhr,settings)},settings.timeout)),xhr.send(settings.data?settings.data:null),xhr)},$.get=function(a,b){return $.ajax({url:a,success:b})},$.post=function(a,b,c,d){return $.isFunction(b)&&(d=d||c,c=b,b=null),$.ajax({type:"POST",url:a,data:b,success:c,dataType:d})},$.getJSON=function(a,b){return $.ajax({url:a,success:b,dataType:"json"})},$.fn.load=function(a,b){if(!this.length)return this;var c=this,d=a.split(/\s/),e;return d.length>1&&(a=d[0],e=d[1]),$.get(a,function(a){c.html(e?$(document.createElement("div")).html(a.replace(rscript,"")).find(e).html():a),b&&b.call(c)}),this};var escape=encodeURIComponent;$.param=function(a,b){var c=[];return c.add=function(a,b){this.push(escape(a)+"="+escape(b))},serialize(c,a,b),c.join("&").replace("%20","+")}}(Zepto),function(a){a.fn.serializeArray=function(){var b=[],c;return a(Array.prototype.slice.call(this.get(0).elements)).each(function(){c=a(this);var d=c.attr("type");this.nodeName.toLowerCase()!="fieldset"&&!this.disabled&&d!="submit"&&d!="reset"&&d!="button"&&(d!="radio"&&d!="checkbox"||this.checked)&&b.push({name:c.attr("name"),value:c.val()})}),b},a.fn.serialize=function(){var a=[];return this.serializeArray().forEach(function(b){a.push(encodeURIComponent(b.name)+"="+encodeURIComponent(b.value))}),a.join("&")},a.fn.submit=function(b){if(b)this.bind("submit",b);else if(this.length){var c=a.Event("submit");this.eq(0).trigger(c),c.defaultPrevented||this.get(0).submit()}return this}}(Zepto),function(a){function d(a){return"tagName"in a?a:a.parentNode}function e(a,b,c,d){var e=Math.abs(a-b),f=Math.abs(c-d);return e>=f?a-b>0?"Left":"Right":c-d>0?"Up":"Down"}function h(){g=null,b.last&&(b.el.trigger("longTap"),b={})}function i(){g&&clearTimeout(g),g=null}var b={},c,f=750,g;a(document).ready(function(){var j,k;a(document.body).bind("touchstart",function(e){j=Date.now(),k=j-(b.last||j),b.el=a(d(e.touches[0].target)),c&&clearTimeout(c),b.x1=e.touches[0].pageX,b.y1=e.touches[0].pageY,k>0&&k<=250&&(b.isDoubleTap=!0),b.last=j,g=setTimeout(h,f)}).bind("touchmove",function(a){i(),b.x2=a.touches[0].pageX,b.y2=a.touches[0].pageY}).bind("touchend",function(a){i(),b.isDoubleTap?(b.el.trigger("doubleTap"),b={}):b.x2&&Math.abs(b.x1-b.x2)>30||b.y2&&Math.abs(b.y1-b.y2)>30?(b.el.trigger("swipe")&&b.el.trigger("swipe"+e(b.x1,b.x2,b.y1,b.y2)),b={}):"last"in b&&(b.el.trigger("tap"),c=setTimeout(function(){c=null,b.el.trigger("singleTap"),b={}},250))}).bind("touchcancel",function(){c&&clearTimeout(c),g&&clearTimeout(g),g=c=null,b={}})}),["swipe","swipeLeft","swipeRight","swipeUp","swipeDown","doubleTap","tap","singleTap","longTap"].forEach(function(b){a.fn[b]=function(a){return this.bind(b,a)}})}(Zepto); -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "__MSG_appName__", 4 | "description": "__MSG_appDesc__", 5 | "default_locale": "en", 6 | "version": "0.0.4", 7 | "icons": { 8 | "16": "img/icon16.png", 9 | "48": "img/icon48.png", 10 | "128": "img/icon128.png" 11 | }, 12 | "options_page": "settings/settings.html", 13 | "action": { 14 | "default_title": "iBookmark 收藏夹", 15 | "default_popup": "popup/popup.html" 16 | }, 17 | "permissions": [ 18 | "bookmarks", 19 | "favicon", 20 | "storage" 21 | ], 22 | "host_permissions": [ 23 | "*://*/*" 24 | ], 25 | "web_accessible_resources": [ 26 | { 27 | "resources": ["_favicon/*"], 28 | "matches": [""], 29 | "extension_ids": ["*"] 30 | } 31 | ], 32 | "background": { 33 | "service_worker": "service-worker.js" 34 | }, 35 | "content_scripts": [{ 36 | "js": ["foreground.js"], 37 | "matches": ["https://github.com/*"] 38 | }] 39 | } 40 | -------------------------------------------------------------------------------- /popup/popup.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 800px; 3 | height: 600px; 4 | margin: 0; 5 | padding: 0; 6 | font-family: STHeiti, '微软雅黑', Arial, sans-serif; 7 | } 8 | .clearfix:after { 9 | content: "."; 10 | display: block; 11 | height: 0; 12 | clear: both; 13 | visibility: hidden; 14 | } 15 | .show { 16 | display: block !important; 17 | } 18 | .icon { 19 | display: block; 20 | position: absolute; 21 | width: 16px; 22 | height: 16px; 23 | background-repeat: no-repeat; 24 | } 25 | .icon-search { 26 | left: 2px; 27 | top: 7px; 28 | background-image: url('../img/icon-search16.png'); 29 | } 30 | .icon-close { 31 | right: 2px; 32 | top: 7px; 33 | background-image: url('../img/icon-close16.png'); 34 | } 35 | .fix-header { 36 | position: fixed; 37 | top: 0; 38 | width: 100%; 39 | background: rgba(255,255,255,0.9); 40 | box-shadow: 0 1px 4px #ccc; 41 | z-index: 100; 42 | } 43 | .fix-header ul { 44 | margin: 0; 45 | padding: 0; 46 | height: 30px; 47 | line-height: 30px; 48 | } 49 | .fix-header li { 50 | float: left; 51 | width: 120px; 52 | list-style: none; 53 | text-align: center; 54 | cursor: pointer; 55 | } 56 | .fix-header li:hover { 57 | background: #f2f7f2; 58 | } 59 | .fix-header .search-bar { 60 | width: 240px; 61 | margin: 0 5px; 62 | position: relative; 63 | } 64 | .fix-header #J_SearchBookmark { 65 | width: 100%; 66 | height: 26px; 67 | padding-left: 20px; 68 | outline: none; 69 | border: none; 70 | background: transparent; 71 | border-bottom: 1px solid #0d9572; 72 | } 73 | #J_BookmarkHot, #J_BookmarkSearchList { 74 | margin-top: 40px; 75 | } 76 | .bookmark-ctr { 77 | margin: 5px; 78 | } 79 | .bookmark-ctr h2 { 80 | position: relative; 81 | padding: 0 0 5px 10px; 82 | margin-bottom: 5px; 83 | height: 20px; 84 | font-size: 16px; 85 | line-height: 20px; 86 | color: #666; 87 | border-bottom: 1px dashed #999; 88 | } 89 | .bookmark-ctr h2::after { 90 | display: block; 91 | content: ' '; 92 | position: absolute; 93 | left: 0; 94 | top: 0; 95 | width: 5px; 96 | height: 20px; 97 | background: #0d9572; 98 | } 99 | .bookmark-ctr ul { 100 | padding: 0; 101 | } 102 | .bookmark-ctr li { 103 | position: relative; 104 | float: left; 105 | width: 170px; 106 | height: 24px; 107 | padding-left: 22px; 108 | margin: 0 5px 5px 0; 109 | font-size: 14px; 110 | line-height: 24px; 111 | cursor: pointer; 112 | text-overflow: ellipsis; 113 | white-space: nowrap; 114 | overflow: hidden; 115 | } 116 | .bookmark-ctr li:hover { 117 | background-color: #f2f7f2; 118 | text-decoration: underline; 119 | } 120 | .bookmark-ctr a, .bookmark-ctr a:active, .bookmark-ctr a:visited { 121 | text-decoration: none; 122 | color: #0d9572; 123 | outline: none; 124 | } 125 | .bookmark-ctr a:hover { 126 | opacity: .5; 127 | } 128 | .bookmark-ctr i { 129 | position: absolute; 130 | display: block; 131 | left: 4px; 132 | top: 4px; 133 | height: 16px; 134 | width: 16px; 135 | background-repeat: no-repeat; 136 | background-size: contain; 137 | } 138 | .add-bookmark-pop { 139 | display: none; 140 | position: fixed; 141 | left: 0; 142 | top: 0; 143 | width: 100%; 144 | height: 100%; 145 | background: rgba(0,0,0,0.5); 146 | z-index: 1000; 147 | } 148 | .add-bookmark-pop .wrapper { 149 | margin: 100px auto; 150 | width: 240px; 151 | } 152 | .add-bookmark-pop input, .add-bookmark-pop button { 153 | display: block; 154 | width: 100%; 155 | height: 30px; 156 | margin: 10px 0; 157 | padding: 0; 158 | line-height: 30px; 159 | } 160 | .add-bookmark-pop input[type="text"] { 161 | padding: 0 2px; 162 | border: 1px solid #0d9572; 163 | outline: #0d9572; 164 | } 165 | .add-bookmark-pop input[type="button"], .add-bookmark-pop button { 166 | border: 1px solid #fff; 167 | background: #0d9572; 168 | color: #fff; 169 | cursor: pointer; 170 | } 171 | .add-bookmark-pop input[type="button"]:hover, .add-bookmark-pop button:hover { 172 | background: #f2f7f2; 173 | border-color: #0d9572; 174 | color: #0d9572; 175 | } 176 | .add-bookmark-pop .tips { 177 | margin: 10px; 178 | text-align: center; 179 | color: #FFFE00; 180 | line-height: 20px; 181 | } 182 | -------------------------------------------------------------------------------- /popup/popup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 |
    10 | 15 |
16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 |
29 | 30 |
31 | 32 |
33 | 34 |
35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 |

43 |
44 |
45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /popup/popup.js: -------------------------------------------------------------------------------- 1 | function faviconURL(u) { 2 | const url = new URL(chrome.runtime.getURL("/_favicon/")); 3 | url.searchParams.set("pageUrl", u); 4 | url.searchParams.set("size", "32"); 5 | return url.toString(); 6 | } 7 | 8 | class Bookmark { 9 | constructor() { 10 | let that = this 11 | 12 | that.bookmarkDict = {} 13 | 14 | that.initI18n() 15 | that.registerHotBookmark() 16 | that.registerSearchBar() 17 | 18 | chrome.bookmarks.getRecent(8, function(list) { 19 | let obj = {} 20 | obj[that.i18n.txtTitleNewest] = list 21 | that.renderBookmarks($('#J_BookmarkRecent'), obj) 22 | }) 23 | 24 | // 获取书签数据 25 | chrome.bookmarks.getTree(function(tree) { 26 | // 获取用户配置 27 | chrome.storage.sync.get('hasNewBookmarkBtn',(obj)=>{ 28 | let hasNew = obj.hasNewBookmarkBtn === false ? false : true 29 | that.recursiveTree(tree[0], '') 30 | that.renderBookmarks($('#J_BookmarkCtr'), that.bookmarkDict, hasNew) 31 | that.bindEvent() 32 | }) 33 | }) 34 | } 35 | 36 | initI18n() { 37 | let i18n = chrome.i18n 38 | this.i18n = { 39 | errTitle: i18n.getMessage('extErrTitle'), 40 | errUrl: i18n.getMessage('extErrUrl'), 41 | btnCancel: i18n.getMessage('extBtnCancel'), 42 | btnDelete: i18n.getMessage('extBtnDelete'), 43 | btnUpdate: i18n.getMessage('extBtnUpdate'), 44 | btnSubmit: i18n.getMessage('extBtnSubmit'), 45 | txtNewSite: i18n.getMessage('extTxtNewSite'), 46 | txtsearchResult: i18n.getMessage('extTitleSearchResult'), 47 | txtTitleNewest: i18n.getMessage('extTitleNewest'), 48 | txtTitleSearchResult: i18n.getMessage('extTitleSearchResult'), 49 | txtTitleViews: i18n.getMessage('extTitleViews') 50 | } 51 | 52 | $('.J_UpdateAddBookmark').val(this.i18n.btnUpdate) 53 | $('.J_DeleteAddBookmark').val(this.i18n.btnDelete) 54 | $('.J_SubmitAddBookmark').val(this.i18n.btnSubmit) 55 | $('.J_CancelAddBookmark').text(this.i18n.btnCancel) 56 | } 57 | 58 | registerSearchBar() { 59 | let that = this 60 | let inputPause = false 61 | let $searchBar = $('#J_SearchBookmark') 62 | let $searchResultList = $('#J_BookmarkSearchList') 63 | $searchBar.on('input', function(params) { 64 | if (inputPause) { 65 | return 66 | } 67 | 68 | inputPause = true 69 | 70 | setTimeout(function(params) { 71 | let val = $searchBar.val() 72 | if (val) { 73 | renderSearchBookmark(val) 74 | } else { 75 | $searchResultList.hide() 76 | } 77 | inputPause = false 78 | }, 1000) 79 | }) 80 | 81 | $('#J_SearchClose').on('click', function() { 82 | $searchResultList.hide() 83 | }) 84 | 85 | function renderSearchBookmark(val) { 86 | chrome.bookmarks.search(val, function(list) { 87 | let obj = {} 88 | obj[that.i18n.txtTitleSearchResult] = list 89 | that.renderBookmarks($searchResultList, obj) 90 | $searchResultList.show() 91 | }) 92 | } 93 | } 94 | 95 | // 实现最热书签排行榜 96 | registerHotBookmark() { 97 | let that = this 98 | let CStorage = chrome.storage.local 99 | $('.bookmark-ctr').on('click', 'li', function(e) { 100 | // e.preventDefault() 101 | if (e.target.href) { 102 | let bkid = e.currentTarget.dataset.id.toString() 103 | CStorage.get(bkid, function(obj) { 104 | let num = obj[bkid] 105 | let saveObj = {} 106 | 107 | saveObj[bkid] = 1 108 | if (num) { 109 | saveObj[bkid] = num + 1 110 | } 111 | CStorage.set(saveObj) 112 | }) 113 | } 114 | }) 115 | 116 | CStorage.get(null, function(obj) { 117 | let objArr = [] 118 | let idList = [] 119 | 120 | // obj=>arr 121 | for (let i in obj) { 122 | if (/^\d+$/.test(i)) { 123 | objArr.push({ 124 | id: i, 125 | num: obj[i] 126 | }) 127 | } 128 | } 129 | 130 | // 过滤出点击num最大的前8个数据 131 | objArr.sort(function(a, b) { 132 | return b.num - a.num 133 | }) 134 | .slice(0, 8) 135 | .forEach(function(item) { 136 | idList.push(item.id) 137 | }) 138 | 139 | chrome.bookmarks.get(idList, function(list) { 140 | let obj = {} 141 | obj[that.i18n.txtTitleViews] = list 142 | that.renderBookmarks($('#J_BookmarkHot'), obj) 143 | }) 144 | }) 145 | } 146 | 147 | // 遍历收藏夹,将叶子节点的父节点作为分类名归类 148 | recursiveTree(dad, dadName, self) { 149 | let that = this 150 | if (dad && dad.children) { 151 | if (dad.children.length) { 152 | dad.children.forEach(function(son) { 153 | let title = dadName 154 | if (son.children) { 155 | title = dadName ? dadName + '-' + son.title : son.title 156 | } 157 | that.recursiveTree(son, title) 158 | }) 159 | } else { 160 | that.recursiveTree(null, dadName, dad) 161 | } 162 | 163 | } else if (that.bookmarkDict[dadName]) { 164 | that.bookmarkDict[dadName].push(dad) 165 | } else if (dadName) { 166 | that.bookmarkDict[dadName] = dad ? [dad] : self 167 | } 168 | } 169 | 170 | // 渲染收藏夹列表 171 | renderBookmarks($ctr, dict, hasNew) { 172 | let tpl = '' 173 | let txtNewSite = this.i18n.txtNewSite 174 | for (let key in dict) { 175 | tpl += '

' + key + '

    ' 176 | if (dict[key].length) { 177 | dict[key].forEach(function(item) { 178 | tpl += '
  • ' + item.title + '
  • ' 179 | }) 180 | 181 | if (hasNew) { 182 | tpl += '
  • ' + txtNewSite + '
' 183 | } else { 184 | tpl += '' 185 | } 186 | 187 | } else if (hasNew) { 188 | // 该分类下无数据,那么在该分类下建立的页面的parentId就是该分类本身的id 189 | tpl += '
  • ' + txtNewSite + '
  • ' 190 | } else { 191 | tpl += '' 192 | } 193 | 194 | } 195 | $ctr.html(tpl) 196 | } 197 | 198 | // 注册所有用户事件 199 | bindEvent() { 200 | let that = this 201 | let currentNewBookmarkParentId = 0 202 | $('.J_BookmarkNew').on('click', function(e) { 203 | let $target = $(e.target) 204 | currentNewBookmarkParentId = $target.attr('data-parentId') 205 | 206 | that.showAddBookmarkPop() 207 | }) 208 | 209 | $('.J_BookmarkEdit').on('click', function(e) { 210 | let $father = $(e.target).parent() 211 | that.showAddBookmarkPop($father.attr('data-id'), $father.attr('data-title'), $father.attr('data-url')) 212 | }) 213 | 214 | $('.J_SubmitAddBookmark').on('click', function(e) { 215 | let title = $('#J_AddBookmarkPop').find('input[name=title]').val() 216 | let url = $('#J_AddBookmarkPop').find('input[name=url]').val() 217 | if (!title.length) { 218 | $('#J_AddBookmarkPop').find('.tips').html(that.i18n.errTitle) 219 | return 220 | } 221 | if (!/(http|https):\/\/.+/g.test(url)) { 222 | $('#J_AddBookmarkPop').find('.tips').html(that.i18n.errUrl) 223 | return 224 | } 225 | 226 | chrome.bookmarks.create({ 227 | parentId: currentNewBookmarkParentId, 228 | title: title, 229 | url: url 230 | }, function(e) { 231 | location.reload() 232 | }); 233 | }) 234 | 235 | $('.J_DeleteAddBookmark').on('click', function(e) { 236 | chrome.bookmarks.remove($('.J_DeleteAddBookmark').attr('data-id'), function(e) { 237 | location.reload() 238 | }); 239 | }) 240 | 241 | $('.J_UpdateAddBookmark').on('click', function(e) { 242 | let title = $('#J_AddBookmarkPop').find('input[name=title]').val() 243 | let url = $('#J_AddBookmarkPop').find('input[name=url]').val() 244 | if (!title.length) { 245 | $('#J_AddBookmarkPop').find('.tips').html(that.i18n.errTitle) 246 | return 247 | } 248 | if (!/(http|https):\/\/.+/g.test(url)) { 249 | $('#J_AddBookmarkPop').find('.tips').html(that.i18n.errUrl) 250 | return 251 | } 252 | 253 | chrome.bookmarks.update($('.J_UpdateAddBookmark').attr('data-id'), { 254 | title: title, 255 | url: url 256 | }, function(e) { 257 | location.reload() 258 | }); 259 | }) 260 | 261 | $('.J_CancelAddBookmark').on('click', function(e) { 262 | $('#J_AddBookmarkPop').removeClass('show') 263 | }) 264 | } 265 | 266 | // 展示书签编辑框,根据参数来判断是更新还是新建或者删除 267 | showAddBookmarkPop(id, title, url) { 268 | $('#J_AddBookmarkPop').addClass('show') 269 | $('#J_AddBookmarkPop').find('input[type=button]').hide() 270 | 271 | if (id) { 272 | $('.J_UpdateAddBookmark').attr('data-id', id).show() 273 | $('.J_DeleteAddBookmark').attr('data-id', id).show() 274 | $('#J_AddBookmarkPop').find('input[name=title]').val(title) 275 | $('#J_AddBookmarkPop').find('input[name=url]').val(url) 276 | } else { 277 | $('.J_SubmitAddBookmark').show() 278 | $('#J_AddBookmarkPop').find('input[name=title]').val('') 279 | $('#J_AddBookmarkPop').find('input[name=url]').val('') 280 | } 281 | } 282 | } 283 | 284 | new Bookmark() -------------------------------------------------------------------------------- /service-worker-utils.js: -------------------------------------------------------------------------------- 1 | // This file can be imported inside the service worker, 2 | // which means all of its functions and variables will be accessible 3 | // inside the service worker. 4 | // The importation is done in the file `service-worker.js`. 5 | 6 | console.log("External file is also loaded!") 7 | -------------------------------------------------------------------------------- /service-worker.js: -------------------------------------------------------------------------------- 1 | // This is the service worker script, which executes in its own context 2 | // when the extension is installed or refreshed (or when you access its console). 3 | // It would correspond to the background script in chrome extensions v2. 4 | 5 | console.log("This prints to the console of the service worker (background script)") 6 | 7 | // Importing and using functionality from external files is also possible. 8 | importScripts('service-worker-utils.js') 9 | 10 | // If you want to import a file that is deeper in the file hierarchy of your 11 | // extension, simply do `importScripts('path/to/file.js')`. 12 | // The path should be relative to the file `manifest.json`. 13 | -------------------------------------------------------------------------------- /settings/settings.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #222; 3 | color: #eee; 4 | } 5 | 6 | .special-text { 7 | color: red; 8 | } 9 | -------------------------------------------------------------------------------- /settings/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | iBookmark 5 | 6 | 13 | 14 | 15 |
    16 | iBookmark 17 |

    iBookmark 0.0.4

    18 |

    fork on GitHub:iBookmark

    19 |
    20 |
    21 |

    功能设置

    22 |
    23 |
    24 | 25 | 26 |
    27 |
    28 |
    29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /settings/settings.js: -------------------------------------------------------------------------------- 1 | 2 | const $ = Zepto 3 | 4 | chrome.storage.sync.get('hasNewBookmarkBtn',(obj)=>{ 5 | let $opt = $('#i_BookmarkNew')[0] 6 | obj.hasNewBookmarkBtn === false ? $opt.checked = false : '默认为true' 7 | }) 8 | 9 | function registerEvent() { 10 | $('#i_BookmarkNew').on('change', (e) => { 11 | chrome.storage.sync.set({'hasNewBookmarkBtn': e.target.checked},() => { 12 | // chrome.storage.sync.get('hasNewBookmarkBtn',(obj)=>{ 13 | // console.log(obj) 14 | // }) 15 | }) 16 | }) 17 | } 18 | 19 | registerEvent() --------------------------------------------------------------------------------