var PLUGIN_INFO = {NAME} Manage Vimperator Plugins Vimpeatorプラグインの管理 teramako 0.4 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 name = fromUTF8Octets(info.toString()); var xml = <>{name}; if (info.@mail.toString() != '') xml += <> <'} highlight="URL">{info.@mail}>; if (info.@homepage.toString() != '') xml += <> ({makeLink(info.@homepage.toString())}); 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){ return updatePlugin(plugin, true); } function updatePlugin(plugin, checkOnly){ var [localResource, serverResource, store] = getResourceInfo(plugin); var localDate = Date.parse(localResource['Last-Modified']) || 0; var serverDate = Date.parse(serverResource.headers['Last-Modified']) || 0; var data = { 'Local Version': plugin.info.version || 'unknown', 'Local Last-Modified': localResource['Last-Modified'] || 'unkonwn', 'Local Path': plugin[0][1] || 'unknown', 'Server Latest Version': serverResource.version || 'unknown', 'Server Last-Modified': serverResource.headers['Last-Modified'] || 'unknown', 'Update URL': plugin.info.updateURL || '-' }; if (checkOnly) return template.table(plugin.name, data); if (!plugin.info.version || !serverResource.version){ data.Information = 'unknown version.'; } else if (plugin.info.version == serverResource.version && localResource['Last-Modified'] == serverResource.headers['Last-Modified']){ data.Information = 'up to date.'; } else if (plugin.info.version > serverResource.version || localDate > serverDate){ data.information = 'local version is newest.'; } else { data.Information = overwritePlugin(plugin, serverResource); localResource = {}; // cleanup pref. localResource['Last-Modified'] = serverResource.headers['Last-Modified']; store.set(plugin.name, localResource); store.save(); } return template.table(plugin.name, data); } function getResourceInfo(plugin){ var store = storage.newMap('plugins-pluginManager', true); var url = plugin.info.updateURL; var localResource = store.get(plugin.name) || {}; var serverResource = { version: '', source: '', headers: {} }; if (url){ let xhr = util.httpGet(url); let version = ''; let source = xhr.responseText || ''; let headers = {}; try { xhr.getAllResponseHeaders().split(/\r?\n/).forEach(function(h){ var pair = h.split(': '); if (pair && pair.length > 1) { headers[pair.shift()] = pair.join(''); } }); } catch(e){} let m = /\bPLUGIN_INFO[ \t\r\n]*=[ \t\r\n]*]*)?>([\s\S]+?)<\/VimperatorPlugin[ \t\r\n]*>/(source); if (m){ m = m[1].replace(/(?:))*\]\]|--(?:[^-]|-(?!-))*--)>)+/g, ''); m = /^[\w\W]*?]*)?>([^<]+)<\/version[ \t\r\n]*>/(m); if (m){ version = m[1]; } } serverResource = {version: version, source: source, headers: headers}; } if (!localResource['Last-Modified']){ localResource['Last-Modified'] = serverResource.headers['Last-Modified']; store.set(plugin.name, localResource); } return [localResource, serverResource, store]; } function overwritePlugin(plugin, serverResource){ if (!plugin[0] || plugin[0][0] != 'path') return 'plugin localpath was not found.'; var source = serverResource.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(/\r\n|[\r\n]/); this.preCount = 0; this.pendingMode = ''; this.xmlstack = new HTMLStack(); } WikiParser.prototype = { inlineParse: function(str){ function replacer(str) ({ '<': '<', '>': '>', '&': '&' })[str] || ''+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)){
            let 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
  1. {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; let 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 (let 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 abbr acronym b basefont bdo big br button cite code dfn em font i iframe img inout kbd label map object q s samp script select small span strike strong sub sup textarea tt u var'.split(' '), isInline: function(xml) xml.length() > 1 || xml.nodeKind() == 'text' || this.inlineElements.indexOf(xml.localName()) >= 0, 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'){ let 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){ let 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){ context.title = ['PluginName', '[Version]Description']; context.completions = getPlugins().map(function(plugin) [ plugin.name, '[' + (plugin.info.version || 'unknown') + ']' + (plugin.info.description || '-') ]).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: