Chrome自定义插件—小温之家的私人订制

in 前端 with 0 comment

Chrome已经位居现代浏览器榜首,其中一个重要原因就是插件丰富,开发一款自己的专属插件也比较容易入手。做一个跳转小温之家前后台和书签管理的页面。记录下大致的操作吧。

【wenqyChromeExt】这是当前扩展插件根目录,运行tree -f命令查看目录结构。

chrome_ext_tree

Mainfest.json

看下Mainfest.json核心入口文件

{
  “update_url”: “https://wenqy.com/crx”,
  // 描述
  “name”: “小温之家-行走在猿类世界”,
  “description”: “小温之家-行走在猿类世界,记下你的世界,做自己最真实的友人!”,
  “version”: “1.0.0”,
  “permissions”: [
    “*://*/*”,
    “activeTab”,
    “tabs”,
    “contextMenus”, // 右键菜单
    “webRequest”,
    “webRequestBlocking”,
    “storage”
  ],
  // 图标
  “icons”:
  {
    “16”: “imgs/map/icon16.png”,
    “48”: “imgs/map/icon48.png”,
    “128”: “imgs/map/icon128.png”
  },
  “background”: {
    “page”: “background.html”
  },
  // 默认语言
  “default_locale”: “zh_CN”,
  // 引入脚本
  “content_scripts”: [
    {
      // 匹配时使用脚本、样式
      “matches”: [“https://*/*”, “http://*/*”],
      “exclude_matches”: [],
      “css” : [“css/wenqy.css”],
      “js”: [“js/wenqy.js”],
      // 运行 文档加载结束
      “run_at”: “document_end”
    }
  ],
  “browser_action”: {
    “default_title”: “点击进入小温之家”,
    “default_icon”: “imgs/map/icon48.png”,
    “default_popup”: “popup.html”
  },
  // 选项页
  “options_page”: “options.html”,
  // 默认策略限制
  // “content_security_policy”: “script-src ‘self’; object-src ‘self'”,
  //”web_accessible_resources”: [
  //  “imgs/map/*.png”
  //],
  “manifest_version”: 2
}

popup.html

popup.html 点击chrome浏览器插件图标后的弹出层,与用户交互界面。

<body id=“yc-baidu-translate”>
        <div class=“container”>
            <div class=“header”>
                <div class=“jumbotron”>
                    <button class=“” id=“wenqy-page”><img title=“前往小温之家” src=“imgs/map/arrow_down_nomal.png” />登录小温之家</button>
                </div>
            </div>
            <div class=“main”>
                <div class=“select-l translate-from” data-click=“no-click”>
                    <div class=“selected-l”>
                        <span class=“selected-l-text” value=“”><img title=“前往小温之家” src=“imgs/map/right-arrow.png” />站点书签</span>
                    </div>
                </div>
            </div>
            <div class=“row”>
                <select id=“select_sites” style=“width:100%”>
                    <option value=“”>请搜索书签</option>
                </select>
            </div>
            <div class=“yc-baidu-translate-footer”>
                <div class=“icon_options”>
                    <a href=“javascript:;” id=“icon_options_home” class=“icon_options_item”>
                        <img title=“前往小温之家” id=“icon_options_home_img” src=“imgs/map/home.png”>
                    </a>
                    <a href=“javascript:;” id=“icon_options_setting” class=“icon_options_item”>
                        <img title=“插件设置” id=“icon_options_setting_img” src=“imgs/map/setup.png”>
                    </a>
                    <a href=“javascript:;” id=“icon_options_help” class=“icon_options_item”>
                        <img title=“帮助和反馈” id=“icon_options_help_img” src=“imgs/map/help.png”>
                    </a>
                </div>
            </div>
        </div>
</body>

popup.js

popup.js popup.html引用的外部js文件,行为操作层

var haitao_sites = {
    ‘www.wenqy.com’: ‘小温之家’,
    ‘www.baidu.com’: ‘百度’
};
function parseURL(url) {
    var domain;
    if (url.indexOf(“/”) >= 0) {
        domain = url.substr(0, url.indexOf(“/”));
    } else {
        domain = url;
    }
    var domainName, port;
    var idx = domain.indexOf(“:”);
    if (idx > 0) {
        domainName = domain.substr(0, idx);
        port = domain.substr(idx + 1);
    } else {
        domainName = domain;
    }
    var shortName = domainName.substr(url.indexOf(“.”) + 1);
    if (shortName.indexOf(“.”) < 0) {
        shortName = domainName;
    }
    var tmp2 = url.substr(url.indexOf(“/”) + 1);
    var webContext = tmp2.substr(0, tmp2.indexOf(‘/’));
    var uri = tmp2.substr(tmp2.indexOf(‘/’));
    return {
        domainName: domainName,
        shortName: shortName,
        port: port,
        webContext: webContext,
        uri: uri
    }
}
function restore_options() {
    chrome.storage.local.get(null, function(items) {
        // console.log(‘items’, items);
        if (items[‘custom_sites’] && items[‘custom_sites’].length > 0) {
            var selectText = “”;
            for (var i in items[‘custom_sites’]) {
                var input_obj = parseURL(items[‘custom_sites’][i]);
                var name = haitao_sites[input_obj.domainName] ? haitao_sites[input_obj.domainName] : input_obj.shortName;
                selectText += ‘<option value=’+items[‘custom_sites’][i]+’>’+name+'<option>’;
            }
            $(“#select_sites”).empty().append(selectText);
        }
    })
}
restore_options();
$(document).ready(function() {
    $(“#select_sites”).select2();
    $(“#select_sites”).change(function(event) {
        window.open(“http://” +$(“#select_sites option:selected”).val());
    });
    $(“#icon_options_setting”).click(function() {
        window.open(‘options.html’);
    });
    $(“#icon_options_setting”).hover(function() {
        $(“#icon_options_setting_img”).attr(‘src’, ‘imgs/map/setuphover.png’);
    }, function() {
        $(“#icon_options_setting_img”).attr(‘src’, ‘imgs/map/setup.png’);
    });
    $(‘#wenqy-page’).click(function() {
        window.open(‘http://wenqy.com/wp-login.php’);
    });
    $(“#icon_options_home”).click(function() {
        window.open(‘http://wenqy.com/’);
    });
    $(‘#icon_options_home’).hover(function () {
        $(‘#icon_options_home_img’).attr(‘src’, ‘imgs/map/home_hover.png’);
    }, function () {
        $(‘#icon_options_home_img’).attr(‘src’, ‘imgs/map/home.png’);
    });
    $(‘#icon_options_help’).click(function () {
        window.open(‘http://wenqy.com’);
    });
    $(‘#icon_options_help’).hover(function () {
        $(‘#icon_options_help_img’).attr(‘src’, ‘imgs/map/help_hover.png’);
    }, function () {
        $(‘#icon_options_help_img’).attr(‘src’, ‘imgs/map/help.png’);
    });
});

options.html

options.html 扩展插件的设置界面

<body>
<h1 style=“text-align:center;”>小温之家| 自定义设置</h1>
<h2>喜欢的颜色:</h2>
<h2 style=“text-align:center;” id=“status”></h2>
<br>
</br>
<div class=“autoTranslate”>
    <div class=“autoTranslate-switch”>
        <h2>喜欢的颜色:</h2>
        <table id=“custom-head” class=“table”>
            <tr>
                <td id=“thead-tip”>
                    <select id=“color”>
                     <option value=“”></option>
                     <option value=“red”>red</option>
                     <option value=“green”>green</option>
                     <option value=“blue”>blue</option>
                     <option value=“yellow”>yellow</option>
                     <option value=“white”>white</option>
                    </select>
                </td>
                <td>
                    <a href=“#” id=“saveOptions”>保存</a>
                </td>
            </tr>
        </table>
        <h2>书签管理</h2>
        <table id=“custom-head” class=“table”>
            <tr>
                <td id=“thead-tip”>
                    书签
                </td>
                <td>
                    <a href=“#” id=“add”>+添加站点</a>
                </td>
            </tr>
        </table>
    </div>
    <div class=“table-container”>
        <table id=“custom” class=“table-custom”>
        </table>
    </div>
</div>
</body>

wenqy.js

wenqy.js options.html 设置界面引用的外部行为操作层

function save_options() {
    custom_sites = [];
    $(‘.site’).each(function() {
        if ($(this).text() != ” && custom_sites.indexOf($(this).text()) == -1) {
            custom_sites.push($(this).text());
        }
    })
    chrome.storage.local.set({
        ‘custom_sites’: custom_sites,
    }, function() {
        location.reload();
    });
}
function restore_options() {
    chrome.storage.local.get(null, function(items) {
        // console.log(‘items’, items);
        if (items[‘custom_sites’] && items[‘custom_sites’].length > 0) {
            $(“#custom”).addClass(“table-custom-padding”);
            for (var i in items[‘custom_sites’]) {
                var input_obj = parseURL(items[‘custom_sites’][i]);
                var name = haitao_sites[input_obj.domainName] ? haitao_sites[input_obj.domainName] : input_obj.shortName;
                $(“#custom”).append(‘\
          <tr>\
            <td class=“site”><a href=“http://’ + items[‘custom_sites’][i] + ‘” target=“_blank”>’ + items[‘custom_sites’][i] + ‘</a></td>\
            <td class=“site-name”>’ + name + ‘</td>\
            <td class=“site-remove”><a href=“#” class=“remove”>移除</a></td>\
          </tr>’);
                $(“.remove”).click(function() {
                    $(this).parent().parent().remove();
                    save_options();
                });
            }
        } else {
            $(“#custom”).append(‘\
        <tr id=“blank”>\
          <td class=“custom-nothing”>无</td>\
        </tr>’);
        }
    })
}
restore_options();
/**
* @author wenqy.com
*
*
*/
$(function(){
    // Saves options to localStorage.
    function save_options_test() {
      var select = $(“#color option:selected”).val();
      localStorage[“favorite_color”] = select;
      // Update status to let user know options were saved.
      $(“#status”).text(“选项已保存”);
      console.log(“select->”+select);
      $(“body”).css(“background-color”,select);
      setTimeout(function() {
        $(“#status”).text(“”);
      }, 750);
    }
    // Restores select box state to saved value from localStorage.
    function restore_options_test() {
      var favorite = localStorage[“favorite_color”];
      if (!favorite) {
        return;
      }
      $(“#color”).val(favorite);
      $(“body”).css(“background-color”,favorite);
    }
    restore_options_test();
    $(“#saveOptions”).click(save_options_test);
    var url_expression = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi;
    var url_regex = new RegExp(url_expression);
    var c_id = 0;
    $(“#add”).on(‘click’, function() {
        if ($(‘.add-tr’).length <= 0 ) {
            if (!$(“#custom”).hasClass(“table-custom-padding”)) {
                $(“#custom”).addClass(“table-custom-padding”);
            }
            $(“#custom”).before(‘\
        <table id=“‘ + c_id + ‘” class=“add-table”>\
          <tr class=“add-tr”>\
            <td class=“td-add-input”>\
              <input type=“text” class=“form-control site” placeholder=“请输入网站地址,如: www.wenqy.com” required>\
            </td>\
            <td class=“td-add-button”>\
              <button type=“button” class=“switch-open add-confirm” style=“width:79px;float: none;margin-left:16px;”>添加</button>\
            </td>\
            <td class=“td-add-remove-button”>\
              <button type=“button” class=“switch-close add-remove” style=“width:79px;float: none;”>取消</button>\
            </td>\
          </tr>\
          <tr><td class=“fail”></td></tr>\
        </table>’);
            $(“.add-remove”).on(‘click’, function() {
                $(this).parent().parent().parent().parent().remove();
                if ($(“.site”).length <= 0) {
                    $(“#custom”).removeClass(“table-custom-padding”);
                }
            });
            $(“#” + c_id + ” .add-confirm”).on(‘click’, function() {
                var input_valid = true;
                var url_input = $(this).parent().prev().children(‘input’).val().replace(‘。’, ‘.’).replace(‘http://’, ”).replace(‘https://’, ”);
                if (url_input == “”) {
                    $(‘.fail’).text(‘输入内容不能为空’);
                    $(‘.fail’).show();
                    input_valid = false;
                } else if (!url_input.match(url_regex)) {
                    $(‘.fail’).text(‘无效网址’);
                    $(‘.fail’).show();
                    input_valid = false;
                }
                if (input_valid) {
                    var input_obj = parseURL(url_input);
                    var name = haitao_sites[input_obj.domainName] ? haitao_sites[input_obj.domainName] : input_obj.shortName;
                    $(“#custom #blank”).remove();
                    $(“#custom”).prepend(‘\
            <tr>\
              <td class=“site”><a href=“http://’ + url_input + ‘” target=“_blank”>’ + url_input + ‘</a></td>\
              <td class=“site-name”>’ + name + ‘</td>\
              <td class=“site-remove”><a href=“#” class=“remove”>移除</a></td>\
            </tr>’);
                    $(this).parent().parent().remove();
                    $(“.remove”).on(‘click’, function() {
                        $(this).parent().parent().remove();
                        save_options();
                    });
                    save_options();
                }
            });
            c_id += 1;
        }
    });
});

直接将百度翻译的插件进行改造,阉割了。。。

该开始编写样例的时候报错,扩展页面动态绑定JS事件提示错误

Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'self' blob: filesystem: chrome-extension-resource:". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.

JavaScript事件都用外部引入的方式就可以了

Chrome插件安装

Chrome浏览器中输入URL:chrome://extensions,进行【扩展程序】管理页面,当然也可以在【设置】里找,选择【加载已解压的扩展程序】,选中扩展插件的目录即可。当然也可以把扩展插件的目录打包,打包成.crx后缀结尾的扩展插件。

chrome_import_ext

安装扩展后,如图所示:

chrome_wenqy

点击【登录小温之家】,跳转到小温之家的后台登录界面;点击主页图标,跳转到小温之家首页,点击帮助图标也是;点击设置图标,跳转到站点管理页面。

chrome_options

添加站点后,就可以搜索选择啦,选择站点后,跳转到站点页面。

chrome_bookmark

Console效果

姑且在加下console的打印效果
var str = “WENQY”;
var mode = “stereo”; // 平面
if (mode === ‘planar’) {
   var character = character_planar;
 }
 if (mode === ‘stereo’) { // 立体
   var character = character_stereo;
 }
 var result = ‘\n’;
 var strArr = str.split(‘\n’);
 for (var k = 0; k < strArr.length; k++) {
   for (var j = 0; j < 7; j++) {
     for (var i = 0, length = strArr[k].length; i < length; i++) {
       result = result + character[strArr[k][i]][j];
     }
     result = result + ‘\n’;
   }
 }
 console.group(“Info”);
 console.log(result);
console.log(“welcome to http://wenqy.com”);
console.log(”   …..∵ ∴★.∴∵∴ ╭ ╯╭ ╯╭ ╯╭ ╯∴∵∴∵∴ “);
console.log(“.☆.∵∴∵.∴∵∴▍▍ ▍▍ ▍▍ ▍▍☆ ★∵∴ “);
console.log(“▍.∴∵∴∵.∴▅███████████☆ ★∵ “);
console.log(“◥█▅▅▅▅███▅█▅█▅█▅█▅█▅███◤ “);
console.log(“. ◥███████████████████◤ “);
console.log(“....◥████████████████■◤”);
console.log(“.. .. .. .. .. .. .. .”);
console.log(“.. .. .. .. . “);
console.log(“…友谊的巨轮向你驶来”);
console.groupEnd();
console.group(“Reference”);
console.log(“https://github.com/starkwang”);
console.log(“http://www.fuhaodq.com/fuhaotuan/1215.html”);
console.log(“http://www.cnblogs.com/tinyTea/p/6072618.html”);
console.log(“http://www.cnblogs.com/zhongxinWang/p/4121111.html”);
console.groupEnd();
);

效果图:

效果

第一版自定义插件算是入门啦,当然存在痛点了,插件卸载后,重新安装并没有保存原先添加的站点记录,也没有为这些站点定义名称字段方便记忆和管理,话说还会再改吗?姑且记之吧。

参考

http://open.chrome.360.cn/extension_dev/overview.html
http://open.chrome.360.cn/extension_dev/manifest.html