var PLUGIN_INFO =
{NAME}
Manage Vimperator Plugin
Vimpeatorプラグインの管理
teramako
0.2
2.0pre
2.0pre
http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/pluginManager.js
||
    var PLUGIN_INFO = ...
||<
とE4X形式でXMLを記述してください
各要素は下記参照
=== 要素 ===
name:
    プラグイン名
description:
    簡易説明
    属性langに"ja"などと言語を指定するとFirefoxのlocaleに合わせたものになります。
author:
    製作者名
    属性mailにe-mail、homepageにURLを付けるとリンクされます
license:
    ライセンスについて
    属性documentにURLを付けるとリンクされます
version:
    プラグインのバージョン
maxVersion:
    プラグインが使用できるVimperatorの最大バージョン
minVersion:
    プラグインが使用できるVimperatorの最小バージョン
updateURL:
    プラグインの最新リソースURL
detail:
    ここにコマンドやマップ、プラグインの説明
    CDATAセクションにwiki的に記述可能
== Wiki書式 ==
見出し:
    - == heading1 == で第一見出し(h1)
    - === heading2 === で第二見出し(h2)
    - ==== heading3 ==== で第三見出し(h3)
リスト:
    - "- "を先頭につけると箇条書きリスト(ul)になります。
      - 改行が可能
        >||
            - 改行
              可能
        ||<
        の場合
        - 改行
          可能
        となります。
      - ネスト可能
    - "+ "を先頭につけると番号付きリスト(ol)になります。
      仕様は箇条書きリストと同じです。
定義リスト:
    - 末尾が":"で終わる行は定義リスト(dl,dt)になります。
    - 次行をネストして始めるとdd要素になります。
    - これもネスト可能です。
整形式テキスト:
    >|| と ||< で囲むと整形式テキスト(pre)になります。
    コードなどを書きたい場合に使用できるでしょう。
インライン:
    - mailtoとhttp、httpsスキームのURLはリンクになります
== ToDo ==
- 更新通知
- スタイルの追加(これはすべき?)
]]>
;
liberator.plugins.pluginManager = (function(){
function id(value) value;
var lang = window.navigator.language;
var tags = {
    name: function(info) fromUTF8Octets(info.toString()),
    author: function(info){
        var xml = <>{fromUTF8Octets(info.toString())}>;
        if (info.@homepage.toString() != '')
            xml += <> {makeLink(info.@homepage.toString())}>;
        if (info.@mail.toString() != '')
            xml += <> ({makeLink("mailto:"+info.@mail)})>;
        return xml;
    },
    description: function(info) makeLink(fromUTF8Octets(info.toString())),
    license: function(info){
        var xml = <>{fromUTF8Octets(info.toString())}>;
        if (info.@document.toString() != '')
            xml += <> {makeLink(info.@document.toString())}>;
        return xml;
    },
    version: id,
    maxVersion: id,
    minVersion: id,
    updateURL: function(info) makeLink(info.toString(), true),
    detail: function(info){
        if (info.* && info.*[0] && info.*[0].nodeKind() == 'element')
            return info.*;
        var text = fromUTF8Octets(info.*.toString());
        var parser = new WikiParser(text);
        var xml = parser.parse();
        return xml;
    }
};
function chooseByLang(elems){
    if (!elems)
        return null;
    function get(lang){
        var i = elems.length();
        while (i-->0){
            if (elems[i].@lang.toString() == lang)
                return elems[i];
        }
    }
    return get(lang) || get(lang.split('-', 2).shift()) || get('') ||
           get('en-US') || get('en') || elems[0] || elems;
}
for (let it in Iterator(tags)){
    let [name, value] = it;
    tags[name] = function(info){
        if (!info[name])
            return null;
        return value.call(tags, chooseByLang(info[name]));
    };
}
function makeLink(str, withLink){
    var href = withLink ? '$&' : '#';
    return XMLList(str.replace(/(?:https?:\/\/|mailto:)\S+/g, '$&'));
}
function fromUTF8Octets(octets){
    return decodeURIComponent(octets.replace(/[%\x80-\xFF]/g, function(c){
        return '%' + c.charCodeAt(0).toString(16);
    }));
}
function getPlugins(){
    var list = [];
    var contexts = liberator.plugins.contexts;
    for (let path in contexts){
        let context = contexts[path];
        let info = context.PLUGIN_INFO || null;
        let plugin = [
            ['path', path]
        ];
        plugin['name'] = context.NAME;
        plugin['info'] = {};
        plugin['orgInfo'] = {};
        if (info){
            for (let tag in tags){
                plugin.orgInfo[tag] = info[tag];
                let value = tags[tag](info);
                if (value && value.toString().length > 0){
                    plugin.push([tag, value]);
                    plugin.info[tag] = value;
                }
            }
        }
        list.push(plugin);
    }
    return list;
}
function itemFormater(plugin, showDetails){
    if (showDetails)
        return template.table(plugin.name, plugin);
    var data = plugin.filter(function($_) $_[0] != 'detail');
    return template.table(plugin.name, data);
}
function checkVersion(plugin){
    var data = {
        "Current Version": plugin.info.version || 'unknown',
        "Latest Version": getLatestVersion(plugin.info.updateURL)['version'] || 'unknown',
        "Update URL": plugin.info.updateURL || '-'
    };
    return template.table(plugin.name, data);
}
function getLatestVersion(url){
    if (!url) return {};
    var source = util.httpGet(url).responseText || '';
    var version = '';
    source.match(/PLUGIN_INFO[\s\S]*[\s\S]*(.*)<\/version>[\s\S]*<\/VimperatorPlugin>/);
    version = RegExp.$1;
    return {source: source, version: version};
}
function updatePlugin(plugin){
    var currentVersion = plugin.info.version || '';
    var latestResource = getLatestVersion(plugin.info.updateURL) || '';
    var data = {
        information: '',
        "Current Version": plugin.info.version || 'unknown',
        "Local Path": plugin[0][1] || 'unknown',
        "Latest Version": latestResource.version || 'unknown',
        "Update URL": plugin.info.updateURL || '-'
    };
    if (!currentVersion || !latestResource.version){
        data.information = 'unknown version.';
    } else if (currentVersion == latestResource.version){
        data.information = 'already latest.';
    } else if (currentVersion > latestResource.version){
        data.information = 'local version is newest.';
    } else {
        data.information = overwritePlugin(plugin, latestResource);
    }
    return template.table(plugin.name, data);
}
function overwritePlugin(plugin, latestResource){
    if (!plugin[0] || plugin[0][0] != 'path')
        return 'plugin localpath notfound.';
    var source = latestResource.source;
    var localpath = plugin[0][1];
    var file = io.getFile(localpath);
    if (!source)
        return 'source is null.';
    try {
        io.writeFile(file, source);
    } catch (e){
        liberaotr.log("Could not write to " + file.path + ": " + e.message);
        return "E190: Cannot open " + filename.quote() + " for writing";
    }
    try {
        io.source(localpath);
    } catch (e){
        return e.message;
    }
    return 'update complete.';
}
function WikiParser(text){
    this.mode = '';
    this.lines = text.split(/\n\r|[\r\n]/);
    this.preCount = 0;
    this.pendingMode = '';
    this.xmlstack = new HTMLStack();
}
WikiParser.prototype = {
    inlineParse: function(str){
        function replacer(str){
            switch(str){
                case '<': return '<';
                case '>': return '>';
                case '&': return '&';
                default:
                    return ''+str+'';
            }
        }
        return XMLList(str.replace(/>|<|&|(?:https?:\/\/|mailto:)\S+/g, replacer));
    },
    wikiReg: { // {{{
        hn: /^(={2,4})\s*(.*?)\s*(\1)$/,
        dt: /^(.*)\s*:$/,
        ul: /^\-\s+(.*)$/,
        ol: /^\+\s+(.*)$/,
        preStart: /^>\|\|$/,
        preEnd: /^\|\|<$/,
    }, // }}}
    blockParse: function(line, prevMode){ // {{{
        if (prevMode == 'pre'){
            if (this.wikiReg.preEnd.test(line)){
                if (this.preCount > 0){
                    this.preCount--;
                    return <>{line}>;
                } else {
                    this.mode = '';
                    return <>>;
                }
                return <>{line}>;
            } else if (this.wikiReg.preStart.test(line)){
                this.preCount++;
            }
            return <>{line}>;
        } else if (this.wikiReg.preStart.test(line)){
            this.mode = 'pre';
            this.pendingMode = prevMode;
            return ;
        } else if (this.wikiReg.hn.test(line)){
            var hn = RegExp.$1.length - 1;
            this.mode = '';
            return {this.inlineParse(RegExp.$2)};
        } else if (this.wikiReg.ul.test(line)){
            this.mode = 'ul';
            return - {this.inlineParse(RegExp.$1)}
 
;
        } else if (this.wikiReg.ol.test(line)){
            this.mode = 'ol';
            return - {this.inlineParse(RegExp.$1)}
 
;
        } else if (this.wikiReg.dt.test(line)){
            this.mode = 'dl';
            return - {this.inlineParse(RegExp.$1)}
 
;
        } else if (prevMode == 'dl'){
            return <>{this.inlineParse(line)}>;
        }
        this.mode = '';
        return <>{this.inlineParse(line)}>;
    }, // }}}
    parse: function(){
        var ite = Iterator(this.lines);
        var num, line, indent;
        var currentIndent = 0, indentList = [0], nest=0;
        var prevMode = "";
        var stack = [];
        var nest;
        var isNest = false;
        var bufXML;
        //try {
        for ([num, line] in ite){
            [,indent, line] = line.match(/^(\s*)(.*)\s*$/);
            currentIndent = indent.length;
            var prevIndent = indentList[indentList.length -1];
            bufXML = this.blockParse(line, prevMode);
            if (prevMode == 'pre'){
                if (this.mode){
                    this.xmlstack.appendLastChild(indent.substr(prevIndent) + line + "\n");
                } else {
                    this.xmlstack.reorg(-2);
                    this.mode = this.pendingMode;
                    indentList.pop();
                    if (indentList.length == 0) indentList = [0];
                }
                prevMode = this.mode;
                continue;
            }
            if (!line){
                //this.xmlstack.append(<>{"\n"}>);
                continue;
            }
            if (currentIndent > prevIndent){
                if (this.mode){
                    if (prevMode == 'dl'){
                        this.xmlstack.appendChild();
                    }
                    this.xmlstack.push(bufXML);
                    indentList.push(currentIndent);
                } else {
                    if (prevMode && this.xmlstack.length > 0){
                        this.xmlstack.appendLastChild(bufXML);
                    } else {
                        this.xmlstack.append(bufXML);
                    }
                    this.mode = prevMode;
                }
            } else if (currentIndent < prevIndent){
                for (var i in indentList){
                    if (currentIndent == indentList[i] || currentIndent < indentList[i+1]){ nest = i; break; }
                }
                indentList.splice(nest);
                indentList.push(currentIndent);
                this.xmlstack.reorg(nest);
                this.xmlstack.append(bufXML);
            } else {
                this.xmlstack.append(bufXML);
            }
            prevMode = this.mode;
        }
        //} catch (e){ alert(num + ":"+ e); }
        this.xmlstack.reorg();
        return this.xmlstack.last;
    }
};
function HTMLStack(){
    this.stack = [];
}
HTMLStack.prototype = {
    get length() this.stack.length,
    get last() this.stack[this.length-1],
    get lastLocalName() this.last[this.last.length()-1].localName(),
    get inlineElements() ['a','b','i','code','samp','dfn','kbd','br','em','strong','sub','sup','img','span'],
    isInline: function(xml){
        return (xml.length() > 1 || xml.nodeKind() == 'text' || this.inlineElements.indexOf(xml.localName()) >= 0) ?  true : false;
    },
    push: function(xml) this.stack.push(xml),
    append: function(xml){
        if (this.length == 0){
            this.push(xml);
            return xml;
        }
        var buf = this.last[this.last.length()-1];
        if (buf.nodeKind() == 'text'){
            this.last[this.last.length()-1] += this.isInline(xml) ? <>
{xml}> : xml;
        } else {
            if(this.isInline(xml)){
                this.stack[this.length-1] += xml;
            } else if (buf.localName() == xml.localName()){
                buf.* += xml.*;
            } else {
                this.stack[this.length-1] += xml;
            }
        }
        return this.last;
    },
    appendChild: function(xml){
        var buf = this.stack[this.length-1];
        buf[buf.length()-1].* += xml;
        return this.last;
    },
    appendLastChild: function(xml){
        var buf = this.last[this.last.length()-1].*;
        if (buf.length() > 0 && buf[buf.length()-1].nodeKind() == 'element'){
            var tmp = buf[buf.length()-1].*;
            if (tmp[tmp.length()-1].nodeKind() == 'element'){
                buf[buf.length()-1].* += xml;
            } else {
                buf[buf.length()-1].* += <>
{xml}>;
            }
        } else {
            this.last[this.last.length()-1].* += xml;
        }
        return this.last;
    },
    reorg: function(from){
        if (this.length == 0) return;
        if (!from) from = 0;
        var xmllist = this.stack.splice(from);
        var xml;
        if (xmllist.length > 1){
            xml = xmllist.reduceRight(function(p, c){
                var buf = c[c.length()-1].*;
                if (buf.length() > 0){
                    if (buf[buf.length()-1].nodeKind() == 'text'){
                        c += p;
                    } else {
                        buf[buf.length()-1].* += p;
                    }
                } else {
                    c += p;
                }
                return c;
            });
        } else if (xmllist.length > 0){
            xml = xmllist[0];
        }
        this.push(xml);
        return this.last;
    },
};
commands.addUserCommand(['plugin[help]'], 'list Vimperator plugins',
    function(args){
        liberator.plugins.pluginManager.list(args);
    }, {
        argCount: '*',
        options: [
            [['-verbose', '-v'], commands.OPTION_NOARG],
            [['-check', '-c'], commands.OPTION_NOARG],
            [['-update', '-u'], commands.OPTION_NOARG],
        ],
        completer: function(context){
            var all = getPlugins().map(function(plugin){
                let ver = plugin.info.version || 'unknown';
                let desc = plugin.info.description || '-';
                return [plugin.name, '[' + ver + ']' + desc];
            });
            context.title = ['PluginName', '[Version]Description'];
            context.completions = all.filter(function(row) row[0].toLowerCase().indexOf(context.filter.toLowerCase()) >= 0);
        }
    }, true);
var public = {
    list: function(args){
        var names = args;
        var showDetails = args['-verbose'];
        var check = args['-check'];
        var update = args['-update'];
        var xml = <>>;
        var plugins = getPlugins();
        var action = itemFormater;
        var params = [showDetails];
        if (check){
            action = checkVersion;
        } else if (update){
            action = updatePlugin;
        }
        if (names.length){
            names.forEach(function(name){
                let plugin = plugins.filter(function(plugin) plugin.name == name)[0];
                if (plugin){
                    xml += action.apply(this, [plugin].concat(params));
                }
            });
        } else {
            plugins.forEach(function(plugin) xml += action.apply(this, [plugin].concat(params)));
        }
        liberator.echo(xml, true);
    }
};
return public;
})();
// vim: sw=4 ts=4 et fdm=marker: