]> gitweb @ CieloNegro.org - sugar.git/commitdiff
Auto commit by The Sugar System.
authorPHO <pho@cielonegro.org>
Fri, 6 Jun 2014 04:19:23 +0000 (13:19 +0900)
committerPHO <pho@cielonegro.org>
Fri, 6 Jun 2014 04:19:23 +0000 (13:19 +0900)
IGNORE
dot-files/_config/autostart/xset_desktop [new file with mode: 0644]
dot-files/_vimperator/plugin/_libly_js [new file with mode: 0644]
dot-files/_vimperator/plugin/bitly_js [new file with mode: 0644]
dot-files/_vimperator/plugin/copy_js [new file with mode: 0644]
dot-files/_vimperator/plugin/multi_requester_js [new file with mode: 0644]
dot-files/_vimperator/plugin/prevent-pseudo-domain_js [new file with mode: 0644]
dot-files/_vimperator/plugin/relative_move_js [new file with mode: 0644]

diff --git a/IGNORE b/IGNORE
index 349f0c45049cfccdfd30485ee63332d5b9739d47..008e5b14471f2c6c7388659fa94644d697e8cd70 100644 (file)
--- a/IGNORE
+++ b/IGNORE
@@ -24,7 +24,9 @@
 .cabal-upload
 .cddbslave
 .config/Trolltech*
+.config/easytag
 .config/enchant
+.config/fontconfig
 .config/inkscape
 .config/gmpc
 .config/gtk-2.0/gtkfilechooser.ini
 .unison
 .viminfo
 .vimperator/info
-.vimperator/plugin
 .w3m/bookmark.bak.html
 .w3m/config
 .w3m/*arrived
 .w3m/w3mtmp*
 .w3m/.textarea
 .wine
+.wireshark
 .wmncach.el
 .xchat2*
 .xdvirc
diff --git a/dot-files/_config/autostart/xset_desktop b/dot-files/_config/autostart/xset_desktop
new file mode 100644 (file)
index 0000000..eb811a6
--- /dev/null
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Type=Application
+Name=xset
+Exec=xset b off
+StartupNotify=false
diff --git a/dot-files/_vimperator/plugin/_libly_js b/dot-files/_vimperator/plugin/_libly_js
new file mode 100644 (file)
index 0000000..be989d7
--- /dev/null
@@ -0,0 +1,737 @@
+/*** BEGIN LICENSE BLOCK {{{
+    Copyright (c) 2008 suVene<suvene@zeromemory.info>
+    Copyright (c) 2008-2011 anekos<anekos@snca.net>
+
+    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`
+<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>
+    <author mail="anekos@snca.net" homepage="http://snca.net/">anekos</author>
+    <license>MIT</license>
+    <version>0.1.38</version>
+    <minVersion>2.3pre</minVersion>
+    <updateURL>https://github.com/vimpr/vimperator-plugins/raw/master/_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, autoRestore):
+  obj がもつ name 関数を、func に置き換えます。
+  func は
+    function (next, args) {...}
+  という形で呼ばれます。
+  next はオリジナルの関数を呼び出すための関数、
+  args はオリジナルの引数列です。
+  通常、next には引数を渡す必要はありません。
+  (任意の引数を渡したい場合は配列で渡します。)
+  また、autoRestore が真であれば、プラグインの再ロードなどで around が再実行されたときに、関数の置き換え前にオリジナル状態に書き戻します。
+  (多重に置き換えられなくなるので、auto_source.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):
+    <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ヘッダの値を指定できる(省略可)
+        >||
+        {
+            '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 読込み成功したら、強制的にキャッシュと置き換えるの作って!
+
+    ]]></detail>
+</VimperatorPlugin>`;
+*/
+//}}}
+//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\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 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/dot-files/_vimperator/plugin/bitly_js b/dot-files/_vimperator/plugin/bitly_js
new file mode 100644 (file)
index 0000000..5d4af2d
--- /dev/null
@@ -0,0 +1,177 @@
+/* NEW BSD LICENSE {{{
+Copyright (c) 2008-2010, 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       #
+# に参考になる日本語訳がありますが、有効なのは上記英文となります。                #
+###################################################################################
+
+}}} */
+
+// PLUGIN_INFO {{{
+let PLUGIN_INFO = xml`
+<VimperatorPlugin>
+  <name>bit.ly</name>
+  <description>Get short alias by bit.ly and j.mp</description>
+  <description lang="ja">bit.ly や j.mp で短縮URLを得る</description>
+  <version>2.1.2</version>
+  <author mail="anekos@snca.net" homepage="http://d.hatena.ne.jp/nokturnalmortum/">anekos</author>
+  <license>new BSD License (Please read the source code comments of this plugin)</license>
+  <license lang="ja">修正BSDライセンス (ソースコードのコメントを参照してください)</license>
+  <updateURL>https://github.com/vimpr/vimperator-plugins/raw/master/bitly.js</updateURL>
+  <minVersion>2.0pre</minVersion>
+  <detail><![CDATA[
+    == Commands ==
+      :bitly [<URL>]
+        Copy to clipboard.
+      :jmp [<URL>]
+        Copy to clipboard.
+    == Require ==
+      bit.ly API Key
+  ]]></detail>
+</VimperatorPlugin>`;
+// }}}
+
+
+(function () {
+
+  const Realm = 'API Key for bit.ly (bitly.js)';
+  const HostName = 'http://api.bit.ly';
+  const ApiUrl = 'http://api.bit.ly/v3';
+  const PasswordManager = Cc['@mozilla.org/login-manager;1'].getService(Ci.nsILoginManager);
+  const LoginInfo =
+    new Components.Constructor(
+      '@mozilla.org/login-manager/loginInfo;1',
+      Ci.nsILoginInfo,
+      'init'
+    );
+
+  function getAuth () {
+    let count = {};
+    let logins = PasswordManager.findLogins(count, HostName, null, Realm);
+    if (logins.length)
+      return logins[0];
+  }
+
+  function setupAuth (callback) {
+    liberator.open('http://bit.ly/a/your_api_key', liberator.NEW_TAB);
+    commandline.input(
+      'Login name for bit.ly: ',
+      function (username) {
+        commandline.input(
+          'API Key: ',
+          function (apiKey) {
+            let login = LoginInfo(HostName, null, Realm, username, apiKey, '', '');
+            PasswordManager.addLogin(login);
+            callback();
+          },
+          {
+            default: let (e = content.document.querySelector('#bitly_api_key')) (e ? e.value : '')
+          }
+        );
+      }
+    );
+  }
+
+  function shorten (url, domain, command, callback) {
+    function fixResponseText (s)
+      s.trim();
+
+    liberator.log(arguments);
+    function get () {
+      let req = new XMLHttpRequest();
+      req.onreadystatechange = function () {
+        if (req.readyState != 4)
+          return;
+        if (req.status == 200)
+          return callback && callback(fixResponseText(req.responseText), req);
+        else
+          return liberator.echoerr(req.statusText);
+      };
+      let requestUri =
+        ApiUrl + '/' + (command || 'shorten') + '?' +
+        'apiKey=' + auth.password + '&' +
+        'login=' + auth.username + '&' +
+        (command !== 'expand' ? 'uri=' : 'shortUrl=') + encodeURIComponent(url) + '&' +
+        'domain=' + (domain || 'bit.ly') + '&' +
+        'format=txt';
+      req.open('GET', requestUri, callback);
+      req.send(null);
+      return !callback && fixResponseText(req.responseText);
+    }
+
+    if (!url)
+      url = buffer.URL;
+
+    let auth = getAuth();
+
+    if (auth)
+      return get();
+
+    if (callback) {
+      let args = Array.slice(arguments);
+      setupAuth(function () shorten.apply(this, args));
+    } else {
+      liberator.echoerr('Not found API Key!! Try :bitly command, before use.');
+    }
+  }
+
+  [
+    ['jmp', 'j.mp'],
+    ['bitly', 'bit.ly'],
+  ].forEach(function ([name, domain]) {
+    commands.addUserCommand(
+      [name],
+      'Copy ' + domain + ' url',
+      function (args) {
+        let url = args.literalArg ? util.stringToURLArray(args.literalArg)[0] : buffer.URL;
+        let cmd = args['-expand'] ? 'expand' : 'shorten';
+
+        shorten(url, domain, cmd, function (short) {
+          util.copyToClipboard(short);
+          liberator.echo(short + ' <= ' + url);
+        });
+      },
+      {
+        literal: 0,
+        options: [
+          [['-expand', '-e'], commands.OPTION_NOARG]
+        ],
+        completer: function (context) {
+          context.completions = [
+            [buffer.URL, 'Current URL']
+          ];
+          context.fork('URL', 0, context, completion.url);
+        }
+      },
+      true
+    );
+    __context__[name] = function (url, cmd, callback) shorten(url, domain, cmd, callback);
+  });
+
+  __context__.get = shorten;
+})();
diff --git a/dot-files/_vimperator/plugin/copy_js b/dot-files/_vimperator/plugin/copy_js
new file mode 100644 (file)
index 0000000..18b61f5
--- /dev/null
@@ -0,0 +1,391 @@
+var INFO =
+xml`<plugin name="copy" version="0.7.6"
+        href="http://github.com/vimpr/vimperator-plugins/blob/master/copy.js"
+        summary="copy strings from the template (like CopyURL+)"
+        xmlns="http://vimperator.org/namespaces/liberator">
+    <author email="teramako@gmail.com">teramako</author>
+    <license>MPL 1.1/GPL 2.0/LGPL 2.1</license>
+    <project name="Vimperator" minVersion="2.3"/>
+    <item>
+    <tags>:copy</tags>
+    <spec>:copy <a>label</a></spec>
+    <description>
+        <p>copy the argument replaced some certain string.</p>
+    </description>
+    </item>
+    <item>
+    <tags>:copy!</tags>
+    <spec>:copy! <a>expr</a></spec>
+    <description>
+        <p>evaluate the argument(javascript code) and copy the result.</p>
+    </description>
+    </item>
+    <item>
+    <tags>copy-keyword</tags>
+    <spec>copy-keyword</spec>
+    <description>
+        <p>replaces following keywords</p>
+        <dl>
+            <dt>%TITLE%</dt>
+            <dd>to the title of the current page</dd>
+            <dt>%URL%</dt>
+            <dd>to the currenet URL</dd>
+            <dt>%SEL</dt>
+            <dd>to the string of selection</dd>
+            <dt>%HTMLSEL</dt>
+            <dd>to the html string of selection</dd>
+            <dt>%HOSTNAME%</dt>
+            <dd>to the hostname of the current location</dd>
+            <dt>%PATHNAME%</dt>
+            <dd>to the pathname of the current location</dd>
+            <dt>%HOST%</dt>
+            <dd>to the host of the current location</dd>
+            <dt>%PORT%</dt>
+            <dd>to the port of the current location</dd>
+            <dt>%PROTOCOL%</dt>
+            <dd>to the protocol of the current location</dd>
+            <dt>%SERCH%</dt>
+            <dd>to the search(?...) of the curernt location</dd>
+            <dt>%HASH%</dt>
+            <dd>to the hash(anchor #..) of the current location</dd>
+        </dl>
+    </description>
+    </item>
+    <item>
+        <tags>copy-template</tags>
+        <spec>copy-template</spec>
+        <description>
+            <p>you can set your own template using inline JavaScript</p>
+            <code><![CDATA[
+javascript <<EOM
+liberator.globalVariables.copy_templates = [
+  { label: 'titleAndURL',    value: '%TITLE%\n%URL%' },
+  { label: 'title',          value: '%TITLE%', map: ',y' },
+  { label: 'anchor',         value: '<a href="%URL%">%TITLE%</a>' },
+  { label: 'selanchor',      value: '<a href="%URL%" title="%TITLE%">%SEL%</a>' },
+  { label: 'htmlblockquote', value: '<blockquote cite="%URL%" title="%TITLE%">%HTMLSEL%</blockquote>' },
+  { label: 'ASIN',   value: 'copy ASIN code from Amazon', custom: function(){return content.document.getElementById('ASIN').value;} },
+];
+EOM
+            ]]></code>
+            <dl>
+                <dt>label</dt>
+                <dd>template name which is command argument</dd>
+                <dt>value</dt>
+                <dd>copy string. <a>copy-keyword</a> is replaced</dd>
+                <dt>map</dt>
+                <dd>key map <a>lhs</a> (optional)</dd>
+                <dt>custom</dt>
+                <dd>
+                    <a>function</a> or <a>Array</a> (optional)
+                    <dl>
+                        <dt><a>function</a></dt>
+                        <dd>execute the function and copy return value, if specified</dd>
+                        <dt><a>Array</a></dt>
+                        <dd>
+                            replace to the <a>value</a> by normal way at first.
+                            then replace words matched <a>Array</a>[0] in the repalced string to <a>Array</a>[1].
+                            <dl>
+                            <dt><a>Array</a>[0]</dt>
+                            <dd>String or RegExp</dd>
+                            <dt><a>Array</a>[1]</dt>
+                            <dd>String or Function</dd>
+                            </dl>
+                            see: <link topic="http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:String:replace">http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:String:replace</link>
+                        </dd>
+                    </dl>
+                </dd>
+            </dl>
+        </description>
+    </item>
+    <item>
+        <tags>copy-option</tags>
+        <spec>copy-option</spec>
+        <description>
+            <code><ex>liberator.globalVariables.copy_use_wedata = false; // false by default</ex></code>
+            <p>true に設定すると wedata からテンプレートを読込みます。</p>
+            <code><ex>liberator.globalVariables.copy_wedata_include_custom = true; // false by default</ex></code>
+            <p>custom が設定された wedata を読込みます。
+            SandBox でなく、window.eval を利用してオブジェクトする為、
+            セキュリティ上の理由で初期設定は false になっています。
+            true に設定する場合は、動作を理解したうえ自己責任でご利用ください。</p>
+            <code><ex>liberator.globalVariables.copy_wedata_exclude_labels = ['pathtraqnormalize', ];</ex></code>
+            <p>wedata から読込まない label のリストを定義します。</p>
+        </description>
+    </item>
+</plugin>`;
+
+liberator.plugins.exCopy = (function(){
+var excludeLabelsMap = {};
+var copy_templates = [];
+if (!liberator.globalVariables.copy_templates){
+    liberator.globalVariables.copy_templates = [
+        { label: 'titleAndURL',    value: '%TITLE%\n%URL%' },
+        { label: 'title',          value: '%TITLE%' },
+        { label: 'anchor',         value: '<a href="%URL%">%TITLE%</a>' },
+        { label: 'selanchor',      value: '<a href="%URL%" title="%TITLE%">%SEL%</a>' },
+        { label: 'htmlblockquote', value: '<blockquote cite="%URL%" title="%TITLE%">%HTMLSEL%</blockquote>' }
+    ];
+}
+
+copy_templates = liberator.globalVariables.copy_templates.map(function(t){
+    return { label: t.label, value: t.value, custom: t.custom, map: t.map }
+});
+
+copy_templates.forEach(function(template){
+    if (typeof template.map == 'string')
+        addUserMap(template.label, [template.map]);
+    else if (template.map instanceof Array)
+        addUserMap(template.label, template.map);
+});
+
+const REPLACE_TABLE = {
+    get TITLE () buffer.title,
+    get URL () buffer.URL,
+    get SEL () {
+        var sel = '';
+        var win = new XPCNativeWrapper(window.content.window);
+        var selection =  win.getSelection();
+        if (selection.rangeCount < 1)
+            return '';
+
+        for (var i=0, c=selection.rangeCount; i<c; i++){
+            sel += selection.getRangeAt(i).toString();
+        }
+        return sel;
+    },
+    get HTMLSEL () {
+        var htmlsel = '';
+        var win = new XPCNativeWrapper(window.content.window);
+        var selection =  win.getSelection();
+        if (selection.rangeCount < 1)
+            return '';
+
+        var serializer = new XMLSerializer();
+        for (var i=0, c=selection.rangeCount; i<c; i++){
+            htmlsel += serializer.serializeToString(selection.getRangeAt(i).cloneContents());
+        }
+        return htmlsel.replace(/<(\/)?(\w+)([\s\S]*?)>/g, function(all, close, tag, attr){
+            return "<" + close + tag.toLowerCase() + attr + ">";
+        });
+    },
+    get CLIP () {
+        return util.readFromClipboard();
+    }
+};
+'hostname pathname host port protocol search hash'.split(' ').forEach(function (name){
+    REPLACE_TABLE[name.toUpperCase()] = function () content.location && content.location[name];
+});
+
+// used when argument is none
+//const defaultValue = templates[0].label;
+commands.addUserCommand(['copy'],'Copy to clipboard',
+    function(args){
+        liberator.plugins.exCopy.copy(args.literalArg, args.bang, !!args["-append"]);
+    },{
+        completer: function(context, args){
+            if (args.bang){
+                completion.javascript(context);
+                return;
+            }
+            context.title = ['Template','Value'];
+            var templates = copy_templates.map(function(template)
+                [template.label, liberator.modules.util.escapeString(template.value, '"')]
+            );
+            if (!context.filter){ context.completions = templates; return; }
+            var candidates = [];
+            var filter = context.filter.toLowerCase();
+            context.completions = templates.filter(function(template) template[0].toLowerCase().indexOf(filter) == 0);
+        },
+        literal: 0,
+        bang: true,
+        options: [
+            [["-append","-a"], commands.OPTION_NOARG]
+        ]
+    },
+    true
+);
+
+function addUserMap(label, map){
+    mappings.addUserMap([modes.NORMAL,modes.VISUAL], map,
+        label,
+        function(){ liberator.plugins.exCopy.copy(label); },
+        { rhs: label }
+    );
+}
+function getCopyTemplate(label){
+    var ret = null;
+    copy_templates.some(function(template)
+        template.label == label ? (ret = template) && true : false);
+    return ret;
+}
+function replaceVariable(str){
+    if (!str) return '';
+    function replacer(orig, name){ //{{{
+        if (name == '')
+            return '%';
+        if (!REPLACE_TABLE.hasOwnProperty(name))
+            return orig;
+        let value = REPLACE_TABLE[name];
+        if (typeof value == 'function')
+            return value();
+        else
+            return value.toString();
+        return orig;
+    } //}}}
+    return str.replace(/%([A-Z]*)%/g, replacer);
+}
+
+function wedataRegister(item){
+    var libly = liberator.plugins.libly;
+    var logger = libly.$U.getLogger("copy");
+    item = item.data;
+    if (excludeLabelsMap[item.label]) return;
+
+    if (item.custom && item.custom.toLowerCase().indexOf('function') != -1) {
+        if (!liberator.globalVariables.copy_wedata_include_custom ||
+             item.label == 'test') {
+            logger.log('skip: ' + item.label);
+            return;
+        }
+
+        let custom = (function(item){
+
+            return function(value, value2){
+                var STORE_KEY = 'plugins-copy-ok-func';
+                var store = storage.newMap(STORE_KEY, {store: true});
+                var check = store.get(item.label);
+                var ans;
+
+                if (!check){
+                    ans = window.confirm(
+                        'warning!!!: execute "' + item.label + '" ok ?\n' +
+                        '(this function is working with unsafe sandbox.)\n\n' +
+                        '----- execute code -----\n\n' +
+                        'value: ' + item.value + '\n' +
+                        'function: ' +
+                        item.custom
+                    );
+                } else {
+                    if (item.value == check.value &&
+                        item.custom == check.custom &&
+                        item.map == check.map){
+                        ans = true;
+                    } else {
+                        ans = window.confirm(
+                            'warning!!!: "' + item.label + '" was changed when you registered the function.\n' +
+                            '(this function is working with unsafe sandbox.)\n\n' +
+                            '----- execute code -----\n\n' +
+                            'value: ' + item.value + '\n' +
+                            'function: ' +
+                            item.custom
+                        );
+                    }
+                }
+
+                if (!ans) return;
+                store.set(item.label, item);
+                store.save();
+
+                var func;
+                try{
+                    func = window.eval('(' + item.custom + ')');
+                } catch (e){
+                    logger.echoerr(e);
+                    logger.log(item.custom);
+                    return;
+                }
+                return func(value, value2);
+            };
+        })(item);
+
+        exCopyManager.add(item.label, item.value, custom, item.map);
+    } else {
+        exCopyManager.add(item.label, item.value, null, item.map);
+    }
+}
+var exCopyManager = {
+    add: function(label, value, custom, map){
+        var template = {label: label, value: value, custom: custom, map: map};
+        copy_templates.unshift(template);
+        if (map) addUserMap(label, map);
+
+        return template;
+    },
+    get: function(label){
+        return getCopyTemplate(label);
+    },
+    copy: function(arg, special, appendMode){
+        var copyString = '';
+        var isError = false;
+        if (special && arg){
+            try {
+                copyString = liberator.eval(arg);
+                switch (typeof copyString){
+                    case 'object':
+                        copyString = copyString === null ? 'null' : copyString.toSource();
+                        break;
+                    case 'function':
+                        copyString = copyString.toString();
+                        break;
+                    case 'number':
+                    case 'boolean':
+                        copyString = '' + copyString;
+                        break;
+                    case 'undefined':
+                        copyString = 'undefined';
+                        break;
+                }
+            } catch (e){
+                isError = true;
+                copyString = e.toString();
+            }
+        } else {
+            if (!arg) arg = copy_templates[0].label;
+
+            var template = getCopyTemplate(arg) || {value: arg};
+            if (typeof template.custom == 'function'){
+                copyString = template.custom.call(this, template.value, replaceVariable(template.value));
+            } else if (template.custom instanceof Array){
+                copyString = replaceVariable(template.value).replace(template.custom[0], template.custom[1]);
+            } else {
+                copyString = replaceVariable(template.value);
+            }
+        }
+
+        if (appendMode){
+            copyString = util.readFromClipboard() + copyString;
+        }
+
+        if (copyString)
+            util.copyToClipboard(copyString);
+        if (isError){
+            liberator.echoerr('CopiedErrorString: `' + copyString + "'");
+        } else {
+            liberator.echo('CopiedString: `' + util.escapeHTML(copyString || '') + "'");
+        }
+    }
+};
+
+if (liberator.globalVariables.copy_use_wedata){
+    function loadWedata(){
+        if (!liberator.plugins.libly){
+            liberator.echomsg("need a _libly.js when use wedata.");
+            return;
+        }
+
+        var libly = liberator.plugins.libly;
+        copy_templates.forEach(function(item) excludeLabelsMap[item.label] = item.value);
+        if (liberator.globalVariables.copy_wedata_exclude_labels)
+            liberator.globalVariables.copy_wedata_exclude_labels.forEach(function(item) excludeLabelsMap[item] = 1);
+        var wedata = new libly.Wedata("vimp copy");
+        wedata.getItems(24 * 60 * 60 * 1000, wedataRegister);
+    }
+    loadWedata();
+}
+
+return exCopyManager;
+})();
+
+// vim: set fdm=marker sw=4 ts=4 et:
+
diff --git a/dot-files/_vimperator/plugin/multi_requester_js b/dot-files/_vimperator/plugin/multi_requester_js
new file mode 100644 (file)
index 0000000..90ca321
--- /dev/null
@@ -0,0 +1,493 @@
+/*** 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 INFO = xml`
+<plugin name="multi_requester" version="0.4.16"
+        href="https://github.com/vimpr/vimperator-plugins/raw/master/multi_requester.js"
+        summary="request, and the result is displayed to the buffer."
+        xmlns="http://vimperator.org/namespaces/liberator">
+    <author href="http://zeromemory.sblo.jp/">suVene</author>
+    <license>MIT</license>
+    <project name="Vimperator" minVersion="3.6"/>
+    <p>
+      <code><![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" を設定してください。
+      ]]></code>
+    </p>
+</plugin>`;
+//}}}
+(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("resultsList")'
+  }
+];
+var libly = liberator.plugins.libly;
+var $U = libly.$U;
+var logger = $U.getLogger("multi_requester");
+var mergedSiteinfo = {};
+var store = storage.newMap('plugins-multi_requester', {store: 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 Requester");
+      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("exception", $U.bind(this, this.onException));
+        req.addEventListener("success", $U.bind(this, this.onSuccess));
+        req.addEventListener("failure", $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("exception", $U.bind(this, this.onException));
+    req.addEventListener("success", $U.bind(this, this.onSuccess));
+    req.addEventListener("failure", $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:
+
diff --git a/dot-files/_vimperator/plugin/prevent-pseudo-domain_js b/dot-files/_vimperator/plugin/prevent-pseudo-domain_js
new file mode 100644 (file)
index 0000000..0e4124c
--- /dev/null
@@ -0,0 +1,45 @@
+// ORIGINAL: http://vimperator.g.hatena.ne.jp/kei-s/20101005
+(function() {
+    function hasMultipleWords (s) {
+        return s.split(/\s/).length > 1;
+    }
+
+    function isWord (s) {
+        return /^[^\s\.\/]+$/i.test(s);
+    }
+
+    function isIPAddress (s) {
+        return /^([0-9]+\.){3}([0-9]+)$/i.test(s);
+    }
+
+    function hasScheme (s) {
+        return /^([a-zA-Z0-9-]+):/i.test(s);
+    }
+
+    let tldList = "local|museum|travel|aero|arpa|coop|info|jobs|name|nvus|biz|com|edu|gov|int|mil|net|org|pro|xxx|ac|ad|ae|af|ag|ai|ak|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|ct|cu|cv|cx|cy|cz|dc|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fl|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hi|hk|hm|hn|hr|ht|hu|ia|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|ks|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mi|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|nd|ne|nf|ng|nh|ni|nj|nl|nm|no|np|nr|nu|ny|nz|oh|ok|om|or|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ri|ro|ru|rw|sa|sb|sc|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tx|tz|ua|ug|uk|um|us|ut|uy|uz|va|vc|ve|vg|vi|vn|vt|vu|wa|wf|wi|ws|wv|wy|ye|yt|yu|za|zm|zw".split("|");
+    function hasTLD (s) {
+        let m = s.match(/^(?:(?:[^\.\s:\/]+\.)+?)+([a-zA-Z]+)(?::\d+)?(?:\/[^\s]*)?$/i);
+        return m != null && tldList.indexOf(m[1]) != -1;
+    }
+
+    function requirePrefix (s) {
+        if (!s)
+          return false;
+        let validators = [hasMultipleWords, isWord, isIPAddress, hasScheme, hasTLD];
+        return !(validators.some(function(validator) { return validator(s); }));
+    }
+
+    plugins.libly.$U.around(
+        liberator,
+        'open',
+        function (next, args) {
+            let [urls, where, force] = args;
+            if (typeof urls == "string") {
+                if (requirePrefix(urls.trim())) {
+                    args[0] = options.defsearch + " " + urls;
+                }
+            }
+            return next();
+        }
+    )
+})();
diff --git a/dot-files/_vimperator/plugin/relative_move_js b/dot-files/_vimperator/plugin/relative_move_js
new file mode 100644 (file)
index 0000000..a75eff3
--- /dev/null
@@ -0,0 +1,97 @@
+// Vimperator plugin: Relative Move
+// Version: 0.1
+//
+// Usage:
+//   If you stay "http://example.com/aaa/bbb/ccc"
+//
+//   :ropen ddd
+//     move to http://example.com/aaa/bbb/cccddd
+//   :ropen ./ddd
+//     move to http://example.com/aaa/bbb/ccc/ddd
+//   :ropen ../ddd
+//     move to http://example.com/aaa/bbb/ddd
+//   :ropen ../../ddd
+//     move to http://example.com/aaa/ddd
+//   :ropen /fuga
+//     move to http://example.com/ddd
+
+
+(function (){
+    function trim_query(url){
+        var _r;
+        var res = (_r = url.match(/^.*(?=\?)/)) ? _r[0] : url;
+        res = (_r = res.match(/^https?:\/\/.*(?=https?:\/\/)/)) ? _r[0] : res;
+        res = (_r = url.match(/^.*(?=#)/)) ? _r[0] : res;
+        return res;
+    }
+
+    function open_path(path, tab){
+        var win = window.content.window;
+        var loc = win.location;
+        var splited_path = path.split(/\/+/);
+        var up = 0;
+
+        if(!tab){
+            tab = liberator.CURRENT_TAB;
+        }
+
+        switch(splited_path[0]){
+            case ".":
+                up = -1;
+                break;
+            case "..":
+                while(splited_path[up] == "..") up++;
+                break;
+            case "":
+                up = -2;
+                break;
+            default:
+                break;
+        }
+
+        var url, base;
+        switch(up){
+            case -2: // "/hoge"
+                base = loc.protocol + "//" + loc.hostname;
+                url = base + path;
+                break;
+            case -1: // "./hoge"
+                base = trim_query(loc.href);
+                path = path.substring(2);
+                if(base[base.length-1] == "/")
+                    url = base + path;
+                else
+                    url = base + "/" + path;
+                break;
+            case 0: // "hoge"
+                url = loc.href + path;
+                break;
+            default: // "../../hoge"
+                base = trim_query(loc.href);
+                let c = 0;
+                while(c < up){
+                    if(c > 0) base = base.substr(0, base.length-1);
+                    [base] = base.match(/^.*\/(?=[^\/]*$)/);
+                    path = path.substring(3);
+                    c++;
+                }
+                url = base + path;
+            break;
+        }
+        liberator.open(url, tab);
+    }
+
+    commands.addUserCommand(
+        ["ro[pen]"],
+        "Open relative URL in the current tab",
+        open_path
+    );
+
+    commands.addUserCommand(
+        ["rt[abopen]"],
+        "Open relative URL in a new tab",
+        function(path){
+            open_path(path, liberator.NEW_TAB);
+        }
+    );
+})();