diff --git a/dot.vimperator/colors/zenburn.vimp b/dot.vimperator/colors/zenburn.vimp new file mode 100644 index 0000000..efd3c60 --- /dev/null +++ b/dot.vimperator/colors/zenburn.vimp @@ -0,0 +1,65 @@ +" ==Vimperator_Color_Scheme== +" name: myzenburn +" ==MyZenburn_Colorscheme_Settings== +hi Bell border: 0 none; background-color: black; +hi Boolean color: #CC9393; +hi CmdLine font-family: monospace; padding: 1px; font-size: 10pt; font-family: monospace; font-weight: normal; padding: 0px 3px; + +hi CmdOutput white-space: pre; +hi CompDesc color: #d0d0d0; width: 50%;font-size: 10pt; +hi CompGroup:not(:first-of-type) margin-top: .5em; +hi CompIcon width: 16px; min-width: 16px; display: inline-block; margin-right: .5ex; +hi CompIcon>img max-width: 16px; max-height: 16px; vertical-align: middle; +hi CompItem font-size: 11px; +hi CompItem>* padding: 0 .5ex; +hi CompItem[selected] color: #ffffd7; background: #444444; +" hi CompLess text-align: center; height: 0; line-height: .5ex; padding-top: 1ex; +" hi CompLess::after content: "\2303" /* Unicode up arrowhead */ +" hi CompMore text-align: center; height: .5ex; line-height: .5ex; margin-bottom: -.5ex; +" hi CompMore::after content: "\2304" /* Unicode down arrowhead */ +hi CompMsg font-style: italic; margin-left: 16px; +hi CompResult width: 45%; overflow: hidden;font-size: 10pt; font-family: monospace; font-weight: normal; +hi CompTitle color: #87af87; background: #242421; font-weight: bold; +hi CompTitle>* padding: 0 .5ex; +hi ErrorMsg color: #242421; background: #CC9393; font-weight: bold; +hi Filter color: #ffffd7; background: #444444; +hi FrameIndicator background-color: #CC9393; opacity: 0.5; z-index: 999; position: fixed; top: 0; bottom: 0; left: 0; right: 0; +hi Function color: navy; +" hi Gradient height: 1px; margin-bottom: -1px; margin-top: -1px; +" hi GradientLeft background-color: #87af87; +" hi GradientRight background-color: #242421; +hi Hint font-family: monospace; font-size: 20px; color: #385F38; background-color: #F8F893; border-color: #F8F893; border-width: 2px; border-style: solid; padding 0px 1px 0px 1px; +hi Hint::after content: attr(number); +hi HintActive background: #F18C96; color: #333333; +hi HintElem background-color: #8FAF9F; color: #000D18; +hi HintImage opacity: .5; +hi Indicator color: #94BFF3; +hi InfoMsg color: #DCDCCD; background: #242421; +hi LineNr color: orange; background: #242421; +hi Message white-space: normal; min-width: 100%; padding-left: 2em; text-indent: -2em; display: block; +hi ModeMsg color: #DCDCCD; background: #242421; +hi MoreMsg color: #9ECE9E; background: #242421; +hi NonText color: #94BFF3; min-height: 16px; padding-left: 2px; +hi Normal color: #d7d7d7; background: #242421; +hi Null color: #94BFF3; +hi Number color: #94BFF3; +hi Object color: maroon; +hi Preview color: gray; +hi Question color: #9ECE9E; background: #242421; font-weight: bold; +" hi Search font-size: inherit; padding: 0; color: #385F38; background-color: #F8F893; padding: 0; +hi StatusLine color: #afaf87; background: #3a3a3a; font-size: 10pt; font-family: monospace; font-weight: bold; padding: 0px 3px; +" hi StatusLineBroken color: #af8787; background: #3a3a3a; +" hi StatusLineSecure color: #87af87; background: #3a3a3a; +hi String color: #9ECE9E; +" hi TabIconNumber font-weight: normal; color: #DCDCCD; text-align: center; text-shadow: black -1px 0 1px, black 0 1px 1px, black 1px 0 1px, black 0 -1px 1px; +hi TabNumber font-weight: bold; margin: 0; padding-right: .3ex; +hi Tag color: #94BFF3; +hi Title color: #87af87; background: #242421; font-weight: bold; +hi URL text-decoration: none; color: #9ECE9E; background: inherit; +hi URL:hover text-decoration: underline; cursor: pointer; +hi WarningMsg color: #CC9393; background: #242421; + +style -name statusbar chrome://* < * { margin: 0 !important; padding: 0 2px 0 0 !important; } +EOM diff --git a/dot.vimperator/plugin/_libly.js b/dot.vimperator/plugin/_libly.js new file mode 100644 index 0000000..db17233 --- /dev/null +++ b/dot.vimperator/plugin/_libly.js @@ -0,0 +1,749 @@ +/*** BEGIN LICENSE BLOCK {{{ + Copyright (c) 2008 suVene + Copyright (c) 2008-2011 anekos + + 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 = + + libly(filename _libly.js) + Vimperator plugins library? + 適当なライブラリっぽいものたち。 + suVene + anekos + MIT + 0.1.38 + 2.3pre + https://github.com/vimpr/vimperator-plugins/raw/master/_libly.js + || + { + original: オリジナルの関数 + current: 現在の関数 + restore: 元に戻すための関数 + } + ||< +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): + ※1 + ※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ヘッダの値を指定できる(省略可) + >|| + { + 'Referer' : '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 +addEventListener(name, func): + イベントリスナを登録する。 + name: + 'success': + 成功時 + 'failure': + 失敗を表すステータスコードが返ってきた時 + 'exception': + 例外発生時 + 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 読込み成功したら、強制的にキャッシュと置き換えるの作って! + + ]]> +; +//}}} +//if (!liberator.plugins.libly) { + +liberator.plugins.libly = {}; +var libly = liberator.plugins.libly; + +// XXX for backward compatibillity +function fixEventName(name) { + return name.replace(/^on/, '').toLowerCase(); +} + +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 () { + function getPluginPath () { + let pluginPath; + Error('hoge').stack.split(/\n/).some( + function (s) + let (m = s.match(/^(?:\(\))?@chrome:\/\/liberator\/content\/liberator\.js -> (.+):\d+$/)) + (m && (pluginPath = m[1].replace(/\?.*$/, ''))) + ); + return pluginPath; + } + + let restores = {}; + + return function (obj, name, func, autoRestore) { + let original; + let restore = function () obj[name] = original; + if (autoRestore) { + let pluginPath = getPluginPath(); + if (pluginPath) { + restores[pluginPath] = + (restores[pluginPath] || []).filter( + function (res) ( + res.object != obj || + res.name != name || + (res.restore() && false) + ) + ); + restores[pluginPath].push({ + object: obj, + name: name, + restore: restore + }); + } else { + liberator.echoerr('getPluginPath failed'); + } + } + original = obj[name]; + let current = obj[name] = function () { + let self = this, args = arguments; + return func.call(self, function (_args) original.apply(self, _args || args), args); + }; + libly.$U.extend(current, {original: original && original.original || original, restore: restore}); + return libly.$U.extend({ + original: original, + current: current, + restore: restore + }, [original, 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.File(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\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]*?]*)?>[\s]*|<\/body[ \t\r\n]*>[\S\s]*$/ig, ''); + let fragment = uhService.parseFragment(text, false, null, root); + let doctype = document.implementation.createDocumentType('html', '-//W3C//DTD HTML 4.01//EN', 'http://www.w3.org/TR/html4/strict.dtd'); + let htmlFragment = document.implementation.createDocument(null, 'html', doctype); + 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) { + name = fixEventName(name); + try { + if (typeof this.observers[name] == 'undefined') this.observers[name] = []; + this.observers[name].push(func); + } catch (e) { + if (!this.fireEvent('exception', new libly.Response(this), e)) throw e; + } + }, + fireEvent: function(name, args, asynchronous) { + name = fixEventName(name); + 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); + + var stateChangeException; + this.transport.onreadystatechange = libly.$U.bind(this, function () { + try { + this._onStateChange(); + } catch (e) { + stateChangeException = e; + } + }); + this.setRequestHeaders(); + this.transport.overrideMimeType(this.options.mimetype || 'text/html; charset=' + this.options.encoding); + + this.body = this.method == 'POST' ? this.options.postBody : null; + + this.transport.send(this.body); + + if (!this.options.asynchronous && stateChangeException) throw stateChangeException; + + // Force Firefox to handle ready state 4 for synchronous requests + if (!this.options.asynchronous && this.transport.overrideMimeType) + this._onStateChange(); + + } catch (e) { + if (!this.fireEvent('exception', 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(this.isSuccess() ? 'success' : 'failure', res, this.options.asynchronous); + } catch (e) { + if (!this.fireEvent('exception', 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.responseXML = this.transport.responseXML; + } + + 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-' + encodeURIComponent(this.dbname) + '-items'; + var store = storage.newMap(STORE_KEY, {store: 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/' + encodeURIComponent(this.dbname) + '/items.json'); + req.addEventListener('success', libly.$U.bind(this, function(res) { + var text = res.responseText; + if (!text) { + errDispatcher('response 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('failure', function() errDispatcher('onFailure', cache)); + req.addEventListener('exception', function() errDispatcher('onException', cache)); + req.get(); + } +}; +//}}} + +//} +// vim: set fdm=marker sw=4 ts=4 sts=0 et: + diff --git a/dot.vimperator/plugin/alert.js b/dot.vimperator/plugin/alert.js new file mode 100644 index 0000000..a4e05d5 --- /dev/null +++ b/dot.vimperator/plugin/alert.js @@ -0,0 +1,476 @@ +/* {{{ +################################################################################### +# SL 部分は、Takahito Yagami さんの著作権物です。 # +# JavaScriptでSLを走らせる「SL.JS」を作りました ::: creazy photograph # +# http://creazy.net/2008/02/sl_js.html # +################################################################################### +}}} */ + +// PLUGIN_INFO {{{ +let PLUGIN_INFO = + + Alert + アラート + Displays an alert after the specified time. + 指定時間後にアラートダイアログを出したりする。タイマー。 + 1.01 + anekos + 2.0pre + 2.0pre + | | ] ...: + +; +// }}} + +(function () { + + //////////////////////////////////////////////////////////////////////////////// + // SL - copyright (c) Takahito Yagami + //////////////////////////////////////////////////////////////////////////////// + + let sl = function (next) { + var document = content.document; + /** + * SL.JS + * + * # execute bookmarklet below + * javascript:(function(){var d=document,sl_open,sl_run,sl_close,s=d.createElement('script');s.charset='UTF-8';s.src='http://labs.creazy.net/sl/bookmarklet.js';d.body.appendChild(s)})(); + * + * @author Takahito Yagami (a.k.a yager) + * @version v1.0.0 2008/02/16 + */ + (function(){ + //------------------------------------------------------------ + // Setting (You can chage options in this block) + //------------------------------------------------------------ + var sl_speed = 100; + var sl_pitch = 15; + var sl_tx_color = "#FFFFFF"; + var sl_bg_color = "#000000"; + //------------------------------------------------------------ + + //------------------------------------------------------------ + // SL Parts + //------------------------------------------------------------ + var sl_steam = []; + sl_steam[0] + =" (@@) ( ) (@) ( ) @@ () @ O @ O @
" + +" ( )
" + +" (@@@@)
" + +" ( )
" + +"
" + +" (@@@)
"; + sl_steam[1] + =" ( ) (@@) ( ) (@) () @@ O @ O @ O
" + +" (@@@)
" + +" ( )
" + +" (@@@@)
" + +"
" + +" ( )
"; + + var sl_body + =" ==== ________ ___________
" + +" _D _| |_______/ \\__I_I_____===__|_________|
" + +" |(_)--- | H\\________/ | | =|___ ___| _________________
" + +" / | | H | | | | ||_| |_|| _| \\_____A
" + +" | | | H |__--------------------| [___] | =| |
" + +" | ________|___H__/__|_____/[][]~\\_______| | -| |
" + +" |/ | |-----------I_____I [][] [] D |=======|____|________________________|_
"; + + var sl_wheels = []; + sl_wheels[0] + ="__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__|__________________________|_
" + +" |/-=|___|= || || || |_____/~\\___/ |_D__D__D_| |_D__D__D_|
" + +" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ \\_/ \\_/ \\_/ \\_/
"; + sl_wheels[1] + ="__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__|__________________________|_
" + +" |/-=|___|=O=====O=====O=====O |_____/~\\___/ |_D__D__D_| |_D__D__D_|
" + +" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ \\_/ \\_/ \\_/ \\_/
"; + sl_wheels[2] + ="__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__|__________________________|_
" + +" |/-=|___|= || || || |_____/~\\___/ |_D__D__D_| |_D__D__D_|
" + +" \\_/ \\O=====O=====O=====O_/ \\_/ \\_/ \\_/ \\_/ \\_/
"; + sl_wheels[3] + ="__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__|__________________________|_
" + +" |/-=|___|= || || || |_____/~\\___/ |_D__D__D_| |_D__D__D_|
" + +" \\_/ \\_O=====O=====O=====O/ \\_/ \\_/ \\_/ \\_/ \\_/
"; + sl_wheels[4] + ="__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__|__________________________|_
" + +" |/-=|___|= O=====O=====O=====O|_____/~\\___/ |_D__D__D_| |_D__D__D_|
" + +" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ \\_/ \\_/ \\_/ \\_/
"; + sl_wheels[5] + ="__/ =| o |=-~O=====O=====O=====O\\ ____Y___________|__|__________________________|_
" + +" |/-=|___|= || || || |_____/~\\___/ |_D__D__D_| |_D__D__D_|
" + +" \\_/ \\__/ \\__/ \\__/ \\__/ \\_/ \\_/ \\_/ \\_/ \\_/
"; + + sl_steam[0] = sl_steam[0].replace(/ /g,' '); + sl_steam[1] = sl_steam[1].replace(/ /g,' '); + sl_body = sl_body.replace(/ /g,' '); + sl_wheels[0] = sl_wheels[0].replace(/ /g,' '); + sl_wheels[1] = sl_wheels[1].replace(/ /g,' '); + sl_wheels[2] = sl_wheels[2].replace(/ /g,' '); + sl_wheels[3] = sl_wheels[3].replace(/ /g,' '); + sl_wheels[4] = sl_wheels[4].replace(/ /g,' '); + sl_wheels[5] = sl_wheels[5].replace(/ /g,' '); + + var sl_patterns = []; + sl_patterns[0] = sl_steam[0] + sl_body + sl_wheels[0]; + sl_patterns[1] = sl_steam[0] + sl_body + sl_wheels[1]; + sl_patterns[2] = sl_steam[0] + sl_body + sl_wheels[2]; + sl_patterns[3] = sl_steam[1] + sl_body + sl_wheels[3]; + sl_patterns[4] = sl_steam[1] + sl_body + sl_wheels[4]; + sl_patterns[5] = sl_steam[1] + sl_body + sl_wheels[5]; + + //------------------------------------------------------------ + // SL Initialize + //------------------------------------------------------------ + var sl_counter = 0; + var sl_position = 0; + var scrollTop = document.body.scrollTop || document.documentElement.scrollTop; + if (window.opera||document.layers) { + var windowWidth = window.innerWidth; + } else if (document.all) { + var windowWidth = document.body.clientWidth; + } else if(document.getElementById){ + var windowWidth = window.innerWidth; + } + var sl_style_base + ='display: block;' + +'position: absolute;' + +'text-align: left;' + +'overflow: visible;' + +'white-space: pre;' + +'font: 12px/12px monospace;'; + + var sl_style_main + =sl_style_base + +'top: '+(scrollTop+100)+'px;' + +'left: '+windowWidth+'px;' + +'padding: 20px;' + +'z-index: 999;' + +'color: '+sl_tx_color+';'; + + document.body.innerHTML += '
'+sl_patterns[0]+'
'; + + var sl_w = document.getElementById("__sl_main__").clientWidth; + var sl_h = document.getElementById("__sl_main__").clientHeight; + + var sl_style_background + =sl_style_base + +'top: '+(scrollTop+100)+'px;' + +'left: 0px;' + +'width: '+windowWidth+'px;' + +'height: '+sl_h+'px;' + +'z-index: 998;' + +'background-color: '+sl_bg_color+';' + +'filter: alpha(opacity=0);' + +'-moz-opacity: 0.0;' + +'opacity: 0.0;'; + + document.body.innerHTML += '

'; + + //------------------------------------------------------------ + // Actions + //------------------------------------------------------------ + var sl_bg_counter = 0; + + /** + * sl_open (gradually open background) + */ + sl_open = function() { + var oid = "__sl_background__"; + var op = sl_bg_counter; + var ua = navigator.userAgent + document.getElementById(oid).style.filter = 'alpha(opacity=' + (op * 10) + ')'; + document.getElementById(oid).style.MozOpacity = op / 10; + document.getElementById(oid).style.opacity = op / 10; + if ( sl_bg_counter < 8 ) { + sl_bg_counter++; + setTimeout('sl_open()',100); + } else { + sl_run(); + } + + } + + /** + * sl_run (move a train) + */ + sl_run = function() { + document.getElementById("__sl_main__").innerHTML = sl_patterns[sl_counter]; + document.getElementById("__sl_main__").style.left = windowWidth - sl_position + "px"; + if ( sl_counter < 5 ) { + sl_counter++; + } else { + sl_counter = 0; + } + sl_position += sl_pitch; + if ( sl_w + (windowWidth - sl_position) < 0 ) { + sl_counter = 0; + sl_position = 0; + document.body.removeChild(document.getElementById("__sl_main__")); + sl_close(); + } else { + setTimeout('sl_run()',sl_speed); + } + } + + /** + * sl_close (gradually close background) + */ + sl_close = function() { + var oid = "__sl_background__"; + var op = sl_bg_counter; + var ua = navigator.userAgent + document.getElementById(oid).style.filter = 'alpha(opacity=' + (op * 10) + ')'; + document.getElementById(oid).style.MozOpacity = op / 10; + document.getElementById(oid).style.opacity = op / 10; + if ( sl_bg_counter > 0 ) { + sl_bg_counter--; + setTimeout('sl_close()',100); + } else { + next(); + document.body.removeChild(document.getElementById(oid)); + } + } + + // start actions ! + sl_open(); + + })(); + }; + //////////////////////////////////////////////////////////////////////////////// + // END OF SL + //////////////////////////////////////////////////////////////////////////////// + + let gv = liberator.globalVariables; + + let defaults = { + methods: (gv.alert_default_methods || 'alert').split(/\W+/), + time: parseFloat(gv.alert_default_time || '3'), + message: gv.alert_default_message || 'Time out!', + }; + + let maxMeow = parseInt(liberator.globalVariables.alert_mex_meow || '60', 10); + + let sound = Cc["@mozilla.org/sound;1"].createInstance(Ci.nsISound); + sound.init(); + + let gunsou = 'data:image/gif;base64,'+ + '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'+ + 'J04QDASAfeIRAgA7'; + + function nop () + undefined; + + function singularize (fs, msg) { + if (!fs.length) + return nop; + let [f, arg] = fs[0]; + return function () { + f(singularize(fs.slice(1), msg), msg, arg); + }; + } + + let alertMethods = { + alert: function (next, msg) { + window.alert(msg); + next(); + }, + pokimon: function (next, msg, arg) { + let times = parseInt(arg || '10', 10); + let colors = ['red', 'blue', 'yellow']; + let elem = content.document.body; + let indicator = elem.appendChild(elem.ownerDocument.createElement('div')); + let rect = elem.getBoundingClientRect(); + indicator.id = 'nyantoro-element-indicator'; + let style = 'background-color: ' + colors[0] + ';' + + 'opacity: 0.5; z-index: 999;' + + 'position: fixed;' + + 'top: ' + 0 + 'px;' + + 'height: ' + content.innerHeight + 'px;' + + 'left: ' + 0 + 'px;' + + 'width: ' + content.innerWidth + 'px'; + indicator.setAttribute('style', style); + let (count = 0) { + let handle = setInterval( + function () { + if (count++ < times) { + indicator.style.backgroundColor = colors[count % colors.length]; + } else { + clearInterval(handle); + elem.removeChild(indicator); + next(); + } + }, + 100 + ); + } + }, + gunsou: function (next, msg, arg) { + let sleep = parseFloat(arg || 3) * 1000; + let sz = innerWidth / msg.length / 1.5; + liberator.echo( +
+ + + + + +
{msg}
+
+ ); + setTimeout(next, sleep); + }, + meow: function (next, msg, arg) { + let times = Math.min(parseInt(arg || '3', 10), maxMeow); + let handle = setInterval( + function () { + if (times--) { + sound.play(makeURI('http://www.kurinton.net/~snca/files/meow.wav')); + } else { + clearInterval(handle); + next(); + } + }, + 1000 + ); + }, + quit: function (next, msg) { + liberator.quit(true); + next(); + }, + SL: function (next, msg) { + sl(next); + } + }; + + + defaults.methods = defaults.methods.map( + function (it) { + let [_, name, arg] = it.match(/^-?(\w+)(?:=(.*))?$/); + return [alertMethods[name] || nop, arg]; + } + ); + + commands.addUserCommand( + ['alert'], + 'Timer alert (:alert [