• 谷歌插件开发(实现CSDN快速登陆)


    谷歌浏览器插件带来了很大的方便,于是就想着是不是也可以开发一个来用用。几经折腾下,开发了个CSDN快速 登陆的插件。下面简述一下开发的步骤。

    1、开发工具:谷歌浏览器(我开发时用的是chrome 30.0.1599.101 )、HTML/JS/CSS/JSON开发工具(UE、VS等,当然也可以用记事本)

    2、开发过程

    (1)新建清单文件manifest.json:该名字不可以改。下面是代码

    {
        "manifest_version": 2,   
        "background": {
            "page": "main.html"//后台运行页,page和scrptis只能选择一个		
        },
        "browser_action":{
            "default_icon":"assets/images/Icon.png",//插件默认图标
            "default_title":"__MSG_manifest_iconTitle__",//插件标题
            "default_popup":"popup.html"//单击插件图标时弹出的页面
        },
        "default_locale":"en",//默认本地化语言
        "description":"__MSG_manifest_appDescription__",//插件描述
        "icons":{//显示的图标
            "16":"assets/images/Icon.png",
            "128":"assets/images/Icon.png"
        },
        "name":"__MSG_manifest_appName__",//插件名字
        "options_page":"options.html",//插件选项页
        "permissions":[ "proxy", "tabs", "<all_urls>","notifications"],//申请需要的权限
        "version":"1.1.1",//插件版本
        "minimum_chrome_version":"18.0.0"//浏览器最低版本    
    }
    

    其中__MSG_manifest_iconTitle__、__MSG_manifest_appDescription__、__MSG_manifest_appName__是依据系统语言的本地化配置,比如在简体中文系统和英文系统时会本地化成相应的语言,见下图

    (2)新建弹出页(popup.html)

    写HTML和CSS代码,排布出如上图所示的界面

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <link rel="stylesheet" href="assets/styles/popup.css" type="text/css">
        <script src="assets/libs/jquery-1.7.2.min.js" type="text/javascript"></script>
        <script src="assets/scripts/Popup.js" type="text/javascript"></script>
    </head>
    <body>   
        <div id="set_config">
            <div class="header title" data-i18n-content="popup_setting">Setting</div>
            <div class="header light">
                <table>
                    <tr>
                        <th>
                            <span data-i18n-content="popup_account_id">User</span>
                        </th>
                        <td>
                            <input type="text" id="setting_account_id" value="" />
    
                        </td>
    
                    </tr>
                    <tr>
                        <th>
                            <span data-i18n-content="popup_password">Password</span>
                        </th>
                        <td>
                            <input type="password" id="setting_password" value="" />
                        </td>
                    </tr>                
                </table>
            </div>
            <div class="header light">
                <table class="control">
                    <tr>
                        <td>
                            <button class="button bold" id="setting_login" data-i18n-content="popup_setting_login">Save</button>
                        </td>
                        <td>    </td>
                        <td>
                            <button class="button bold" id="setting_cancel" data-i18n-content="popup_setting_cancel">Cancel</button>
                        </td>
                    </tr>
                </table>
            </div>
        </div>
    </body>
    
    </html>
    

    注意:这里面也采用了本地化配置,data-i18n-content="popup_setting"一类的写法,就是进行本地化配置,不过需要通过i18n.js来配套实现。i18n表示的是国际化。人们常把I18N作为“国际化”的简称,其来源是英文单词 internationalization的首末字符i和n。18为中间的字符数。
    这里面还用到了popup.js和jquery。jquery是JS库,网上可以下载到。popup.js代码如下

    /// <reference path="Config.js"/>
    /// <reference path="Settings.js"/>
    /// <reference path="Notify.js"/>
    
    var Popup = {};
    Popup.CONTROL_IDS = {
        SET_CANCEL: "setting_cancel",
        SET_LOGIN: "setting_login",
        SETTING_ACCOUNT_ID: "setting_account_id",
        SETTING_PASSWORD: "setting_password"
    };
    
    $(document).ready(function () {
        Popup.init();
        $("#" + Popup.CONTROL_IDS.SET_LOGIN).click(Popup.setLogin);
        $("#" + Popup.CONTROL_IDS.SET_CANCEL).click(Popup.setCancel);
    });
    
    
    Popup.init = function () {
        Popup.extension = chrome.extension.getBackgroundPage();
        Settings = Popup.extension.Settings;//Settings in Settings.js  
        Config = Popup.extension.Config;//Config in Config.js
        Notify = Popup.extension.Notify;
        I18n = Popup.extension.I18n;//I18n in I18n.js 
        I18n.process(document);
        document.body.style.visibility = "visible";
        Popup.initUI();
    }
    
    Popup.initUI = function () {
        $("#" + Popup.CONTROL_IDS.SETTING_ACCOUNT_ID).attr("value", Settings.getValue(Config.KEYS.ACCOUNT_ID, ''));
        $("#" + Popup.CONTROL_IDS.SETTING_PASSWORD).attr("value", Settings.getValue(Config.KEYS.PASSOWRD, ''));
    }
    
    Popup.openPage = function () {
        Popup.closePopup();
        chrome.tabs.create({//新建标签页
            url: Config.URLS.LOGIN
        });
    }
    
    Popup.openOptions = function () {
        Popup.closePopup();
        extension.openOptions();
    }
    
    Popup.closePopup = function () {
        window.close();
    }
    
    Popup.setCancel = function () {
        Popup.closePopup();
    }
    
    Popup.setLogin = function () {
    
        Settings.setValue(Config.KEYS.ACCOUNT_ID, $("#" + Popup.CONTROL_IDS.SETTING_ACCOUNT_ID).val());
        Settings.setValue(Config.KEYS.PASSOWRD, $("#" + Popup.CONTROL_IDS.SETTING_PASSWORD).val());
    
        Notify.notifyText(Config.FILES.NOTIFY_IMAGE, '', 'OK');
    
        Popup.openPage();
    }
    

    说明:

    1) /// <reference path="Config.js"/>是VS中为了可以像类一样点出相应的字段和方法的引用方法。

    2)$(document).ready是popup.html加载完成后进行相应的操作,比如初始化按钮的点击事件等。chrome是不可以在页面中直接写内连的JS的,那样有安全隐患。

    <scritp type="text/javascript">
    //内连脚本
    function test(){ alert("test");}
    </script>

    如果非要进行内连,那必须将其转换成64码,然后连到src中(直接放到scrpit中的scr中不可以,需要放到iframe中。不知道具体是什么原因。),如下面代码。

    <iframe id="sandbox-frame" sandbox="allow-scripts" src="data:text/html;base64,JTNDc2NyaXB0JTIwdHlwZSUzRCUyMnRleHQvamF2YXNjcmlwdCUyMiUzRSUwQWFsZXJ0JTI4JTIyQUFBQSUyMiUyOSUzQiUwQSUzQy9zY3JpcHQlM0U="></iframe>

    3)最重要的是Popup.init函数,这个函数将后台的js引用到前台(popup.js是从前台界面popup.html中引用的,所以为前台JS),以便于更好的调用。

    Popup.extension = chrome.extension.getBackgroundPage()先把后台引用到前台。

    Config = Popup.extension.Config是从后台将Config引用到前台来,其中Config不是Config.js的脚本名字,而是Config.js中的var Config={}。其他的引用同理。

    I18n.process(document)这是进行本地化配置。

    (3)后台JS

    后台JS需要放到main.html中,代码如下

    <html>
    <head>
        <meta charset="UTF-8" />
        <title></title>
        <script src="assets/libs/jquery-1.7.2.min.js" type="text/javascript"></script>
        <script src="assets/scripts/CSDN.js" type="text/javascript"></script>
        <script src="assets/scripts/Settings.js" type="text/javascript"></script>
        <script src="assets/scripts/Config.js" type="text/javascript"></script>
        <script src="assets/scripts/I18n.js" type="text/javascript"></script>
        <script src="assets/scripts/Notify.js" type="text/javascript"></script>
    </head>
    <body>
    </body>
    </html>
    

    1)CSDN.js

    /// <reference path="Config.js"/>
    var CSDN = {};
    
    chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {//添加监听事件
        var url = tab.url;
        var config = null;
        if (tab.status !== "complete") {//需要增加这一判断,不然会在页面加载和加载完成后分别触发
            return;
        }
        CSDN.login(url);
    });
    
    CSDN.CONTROL_IDS = {
        ACCOUNT_ID: "u",
        PASSWORD: "p",
        LOGIN: "aLogin",
        LOGIN_IFRAME: "logfrm"
    };
    
    CSDN.isLoginUrl = function (url) {
        if (url === Config.URLS.LOGIN) {
    
            return true;
        }
        else {
            return false;
        }
    }
    
    CSDN.login = function (url) {
        if (!CSDN.isLoginUrl(url)) {
            return;
        }
        var accountID = Settings.getValue(Config.KEYS.ACCOUNT_ID, '');
        var password = Settings.getValue(Config.KEYS.PASSOWRD, '');
        var scripts = "";
        if (accountID === undefined || accountID === null || accountID.replace(/(^s*)|(s*$)/g, "").length <= 0) {
            return;
        }
    
        scripts += "var loginIFrame = document.getElementById('" + CSDN.CONTROL_IDS.LOGIN_IFRAME + "');"
                + " var loginDocument=loginIFrame.contentWindow.document;"
                + "loginDocument.getElementById('" + CSDN.CONTROL_IDS.ACCOUNT_ID + "').value='" + accountID + "';"
                + "loginDocument.getElementById('" + CSDN.CONTROL_IDS.PASSWORD + "').value='" + password + "';"
                + "loginDocument.getElementById('" + CSDN.CONTROL_IDS.LOGIN + "').click();"
        chrome.tabs.executeScript(null, {//嵌入JS脚本到所打开的页面中。
            code: scripts
        });
    }

    2)Notify.js

    从浏览器中发出桌面通知,代码如下

    var Notify = {};
    Notify.notifyText = function (iconUrl, title, content) {//这个要触发,必须在manifest.json中添加notifications,以申请桌面通知权限
        var notification = window.webkitNotifications.createNotification(iconUrl, title, content);
        notification.show();
    }

    3)其他JS代码如下

    Config.js

    var Config = {};
    
    Config.KEYS = {
        ACCOUNT_ID: "accountID",   
        PASSOWRD: "password"
    };
    
    Config.FILES = {
        NOTIFY_IMAGE: "assets/images/Notify.png"
    };
    
    Config.URLS = {
        LOGIN: "https://passport.csdn.net/account/login"
    };
    


    Setting.js

    var Settings = {};
    
    Settings.configCache = {};
    
    Settings.setValue = function setValue(key, value) {
        Settings.configCache[key] = value;
    
        var config = {};
        if (localStorage.config)
            config = JSON.parse(localStorage.config);
    
        config[key] = value;
        localStorage.config = JSON.stringify(config);
        return value;
    };
    
    Settings.getValue = function getValue(key, defaultValue) {
        if (typeof Settings.configCache[key] != "undefined")
            return Settings.configCache[key];
    
        if (!localStorage.config)
            return defaultValue;
    
        var config = JSON.parse(localStorage.config);
        if (typeof config[key] == "undefined")
            return defaultValue;
    
        Settings.configCache[key] = config[key];
        return config[key];
    };
    
    Settings.keyExists = function keyExists(key) {
        if (!localStorage.config)
            return false;
    
        var config = JSON.parse(localStorage.config);
        return (config[key] != undefined);
    };
    
    Settings.setObject = function setObject(key, object) {
        localStorage[key] = JSON.stringify(object);
        return object;
    };
    
    Settings.getObject = function getObject(key) {
        if (localStorage[key] == undefined)
            return undefined;
    
        return JSON.parse(localStorage[key]);
    };
    
    Settings.refreshCache = function refreshCache() {
        Settings.configCache = {};
    };


    I18n.js

    var I18n = {};
    
    I18n.messages = null;
    
    I18n.init = function init() {
        I18n.messages = I18n.readMessages();
        //	I18n.readMessages(function(messages) {
        //		I18n.messages = messages;
        //	});
    };
    
    I18n.buildMessages = function buildMessages() {
        var result = "
    ";
    
        $("*[data-i18n-content]").each(function (i, item) {
            result += '"' + item.getAttribute("i18n-content") + '"' +
                ': { "message": "' + item.innerHTML.replace(/[ 
    	]+/g, " ") + '" },
    ';
        });
    
        $("*[data-i18n-values]").each(function (i, item) {
            $(item.getAttribute("i18n-values").split(";")).each(function (i, subItem) {
                var subItemParts = subItem.split(":");
                if (subItemParts.length == 2 && subItemParts[0].charAt(0) != ".") {
                    result += '"' + subItemParts[1] + '"' +
                        ': { "message": "' + item.getAttribute(subItemParts[0]).replace(/[
    ]/g, "\n") + '" },
    ';
                }
            });
        });
    
        return result;
    };
    
    I18n.readMessages = function readMessages(callback) {
        var async = (callback != undefined);
        var data = null;
        var request = new XMLHttpRequest();
        request.open("GET", chrome.extension.getURL("_locales/en/messages.json"), async);
        request.onreadystatechange = function () {
            if (this.readyState == XMLHttpRequest.DONE) {
                data = this.responseText;
                data = JSON.parse(data.replace(/[
    	]+/g, " "));
                if (async)
                    callback(data);
            }
        };
        request.send("");
    
        return data;
    };
    
    I18n.getMessage = function getMessage(messageName, substitution) {
        var result = chrome.i18n.getMessage(messageName, substitution);
        if (result == undefined || result.length == 0) {
            var messageObject = I18n.messages[messageName];
            if (messageObject != undefined) {
                result = messageObject.message;
                if (result != undefined)
                    result = result.replace("$1", substitution);
            }
        }
        return result;
    };
    
    I18n.process = function process(node) {
        return I18nTemplate.process(node);
    };
    
    I18n.init();
    
    //-------------------------------------------------------
    
    /**
     * i18nTemplate: http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/resources/i18n_template.js
     */
    var I18nTemplate = (function () {
        var handlers = {
            /**
             * This handler sets the textContent of the element.
             */
            'data-i18n-content': function (element, attributeValue) {
                element.innerHTML/*textContent*/ = I18n.getMessage(attributeValue);
            },
    
            /**
             * This is used to set HTML attributes and DOM properties,. The syntax
             * is: attributename:key; .domProperty:key; .nested.dom.property:key
             */
            'data-i18n-values': function (element, attributeValue) {
                var parts = attributeValue.replace(/s/g, '').split(/;/);
                for (var j = 0; j < parts.length; j++) {
                    var a = parts[j].match(/^([^:]+):(.+)$/);
                    if (a) {
                        var propName = a[1];
                        var propExpr = a[2];
    
                        var value = I18n.getMessage(propExpr);
                        if (propName.charAt(0) == '.') {
                            var path = propName.slice(1).split('.');
                            var object = element;
                            while (object && path.length > 1) {
                                object = object[path.shift()];
                            }
                            if (object) {
                                object[path] = value;
                                // In case we set innerHTML (ignoring others) we need to
                                // recursively check the content
                                if (path.toString() === 'innerHTML') {
                                    process(element);
                                }
                            }
                        } else {
                            element.setAttribute(propName, value);
                        }
                    }
                }
            }
        };
    
        var attributeNames = [];
        for (var key in handlers) {
            if (handlers.hasOwnProperty(key)) {
                attributeNames.push(key);
            }
        }
        var selector = '[' + attributeNames.join('],[') + ']';
    
        function process(node) {
            var elements = node.querySelectorAll(selector);
            for (var element, i = 0; element = elements[i]; i++) {
                for (var j = 0; j < attributeNames.length; j++) {
                    var name = attributeNames[j];
                    var att = element.getAttribute(name);
                    if (att != null) {
                        handlers[name](element, att);
                    }
                }
            }
        }
    
        return {
            process: process
        };
    })();
    


    开发时参考了SwitchProxy的源码https://code.google.com/p/switchysharp/source/。

    转载请注明出处http://blog.csdn.net/xxdddail/article/details/13504259

    源码在此处下载http://download.csdn.net/detail/xxdddail/6469683




  • 相关阅读:
    【用程序思维学习英语】
    【python3】修饰器简单理解
    【FLASK】发送QQ邮件
    【FLASK】数据库迁移
    【python3】with的用法
    【flask】工厂函数和蓝本的作用
    使用Python中的xltpl模块填充excel表格模板文件
    Python添加excel表格的批注
    在原有表格基础上面进行添加内容修改格式等操作
    Python操作excel表格库的介绍
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3395197.html
Copyright © 2020-2023  润新知