var PLUGIN_INFO =
{NAME}
Manage Vimperator Plugin
Vimpeatorプラグインの管理
teramako
0.2
2.0pre
2.0pre
||
    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の最小バージョン
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 = <>{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 = <>{info.toString()}>;
        if (info.@document.toString() != '')
            xml += <> {makeLink(info.@document.toString())}>;
        return xml;
    },
    version: id,
    maxVersion: id,
    minVersion: id,
    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){
    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;
        if (info){
            for (let tag in tags){
                let value = tags[tag](info);
                if (value && value.toString().length > 0)
                    plugin.push([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 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[0], args['-verbose']);
    }, {
        argCount: '*',
        options: [
            [['-verbose', '-v'], commands.OPTION_NOARG],
        ],
        completer: function(context){
            var all = getPlugins().map(function(plugin){
                let desc = '-';
                for (let i=plugin.length; i-->0;){
                    if (plugin[i][0] == 'description'){
                        desc = plugin[i][1];
                        break;
                    }
                }
                return [plugin.name, desc];
            });
            context.title = ['PluginName', 'Description'];
            context.completions = all.filter(function(row) row[0].toLowerCase().indexOf(context.filter.toLowerCase()) >= 0);
        }
    }, true);
var public = {
    list: function(name, showDetails){
        var xml = <>>;
        var plugins = getPlugins();
        if (name){
            let plugin = plugins.filter(function(plugin) plugin.name == name)[0];
            if (plugin){
                xml = itemFormater(plugin, showDetails);
            }
        } else {
            plugins.forEach(function(plugin) xml += itemFormater(plugin, showDetails));
        }
        liberator.echo(xml, true);
    }
};
return public;
})();
// vim: sw=4 ts=4 et fdm=marker: