--- /dev/null
+/*** BEGIN LICENSE BLOCK {{{
+ Copyright (c) 2008 suVene<suvene@zeromemory.info>
+
+ distributable under the terms of an MIT-style license.
+ http://www.opensource.jp/licenses/mit-license.html
+}}} END LICENSE BLOCK ***/
+// PLUGIN_INFO//{{{
+var PLUGIN_INFO =
+<VimperatorPlugin>
+ <name>libly(filename _libly.js)</name>
+ <description>Vimperator plugins library?</description>
+ <description lang="ja">適当なライブラリっぽいものたち。</description>
+ <author mail="suvene@zeromemory.info" homepage="http://zeromemory.sblo.jp/">suVene</author>
+ <license>MIT</license>
+ <version>0.1.24</version>
+ <minVersion>1.2</minVersion>
+ <maxVersion>2.0pre</maxVersion>
+ <updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/_libly.js</updateURL>
+ <detail><![CDATA[
+== Objects ==
+- liberator.plugins.libly.$U
+- liberator.plugins.libly.Request
+- liberator.plugins.libly.Response
+- liberator.plugins.libly.Wedata
+
+== Logger ==
+getLogger(prefix):
+ log(msg, level), echo(msg, flg), echoerr(msg)
+ のメソッドを持つ logger インスタンスを取得します。
+ ログの書式は prefix + ': ' + yyyy/MM/dd + msg となります。
+
+== Object Utility ==
+extend(dst, src):
+ オブジェクトを拡張します。
+A(iterable):
+ オブジェクトを配列にします。
+around(obj, name, func):
+ obj がもつ name 関数を、func に置き換えます。
+ func は
+ function (next, args) {...}
+ という形で呼ばれます。
+ next はオリジナルの関数を呼び出すための関数、
+ args はオリジナルの引数列です。
+ next には引数を渡す必要はありません。
+bind(obj, func):
+ func に obj を bind します。
+ func内からは this で obj が参照できるようになります。
+eval(text):
+ Sandbox による、window.eval を極力利用するようにします。
+ Snadbox が利用できない場合は、unsafe な window の eval が直接利用されます。
+evalJson(str, toRemove):
+ str を decode します。
+ toRemove が true の場合、文字列の前後を1文字削除します。
+ "(key:value)" 形式の場合などに true を指定して下さい。
+dateFormat(dtm, fmt):
+ Date型インスタンスを、指定されたフォーマットで文字列に変換します。
+ fmt を省略した場合、"%y/%M/%d %h:%m:%s" となります。
+runnable(generator):
+ gererator を実行し、再帰的に resume する為の引数を渡します。
+
+== Browser ==
+getSelectedString:
+ window の選択文字列を返却します。
+getUserAndPassword(hostname, formSubmitURL, username):
+ login-manager から [username, password] を取得します。
+ 引数の username が省略された場合、検索された 1件目を返却します。
+ データが存在しない場合は、null を返却します。
+
+== System ==
+readDirectory(path, fileter, func):
+ path で指定したディレクトリから、filter で指定された正規表現に match する場合、
+ func をファイル名を引数にコールバックします。
+ filter は Function を指定することも可能です。
+
+== HTML, XML, DOM, E4X ==
+pathToURL(a, baseURL, doc):
+ 相対パスを絶対パスに変換します。
+getHTMLFragment(html):
+ <html>※1</html>
+ ※1 の文字列を取得します。
+stripTags(str, tags):
+ str から tags で指定されたタグを取り除いて返却します。
+ tags は文字列、または配列で指定して下さい。
+createHTMLDocument(str, xmlns):
+ 引数 str より、HTMLFragment を作成します。
+getFirstNodeFromXPath(xpath, context):
+ xpath を評価しオブジェクトをを返却します。
+getNodesFromXPath(xpath, context, callback, thisObj):
+ xpath を評価し snapshot の配列を返却します。
+xmlSerialize(xml):
+ xml を文字列化します。
+xmlToDom(node, doc, nodes):
+ for vimperator1.2.
+ @see vimperator2.0pre util.
+getElementPosition(elem):
+ elem の offset を返却します。
+ return {top: 0, left: 0}
+toStyleText(style):
+ スタイルが格納されているオブジェクトを
+ >||
+ position: fixed;
+ left: 10px;
+ ||<
+ のような文字列に変換します。
+
+== Object Request ==
+Request(url, headers, options):
+ コンストラクタ
+ url:
+ HTTPリクエスト先のURL
+ headers:
+ 以下のようにHTTPヘッダの値を指定できる(省略可)
+ >||
+ {
+ 'Referrer' : 'http://example.com/'
+ }
+ ||<
+ 以下の値はデフォルトで設定される('Content-type'はPOST時のみ)
+ >||
+ {
+ 'Accept': 'text/javascript, application/javascript, text/html, application/xhtml+xml, application/xml, text/xml, */*;q=0.1',
+ 'Content-type': 'application/x-www-form-urlencoded; charset=' + options.encodingの値
+ }
+ ||<
+
+ options:
+ オプションとして以下のようなオブジェクトを指定できる(省略可)
+ asynchronous:
+ true: 同期モード/false: 非同期モード(デフォルト:true)
+ encoding:
+ エンコーディング(デフォルト: 'UTF-8')
+ username:
+ BASIC認証時のuser名
+ password:
+ BASIC認証時のパスワード
+ postBody:
+ POSTメソッドにより送信するbody
+addEventLister(name, func):
+ イベントリスナを登録する。
+ name:
+ 'onSuccess':
+ 成功時
+ 'onFailure':
+ 失敗を表すステータスコードが返ってきた時
+ 'onException':
+ 例外発生時
+ func:
+ イベント発火時の処理
+ 引数として以下Responseオブジェクトが渡される
+get():
+ GETメソッドによりHTTPリクエストを発行する。
+post():
+ POSTメソッドによりHTTPリクエストを発行する。
+
+== Object Response ==
+HTTPレスポンスを表すオブジェクト
+req:
+ レスポンスと対となるRequestオブジェクト
+doc:
+ レスポンスから生成されたHTMLDocumentオブジェクト
+isSuccess():
+ ステータスコードが成功を表していればtrue、失敗であればfalse
+getStatus():
+ ステータスコードを取得する
+getStatusText():
+ ステータを表す文字列を取得する
+getHTMLDocument(xpath, xmlns, ignoreTags, callback, thisObj):
+ レスポンスからHTMLDocumentオブジェクトを生成し、xpath を評価した結果の snapshot の配列を返す
+
+== Object Wedata ==
+~/vimperator/info/profile_name/plugins-libly-wedata-?????
+に store されます。
+getItems(expire, itemCallback, finalCallback):
+ インスタンス作成時に指定した dbname から、item を読込みます。
+=== TODO ===
+clearCache:
+ wedata 読込み成功したら、強制的にキャッシュと置き換えるの作って!
+
+ ]]></detail>
+</VimperatorPlugin>;
+//}}}
+//if (!liberator.plugins.libly) {
+
+liberator.plugins.libly = {};
+var libly = liberator.plugins.libly;
+
+libly.$U = {//{{{
+ // Logger {{{
+ getLogger: function(prefix) {
+ return new function() {
+ this.log = function(msg, level) {
+ if (typeof msg == 'object') msg = util.objectToString(msg);
+ liberator.log(libly.$U.dateFormat(new Date()) + ': ' + (prefix || '') + ': ' + msg, (level || 0));
+ };
+ this.echo = function(msg, flg) {
+ flg = flg || commandline.FORCE_MULTILINE;
+ // this.log(msg);
+ liberator.echo(msg, flg);
+ };
+ this.echoerr = function(msg) {
+ this.log('error: ' + msg);
+ liberator.echoerr(msg);
+ };
+ }
+ },
+ // }}}
+ // Object Utility {{{
+ extend: function(dst, src) {
+ for (let prop in src)
+ dst[prop] = src[prop];
+ return dst;
+ },
+ A: function(iterable) {
+ var ret = [];
+ if (!iterable) return ret;
+ if (typeof iterable == 'string') return [iterable];
+ if (!(typeof iterable == 'function' && iterable == '[object NodeList]') &&
+ iterable.toArray) return iterable.toArray();
+ if (typeof iterable.length != 'undefined') {
+ for (let i = 0, len = iterable.length; i < len; ret.push(iterable[i++]));
+ } else {
+ for each (let item in iterable) ret.push(item);
+ }
+ return ret;
+ },
+ around: function around (obj, name, func) {
+ let next = obj[name];
+ let current = obj[name] = function () {
+ let self = this, args = arguments;
+ return func.call(self, function () next.apply(self, args), args);
+ };
+ return [next, current];
+ },
+ bind: function(obj, func) {
+ return function() {
+ return func.apply(obj, arguments);
+ }
+ },
+ eval: function(text) {
+ var fnc = window.eval;
+ var sandbox;
+ try {
+ sandbox = new Components.utils.Sandbox("about:blank");
+ if (Components.utils.evalInSandbox('true', sandbox) === true) {
+ fnc = function(text) { return Components.utils.evalInSandbox(text, sandbox); };
+ }
+ } catch (e) { liberator.log('warning: _libly.js is working with unsafe sandbox.'); }
+
+ return fnc(text);
+ },
+ evalJson: function(str, toRemove) {
+ var json;
+ try {
+ json = Components.classes['@mozilla.org/dom/json;1'].getService(Components.interfaces.nsIJSON);
+ if (toRemove) str = str.substring(1, str.length - 1);
+ return json.decode(str);
+ } catch (e) { return null; }
+ },
+ dateFormat: function(dtm, fmt) {
+ var d = {
+ y: dtm.getFullYear(),
+ M: dtm.getMonth() + 1,
+ d: dtm.getDate(),
+ h: dtm.getHours(),
+ m: dtm.getMinutes(),
+ s: dtm.getSeconds(),
+ '%': '%'
+ };
+ for (let [n, v] in Iterator(d)) {
+ if (v < 10)
+ d[n] = '0' + v;
+ }
+ return (fmt || '%y/%M/%d %h:%m:%s').replace(/%([yMdhms%])/g, function (_, n) d[n]);
+ },
+ /**
+ * example)
+ * $U.runnable(function(resume) {
+ * // execute asynchronous function.
+ * // goto next yield;
+ * var val = yield setTimeout(function() { resume('value!'), 1000) });
+ * alert(val); // value!
+ * yield;
+ * });
+ */
+ runnable: function(generator) {
+ var it = generator(function(value) {
+ try { it.send(value); } catch (e) {}
+ });
+ it.next();
+ },
+ // }}}
+ // Browser {{{
+ getSelectedString: function() {
+ return (new XPCNativeWrapper(window.content.window)).getSelection().toString();
+ },
+ getUserAndPassword: function(hostname, formSubmitURL, username) {
+ var passwordManager, logins;
+ try {
+ passwordManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+ logins = passwordManager.findLogins({}, hostname, formSubmitURL, null);
+ if (logins.length) {
+ if (username) {
+ for (let i = 0, len = logins.lengh; i < len; i++) {
+ if (logins[i].username == username)
+ return [logins[i].username, logins[i].password]
+ }
+ liberator.log(this.dateFormat(new Date()) +': [getUserAndPassword] username notfound');
+ //throw 'username notfound.';
+ return [];
+ } else {
+ return [logins[0].username, logins[0].password];
+ }
+ } else {
+ liberator.log(this.dateFormat(new Date()) + ': [getUserAndPassword] account notfound');
+ return [];
+ }
+ } catch (e) {
+ liberator.log(this.dateFormat(new Date()) + ': [getUserAndPassword] error: ' + e, 0);
+ return null;
+ }
+ },
+ // }}}
+ // System {{{
+ readDirectory: function(path, filter, func) {
+ var d = io.getFile(path);
+ if (d.exists() && d.isDirectory()) {
+ let enm = d.directoryEntries;
+ let flg = false;
+ while (enm.hasMoreElements()) {
+ let item = enm.getNext();
+ item.QueryInterface(Components.interfaces.nsIFile);
+ flg = false;
+ if (typeof filter == 'string') {
+ if ((new RegExp(filter)).test(item.leafName)) flg = true;
+ } else if (typeof filter == 'function') {
+ flg = filter(item);
+ }
+ if (flg) func(item);
+ }
+ }
+ },
+ // }}}
+ // HTML, XML, DOM, E4X {{{
+ pathToURL: function(a, baseURL, doc) {
+ if (!a) return '';
+ var XHTML_NS = "http://www.w3.org/1999/xhtml";
+ var XML_NS = "http://www.w3.org/XML/1998/namespace";
+ //var path = (a.href || a.getAttribute('src') || a.action || a.value || a);
+ var path = (a.getAttribute('href') || a.getAttribute('src') || a.action || a.value || a);
+ if (/^https?:\/\//.test(path)) return path;
+ var link = (doc || window.content.documtent).createElementNS(XHTML_NS, 'a');
+ link.setAttributeNS(XML_NS, 'xml:base', baseURL);
+ link.href = path;
+ return link.href;
+ },
+ getHTMLFragment: function(html) {
+ if (!html) return html;
+ return html.replace(/^[\s\S]*?<html(?:[ \t\n\r][^>]*)?>|<\/html[ \t\r\n]*>[\S\s]*$/ig, '');
+ },
+ stripTags: function(str, tags) {
+ var ignoreTags = '(?:' + [].concat(tags).join('|') + ')';
+ return str.replace(new RegExp('<' + ignoreTags + '(?:[ \\t\\n\\r][^>]*|/)?>([\\S\\s]*?)<\/' + ignoreTags + '[ \\t\\r\\n]*>', 'ig'), '');
+ },
+ createHTMLDocument: function(str, xmlns, doc) {
+ let root = document.createElementNS("http://www.w3.org/1999/xhtml", "html");
+ let uhService = Cc["@mozilla.org/feed-unescapehtml;1"].getService(Ci.nsIScriptableUnescapeHTML);
+ let text = str.replace(/^[\s\S]*?<body([ \t\n\r][^>]*)?>[\s]*|<\/body[ \t\r\n]*>[\S\s]*$/ig, '');
+ let fragment = uhService.parseFragment(text, false, null, root);
+ let htmlFragment = document.implementation.createDocument(null, 'html', null);
+ htmlFragment.documentElement.appendChild(htmlFragment.importNode(fragment,true));
+ return htmlFragment;
+ /* うまく動いていない場合はこちらに戻してください
+ doc = doc || window.content.document;
+ var htmlFragment = doc.implementation.createDocument(null, 'html', null);
+ var range = doc.createRange();
+ range.setStartAfter(doc.body);
+ htmlFragment.documentElement.appendChild(htmlFragment.importNode(range.createContextualFragment(str), true));
+ return htmlFragment;
+ */
+ },
+ getFirstNodeFromXPath: function(xpath, context) {
+ if (!xpath) return null;
+ context = context || window.content.document;
+ var result = (context.ownerDocument || context).evaluate(xpath, context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
+ return result.singleNodeValue || null;
+ },
+ getNodesFromXPath: function(xpath, context, callback, thisObj) {
+ var ret = [];
+ if (!xpath) return ret;
+ context = context || window.content.document;
+ var nodesSnapshot = (context.ownerDocument || context).evaluate(xpath, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (let i = 0, l = nodesSnapshot.snapshotLength; i < l; i++) {
+ if (typeof callback == 'function') callback.call(thisObj, nodesSnapshot.snapshotItem(i), i);
+ ret.push(nodesSnapshot.snapshotItem(i));
+ }
+ return ret;
+ },
+ xmlSerialize: function(xml) {
+ try {
+ return (new XMLSerializer()).serializeToString(xml)
+ .replace(/<!--(?:[^-]|-(?!->))*-->/g, '')
+ .replace(/<\s*\/?\s*\w+/g, function(all) all.toLowerCase());
+ } catch (e) { return '' }
+ },
+ xmlToDom: function xmlToDom(node, doc, nodes)
+ {
+ XML.prettyPrinting = false;
+ switch (node.nodeKind())
+ {
+ case "text":
+ return doc.createTextNode(node);
+ case "element":
+ let domnode = doc.createElementNS(node.namespace(), node.localName());
+ for each (let attr in node.@*)
+ domnode.setAttributeNS(attr.name() == "highlight" ? NS.uri : attr.namespace(), attr.name(), String(attr));
+ for each (let child in node.*)
+ domnode.appendChild(arguments.callee(child, doc, nodes));
+ if (nodes && node.@key)
+ nodes[node.@key] = domnode;
+ return domnode;
+ }
+ },
+ getElementPosition: function(elem) {
+ var offsetTrail = elem;
+ var offsetLeft = 0;
+ var offsetTop = 0;
+ while (offsetTrail) {
+ offsetLeft += offsetTrail.offsetLeft;
+ offsetTop += offsetTrail.offsetTop;
+ offsetTrail = offsetTrail.offsetParent;
+ }
+ offsetTop = offsetTop || null;
+ offsetLeft = offsetLeft || null;
+ return {top: offsetTop, left: offsetLeft};
+ },
+ toStyleText: function(style) {
+ var result = '';
+ for (let name in style) {
+ result += name.replace(/[A-Z]/g, function (c) ('-' + c.toLowerCase())) +
+ ': ' +
+ style[name] +
+ ';\n';
+ }
+ return result;
+ }
+ // }}}
+};
+//}}}
+
+libly.Request = function() {//{{{
+ this.initialize.apply(this, arguments);
+};
+libly.Request.EVENTS = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+libly.Request.requestCount = 0;
+libly.Request.prototype = {
+ initialize: function(url, headers, options) {
+ this.url = url;
+ this.headers = headers || {};
+ this.options = libly.$U.extend({
+ asynchronous: true,
+ encoding: 'UTF-8'
+ }, options || {});
+ this.observers = {};
+ },
+ addEventListener: function(name, func) {
+ try {
+ if (typeof this.observers[name] == 'undefined') this.observers[name] = [];
+ this.observers[name].push(func);
+ } catch (e) {
+ if (!this.fireEvent('onException', new libly.Response(this), e)) throw e;
+ }
+ },
+ fireEvent: function(name, args, asynchronous) {
+ if (!(this.observers[name] instanceof Array)) return false;
+ this.observers[name].forEach(function(event) {
+ if (asynchronous) {
+ setTimeout(event, 10, args);
+ } else {
+ event(args);
+ }
+ });
+ return true;
+ },
+ _complete: false,
+ _request: function(method) {
+
+ try {
+ libly.Request.requestCount++;
+
+ this.method = method;
+ this.transport = new XMLHttpRequest();
+ this.transport.open(method, this.url, this.options.asynchronous, this.options.username, this.options.password);
+
+ this.transport.onreadystatechange = libly.$U.bind(this, this._onStateChange);
+ this.setRequestHeaders();
+ this.transport.overrideMimeType('text/html; charset=' + this.options.encoding);
+
+ this.body = this.method == 'POST' ? this.options.postBody : null;
+
+ this.transport.send(this.body);
+
+ // Force Firefox to handle ready state 4 for synchronous requests
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this._onStateChange();
+
+ } catch (e) {
+ if (!this.fireEvent('onException', new libly.Response(this), e)) throw e;
+ }
+ },
+ _onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !(readyState == 4 && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) { return 0; }
+ },
+ isSuccess: function() {
+ var status = this.getStatus();
+ return !status || (status >= 200 && status < 300);
+ },
+ respondToReadyState: function(readyState) {
+ var state = libly.Request.EVENTS[readyState];
+ var res = new libly.Response(this);
+
+ if (state == 'Complete') {
+ libly.Request.requestCount--;
+ try {
+ this._complete = true;
+ this.fireEvent('on' + (this.isSuccess() ? 'Success' : 'Failure'), res, this.options.asynchronous);
+ } catch (e) {
+ if (!this.fireEvent('onException', res, e)) throw e;
+ }
+ }
+ },
+ setRequestHeaders: function() {
+ var headers = {
+ 'Accept': 'text/javascript, application/javascript, text/html, application/xhtml+xml, application/xml, text/xml, */*;q=0.1'
+ };
+
+ if (this.method == 'POST') {
+ headers['Content-type'] = 'application/x-www-form-urlencoded' +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ if (this.transport.overrideMimeType) {
+ let year = parseInt((navigator.userAgent.match(/\bGecko\/(\d{4})/) || [0, 2005])[1], 10);
+ if (0 < year && year < 2005)
+ headers['Connection'] = 'close';
+ }
+ }
+
+ for (let key in this.headers)
+ if (this.headers.hasOwnProperty(key)) headers[key] = this.headers[key];
+
+ for (let name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+ get: function() {
+ this._request('GET');
+ },
+ post: function() {
+ this._request('POST');
+ }
+};//}}}
+
+libly.Response = function() {//{{{
+ this.initialize.apply(this, arguments);
+};
+libly.Response.prototype = {
+ initialize: function(req) {
+ this.req = req;
+ this.transport = req.transport;
+ this.isSuccess = req.isSuccess;
+ this.readyState = this.transport.readyState;
+
+ if (this.readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = (this.transport.responseText == null) ? '' : this.transport.responseText;
+ }
+
+ this.doc = null;
+ this.htmlFragmentstr = '';
+ },
+ status: 0,
+ statusText: '',
+ getStatus: libly.Request.prototype.getStatus,
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) { return ''; }
+ },
+ getHTMLDocument: function(xpath, xmlns, ignoreTags, callback, thisObj) {
+ if (!this.doc) {
+ //if (doc.documentElement.nodeName != 'HTML') {
+ // return new DOMParser().parseFromString(str, 'application/xhtml+xml');
+ //}
+ this.htmlFragmentstr = libly.$U.getHTMLFragment(this.responseText);
+ this.htmlStripScriptFragmentstr = libly.$U.stripTags(this.htmlFragmentstr, ignoreTags);
+ this.doc = libly.$U.createHTMLDocument(this.htmlStripScriptFragmentstr, xmlns);
+ }
+ if (!xpath) xpath = '//*';
+ return libly.$U.getNodesFromXPath(xpath, this.doc, callback, thisObj);
+ }
+};
+//}}}
+
+libly.Wedata = function(dbname) { // {{{
+ this.initialize.apply(this, arguments);
+};
+libly.Wedata.prototype = {
+ initialize: function(dbname) {
+ this.HOST_NAME = 'http://wedata.net/';
+ this.dbname = dbname;
+ this.logger = libly.$U.getLogger('libly.Wedata');
+ },
+ getItems: function(expire, itemCallback, finalCallback) {
+
+ var logger = this.logger;
+ var STORE_KEY = 'plugins-libly-wedata-' + this.dbname + '-items';
+ var store = storage.newMap(STORE_KEY, true);
+ var cache = store && store.get('data');
+
+ if (store && cache && new Date(store.get('expire')) > new Date()) {
+ logger.log('return cache. ');
+ cache.forEach(function(item) { if (typeof itemCallback == 'function') itemCallback(item); });
+ if (typeof finalCallback == 'function')
+ finalCallback(true, cache);
+ return;
+ }
+
+ expire = expire || 0;
+
+ function errDispatcher(msg, cache) {
+ if (cache) {
+ logger.log('return cache. -> ' + msg);
+ cache.forEach(function(item) { if (typeof itemCallback == 'function') itemCallback(item); });
+ if (typeof finalCallback == 'function')
+ finalCallback(true, cache);
+ } else {
+ logger.log(msg + ': cache notfound.');
+ if (typeof finalCallback == 'function')
+ finalCallback(false, msg);
+ }
+ }
+
+ var req = new libly.Request(this.HOST_NAME + 'databases/' + this.dbname + '/items.json');
+ req.addEventListener('onSuccess', libly.$U.bind(this, function(res) {
+ var text = res.responseText;
+ if (!text) {
+ errDispatcher('respons is null.', cache);
+ return;
+ }
+ var json = libly.$U.evalJson(text);
+ if (!json) {
+ errDispatcher('failed eval json.', cache);
+ return;
+ }
+ logger.log('success get wedata.');
+ store.set('expire', new Date(new Date().getTime() + expire).toString());
+ store.set('data', json);
+ store.save();
+ json.forEach(function(item) { if (typeof itemCallback == 'function') itemCallback(item); });
+ if (typeof finalCallback == 'function')
+ finalCallback(true, json);
+ }));
+ req.addEventListener('onFailure', function() errDispatcher('onFailure', cache));
+ req.addEventListener('onException', function() errDispatcher('onException', cache));
+ req.get();
+ }
+};
+//}}}
+
+//}
+// vim: set fdm=marker sw=4 ts=4 sts=0 et:
+
--- /dev/null
+/*** BEGIN LICENSE BLOCK {{{
+ Copyright (c) 2008 suVene<suvene@zeromemory.info>
+
+ distributable under the terms of an MIT-style license.
+ http://www.opensource.jp/licenses/mit-license.html
+}}} END LICENSE BLOCK ***/
+// PLUGIN_INFO//{{{
+var PLUGIN_INFO =
+<VimperatorPlugin>
+ <name>{NAME}</name>
+ <description>request, and the result is displayed to the buffer.</description>
+ <description lang="ja">リクエストの結果をバッファに出力する。</description>
+ <author mail="suvene@zeromemory.info" homepage="http://zeromemory.sblo.jp/">suVene</author>
+ <version>0.4.15</version>
+ <license>MIT</license>
+ <minVersion>2.0pre</minVersion>
+ <maxVersion>2.1pre</maxVersion>
+ <updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/multi_requester.js</updateURL>
+ <detail><![CDATA[
+== Needs Library ==
+- _libly.js(ver.0.1.19)
+ @see http://coderepos.org/share/browser/lang/javascript/vimperator-plugins/trunk/_libly.js
+
+== Usage ==
+>||
+command[!] subcommand [ANY_TEXT]
+||<
+- ! create new tab.
+- ANY_TEXT your input text
+
+e.g.)
+>||
+:mr alc[,goo,any1,any2…] ANY_TEXT -> request by the input text, and display to the buffer.
+:mr! goo[,any1,any2,…] {window.selection} -> request by the selected text, and display to the new tab.
+||<
+
+== Custumize .vimperatorrc ==
+=== Command(default [ mr ]) ===
+>||
+let g:multi_requester_command = "ANY1, ANY2, ……"
+or
+liberator.globalVariables.multi_requester_command = [ ANY1, ANY2, …… ];
+||<
+
+=== Default Sites (default undefined) ===
+>||
+liberator.globalVariables.multi_requester_default_sites = "alc,goo"
+||<
+These sites(subcommands) will be used, if this variable has been defined and you do not specify subcommands.
+
+=== SITEINFO ===
+e.g.)
+>||
+javascript <<EOM
+liberator.globalVariables.multi_requester_siteinfo = [
+ {
+ map: ",me", // optional: keymap for this siteinfo call
+ bang: true, // optional:
+ args: "any" // optional:
+ name: "ex", // required: subcommand name
+ description: "example", // required: commandline short help
+ url: "http://example.com/?%s", // required: %s <-- replace string
+ xpath: "//*", // optional: default all
+ srcEncode: "SHIFT_JIS", // optional: default UTF-8
+ urlEncode: "SHIFT_JIS", // optional: default srcEncode
+ ignoreTags: "img", // optional: default script, syntax "tag1,tag2,……"
+ extractLink: "//xpath" // optional: extract permalink
+ },
+];
+EOM
+||<
+
+=== other siteinfo by wedata. ===
+ @see http://wedata.net/databases/Multi%20Requester/items
+
+=== Mappings ===
+e.g.)
+>||
+javascript <<EOM
+liberator.globalVariables.multi_requester_mappings = [
+ [ ",ml", "ex" ], // == :mr ex
+ [ ",mg", "goo", "!" ], // == :mr! goo
+ [ ",ma", "alc", , "args" ], // == :mr alc args
+];
+EOM
+||<
+
+=== Other Options ===
+>||
+let g:multi_requester_use_wedata = "false" // true by default
+||<
+wedata を利用しない場合は false を設定してください。
+>||
+let g:multi_requester_default_sites = 'alc';
+||<
+subcommand を省略した場合に利用されるサイトを設定します。
+>||
+let g:multi_requester_order = 'count'; // date by default
+||<
+補完の順番を設定します。(大きい順に並びます)
+"count" または "date" を設定してください。
+
+ ]]></detail>
+</VimperatorPlugin>;
+//}}}
+(function() {
+if (!liberator.plugins.libly) {
+ liberator.log("multi_requester: needs _libly.js");
+ return;
+}
+
+// global variables {{{
+var DEFAULT_COMMAND = [ "mr" ];
+var SITEINFO = [
+ {
+ name: "alc",
+ description: "SPACE ALC (\u82F1\u8F9E\u6717 on the Web)",
+ url: "http://eow.alc.co.jp/%s/UTF-8/",
+ xpath: 'id("resultList")'
+ }
+];
+var libly = liberator.plugins.libly;
+var $U = libly.$U;
+var logger = $U.getLogger("multi_requester");
+var mergedSiteinfo = {};
+var store = storage.newMap('plugins-multi_requester', true);
+//}}}
+
+// Vimperator plugin command register {{{
+var CommandRegister = {
+ register: function(cmdClass, siteinfo) {
+ cmdClass.siteinfo = siteinfo;
+
+ commands.addUserCommand(
+ cmdClass.name,
+ cmdClass.description,
+ $U.bind(cmdClass, cmdClass.cmdAction),
+ {
+ completer: cmdClass.cmdCompleter || function(context, arg) {
+ if (arg.length > 1)
+ return;
+ context.title = [ "Name", "Descprition" ];
+ var sorted = siteinfo.sort(function(a, b)
+ typeof liberator.globalVariables.multi_requester_order == "undefined" ||
+ liberator.globalVariables.multi_requester_order == "date" ? store.get(b.name).lastPostTime - store.get(a.name).lastPostTime :
+ liberator.globalVariables.multi_requester_order == "count" ? store.get(b.name).count - store.get(a.name).count :
+ store.get(b.name).lastPostTime - store.get(a.name).lastPostTime);
+ var filters = context.filter.split(",");
+ var prefilters = filters.slice(0, filters.length - 1);
+ var prefilter = !prefilters.length ? "" : prefilters.join(",") + ",";
+ var subfilters = sorted.filter(function(s) prefilters.every(function(p) s.name != p));
+ var allSuggestions = subfilters.map(function(s) [prefilter + s.name, s.description]);
+ context.completions = context.filter
+ ? allSuggestions.filter(function(s) s[0].indexOf(context.filter) == 0)
+ : allSuggestions;
+ },
+ options: cmdClass.cmdOptions,
+ argCount: cmdClass.argCount || undefined,
+ bang: cmdClass.bang || true,
+ count: cmdClass.count || false
+ },
+ true // replace
+ );
+
+ },
+ addUserMaps: function(prefix, mapdef) {
+ mapdef.forEach(function([ key, command, bang, args ]) {
+ var cmd = prefix + (bang ? "! " : " ") + command + " ";
+ mappings.addUserMap(
+ [ modes.NORMAL, modes.VISUAL ],
+ [ key ],
+ "user defined mapping",
+ function() {
+ if (args) {
+ liberator.execute(cmd + args);
+ } else {
+ let sel = $U.getSelectedString();
+ if (sel.length) {
+ liberator.execute(cmd + sel);
+ } else {
+ commandline.open(":", cmd, modes.EX);
+ }
+ }
+ },
+ {
+ rhs: ":" + cmd,
+ norremap: true
+ }
+ );
+ });
+ }
+};
+//}}}
+
+// initial data access class {{{
+var DataAccess = {
+ getCommand: function() {
+ var c = liberator.globalVariables.multi_requester_command;
+ var ret;
+ if (typeof c == "string") {
+ ret = [ c ];
+ } else if (typeof c == "Array") {
+ ret = check;
+ } else {
+ ret = DEFAULT_COMMAND;
+ }
+ return ret;
+ },
+ getSiteInfo: function() {
+
+ var self = this;
+ var useWedata = typeof liberator.globalVariables.multi_requester_use_wedata == "undefined" ?
+ true : $U.eval(liberator.globalVariables.multi_requester_use_wedata);
+
+ if (liberator.globalVariables.multi_requester_siteinfo) {
+ liberator.globalVariables.multi_requester_siteinfo.forEach(function(site) {
+ if (!mergedSiteinfo[site.name]) mergedSiteinfo[site.name] = {};
+ $U.extend(mergedSiteinfo[site.name], site);
+ if (!store.get(site.name)) {
+ store.set(site.name, { count: 0, lastPostTime: (new Date()).getTime() });
+ store.save();
+ }
+ if (site.map) {
+ CommandRegister.addUserMaps(MultiRequester.name[0],
+ [[ site.map, site.name, site.bang, site.args ]]);
+ }
+ });
+ }
+
+ SITEINFO.forEach(function(site) {
+ if (!mergedSiteinfo[site.name]) mergedSiteinfo[site.name] = {};
+ $U.extend(mergedSiteinfo[site.name], site);
+ if (!store.get(site.name)) {
+ store.set(site.name, { count: 0, lastPostTime: (new Date()).getTime() });
+ store.save();
+ }
+ if (site.map) {
+ CommandRegister.addUserMaps(MultiRequester.name[0],
+ [[ site.map, site.name, site.bang, site.args ]]);
+ }
+ });
+
+ if (useWedata) {
+ logger.log("use wedata");
+ var wedata = new libly.Wedata("Multi%20Requester");
+ wedata.getItems(24 * 60 * 60 * 1000,
+ function(item) {
+ var site = item.data;
+ if (mergedSiteinfo[site.name]) return;
+ mergedSiteinfo[site.name] = {};
+ $U.extend(mergedSiteinfo[site.name], site);
+ if (!store.get(site.name)) {
+ store.set(site.name, { count: 0, lastPostTime: (new Date()).getTime() });
+ store.save();
+ }
+ },
+ function(isSuccess, data) {
+ if (!isSuccess) return;
+ CommandRegister.register(MultiRequester, $U.A(mergedSiteinfo));
+ }
+ );
+ }
+
+ return $U.A(mergedSiteinfo);
+ }
+};
+//}}}
+
+// main controller {{{
+var MultiRequester = {
+ name: DataAccess.getCommand(),
+ description: "request, and display to the buffer",
+ defaultSites: liberator.globalVariables.multi_requester_default_sites,
+ doProcess: false,
+ requestNames: "",
+ requestCount: 0,
+ echoHash: {},
+ cmdAction: function(args) { //{{{
+
+ if (MultiRequester.doProcess) return;
+
+ var bang = args.bang;
+ var count = args.count;
+
+ var parsedArgs = this.parseArgs(args);
+ if (parsedArgs.count == 0) { return; } // do nothing
+
+ MultiRequester.doProcess = true;
+ MultiRequester.requestNames = parsedArgs.names;
+ MultiRequester.requestCount = 0;
+ MultiRequester.echoHash = {};
+ var siteinfo = parsedArgs.siteinfo;
+ for (let i = 0, len = parsedArgs.count; i < len; i++) {
+
+ let info = siteinfo[i];
+ let name = info.name;
+
+ let history = store.get(name);
+ history.count++;
+ history.lastPostTime = (new Date()).getTime();
+ store.set(name, history);
+ store.save();
+
+ let url = info.url;
+ // see: http://fifnel.com/2008/11/14/1980/
+ let srcEncode = info.srcEncode || "UTF-8";
+ let urlEncode = info.urlEncode || srcEncode;
+
+ let repStrCount = let (m = url.match(/%s/g)) (m && m.length);
+ if (repStrCount && !parsedArgs.strs.length) continue;
+
+ // via. lookupDictionary.js
+ let ttbu = Components.classes["@mozilla.org/intl/texttosuburi;1"]
+ .getService(Components.interfaces.nsITextToSubURI);
+
+ let cnt = 0;
+ url = url.replace(/%s/g, function(m, i) ttbu.ConvertAndEscape(urlEncode,
+ (cnt >= parsedArgs.strs.length ? parsedArgs.strs[cnt - 1] :
+ cnt >= (repStrCount - 1) ? parsedArgs.strs.splice(cnt).join(' ') :
+ parsedArgs.strs[cnt++])));
+ logger.log(url + "[" + srcEncode + "][" + urlEncode + "]::" + info.xpath);
+
+ if (bang) {
+ liberator.open(url, liberator.NEW_TAB);
+ } else {
+ let req = new libly.Request(url, null, {
+ encoding: srcEncode,
+ siteinfo: info,
+ args: {
+ args: args,
+ bang: bang,
+ count: count
+ }
+ });
+ req.addEventListener("onException", $U.bind(this, this.onException));
+ req.addEventListener("onSuccess", $U.bind(this, this.onSuccess));
+ req.addEventListener("onFailure", $U.bind(this, this.onFailure));
+ req.get();
+ MultiRequester.requestCount++;
+ }
+ }
+
+ if (MultiRequester.requestCount) {
+ logger.echo("Loading " + parsedArgs.names + " ...", commandline.FORCE_SINGLELINE);
+ } else {
+ MultiRequester.doProcess = false;
+ }
+ },
+ // return {names: "", strs: [""], count: 0, siteinfo: [{}]}
+ parseArgs: function(args) {
+
+ var self = this;
+ var ret = {};
+ ret.names = "";
+ ret.strs = [];
+ ret.count = 0;
+ var sel = $U.getSelectedString();
+
+ if (args.length < 1 && !sel.length) return ret;
+
+ function parse(args, names) {
+ args = Array.concat(args);
+ ret.siteinfo = [];
+ ret.names = names || args.shift() || "";
+ ret.strs = (args.length < 1 ? [ sel.replace(/[\n\r]+/g, "") ] : args);
+
+ ret.names.split(",").forEach(function(name) {
+ var site = self.getSite(name);
+ if (site) {
+ ret.count++;
+ ret.siteinfo.push(site);
+ }
+ });
+ }
+
+ parse(args);
+
+ if (!ret.siteinfo.length && this.defaultSites)
+ parse(args, this.defaultSites);
+
+ return ret;
+ },
+ getSite: function(name) {
+ if (!name) this.siteinfo[0];
+ var ret = null;
+ this.siteinfo.forEach(function(s) {
+ if (s.name == name) ret = s;
+ });
+ return ret;
+ },//}}}
+ extractLink: function(res, extractLink) { //{{{
+
+ var el = res.getHTMLDocument(extractLink);
+ if (!el) throw "extract link failed.: extractLink -> " + extractLink;
+ var url = $U.pathToURL(el[0], res.req.url);
+ var req = new libly.Request(url, null, $U.extend(res.req.options, { extractLink: true }));
+ req.addEventListener("onException", $U.bind(this, this.onException));
+ req.addEventListener("onSuccess", $U.bind(this, this.onSuccess));
+ req.addEventListener("onFailure", $U.bind(this, this.onFailure));
+ req.get();
+ MultiRequester.requestCount++;
+ MultiRequester.doProcess = true;
+
+ },//}}}
+ onSuccess: function(res) { //{{{
+
+ if (!MultiRequester.doProcess) {
+ MultiRequester.requestCount = 0;
+ return;
+ }
+
+ logger.log("success!!: " + res.req.url);
+ MultiRequester.requestCount--;
+ if (MultiRequester.requestCount == 0) {
+ MultiRequester.doProcess = false;
+ }
+
+ var url, escapedUrl, xpath, doc, html, extractLink, ignoreTags;
+
+ try {
+
+ if (!res.isSuccess() || res.responseText == "") throw "response is fail or null";
+
+ url = res.req.url;
+ escapedUrl = util.escapeHTML(url);
+ xpath = res.req.options.siteinfo.xpath;
+ extractLink = res.req.options.siteinfo.extractLink;
+
+ if (extractLink && !res.req.options.extractLink) {
+ this.extractLink(res, extractLink);
+ return;
+ }
+ ignoreTags = [ "script" ].concat(libly.$U.A(res.req.options.siteinfo.ignoreTags));
+ doc = document.createElementNS(null, "div");
+ res.getHTMLDocument(xpath, null, ignoreTags, function(node, i) {
+ if (node.tagName.toLowerCase() != "html")
+ doc.appendChild(node);
+ });
+ if (!doc || !doc.childNodes.length) throw "XPath result is undefined or null.: XPath -> " + xpath;
+
+ $U.getNodesFromXPath("descendant-or-self::a | descendant-or-self::img", doc, function(node) {
+ var tagName = node.tagName.toLowerCase();
+ if (tagName == "a") {
+ node.href = $U.pathToURL(node, url, res.doc);
+ } else if (tagName == "img") {
+ node.src = $U.pathToURL(node, url, res.doc);
+ }
+ });
+
+ html = '<a href="' + escapedUrl + '" class="hl-Title" target="_self">' + escapedUrl + '</a>' +
+ $U.xmlSerialize(doc);
+
+ MultiRequester.echoHash[res.req.options.siteinfo.name] = html;
+
+ } catch (e) {
+ logger.log("error!!: " + e);
+ MultiRequester.echoHash[res.req.options.siteinfo.name] =
+ '<span style="color: red;">error!!: ' + e + '</span>';
+ }
+
+ if (MultiRequester.requestCount == 0) {
+ let echoList = [];
+ MultiRequester.requestNames.split(",").forEach(function(name) {
+ echoList.push(MultiRequester.echoHash[name]);
+ });
+ html = '<div style="white-space:normal;"><base href="' + escapedUrl + '"/>' +
+ echoList.join("") +
+ '</div>';
+ try { logger.echo(new XMLList(html)); } catch (e) { logger.log(e); logger.echo(html); }
+ }
+
+ },
+ onFailure: function(res) {
+ MultiRequester.doProcess = false;
+ logger.echoerr("request failure!!: " + res.statusText);
+ },
+ onException: function(e) {
+ MultiRequester.doProcess = false;
+ logger.echoerr("exception!!: " + e);
+ }//}}}
+};
+//}}}
+
+// boot strap {{{
+CommandRegister.register(MultiRequester, DataAccess.getSiteInfo());
+if (liberator.globalVariables.multi_requester_mappings) {
+ CommandRegister.addUserMaps(MultiRequester.name[0], liberator.globalVariables.multi_requester_mappings);
+}
+//}}}
+
+return MultiRequester;
+
+})();
+// vim: set fdm=marker sw=2 ts=2 sts=0 et:
+