diff --git a/Makefile b/Makefile index 56c72c7..92739ec 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ FILES = bashrc vimrc zshrc emacs.el gitconfig screenrc commonshrc \ - gnomerc gvimrc muttrc pentadactylrc + gnomerc gvimrc muttrc pentadactylrc vimperatorrc FILES2 = msmtprc .PHONY: install clean check diff --git a/vimperator/plugin/_libly.js b/vimperator/plugin/_libly.js new file mode 100644 index 0000000..be989d7 --- /dev/null +++ b/vimperator/plugin/_libly.js @@ -0,0 +1,737 @@ +/*** 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 = xml` + + 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(/-> liberator:\/\/template\/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) + { + return util.xmlToDom(node, doc, nodes); + }, + 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/vimperator/plugin/feedSomeKeys_3.js b/vimperator/plugin/feedSomeKeys_3.js new file mode 100644 index 0000000..fd08ec6 --- /dev/null +++ b/vimperator/plugin/feedSomeKeys_3.js @@ -0,0 +1,726 @@ +/* NEW BSD LICENSE {{{ +Copyright (c) 2010-2011, anekos. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + + +################################################################################### +# http://sourceforge.jp/projects/opensource/wiki/licenses%2Fnew_BSD_license # +# に参考になる日本語訳がありますが、有効なのは上記英文となります。 # +################################################################################### + +}}} */ + +// INFO {{{ +let INFO = +xml` + anekos + New BSD License + +

+ Feed key events directly into web contents. +

+ + :fmap + :fmap -events=eventnamelist -xpath=xpath -frame=framenumber -urls=urlpattern -modes=modes lhs rhs + +

+ Define one mapping. +

+

+ If -urls=urlpattern is given, + the mappings becomes effective mappings only on the page specifed by urlpattern. +

+
+
+ + :fmaps + :fmaps -events=eventnamelist -xpath=xpath -frame=framenumber -urls=urlpattern -pprefix=prefix -modes=modes mappingpair .... + +

+ Two or more mappings are defined at once. + mappingpair is a pair of key names separated by ",". +

e.g. "<Leader><S-j>,j"

+

+

+ If -urls=urlpattern is given, + the mappings becomes effective mappings only on the page specifed by urlpattern. +

+
+
+ + :fmapc + :fmapc! -modes=modes urlpattern + +

+ Remove the mappings matched with urlpattern. + If "!" is given, remove all mappings. +

+
+
+ + :funmap + :funmap -urls=urlpattern -modes=modes lhs + +

+ Remove the mappings. +

+

+ If you wish to remove url-local mappings, give -urls=urlpattern. +

+
+
+

urlpattern

+

+ The value of urlpattern should be regular expression. +

+

xpath

+

+ The XPath for a target element. +

+

framenumber

+

+ The number of a target frame. + Refer the completion for this number. +

+

eventnamelist

+

+ eventnamelist is the list constructed with the below values. +

+
    +
  • keypress
  • +
  • keydown
  • +
  • keyup
  • +
  • vkeypress
  • +
  • vkeydown
  • +
  • vkeyup
  • +
+

"v..." values use virtual key code.

+

The event is generated in the order of writing of each key.

+

The default value of this option is "keypress".

+

modes

+

+ List of mapping modes. (Default is NORMAL mode) +

+

fmaps examples for .vimperatorrc

+

If you input directly these commands in vimperator commandline, remove the ":lazy".

+ +:command! -nargs=+ lazy autocmd VimperatorEnter .* <args> +:lazy fmaps -u='mail\.google\.com/mail' c / j k n p o u e x s r a # [ ] ? gi gs gt gd ga gc +:lazy fmaps -u='mail\.google\.com/mail/.*/[0-9a-f]+$' c / j,n k,p n,j p,k o u e x s r a # [ ] ? gi gs gt gd ga gc +:lazy fmaps -u='www\.google\.co\.jp/reader' -events=vkeypress j k n p m s v A r S N P X O gh ga gs gt gu u / ? J K +:lazy fmaps -u='(fastladder|livedoor)\.com/reader' j k s a p o v c i,p <Space> <S-Space> z b < > q w e,g +:lazy fmaps -u='https?://www\.rememberthemilk\.com/home/' j k m i c t ? d F,f G,g S,s L,l Y,y H,h M,m <Del> <C-S-Left> <C-S-Right> +:lazy fmaps -u='http://code.google.com/p/vimperator-labs/issues/list' o j k +:lazy fmaps -u='http://code.google.com/p/vimperator-labs/issues/detail' u + +
+ + anekos + New BSD License + +

+ Web コンテンツに直接キーイベントを送ります。 +

+ + :fmap + :fmap -events=eventnamelist -xpath=xpath -frame=framenumber -urls=urlpattern -modes=modes lhs rhs + +

+ マッピングを一つ定義します。 +

+

+ -urls=urlpattern が与えられたとき、 + そのマッピングは urlpattern で指定されたページのみで有効になります。 +

+
+
+ + :fmaps + :fmaps -events=eventnamelist -xpath=xpath -frame=framenumber -urls=urlpattern -pprefix=prefix -modes=modes mappingpair .... + +

+ 一度に複数のマッピングを定義できます。 + mappingpair は、"," で区切られたキー名の組です。 +

例: "<Leader><S-j>,j"

+

+

+ -urls=urlpattern が与えられたとき、 + そのマッピングは urlpattern で指定されたページのみで有効になります。 +

+
+
+ + :fmapc + :fmapc! -modes=modes urlpattern + +

+ urlpattern のマッピングを削除します。 + "!" が与えられたときは、全てのマッピングが削除されます。 +

+
+
+ + :funmap + :funmap -urls=urlpattern -modes=modes lhs + +

+ マッピングを削除します。 +

+

+ urlpattern 付きのマッピングを削除するときは、-urls を指定する必要があります。 +

+
+
+

urlpattern

+

+ urlpattern の値は正規表現でなければいけません。 +

+

eventnamelist

+

+ eventnamelist は以下の値から構成されたリストです。 +

+
    +
  • keypress
  • +
  • keydown
  • +
  • keyup
  • +
  • vkeypress
  • +
  • vkeydown
  • +
  • vkeyup
  • +
+

"v..." のものは、仮想キーコードでイベントを発行します。

+

キー毎に、書かれた順にイベントが発行されます。

+

このオプションのデフォルト値は "keypress" です。

+

xpath

+

+ キーイベントを送るべき要素を指定するための XPath。 +

+

framenumber

+

+ キーイベントを送るべきフレームの番号。 + 番号は、補完を参考にしてください。 +

+

modes

+

+ マップするモードのリスト (デフォルトは、NORMALモード) +

+

.vimperatorrc 用の fmaps サンプル

+

コマンドラインで直接に入力するときは、":lazy" を除いてください。

+ +:command! -nargs=+ lazy autocmd VimperatorEnter .* <args> +:lazy fmaps -u='mail\.google\.com/mail' c / j k n p o u e x s r a # [ ] ? gi gs gt gd ga gc +:lazy fmaps -u='mail\.google\.com/mail/.*/[0-9a-f]+$' c / j,n k,p n,j p,k o u e x s r a # [ ] ? gi gs gt gd ga gc +:lazy fmaps -u='www\.google\.co\.jp/reader' -events=vkeypress j k n p m s v A r S N P X O gh ga gs gt gu u / ? J K +:lazy fmaps -u='(fastladder|livedoor)\.com/reader' j k s a p o v c i,p <Space> <S-Space> z b < > q w e,g +:lazy fmaps -u='https?://www\.rememberthemilk\.com/home/' j k m i c t ? d F,f G,g S,s L,l Y,y H,h M,m <Del> <C-S-Left> <C-S-Right> +:lazy fmaps -u='http://code.google.com/p/vimperator-labs/issues/list' o j k +:lazy fmaps -u='http://code.google.com/p/vimperator-labs/issues/detail' u + +
`; + +// }}} + +(function () { + + const EVENTS = 'keypress keydown keyup'.split(/\s+/); + const EVENTS_WITH_V = EVENTS.concat(['v' + n for each (n in EVENTS)]); + const IGNORE_URLS = //; + + const VKeys = { + '0': KeyEvent.DOM_VK_0, + '1': KeyEvent.DOM_VK_1, + '2': KeyEvent.DOM_VK_2, + '3': KeyEvent.DOM_VK_3, + '4': KeyEvent.DOM_VK_4, + '5': KeyEvent.DOM_VK_5, + '6': KeyEvent.DOM_VK_6, + '7': KeyEvent.DOM_VK_7, + '8': KeyEvent.DOM_VK_8, + '9': KeyEvent.DOM_VK_9, + ';': KeyEvent.DOM_VK_SEMICOLON, + '=': KeyEvent.DOM_VK_EQUALS, + 'a': KeyEvent.DOM_VK_A, + 'b': KeyEvent.DOM_VK_B, + 'c': KeyEvent.DOM_VK_C, + 'd': KeyEvent.DOM_VK_D, + 'e': KeyEvent.DOM_VK_E, + 'f': KeyEvent.DOM_VK_F, + 'g': KeyEvent.DOM_VK_G, + 'h': KeyEvent.DOM_VK_H, + 'i': KeyEvent.DOM_VK_I, + 'j': KeyEvent.DOM_VK_J, + 'k': KeyEvent.DOM_VK_K, + 'l': KeyEvent.DOM_VK_L, + 'm': KeyEvent.DOM_VK_M, + 'n': KeyEvent.DOM_VK_N, + 'o': KeyEvent.DOM_VK_O, + 'p': KeyEvent.DOM_VK_P, + 'q': KeyEvent.DOM_VK_Q, + 'r': KeyEvent.DOM_VK_R, + 's': KeyEvent.DOM_VK_S, + 't': KeyEvent.DOM_VK_T, + 'u': KeyEvent.DOM_VK_U, + 'v': KeyEvent.DOM_VK_V, + 'w': KeyEvent.DOM_VK_W, + 'x': KeyEvent.DOM_VK_X, + 'y': KeyEvent.DOM_VK_Y, + 'z': KeyEvent.DOM_VK_Z, + '*': KeyEvent.DOM_VK_MULTIPLY, + '+': KeyEvent.DOM_VK_ADD, + '-': KeyEvent.DOM_VK_SUBTRACT, + ',': KeyEvent.DOM_VK_COMMA, + '.': KeyEvent.DOM_VK_PERIOD, + '/': KeyEvent.DOM_VK_SLASH, + '?': KeyEvent.DOM_VK_SLASH, + '`': KeyEvent.DOM_VK_BACK_QUOTE, + '{': KeyEvent.DOM_VK_OPEN_BRACKET, + '\\': KeyEvent.DOM_VK_BACK_SLASH, + '}': KeyEvent.DOM_VK_CLOSE_BRACKET, + '\'': KeyEvent.DOM_VK_QUOTE + }; + + const State = { + feeding: false + }; + + function id (v) + v; + + function or (list, func) + (list.length && let ([head,] = list) (func(head) || or(list.slice(1), func))); + + function getFrames () { + function bodyCheck (content) + (content.document && content.document.body && content.document.body.localName.toLowerCase() === 'body'); + + function get (content) + (bodyCheck(content) && result.push(content), Array.slice(content.frames).forEach(get)); + + let result = []; + get(content); + return result; + } + + function fromXPath (doc, xpath) { + let result = util.evaluateXPath(xpath, doc); + return result.snapshotLength && result.snapshotItem(0); + } + + function createEvent (eventName, event) { + let result = content.document.createEvent('KeyEvents'); + result.initKeyEvent( + eventName, + true, + true, + content, + event.ctrlKey, + event.altKey, + event.shiftKey, + event.metaKey, + event.keyCode, + event.charCode + ); + return result; + } + + function virtualize (event) { + let cc = event.charCode; + if (/^[A-Z]$/.test(String.fromCharCode(cc))) + event.shiftKey = true; + event.keyCode = VKeys[String.fromCharCode(cc).toLowerCase()]; + event.charCode = 0; + return event; + } + + function feed (keys, eventNames, target) { + function finalize (){ + modes.passAllKeys = _passAllKeys; + State.feeding = false; + } + + State.feeding = true; + + let _passAllKeys = modes.passAllKeys; + + try { + modes.passAllKeys = true; + modes.passNextKey = false; + + for (let [, keyEvent] in Iterator(events.fromString(keys))) { + eventNames.forEach(function (eventName) { + let ke = util.cloneObject(keyEvent); + let [, vkey, name] = eventName.match(/^(v)?(.+)$/); + if (vkey) + virtualize(ke); + let event = createEvent(name, ke); + target.dispatchEvent(event); + }); + } + } catch (e) { + finalize(); + throw e; + } + + finalize(); + } + + function makeTryValidator (func) + function (value) { + try { + liberator.log(value); + func(value); + return true; + } catch (e) {} + return false; + }; + + let regexpValidator = makeTryValidator(RegExp); + + let xpathValidator = + makeTryValidator(function (expr) document.evaluate(expr, document, null, null, null)) + + function fromModeString (s){ + for (let [, {name, mask, char}] in Iterator(modes._modeMap)) + if (s === name || s === char) + return mask; + } + + function fromModeStrings (ss, def) { + if (def && (!ss || ss.length < 1)) + return [modes.NORMAL]; + return ss.map(fromModeString).filter(function (it) (typeof it === 'number')); + } + + function modeStringsValidator (ss) + (!ss || (fromModeStrings(ss).length === ss.length)); + + function makeListValidator (list) + function (values) + (values && !values.some(function (value) !list.some(function (event) event === value))); + + function findMappings ({all, filter, urls, ignoreUrls, not, result, modes: targetModes}) { + function match (map) { + let r = ( + map.feedSomeKeys && + (all || + (!filter || filter === map.names[0]) && + (ignoreUrls || urls === IGNORE_URLS || mappings._matchingUrlsTest(map, urls))) + ); + if (result && r) { + if (typeof result.matched === 'number') + result.matched++; + else + result.matched = 1; + } + return !!r ^ !!not; + } + + if (filter) + filter = mappings._expandLeader(filter); + if (urls) + urls = RegExp(urls); + + // FIXME 同じオブジェクトがダブって返るかも(あるいはそれで良い?) + let result = []; + for (let [, m] in Iterator(targetModes || [modes.NORMAL])) + result = result.concat(mappings._user[m].filter(match)); + + return result; + } + + function unmap (condition) { + condition = Object.create(condition); + let ms = condition.modes || [modes.NORMAL]; + condition.not = true; + condition.modes = undefined; + for (let [, m] in Iterator(ms)) { + condition.modes = [m]; + mappings._user[m] = findMappings(condition); + } + } + + function list (condition) { + let maps = findMappings(condition); + let template = modules.template; + let length = 0; + let list = + xml` + ${ + template.map(maps, function (map){ + ++length; + return template.map(map.names, function (name) + xml` + + + + `) + }) + } +
${name}${map.feedSomeKeys.rhs}${map.matchingUrls ? map.matchingUrls : '[Global]'}
`; + + if (length == 0) { + liberator.echomsg("No mapping found"); + return; + } + commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE); + } + + function fmapCompleter (context, args) { + context.title = ['name', 'rhs & url']; + context.completions = [ + [ + xml`${map.names[0]}, + + ${map.feedSomeKeys.rhs} + ${ + args['-ignoreurls'] + ? xml` for ${map.matchingUrls ? map.matchingUrls : 'Global'}` + : '' + } + ` + ] + for each (map in findMappings({urls: args['-urls'], ignoreUrls: args['-ignoreurls']})) + ]; + } + + function urlCompleter ({currentURL}) { + return function (context, args) { + let maps = findMappings({all: true}); + let uniq = {}; + let result = [ + (uniq[map.matchingUrls] = 1, [map.matchingUrls.source, map.names]) + for each (map in maps) + if (map.matchingUrls && !uniq[map.matchingUrls]) + ]; + if (currentURL) { + result.unshift(['^' + util.escapeRegex(buffer.URL), 'Current URL']); + if (content.document.domain) + result.unshift([util.escapeRegex(content.document.domain), 'Current domain']); + } + return result; + }; + } + + function frameCompleter (context, args) { + return [ + [i, frame.document.location] + for each ([i, frame] in Iterator(getFrames())) + ]; + } + + const ModeStringsCompleter = [ + [name, disp + ' mode' + (char ? ' (alias: ' + char + ')' : '')] + for ([n, {name, char, disp, extended}] in Iterator(modes._modeMap)) + if (!extended && /^\D+$/.test(n)) + ]; + + + 'fmap fmaps'.split(/\s+/).forEach(function (cmd) { + let multi = cmd === 'fmaps'; + + function action (multi) { + return function (args) { + let prefix = args['-prefix'] || ''; + let ms = fromModeStrings(args['-modes'], true); + + function add ([lhs, rhs]) { + if (!lhs) + return; + + rhs = rhs || lhs; + mappings.addUserMap( + ms, + [prefix + lhs], + args['description'] || 'by feedSomeKeys_3.js', + function () { + function body (win) + (win.document.body || win.document); + + let win = document.commandDispatcher.focusedWindow; + let frames = getFrames(); + + let elem = liberator.focus || body(win); + + if (typeof args['-frame'] !== 'undefined') { + frames = [frames[args['-frame']]]; + elem = body(frames[0]); + } + + if (args['-xpath']) { + elem = or(frames, function (f) fromXPath(f.document, args['-xpath'])) || elem; + } + + if (args['-selector']) { + elem = or(frames, function (f) f.document.querySelector(args['-selector'])) || elem; + } + + feed(rhs, args['-events'] || ['keypress'], elem); + }, + { + matchingUrls: args['-urls'], + feedSomeKeys: { + rhs: rhs, + } + }, + true + ); + } + + if (multi) { + let sep = let (s = args['-separator'] || ',') function (v) v.split(s); + args.literalArg.split(/\s+/).map(String.trim).map(sep).forEach(add); + } else { + let [, lhs, rhs] = args.literalArg.match(/^(\S+)\s+(.*)$/) || args.literalArg; + if (!rhs) { + list({ + filter: prefix + args.literalArg.trim(), + urls: args['-urls'], + ignoreUrls: !args['-urls'], + modes: ms + }); + } else { + add([lhs, rhs]); + } + } + }; + } + + commands.addUserCommand( + [cmd], + 'Feed map a key sequence', + action(multi), + { + literal: 0, + options: [ + [['-modes', '-m'], commands.OPTION_LIST, modeStringsValidator, ModeStringsCompleter], + [['-urls', '-u'], commands.OPTION_STRING, regexpValidator, urlCompleter({currentURL: true})], + [['-desc', '-description', '-d'], commands.OPTION_STRING], + [['-frame', '-f'], commands.OPTION_INT, null, frameCompleter], + [['-xpath', '-x'], commands.OPTION_STRING, xpathValidator], + [['-selector', '-s'], commands.OPTION_STRING], + [['-prefix', '-p'], commands.OPTION_STRING], + [ + ['-events', '-e'], + commands.OPTION_LIST, + makeListValidator(EVENTS_WITH_V), + EVENTS_WITH_V.map(function (v) [v, v]) + ] + ].concat( + multi ? [[['-separator', '-s'], commands.OPTION_STRING]] + : [] + ), + completer: multi ? null : fmapCompleter + }, + true + ); + }); + + commands.addUserCommand( + ['fmapc'], + 'Clear fmappings', + function (args) { + let ms = fromModeStrings(args['-modes'], true); + if (args.bang) { + unmap({ignoreUrls: true, modes: ms}); + liberator.log('All fmappings were removed.'); + } else { + let result = {}; + unmap({urls: args.literalArg, result: result, modes: ms}); + liberator.echo(result.matched ? 'Some fmappings were removed.' : 'Not found specifed fmappings.'); + } + }, + { + literal: 0, + bang: true, + completer: function (context) { + context.title = ['URL Pattern']; + context.completions = urlCompleter({})(context); + }, + options: [ + [['-modes', '-m'], commands.OPTION_LIST], + ] + }, + true + ); + + commands.addUserCommand( + ['funmap'], + 'Remove fmappings', + function (args) { + let urls = args['-urls']; + let name = args.literalArg; + if (!name) + return liberator.echoerr('E471: Argument required'); + + let result = {}; + unmap({ + filter: name, + urls: urls, + ignoreUrls: args['-ignoreurls'], + result: result, + modes: fromModeStrings(args['-modes'], true) + }); + liberator.echo(result.matched ? 'Some fmappings were removed.' : 'Not found specifed fmappings.'); + }, + { + literal: 0, + options: [ + [['-modes', '-m'], commands.OPTION_LIST], + [['-urls', '-u'], commands.OPTION_STRING, regexpValidator, urlCompleter({})], + [['-ignoreurls', '-iu'], commands.OPTION_NOARG] + ], + completer: fmapCompleter + }, + true + ); + + plugins.libly.$U.around( + mappings, + 'getCandidates', + function (next, [mode, prefix, patternOrUrl]) { + let map = mappings.get(mode, prefix, patternOrUrl); + if (map && map.matchingUrls) + return []; + return next(); + } + ); + + __context__.API = + 'State VKeys feed getFrames fromXPath virtualize unmap findMappings list'.split(/\s+/).reduce( + function (result, name) + (result[name] = eval(name), result), + {} + ); + +})(); + +// vim:sw=2 ts=2 et si fdm=marker: diff --git a/vimperator/plugin/wallabag.js b/vimperator/plugin/wallabag.js new file mode 100644 index 0000000..363677b --- /dev/null +++ b/vimperator/plugin/wallabag.js @@ -0,0 +1,35 @@ +let PLUGIN_INFO = xml` + + wallabag + Pocket + 0.1.1 + 3.2 + Philipp Schmitt + https://raw.github.com/pschmitt/vimperator-wallabag/master/wallabag.js + +`; + +(function() { + + let wallabag_url = (liberator.globalVariables.wallabag_url) ? liberator.globalVariables.wallabag_url : ''; + + commands.addUserCommand(['wallabag', 'wg'], 'Wallabag plugin', + function(args) { + let url = buffer.URL; + + if (typeof args != 'undefined' && args.length > 0) { + url = args; + } + + if (wallabag_url == '') { + liberator.echoerr('Wallabag URL undefined. Please add let g:wallabag_url="http://YOURURL" to your vimperatorrc'); + } else { + liberator.open(wallabag_url + '/?action=add&url=' + btoa(url), liberator.NEW_TAB); + } + } + ); + + +})(); diff --git a/vimperatorrc b/vimperatorrc new file mode 100644 index 0000000..09e95b8 --- /dev/null +++ b/vimperatorrc @@ -0,0 +1,50 @@ +set runtimepath=~/etc/vimperator + +set gui=none,tabs +"set tabnumbers + +set defsearch=g + +" follow hints on return +set followhints=1 + +set incsearch hlsearch +set ignorecase smartcase + +set noerrorbells novisualbell +js liberator.beep = function() { return false; } + +" google +map s og +map S tg + +" fast scrolling +noremap j +noremap k + +let g:wallabag_url="https://wllbg.gebner.org" + +js < diff --git a/vimrc b/vimrc index d881929..7756e1b 100644 --- a/vimrc +++ b/vimrc @@ -59,6 +59,7 @@ NeoBundle 'derekwyatt/vim-scala' NeoBundle 'othree/html5.vim' NeoBundle 'dogrover/vim-pentadactyl' +NeoBundle 'superbrothers/vim-vimperator' NeoBundle 'idris-hackers/idris-vim'