/** * ==VimperatorPlugin== * @name gmperator * @description Vimperator plugin for Greasemonkey * @author teramako teramako@gmail.com * @namespace http://d.hatena.ne.jp/teramako/ * @version 0.3a * ==/VimperatorPlugin== * * --------------------------- * Usage: * --------------------------- * {{{ * * :gmli[st] {filter} -> show user scripts matches {filter} * :gmli[st]! -> show all user scripts * :gmli[st] full -> same as :gmli[st]! * * :gmlo[ad] {name|filename} -> load the user script to the current page * but, don't dispatch load event * so maybe you should edit the scripts before load * :gmlo[ad]! {name|filename} -> force load the user script * * :gmset! -> toggle enable/disable Greasemonkey * :gmset! {filename} -> toogle enable/disable the script * :gmset {filename} {options} * {options}: * -n[ame] {value} -> change name to {value} * -i[nclude] {expr[,expr,...]} -> change includes to expr list ("," demiliter) * -e[xclude] {expr[,expr,...]} -> change excludes to expr list ("," demiliter) * * Caution: * The change is permanent, not only the session. * And cannot get back. * * e.g.) * :gmset! {filename} -n fooScriptName -i http://*,https://* -e http://example.com/* * toggle enable or disable, * name to "fooScriptName", * includes to "http://*" and "https://*", * and excludes to "http://example.com/*" * * }}} * --------------------------- * For plugin developer: * --------------------------- * {{{ * * 1). you can access to the sandbox of Greasemonkey !!! * 2). you can register commands to execute * when the user script is executed on the URI * @see liberator.plugins.gmperator.addAutoCommand * * liberator.plugins.gmperator => ( * allItem : return object of key : {panalID}, * value : {GmContainer} * {panelID} => @see gBrowser.mTags[].linkedPanel * currentPanel * currentContainer : return the current {GmContainer} object * currentSandbox : return the current sandbox object * gmScripts : return array of {userScripts} * {userScripts} => ( * filename : {String} * name : {String} * namespace : {String} * description: {String} * enabled : {Boolean} * includes : {String[]} * encludes : {String[]} * ) * addAutoCommand : function( uri, script, cmd ) * If both of uri and script are matched * * ) * }}} */ (function(){ const Cc = Components.classes; const Ci = Components.interfaces; const gmID = '@greasemonkey.mozdev.org/greasemonkey-service;1'; if (!Cc[gmID]) { log('Greasemonkey is not installed'); return; } if(!liberator.plugins) liberator.plugins = {}; liberator.plugins.gmperator = (function(){ //{{{ // ----------------------- // PUBLIC section // ----------------------- // {{{ var manager = { register: function (uri,sandbox,script){ var panelID = getPanelID(sandbox.window); var gmCon; if (containers[panelID]){ gmCon = containers[panelID]; } else { gmCon = new GmContainer(uri,sandbox); containers[panelID] = gmCon; this.__defineGetter__(panelID,function() gmCon); //log('gmpeartor: Registered: ' + panelID + ' - ' + uri); } gmCon.sandbox = sandbox; gmCon.addScript(script); gmCon.uri = uri; autocommands.trigger('GMInjectedScript',uri+'\n'+script.filename); }, get gmScripts() getScripts(), get allItem() containers, get currentPanel() getBrowser().mCurrentTab.linkedPanel, get currentContainer() containers[this.currentPanel] || null, get currentSandbox(){ var id = this.currentPanel; return containers[id] ? containers[id].sandbox : null; }, getSandboxFromWindow: function(win){ for each(var c in containers){ if(c.sandbox.window == win) return sandbox; } return null; }, getContainersFromURI: function(uri){ var list = []; for each(var c in containers){ if (c.uri == uri) list.push(c); } return list.length > 0 ? list : null; }, addAutoCommand: function(uri, script, cmd){ var reg = uri+'.*\n'+script+'\.user\.js'; autocommands.add('GMInjectedScript', reg, cmd); }, removeAutoCommand: function(uri, script){ var reg = uri+'.*\n'+script+'\.user\.js'; autocommands.remove('GMInjectedScript', reg); }, }; // }}} // ----------------------- // PRIVATE section // ----------------------- // {{{ var containers = {}; var gmSvc = Cc[gmID].getService().wrappedJSObject; function appendCode(target,name,func){ var original = target[name]; target[name] = function(){ var tmp = original.apply(target,arguments); func.apply(this,arguments); return tmp; }; } appendCode(gmSvc, 'evalInSandbox', function(code,uri,sandbox,script){ liberator.plugins.gmperator.register(uri,sandbox,script); }); function getPanelID(win){ var tabs = getBrowser().mTabs; for (var i=0; tabs.length; i++){ var tab = tabs.item(i); if (tab.linkedBrowser.contentWindow == win){ return tab.linkedPanel; } } //liberator.log(win + 'is not found'); } function updateGmContainerList(e){ var t = e.target; if (t && t.localName == 'tab' && t.linkedPanel){ delete containers[t.linkedPanel]; delete plugins.gmperator[t.linkedPanel]; } } getBrowser().mTabContainer.addEventListener('TabClose',updateGmContainerList,false); // }}} return manager; })(); //}}} // --------------------------- // User Command // --------------------------- commands.addUserCommand(['gmli[st]','lsgm'], 'list Greasemonkey scripts', //{{{ function(arg,special){ var str = ''; var scripts = getScripts(); var reg; if (special || arg == 'full'){ reg = new RegExp('.*'); } else if( arg ){ reg = new RegExp(arg,'i'); } if (reg){ scripts.forEach(function(s){ if ( reg.test(s.name) || reg.test(s.filename) ) { str += scriptToString(s) + '\n\n'; } }); } else { var table = ; var tr; scripts.forEach(function(script){ tr = ; if (script.enabled){ tr.* += ; } else { tr.* += ; } tr.* += ; table.* += tr; }); str += table.toXMLString(); } echo(str,true); function scriptToString(script){ var table =
{script.name}{script.name}({script.filename})
{script.name}
; ['FileName', 'NameSpace', 'Description', 'Includes', 'Excludes', 'Enabled'].forEach(function(prop){ var tr = {prop} ; var contents = script[prop.toLowerCase()]; if (typeof contents == "string"){ tr.* += {contents}; } else { var td = ; contents.forEach(function(c,i,a){ td.* += c; if (a[i+1]) td.* +=
; }); tr.* += td; } table.* += tr; }); return table.toXMLString(); } } ); //}}} commands.addUserCommand(['gmlo[ad]'], 'load Greasemonkey scripts', //{{{ function(arg, special){ if (!arg) { echoerr('Usage: :gmlo[ad][!] {name|filename}'); return; } var scripts = getScripts(); var script; for (var i=0; i 0){ script.name = commands.getOption(res.opts, '-name', script.name); script.includes = commands.getOption(res.opts, '-include', script.includes); script.excludes = commands.getOption(res.op
// Vimperator Plugin: Param Editor
// Version: 0.1

(function (){
    var doc = null;

    var xpath = function(query, node){
        var snap = doc.evaluate(query, node, null,
            XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
        var res = [];
        for(let i = 0, l = snap.snapshotLength; i < l; i++){
            res.push(snap.snapshotItem(i));
        }
        return res;
    };

    var Form = function(form, i){
        this.uid = i;
        this.method = (form.method.length) ? form.method.toUpperCase() : "GET";
        this.action = form.action;
        this.name = (form.name.length) ? form.name : this.uid;
        this.e = form;
        this.member = [];

        //var elems = xpath("descendant::*[@name]", form);
        var elems = Array.slice(form.elements);
        var count = 0;
        elems.forEach(function(e, i){
            if(!e.type) return;
            var type = e.type.toLowerCase();
            if(type == "radio" || type == "checkbox"){
                let mg = null;
                for(let i = 0, l = this.member.length; i < l; i++){
                    if(this.member[i].constructor == FormMemberGroup &&
                       this.member[i].name == e.name){
                        mg = this.member[i];
                        break;
                    }
                }
                if(!mg){
                    mg = new FormMemberGroup(e, count++);
                    this.member.push(mg);
                }
                mg.add_elem(e);
            }else{
                this.member.push(new FormMember(e, count++));
            }
        }, this);

        this.html = form2html(this);
    };

    var FormMember = function(elem, i){
        this.uid = i;
        this.elem = elem;
    };

    ["name", "value", "type"].forEach(function(n){
        FormMember.prototype.__defineGetter__(n, function(){
            return this.elem[n];
        });

        FormMember.prototype.__defineSetter__(n, function(v){
            this.elem[n] = v;
        });
    });

    var FormMemberGroup = function(e, i){
        this.uid = i;
        this.type = e.type.toLowerCase();
        this.name = e.name;
        this.elems = [];
    };

    FormMemberGroup.prototype.__defineGetter__("value", function(){
        var v = [];
        this.elems.forEach(function(e){
            var _v = e.value;
            if(e.checked){
                _v = "[" + _v + "]";
            }
            v.push(_v);
        });
        return v.join(" ");
    });

    FormMemberGroup.prototype.__defineSetter__("value", function(v){
        var check = function(n){
            if(this.type == "radio"){
                this.elems[n].checked = true;
            }else if(this.type == "checkbox"){
                this.elems[n].checked = (this.elems[n].checked) ? false : true;
            }
        };

        for(let i = 0, l = this.elems.length; i < l; i++){
            if(this.elems[i].value == v){
                check.call(this, i);
                return;
            }
        }
        if(this.elems[v]){
            check.call(this, v);
        }
    });

    FormMemberGroup.prototype.add_elem = function(e){
        this.elems.push(e);
    };

    var form2html = function(form){
        var html = [
            '<style type="text/css">',
                '.red { color:#c00 !important}',
                '.blue { color:#00c !important}',
                'caption { margin:5px 0; text-align:left; font-weight:bold !important;}',
                'th { padding:0 7px; text-align:left; font-weight:bold !important;}',
                'td { padding:0 7px;}',
            '</style>',
            '<table style="width:100%;">',
                '<caption><span class="blue">' + form.method + '</span> name:<span class="red">' + form.name + '</span> =>' + form.action + '</caption>',
                '<tr><th style="width:15%;"> Name</th><th style="width:15%;"> Type</th><th> Value</th></tr>'];

        form.member.forEach(function(e){
            var uid = (e.uid < 10) ? e.uid + " " : e.uid;
            html.push('<tr><td>'+ uid + ' ' + e.name + '</td><td>| ' + e.type + '</td><td>| ' + e.value + '</td></tr>');
        });

        html.push(
            '</table>'
        );

        return html.join('');
    };

    var get_forms = function(){
        var r = [];
        var f = doc.forms;
        for(let i = 0, l = f.length; i < l; i++){
            r.push(new Form(f[i], i));
        }
        return r;
    };

    var select = function(a, q){
        var cand = [];
        for(let i = 0, l = a.length; i < l; i++){
            if(a[i].name == q || a[i].uid == q){
                cand.push(a[i]);
                break;
            }else if(new RegExp("^"+q).test(a[i].name)){
                cand.push(a[i]);
            }
        }
        if(cand.length == 1){
            return cand[0];
        }else{
            return null;
        }
    };

    liberator.commands.addUserCommand(
        ["pls"],
        "Listing current value of forms",
        function(q){
            doc = window.content.document;
            var forms = get_forms();

            var html = null;
            if(q){
                let form = select(forms, q);
                if(form) html = form.html;
            }else if(forms.length){
                html = "";
                forms.forEach(function(f){
                    html += f.html;
                    html += "<br/>";
                });
            }

            if(html){
                liberator.commandline.echo(html,
                    liberator.commandline.HL_NORMAL, liberator.commandline.FORCE_MULTILINE);
            }else{
                liberator.echoerr("Form not found");
            }

            /*
            forms.forEach(function(f){
                liberator.log(f, 9);
                f.member.forEach(function(m){
                    liberator.log(m, 9);
                });
            });
            */
            window.content.vimp_param_editor_forms = forms;
        },
        null, true
    );

    liberator.commands.addUserCommand(
        ["pe[dit]"],
        "Edit value of a form element",
        function(q, submit){
            var _ = q.match(/^\s*([^.\s]+)\.([^=\s]+)\s*=\s*(.*)$/);
            if(!_){
                liberator.echoerr("Failed to parse query");
                return;
            }
            var [, f, m, v] = _;
            //liberator.log([f, m, v], 9);

            doc = window.content.document;
            var forms = window.content.vimp_param_editor_forms || get_forms();
            var form = select(forms, f);
            if(form == null){
                liberator.echoerr("Form not found");
                return;
            }
            //liberator.log(form, 9);

            var mem = select(form.member, m);
            if(mem == null){
                liberator.echoerr("FormMember not found");
                return;
            }
            //liberator.log(mem, 9);

            mem.value = v;

            if(submit){
                form.e.submit();
            }
        },
        null, true
    );

})();