高天昊 4 éve
szülő
commit
4288b6bf57
14 módosított fájl, 967 hozzáadás és 67 törlés
  1. 34 33
      README.en.md
  2. 34 34
      README.md
  3. 231 0
      css/popup.css
  4. BIN
      icon/icon.png
  5. BIN
      icon/icon128.png
  6. BIN
      icon/icon16.png
  7. BIN
      icon/icon48.png
  8. 113 0
      js/base64.js
  9. 1 0
      js/jquery-2.1.0.min.js
  10. 431 0
      js/popup.js
  11. 32 0
      manifest.json
  12. 1 0
      pic/background.svg
  13. 10 0
      pic/right.svg
  14. 80 0
      popup.html

+ 34 - 33
README.en.md

@@ -1,36 +1,37 @@
-# chrome_bookmark_extension
+# bookmark_sync(书签同步到码云[Gitee](https://gitee.com/))
+此插件为Chrome浏览器的插件,支持将Chrome浏览器的书签数据同步到码云以及从码云上下载已经保存的书签数据,使得不方便访问谷歌的人能够有另外一种同步浏览器书签的选择。
+
+#### 1:界面
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/103915_9cf93b07_4859264.png "屏幕截图.png")
+
+#### 2.填写说明
+* access_token:码云的用户授权码(获取方式见[3:access_token获取说明](#3access_token获取说明))
+* owner:仓库所属空间地址(设置方法如下,只能设置一次)
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/111658_15a42660_4859264.png "屏幕截图.png")
+* repo:仓库路径(新建仓库方法如下)
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/111855_c71b934f_4859264.png "屏幕截图.png")
+* path:文件的路径(获取如下)
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/111950_1f8806b3_4859264.png "屏幕截图.png")
+* branch:分支(通常是master)
+
+
+#### 3:access_token获取说明
+###### 3.1:点击头像下拉框的“设置”按钮
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/104632_64161a1c_4859264.png "屏幕截图.png")
+###### 3.2:点击左边菜单的“私人令牌”
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/104732_a3377c1d_4859264.png "屏幕截图.png")
+###### 3.3:点击界面右上角的“生成新令牌”按钮
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/104805_3632f013_4859264.png "屏幕截图.png")
+###### 3.4:输入私人令牌描述,勾选全部的权限,点击提交按钮
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105041_f3c38794_4859264.png "屏幕截图.png")
+###### 3.5:输入账号密码,然后点击验证按钮
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105234_e9154306_4859264.png "屏幕截图.png")
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105255_4e4cfd6d_4859264.png "屏幕截图.png")
+###### 3.6:点击右边的复制按钮(只会出现一次,下次刷新页面就不会出现了,所以一定要及时保存和记住)
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105523_b31fd892_4859264.png "屏幕截图.png")
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105654_d57107d6_4859264.png "屏幕截图.png")
+
+欢迎交流:新浪微博:@x扬f
 
-#### Description
-书签同步码云 chrome 拓展
 
-#### Software Architecture
-Software architecture description
 
-#### Installation
-
-1.  xxxx
-2.  xxxx
-3.  xxxx
-
-#### Instructions
-
-1.  xxxx
-2.  xxxx
-3.  xxxx
-
-#### Contribution
-
-1.  Fork the repository
-2.  Create Feat_xxx branch
-3.  Commit your code
-4.  Create Pull Request
-
-
-#### Gitee Feature
-
-1.  You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
-2.  Gitee blog [blog.gitee.com](https://blog.gitee.com)
-3.  Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
-4.  The most valuable open source project [GVP](https://gitee.com/gvp)
-5.  The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
-6.  The most popular members  [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 34 - 34
README.md

@@ -1,37 +1,37 @@
-# chrome_bookmark_extension
+# bookmark_sync(书签同步到码云[Gitee](https://gitee.com/))
+此插件为Chrome浏览器的插件,支持将Chrome浏览器的书签数据同步到码云以及从码云上下载已经保存的书签数据,使得不方便访问谷歌的人能够有另外一种同步浏览器书签的选择。
+
+#### 1:界面
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/103915_9cf93b07_4859264.png "屏幕截图.png")
+
+#### 2.填写说明
+* access_token:码云的用户授权码(获取方式见[3:access_token获取说明](#3access_token获取说明))
+* owner:仓库所属空间地址(设置方法如下,只能设置一次)
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/111658_15a42660_4859264.png "屏幕截图.png")
+* repo:仓库路径(新建仓库方法如下)
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/111855_c71b934f_4859264.png "屏幕截图.png")
+* path:文件的路径(获取如下)
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/111950_1f8806b3_4859264.png "屏幕截图.png")
+* branch:分支(通常是master)
+
+
+#### 3:access_token获取说明
+###### 3.1:点击头像下拉框的“设置”按钮
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/104632_64161a1c_4859264.png "屏幕截图.png")
+###### 3.2:点击左边菜单的“私人令牌”
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/104732_a3377c1d_4859264.png "屏幕截图.png")
+###### 3.3:点击界面右上角的“生成新令牌”按钮
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/104805_3632f013_4859264.png "屏幕截图.png")
+###### 3.4:输入私人令牌描述,勾选全部的权限,点击提交按钮
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105041_f3c38794_4859264.png "屏幕截图.png")
+###### 3.5:输入账号密码,然后点击验证按钮
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105234_e9154306_4859264.png "屏幕截图.png")
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105255_4e4cfd6d_4859264.png "屏幕截图.png")
+###### 3.6:点击右边的复制按钮(只会出现一次,下次刷新页面就不会出现了,所以一定要及时保存和记住)
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105523_b31fd892_4859264.png "屏幕截图.png")
+![输入图片说明](https://images.gitee.com/uploads/images/2019/0808/105654_d57107d6_4859264.png "屏幕截图.png")
+
+欢迎交流:新浪微博:@x扬f
 
-#### 介绍
-书签同步码云 chrome 拓展
 
-#### 软件架构
-软件架构说明
 
-
-#### 安装教程
-
-1.  xxxx
-2.  xxxx
-3.  xxxx
-
-#### 使用说明
-
-1.  xxxx
-2.  xxxx
-3.  xxxx
-
-#### 参与贡献
-
-1.  Fork 本仓库
-2.  新建 Feat_xxx 分支
-3.  提交代码
-4.  新建 Pull Request
-
-
-#### 特技
-
-1.  使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2.  Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3.  你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4.  [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5.  Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6.  Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)

+ 231 - 0
css/popup.css

@@ -0,0 +1,231 @@
+* {
+    margin: 0;
+    padding: 0;
+}
+
+body {
+    font-family: "Segoe UI", "Lucida Grande", Tahoma, sans-serif;
+    font-size: 14px;
+    color: #333;
+}
+
+/** 内容面板样式 */
+#content {
+    width: 320px;
+    padding: 0 15px 15px 15px;
+    min-height: 300px;
+    background: url(../pic/background.svg) center center no-repeat #fff;
+    background-size: contain;
+}
+
+.container {
+    display: -webkit-flex;
+    display: flex;
+    width: 100%;
+    flex-direction: row;
+    flex-wrap: wrap;
+}
+
+.item {
+    align-content: stretch;
+    width: 100%;
+    margin-top: 10px;
+    line-height: 30px;
+}
+
+.item .label {
+    height: 30px;
+}
+
+.item .input {
+    border: solid 1px #ccc;
+    float: right;
+    width: 200px;
+    height: 30px;
+    padding: 0 5px;
+}
+
+.item .input.error {
+    border: solid 1px #c00;
+}
+
+/** 提示框样式 */
+.toast {
+    display: none;
+    word-break: break-all;
+    word-wrap: break-word;
+    text-indent: 110px;
+    color: red;
+}
+
+/** 记住按钮样式 */
+.rememberWrap {
+    padding-left: 80px;
+}
+
+.rememberWrap .text {
+    opacity: 1;
+    transition: all .4s ease-in-out;
+}
+
+.rememberWrap .text.grey {
+    opacity: .5;
+}
+
+.rememberWrap .dot {
+    position: relative;
+    display: inline-block;
+    margin: 0 0 0 10px;
+    width: 25px;
+    height: 16px;
+    vertical-align: middle;
+    cursor: pointer;
+}
+
+.rememberWrap .dot::before {
+    content: "";
+    position: absolute;
+    display: inline-block;
+    top: 50%;
+    width: 25px;
+    height: 2px;
+    border-radius: 1px;
+    box-shadow: 0 0 1px #000;
+}
+
+.rememberWrap .dot::after {
+    content: "";
+    position: absolute;
+    display: inline-block;
+    top: 50%;
+    left: -5px;
+    width: 10px;
+    height: 10px;
+    border-radius: 50%;
+    box-shadow: 0 0 1px #000;
+    background: #fff;
+    transform: translate(0, -50%);
+    transition: all .4s ease-in-out;
+}
+
+.rememberWrap .dot.green::after {
+    width: 16px;
+    height: 16px;
+    border-radius: 50%;
+    left: 12px;
+    background: #009933;
+    box-shadow: none;
+}
+
+/** 按钮样式 */
+.buttonWrap {
+    padding-left: 80px;
+}
+
+.button {
+    float: left;
+    width: 80px;
+    height: 30px;
+    text-align: center;
+    margin-right: 30px;
+}
+
+.buttonGreen {
+    border: solid 1px #009933;
+    background: #009933;
+    color: #fff;
+}
+
+.buttonGreen:hover {
+    border: solid 1px #006633;
+    background: #006633;
+}
+
+.buttonGray {
+    border: solid 1px #ccc;
+    background: #fefefe;
+    color: #999;
+}
+
+.buttonGray:hover {
+    border: solid 1px #ccc;
+    background: #eee;
+}
+
+/** 进度条样式 */
+#loaderWrap {
+    display: none;
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 9999;
+}
+
+.loader, .loader:after {
+    border-radius: 50%;
+    width: 10em;
+    height: 10em;
+}
+
+.loader {
+    margin: 100px auto;
+    font-size: 10px;
+    position: relative;
+    text-indent: -9999em;
+    border-top: 1.1em solid rgba(255, 255, 255, 0.1);
+    border-right: 1.1em solid rgba(255, 255, 255, 0.1);
+    border-bottom: 1.1em solid rgba(255, 255, 255, 0.1);
+    border-left: 1.1em solid green;
+    -webkit-transform: translateZ(0);
+    -ms-transform: translateZ(0);
+    transform: translateZ(0);
+    -webkit-animation: load 1.1s infinite linear;
+    animation: load 1.1s infinite linear;
+}
+
+@-webkit-keyframes load {
+    0% {
+        -webkit-transform: rotate(0deg);
+        transform: rotate(0deg);
+    }
+    100% {
+        -webkit-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}
+
+@keyframes load {
+    0% {
+        -webkit-transform: rotate(0deg);
+        transform: rotate(0deg);
+    }
+    100% {
+        -webkit-transform: rotate(360deg);
+        transform: rotate(360deg);
+    }
+}
+
+/** 成功样式 */
+#successWrap {
+    display: none;
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 10000;
+    background: #fff;
+}
+
+.successIcon {
+    width: 100%;
+    height: 80%;
+    background: url(../pic/right.svg) center center/100% 100% no-repeat #fff;
+}
+
+.successText {
+    width: 100%;
+    text-align: center;
+}

BIN
icon/icon.png


BIN
icon/icon128.png


BIN
icon/icon16.png


BIN
icon/icon48.png


+ 113 - 0
js/base64.js

@@ -0,0 +1,113 @@
+/** 
+ * 
+ *  Base64 encode / decode 
+ * 
+ *  @author haitao.tu 
+ *  @date   2010-04-26 
+ *  @email  [email protected] 
+ * 
+ */
+
+function Base64() {
+
+    // private property  
+    _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+    // public method for encoding  
+    this.encode = function(input) {
+        var output = "";
+        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+        var i = 0;
+        input = _utf8_encode(input);
+        while (i < input.length) {
+            chr1 = input.charCodeAt(i++);
+            chr2 = input.charCodeAt(i++);
+            chr3 = input.charCodeAt(i++);
+            enc1 = chr1 >> 2;
+            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
+            enc4 = chr3 & 63;
+            if (isNaN(chr2)) {
+                enc3 = enc4 = 64;
+            } else if (isNaN(chr3)) {
+                enc4 = 64;
+            }
+            output = output +
+                _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
+                _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
+        }
+        return output;
+    }
+
+    // public method for decoding  
+    this.decode = function(input) {
+        var output = "";
+        var chr1, chr2, chr3;
+        var enc1, enc2, enc3, enc4;
+        var i = 0;
+        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+        while (i < input.length) {
+            enc1 = _keyStr.indexOf(input.charAt(i++));
+            enc2 = _keyStr.indexOf(input.charAt(i++));
+            enc3 = _keyStr.indexOf(input.charAt(i++));
+            enc4 = _keyStr.indexOf(input.charAt(i++));
+            chr1 = (enc1 << 2) | (enc2 >> 4);
+            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+            chr3 = ((enc3 & 3) << 6) | enc4;
+            output = output + String.fromCharCode(chr1);
+            if (enc3 != 64) {
+                output = output + String.fromCharCode(chr2);
+            }
+            if (enc4 != 64) {
+                output = output + String.fromCharCode(chr3);
+            }
+        }
+        output = _utf8_decode(output);
+        return output;
+    }
+
+    // private method for UTF-8 encoding  
+    _utf8_encode = function(string) {
+        string = string.replace(/\r\n/g, "\n");
+        var utftext = "";
+        for (var n = 0; n < string.length; n++) {
+            var c = string.charCodeAt(n);
+            if (c < 128) {
+                utftext += String.fromCharCode(c);
+            } else if ((c > 127) && (c < 2048)) {
+                utftext += String.fromCharCode((c >> 6) | 192);
+                utftext += String.fromCharCode((c & 63) | 128);
+            } else {
+                utftext += String.fromCharCode((c >> 12) | 224);
+                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
+                utftext += String.fromCharCode((c & 63) | 128);
+            }
+
+        }
+        return utftext;
+    }
+
+    // private method for UTF-8 decoding  
+    _utf8_decode = function(utftext) {
+        var string = "";
+        var i = 0;
+        var c = c1 = c2 = 0;
+        while (i < utftext.length) {
+            c = utftext.charCodeAt(i);
+            if (c < 128) {
+                string += String.fromCharCode(c);
+                i++;
+            } else if ((c > 191) && (c < 224)) {
+                c2 = utftext.charCodeAt(i + 1);
+                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
+                i += 2;
+            } else {
+                c2 = utftext.charCodeAt(i + 1);
+                c3 = utftext.charCodeAt(i + 2);
+                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
+                i += 3;
+            }
+        }
+        return string;
+    }
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
js/jquery-2.1.0.min.js


+ 431 - 0
js/popup.js

@@ -0,0 +1,431 @@
+$(function () {
+    sync = {
+        /**
+         * 初始化函数
+         */
+        init: function () {
+            let that = this;
+            that.base64 = new Base64();
+            // 码云的API根地址
+            that.baseUrl = "https://gitee.com/api/v5/";
+            that.getCacheUserInfo();
+            that.bindEvent();
+        },
+        /**
+         * 表单收集
+         */
+        createFormObj: function () {
+            let formObj = {};
+            formObj.access_token = $("#tbAccessToken").val();
+            formObj.owner = $("#tbOwner").val();
+            formObj.repo = $("#tbRepo").val();
+            formObj.path = $("#tbPath").val();
+            formObj.branch = $("#tbBranch").val();
+            return formObj;
+        },
+        /**
+         * 弹出窗开始时从缓存读取已经存储的数据
+         */
+        getCacheUserInfo: function () {
+            let that = this;
+            // 从缓存中读取数据并设置到表单中
+            chrome.storage.local.get(["access_token", "owner", "repo", "path", "branch", "remember"], function (obj) {
+                // 插件最开始的状态是记住开关打开了,但是chrome本地缓存中还没有设置值
+                if (!obj.remember) {
+                    if ($("#rememberDot").hasClass("green")) {
+                        that.saveRememberState("on");
+                    } else {
+                        that.saveRememberState("off");
+                    }
+                } else if (obj.remember == "on") {
+                    $("#rememberDot").addClass("green");
+                } else {
+                    $("#rememberDot").removeClass("green");
+                }
+                if ($("#rememberDot").hasClass("green")) {
+                    if (obj.access_token || obj.owner || obj.repo || obj.path || obj.branch) {
+                        $("#tbAccessToken").val(obj.access_token);
+                        $("#tbOwner").val(obj.owner);
+                        $("#tbRepo").val(obj.repo);
+                        $("#tbPath").val(obj.path);
+                        $("#tbBranch").val(obj.branch);
+                        that.rememberOn();
+                    }
+                }
+            });
+        },
+        rememberOn: function () {
+            let that = this;
+            $("#rememberText").addClass("grey");
+            $("#rememberDot").addClass("green");
+            that.saveRememberState("on");
+        },
+        rememberOff: function () {
+            let that = this;
+            $("#rememberText").removeClass("grey");
+            $("#rememberDot").removeClass("green");
+            that.saveRememberState("off");
+        },
+        /**
+         * 清除用户信息
+         */
+        clearUserInfo: function () {
+            chrome.storage.local.clear(function () {
+                console.log('clear user info success');
+            });
+        },
+        /**
+         * 保存用户信息
+         */
+        saveUserInfo: function () {
+            let that = this;
+            let formObj = that.createFormObj();
+            chrome.storage.local.set(formObj, function () {
+                console.log('save user info success');
+            });
+        },
+        /**
+         * 保存记住按钮的状态
+         * state:on(记住),off(不记住)
+         */
+        saveRememberState: function (state) {
+            let rememberObj = {};
+            rememberObj.remember = state;
+            chrome.storage.local.set(rememberObj, function () {
+                console.log('save remember state success');
+            });
+        },
+        /**
+         * 获取浏览器的书签数据
+         */
+        getBookmarks: function (fn) {
+            // 获取整个书签树(包括书签栏和其他书签,相当于chrome.bookmarks.getSubTree("0", fn))
+            // chrome.bookmarks.getTree(fn);
+            // 0-根目录 1-书签栏 2-其他书签
+            chrome.bookmarks.getSubTree("1", fn);
+        },
+        /**
+         * 清空书签栏文件夹(不能直接清除根书签栏,只能遍历一个一个文件夹清除)
+         */
+        emptyBookmarks: function (fn) {
+            chrome.bookmarks.getChildren("1", function (children) {
+                // 需要判断书签栏是否原来就是空的
+                if (children.length <= 0) {
+                    fn();
+                    return;
+                }
+                for (let i = 0; i < children.length; i++) {
+                    let item = children[i];
+                    chrome.bookmarks.removeTree(item.id, function () {
+                        // 判断是不是已经删除到最后一个了,是的话就调用回调函数
+                        if (i == (children.length - 1)) {
+                            fn();
+                        }
+                    });
+                }
+            });
+        },
+        bindEvent: function () {
+            let that = this;
+            // 记住按钮点击事件
+            $("#rememberDot").on("click", function () {
+                // 先判断当前记住按钮的状态,假如已经记住则不再记住,清空缓存
+                if ($("#rememberDot").hasClass("green")) {
+                    that.clearUserInfo();
+                    that.rememberOff();
+                } else {
+                    // 获取表单数据,有则保存到缓存中
+                    let formObj = that.createFormObj();
+                    let existRes = that.checkFormExist(formObj);
+                    if (existRes) {
+                        that.saveUserInfo();
+                    }
+                    that.rememberOn();
+                }
+            });
+            // 输入框的输入事件,用来记忆表单数据
+            $(".input").on("input", function () {
+                if ($("#rememberDot").hasClass("green")) {
+                    that.saveUserInfo();
+                    that.rememberOn();
+                }
+            });
+            // 点击上传按钮
+            $("#btnUpload").on("click", function () {
+                let formObj = that.createFormObj();
+                let formRes = that.checkForm(formObj);
+                if (formRes != "ok") {
+                    $("#toast").text(formRes).show();
+                    return;
+                }
+                // 假如记住按钮已经点击了
+                if ($("#rememberDot").hasClass("green")) {
+                    that.saveUserInfo();
+                    that.rememberOn();
+                } else {
+                    that.clearUserInfo();
+                    that.rememberOff();
+                }
+                $("#loaderWrap").show();
+                // 从码云上获取书签内容
+                that.getGit(formObj, function (getState, getRes) {
+                    // 获取浏览器的书签
+                    that.getBookmarks(function (bookmarks) {
+                        // 获取码云的内容存在则更新文件,否则更新文件
+                        if (getState) {
+                            let sha = getRes.sha;
+                            that.updateGit(formObj, bookmarks, sha, function (updState, updRes) {
+                                // 更新成功,则提示,否则提示错误
+                                if (updState) {
+                                    $("#successWrap").show();
+                                    $(".successText").text("Upload Success");
+                                } else {
+                                    $("#toast").text(updRes).show();
+                                }
+                                $("#loaderWrap").hide();
+                            });
+                        } else {
+                            that.createGit(formObj, bookmarks, function (createState, createRes) {
+                                // 创建成功,则提示,否则提示错误
+                                if (createState) {
+                                    $("#successWrap").show();
+                                    $(".successText").text("Upload Success");
+                                } else {
+                                    $("#toast").text(createRes).show();
+                                }
+                                $("#loaderWrap").hide();
+                            });
+                        }
+                    });
+                });
+            });
+            // 点击下载按钮事件
+            $("#btnDownload").on("click", function () {
+                let formObj = that.createFormObj();
+                let formRes = that.checkForm(formObj);
+                if (formRes != "ok") {
+                    $("#toast").text(formRes).show();
+                    return;
+                }
+                // 假如记住按钮已经点击了
+                if ($("#rememberDot").hasClass("green")) {
+                    that.saveUserInfo();
+                    that.rememberOn();
+                } else {
+                    that.clearUserInfo();
+                    that.rememberOff();
+                }
+                $("#loaderWrap").show();
+                // 从码云上获取书签内容
+                that.getGit(formObj, function (getState, getRes) {
+                    // 获取码云的内容存在则创建文件,否则提示文件不存在
+                    if (getState) {
+                        let gitContent = that.base64.decode(getRes["content"]);
+                        let bookmarksParent = JSON.parse(gitContent);
+                        let bookmarks = bookmarksParent[0];
+                        // 清空书签栏
+                        that.emptyBookmarks(function () {
+                            // 等所有书签都清空后开始设置书签
+                            that.setBookmarks("1", bookmarks);
+                            $("#successWrap").show();
+                            $(".successText").text("Download Success");
+                        });
+                    } else {
+                        $("#toast").text("Git文件不存在").show();
+                    }
+                    $("#loaderWrap").hide();
+                });
+            });
+        },
+        /**
+         * 将git获取的内容设置到浏览器书签上去
+         */
+        setBookmarks: function (parentId, bookmarks) {
+            let that = this;
+            if (!bookmarks.children || bookmarks.children.length <= 0) {
+                return;
+            }
+            for (let i = 0; i < bookmarks.children.length; i++) {
+                let item = bookmarks.children[i];
+                chrome.bookmarks.create({
+                    parentId: parentId,
+                    index: item.index,
+                    title: item.title,
+                    url: item.url
+                }, function (res) {
+                    // 判断假如还有子元素则递归调用自身添加
+                    if (item.children && item.children.length > 0) {
+                        that.setBookmarks(res.id, item);
+                    }
+                });
+            }
+        },
+        /**
+         * 校验表单
+         */
+        checkForm: function (formObj) {
+            if (!formObj.access_token) {
+                $("#tbAccessToken").addClass('error');
+                return 'access_token is required';
+            }
+            $("#tbAccessToken").removeClass('error');
+
+            if (!formObj.owner) {
+                $("#tbOwner").addClass('error');
+                return 'owner is required';
+            }
+            $("#tbOwner").removeClass('error');
+
+            if (!formObj.repo) {
+                $("#tbRepo").addClass('error');
+                return 'repo is required';
+            }
+            $("#tbRepo").removeClass('error');
+
+            if (!formObj.path) {
+                $("#tbPath").addClass('error');
+                return 'path is required';
+            }
+            $("#tbPath").removeClass('error');
+
+            if (!formObj.branch) {
+                $("#tbBranch").addClass('error');
+                return 'branch is required';
+            }
+            $("#tbBranch").removeClass('error');
+            return "ok";
+        },
+        /**
+         * 查看表单是否至少有一个值
+         */
+        checkFormExist: function (formObj) {
+            if (formObj.access_token != '') {
+                return true;
+            }
+            if (formObj.owner != '') {
+                return true;
+            }
+            if (formObj.repo != '') {
+                return true;
+            }
+            if (formObj.path != '') {
+                return true;
+            }
+            if (formObj.branch != '') {
+                return true;
+            }
+            return false;
+        },
+        /**
+         * 获取Gitee上的数据
+         * curl -X GET --header 'Content-Type: application/json;charset=UTF-8' 'https://gitee.com/api/v5/repos/xieyf00/chrome/contents/bookmark/bookmark-4.json?access_token=16c6176faea12d4b2dba667872d9b21c&ref=master'
+         */
+        getGit: function (formObj, fn) {
+            let that = this;
+            let getUrl = that.baseUrl + "repos/" + formObj.owner + "/" + formObj.repo + "/contents/" + formObj.path + "?access_token=" + formObj.access_token + "&ref=" + formObj.branch;
+            $.ajax({
+                type: "GET",
+                url: getUrl,
+                crossDomain: true,
+                success: function (res) {
+                    fn && fn(true, res);
+                },
+                error: function (xhr, textStatus, error) {
+                    fn && fn(false, error);
+                }
+            });
+        },
+        /**
+         * 创建Git文件
+         * curl -X POST --header 'Content-Type: application/json;charset=UTF-8' 'https://gitee.com/api/v5/repos/xieyf00/chrome/contents/bookmark/bookmark-copy.json' -d '{"access_token":"16c6176faea12d4b2dba667872d9b21c","content":"xxx","message":"xieyangfan commit","branch":"master"}'
+         * createContentRaw:文件内容, 要用 base64 编码
+         */
+        createGit: function (formObj, createContentRaw, fn) {
+            let that = this;
+            let createContent = that.base64.encode(JSON.stringify(createContentRaw));
+            // Git提交信息
+            let createMessage = "Chrome Browser Bookmark Created" + new Date();
+            // 构建
+            let createData = {};
+            createData.access_token = formObj.access_token;
+            createData.content = createContent;
+            createData.message = createMessage;
+            createData.branch = formObj.branch;
+            let createUrl = that.baseUrl + "repos/" + formObj.owner + "/" + formObj.repo + "/contents/" + formObj.path;
+            $.ajax({
+                type: "POST",
+                url: createUrl,
+                data: createData,
+                crossDomain: true,
+                success: function (res) {
+                    fn && fn(true, res);
+                },
+                error: function (xhr, textStatus, error) {
+                    fn && fn(false, error);
+                }
+            });
+        },
+        /**
+         * 更新Git文件
+         * curl -X PUT --header 'Content-Type: application/json;charset=UTF-8' 'https://gitee.com/api/v5/repos/xieyf00/chrome/contents/bookmark/bookmark-6.json' -d '{"access_token":"16c6176faea12d4b2dba667872d9b21c","content":"InhpZXlhbmdmYW4gMjAxOTA3MjQgMTU0OCI=","sha":"dce85293664c50792d2ebcfc4ede23bf3e1197c2","message":"xieyangfan 20190724 1736","branch":"master"}'
+         * updateContentRaw:文件内容, 要用 base64 编码
+         * sha:文件的 Blob SHA,可通过 [获取仓库具体路径下的内容] API 获取
+         */
+        updateGit: function (formObj, updateContentRaw, sha, fn) {
+            let that = this;
+            let updateContent = that.base64.encode(JSON.stringify(updateContentRaw));
+            // Git提交信息
+            let updateMessage = "Chrome Browser Bookmark Updated" + new Date();
+            // 构建
+            let updateData = {};
+            updateData.access_token = formObj.access_token;
+            updateData.content = updateContent;
+            updateData.message = updateMessage;
+            updateData.sha = sha;
+            updateData.branch = formObj.branch;
+            let updateUrl = that.baseUrl + "repos/" + formObj.owner + "/" + formObj.repo + "/contents/" + formObj.path;
+            $.ajax({
+                type: "PUT",
+                url: updateUrl,
+                data: updateData,
+                crossDomain: true,
+                success: function (res) {
+                    fn && fn(true, res);
+                },
+                error: function (xhr, textStatus, error) {
+                    fn && fn(false, error);
+                }
+            });
+        },
+        /**
+         * 删除Git文件
+         * curl -X DELETE --header 'Content-Type: application/json;charset=UTF-8' 'https://gitee.com/api/v5/repos/xieyf00/chrome/contents/bookmark/bookmark-6.json?access_token=16c6176faea12d4b2dba667872d9b21c&sha=dce85293664c50792d2ebcfc4ede23bf3e1197c2&message=Chrome%20Browser%20Bookmark%20Deleted&branch=master'
+         * sha:文件的 Blob SHA,可通过 [获取仓库具体路径下的内容] API 获取
+         */
+        deleteGit: function (formObj, sha, fn) {
+            let that = this;
+            // Git提交信息
+            let deleteMessage = "Chrome Browser Bookmark Deleted" + new Date();
+            // 构建
+            let deleteData = {};
+            deleteData.access_token = formObj.access_token;
+            deleteData.message = deleteMessage;
+            deleteData.sha = sha;
+            deleteData.branch = formObj.branch;
+            let deleteUrl = that.baseUrl + "repos/" + formObj.owner + "/" + formObj.repo + "/contents/" + formObj.path;
+            $.ajax({
+                type: "DELETE",
+                url: deleteUrl,
+                data: deleteData,
+                crossDomain: true,
+                success: function (res) {
+                    fn && fn(true, res);
+                },
+                error: function (xhr, textStatus, error) {
+                    fn && fn(false, error);
+                }
+            });
+        }
+    }
+    sync.init();
+});

+ 32 - 0
manifest.json

@@ -0,0 +1,32 @@
+{
+  "name": "书签同步码云",
+  "version": "1.0.0",
+  "manifest_version": 2,
+  "description": "上传/下载书签信息到码云",
+  "browser_action": {
+    // browser actions 可以在chrome主工具条的地址栏右侧增加一个图标
+    "default_icon": "./icon/icon.png",
+    "default_title": "书签同步码云",
+    "default_popup": "popup.html"
+    // 点击插件图标弹出来的设置框
+  },
+  "icons": {
+    // 设置不同分辨率的图标
+    "16": "./icon/icon16.png",
+    "48": "./icon/icon48.png",
+    "128": "./icon/icon128.png"
+  },
+  "permissions": [
+    // web请求
+    "webRequest",
+    "webRequestBlocking",
+    // 插件本地存储
+    "storage",
+    // 书签
+    "bookmarks",
+    // 可以通过executeScript或者insertCSS访问的网站
+    "*://*/*"
+  ],
+  // 360安全浏览器升级检测地址
+  "update_url": "http://upext.chrome.360.cn/intf.php?method=ExtUpdate.query"
+}

+ 1 - 0
pic/background.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1565150251746" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3987" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><defs><style type="text/css"></style></defs><path d="M793.490046 949.181991 511.428995 743.862765 229.368968 949.181991l114.072035-335.980078L63.451124 407.879618l335.986217 0L511.428995 73.969689l111.992677 333.909929 335.986217 0L679.418011 613.201913 793.490046 949.181991zM511.428995 702.383048l215.693504 159.699212-85.029582-261.32375 217.763652-159.695119L598.538959 441.063391l-87.10894-263.394922-87.10894 263.394922L163.002444 441.063391 380.766097 600.757486l-85.029582 261.32375L511.428995 702.383048z" p-id="3988" fill="#cdcdcd"></path></svg>

+ 10 - 0
pic/right.svg

@@ -0,0 +1,10 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1504406023517" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" 
+    xmlns="http://www.w3.org/2000/svg" p-id="995" 
+    xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%">
+    <defs>
+        <style type="text/css"></style>
+    </defs>
+    <path d="M407.36 807.552a16 16 0 0 1-12.16-5.632L147.84 512a16 16 0 1 1 24.32-20.8l234.944 275.2 444.544-544a16 16 0 1 1 24.768 20.224l-456.704 559.04a16 16 0 0 1-12.224 5.888z" p-id="996" fill="#009933"></path>
+</svg>

+ 80 - 0
popup.html

@@ -0,0 +1,80 @@
+<!doctype html>
+<html>
+<head>
+    <meta name="viewport"
+          content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
+    <meta charset="UTF-8">
+    <title>书签同步码云</title>
+    <link rel="stylesheet" href="css/popup.css">
+</head>
+<body>
+
+<div id="content">
+    <div class="container">
+        <div class="item">
+            <label for="tbAccessToken" class="label">access_token</label>
+            <input type="password" class="input" id="tbAccessToken" placeholder="用户授权码"
+                   value=""/>
+        </div>
+        <div class="item">
+            <label for="tbOwner" class="label">owner</label>
+            <input type="text" class="input" id="tbOwner" placeholder="仓库所属空间地址"
+                   value=""/>
+        </div>
+        <div class="item">
+            <label for="tbRepo" class="label">repo</label>
+            <input type="text" class="input" id="tbRepo" placeholder="仓库路径"
+                   value=""/>
+        </div>
+        <div class="item">
+            <label for="tbPath" class="label">path</label>
+            <input type="text" class="input" id="tbPath" placeholder="文件的路径"
+                   value=""/>
+        </div>
+        <div class="item">
+            <label for="tbBranch" class="label">branch</label>
+            <input type="text" class="input" id="tbBranch" placeholder="分支(通常是master)"
+                   value=""/>
+        </div>
+
+        <!-- toast -->
+        <div class="item">
+            <div class="toast" id="toast"></div>
+        </div>
+
+        <!-- remember -->
+        <div class="item">
+            <div class="rememberWrap">
+                <span class="text grey" id="rememberText">Remember</span>
+                <span class="dot green" id="rememberDot"></span>
+            </div>
+        </div>
+
+        <!-- 按钮 -->
+        <div class="item">
+            <div class="buttonWrap">
+                <button class="button buttonGreen" id="btnUpload">Upload</button>
+                <button class="button buttonGray" id="btnDownload">Download</button>
+            </div>
+        </div>
+    </div>
+</div>
+
+<!-- loader -->
+<div id="loaderWrap">
+    <div class="loader"></div>
+</div>
+
+<!-- success -->
+<div id="successWrap">
+    <div class="successIcon"></div>
+    <div class="successText"></div>
+</div>
+
+<!-- scripts -->
+<script src="js/jquery-2.1.0.min.js"></script>
+<script src="js/base64.js"></script>
+<script src="js/popup.js"></script>
+</body>
+
+</html>

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott