aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--appendAnchor.js41
-rw-r--r--copy.js193
-rw-r--r--direct_bookmark.js648
-rw-r--r--feedSomeKeys.js320
-rw-r--r--googlekanji.js82
-rw-r--r--googlesuggest.js34
-rw-r--r--hatenaStar.js69
-rw-r--r--ime_controller.js73
-rw-r--r--lo.js150
-rw-r--r--lookupDictionary.js269
-rw-r--r--migemized_find.js470
-rw-r--r--migemo_completion.js51
-rw-r--r--migemo_hint.js21
-rw-r--r--proxy.js133
-rw-r--r--sbmcommentsviewer.js543
-rw-r--r--tombloo.js72
-rw-r--r--uaSwitch.js37
-rw-r--r--ubiquity.js144
18 files changed, 3350 insertions, 0 deletions
diff --git a/appendAnchor.js b/appendAnchor.js
new file mode 100644
index 0000000..9dc410d
--- /dev/null
+++ b/appendAnchor.js
@@ -0,0 +1,41 @@
+/**
+ * == VimperatorPlugin==
+ * @name appendAnchor
+ * @description append anchors to texts look like url.
+ * @author SAKAI, Kazuaki
+ * @version 0.02
+ * == VimperatorPlugin==
+ */
+
+(function(){
+
+ liberator.commands.addUserCommand(['anc'], 'append anchors to texts look like url',
+ function(arg, special) {
+ var doc = window.content.document;
+ var nodes = liberator.buffer.evaluateXPath(
+ '/descendant::*[not(contains(" TITLE STYLE SCRIPT TEXTAREA XMP A ", concat(" ", translate(local-name(), "aceilmprstxy", "ACEILMPRSTXY"), " ")))]/child::text()'
+ );
+ var regex = new RegExp("h?(ttps?):/+([a-zA-Z0-9][-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+[-_~*(a-zA-Z0-9;/?@&=+$%#])");
+
+ var range = doc.createRange();
+ var last;
+ var href;
+ for (let i = 0, l = nodes.snapshotLength; i < l; i++) {
+ let node = nodes.snapshotItem(i);
+ range.selectNode(node);
+ while (node && (last = range.toString().search(regex)) > -1) {
+ range.setStart(node, last);
+ range.setEnd(node, last + RegExp.lastMatch.length);
+ href = 'h' + RegExp.$1 + '://' + RegExp.$2;
+ let anchor = doc.createElement('a');
+ range.insertNode(anchor);
+ anchor.setAttribute('href', href);
+ range.surroundContents(anchor);
+ node = node.nextSibling.nextSibling;
+ range.selectNode(node);
+ }
+ }
+ range.detach();
+ },{}
+ );
+})();
diff --git a/copy.js b/copy.js
new file mode 100644
index 0000000..4bcd3e3
--- /dev/null
+++ b/copy.js
@@ -0,0 +1,193 @@
+/**
+ * ==VimperatorPlugin==
+ * @name copy.js
+ * @description enable to copy strings from a template (like CopyURL+)
+ * @description-ja テンプレートから文字列のコピーを可能にします(CopyURL+みたなもの)
+ * @minVersion 1.1
+ * @author teramako teramako@gmail.com
+ * @version 0.5a
+ * ==/VimperatorPlugin==
+ *
+ * Usage:
+ * :copy {copyString} -> copy the argument replaced some certain string
+ * :copy! {expr} -> evaluate the argument and copy the result
+ *
+ * e.g.)
+ * :copy %TITLE% -> copied the title of the current page
+ * :copy title -> same as `:copy %TITLE%' by default
+ * :copy! liberator.version -> copy the value of liberator.version
+ *
+ * If non-argument, used `default'
+ *
+ * label: template name which is command argument
+ * value: copy string
+ * the certian string is replace to ...
+ * %TITTLE% -> to the title of the current page
+ * %URL% -> to the URL of the current page
+ * %SEL% -> to the string of selection
+ * %HTMLSEL% -> to the html string of selection
+ *
+ * map: key map (optional)
+ *
+ * custom: {function} or {Array} (optional)
+ * {function}:
+ * execute the function and copy return value, if specified.
+ *
+ * {Array}:
+ * replaced to the {value} by normal way at first.
+ * and replace words matched {Array}[0] in the replaced string to {Array}[1].
+ * {Array}[0] is string or regexp
+ * {Array}[1] is string or function
+ * see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:String:replace
+ *
+ * The copy_templates is a string variable which can set on
+ * vimperatorrc as following.
+ *
+ * let copy_templates = "[{ label: 'titleAndURL', value: '%TITLE%\n%URL%' }, { label: 'title', value: '%TITLE%' }]"
+ *
+ * or your can set it using inline JavaScript.
+ *
+ * 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
+ */
+liberator.plugins.exCopy = (function(){
+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>' }
+ ];
+}
+
+liberator.globalVariables.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);
+});
+
+// used when argument is none
+//const defaultValue = templates[0].label;
+commands.addUserCommand(['copy'],'Copy to clipboard',
+ function(arg, special){
+ liberator.plugins.exCopy.copy(arg, special);
+ },{
+ completer: function(filter, special){
+ if (special){
+ return completion.javascript(filter);
+ }
+ var templates = liberator.globalVariables.copy_templates.map(function(template)
+ [template.label, template.value]
+ );
+ if (!filter){ return [0,templates]; }
+ var candidates = [];
+ templates.forEach(function(template){
+ if (template[0].toLowerCase().indexOf(filter.toLowerCase()) == 0){
+ candidates.push(template);
+ }
+ });
+ return [0, candidates];
+ },
+ bang: 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;
+ liberator.globalVariables.copy_templates.some(function(template)
+ template.label == label ? (ret = template) && true : false);
+ return ret;
+}
+function replaceVariable(str){
+ if (!str) return '';
+ var win = new XPCNativeWrapper(window.content.window);
+ var sel = '',htmlsel = '';
+ if (str.indexOf('%SEL%') >= 0 || str.indexOf('%HTMLSEL%') >= 0){
+ sel = win.getSelection().getRangeAt(0);
+ }
+ if (str.indexOf('%HTMLSEL%') >= 0){
+ var serializer = new XMLSerializer();
+ htmlsel = serializer.serializeToString(sel.cloneContents());
+ }
+ return str.replace(/%TITLE%/g,buffer.title)
+ .replace(/%URL%/g,buffer.URL)
+ .replace(/%SEL%/g,sel.toString())
+ .replace(/%HTMLSEL%/g,htmlsel);
+}
+
+var exCopyManager = {
+ add: function(label, value, custom, map){
+ var template = {label: label, value: value, custom: custom, map: map};
+ liberator.globalVariables.copy_templates.unshift(template);
+ if (map) addUserMap(label, map);
+
+ return template;
+ },
+ get: function(label){
+ return getCopyTemplate(label);
+ },
+ copy: function(arg, special){
+ var copyString = '';
+ var isError = false;
+ if (special && arg){
+ try {
+ copyString = window.eval('with(liberator){' + 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 = liberator.globalVariables.copy_templates[0];
+ var template = getCopyTemplate(arg) || arg;
+ if (typeof template.custom == 'function'){
+ copyString = template.custom.call(this, template.value);
+ } else if (template.custom instanceof Array){
+ copyString = replaceVariable(template.value).replace(tempalte.custom[0], template.custom[1]);
+ } else {
+ copyString = replaceVariable(template.value);
+ }
+ }
+ util.copyToClipboard(copyString);
+ if (isError){
+ liberator.echoerr('CopiedErrorString: `' + copyString + "'");
+ } else {
+ liberator.echo('CopiedString: `' + util.escapeHTML(copyString) + "'");
+ }
+ }
+};
+return exCopyManager;
+})();
+
+// vim: set fdm=marker sw=4 ts=4 et:
diff --git a/direct_bookmark.js b/direct_bookmark.js
new file mode 100644
index 0000000..d1058e1
--- /dev/null
+++ b/direct_bookmark.js
@@ -0,0 +1,648 @@
+// Vimperator plugin: 'Direct Post to Social Bookmarks'
+// Version: 0.12
+// Last Change: 22-Sep-2008. Jan 2008
+// License: Creative Commons
+// Maintainer: Trapezoid <trapezoid.g@gmail.com> - http://unsigned.g.hatena.ne.jp/Trapezoid
+// Parts:
+// http://d.hatena.ne.jp/fls/20080309/p1
+// Pagerization (c) id:ofk
+// AutoPagerize (c) id:swdyh
+// direct_delb.js id:mattn
+// JSDeferred id:cho45
+//
+// Social Bookmark direct add script for Vimperator 0.6.*
+// for Migemo search: require XUL/Migemo Extension
+//
+// Variables:
+// 'g:direct_sbm_use_services_by_tag'
+// Use social bookmark services to extract tags
+// 'h': Hatena Bookmark
+// 'd': del.icio.us
+// 'l': livedoor clip
+// 'p': Places (Firefox bookmarks)
+// Usage: let g:direct_sbm_use_services_by_tag = "hdl"
+// 'g:direct_sbm_use_services_by_post'
+// Use social bookmark services to post
+// 'h': Hatena Bookmark
+// 'd': del.icio.us
+// 'l': livedoor clip
+// 'g': Google Bookmarks
+// 'p': Places (Firefox bookmarks)
+// Usage: let g:direct_sbm_use_services_by_post = "hdl"
+// 'g:direct_sbm_is_normalize'
+// Use normalize permalink
+// 'g:direct_sbm_is_use_migemo'
+// Use Migemo completion
+// Commands:
+// ':btags'
+// Extract tags from social bookmarks for completion
+// ':sbm'
+// Post a current page to social bookmarks
+// Arguments
+// -s,-service: default:"hdl"
+// Specify target SBM services to post
+// ':bentry'
+// Goto Bookmark Entry Page
+// ':bicon'
+// Show Bookmark Count as Icon
+(function(){
+ var evalFunc = window.eval;
+ try {
+ var sandbox = new Components.utils.Sandbox(window);
+ if (Components.utils.evalInSandbox("true", sandbox) === true) {
+ evalFunc = function(text) {
+ return Components.utils.evalInSandbox(text, sandbox);
+ }
+ }
+ } catch(e) { liberator.log('warning: direct_bookmark.js is working with unsafe sandbox.'); }
+
+ var useServicesByPost = liberator.globalVariables.direct_sbm_use_services_by_post || 'hdl';
+ var useServicesByTag = liberator.globalVariables.direct_sbm_use_services_by_tag || 'hdl';
+ var isNormalize = liberator.globalVariables.direct_sbm_is_normalize ?
+ evalFunc(liberator.globalVariables.direct_sbm_is_normalize) : true;
+ var isUseMigemo = liberator.globalVariables.direct_sbm_is_use_migemo ?
+ evalFunc(liberator.globalVariables.direct_sbm_is_use_migemo) : true;
+
+ var XMigemoCore;
+ try{
+ XMigemoCore = Components.classes['@piro.sakura.ne.jp/xmigemo/factory;1']
+ .getService(Components.interfaces.pIXMigemoFactory)
+ .getService("ja");
+ }
+ catch(ex if ex instanceof TypeError){}
+
+
+ function Deferred () this instanceof Deferred ? this.init(this) : new Deferred();
+ Deferred.prototype = {
+ init : function () {
+ this._next = null;
+ this.callback = {
+ ok: function (x) x,
+ ng: function (x) { throw x }
+ };
+ return this;
+ },
+
+ next : function (fun) this._post("ok", fun),
+ error : function (fun) this._post("ng", fun),
+ call : function (val) this._fire("ok", val),
+ fail : function (err) this._fire("ng", err),
+
+ cancel : function () {
+ (this.canceller || function () {})();
+ return this.init();
+ },
+
+ _post : function (okng, fun) {
+ this._next = new Deferred();
+ this._next.callback[okng] = fun;
+ return this._next;
+ },
+
+ _fire : function (okng, value) {
+ var self = this, next = "ok";
+ try {
+ value = self.callback[okng].call(self, value);
+ } catch (e) {
+ next = "ng";
+ value = e;
+ }
+ if (value instanceof Deferred) {
+ value._next = self._next;
+ } else if (self._next) {
+ self._next._fire(next, value);
+ }
+ return this;
+ }
+ };
+
+ Deferred.next = function (fun) {
+ var d = new Deferred();
+ var id = setTimeout(function () { clearTimeout(id); d.call() }, 0);
+ if (fun) d.callback.ok = fun;
+ d.canceller = function () { try { clearTimeout(id) } catch (e) {} };
+ return d;
+ };
+
+ function http (opts) {
+ var d = Deferred();
+ var req = new XMLHttpRequest();
+ req.open(opts.method, opts.url, true, opts.user || null, opts.password || null);
+ if (opts.headers) {
+ for (var k in opts.headers) if (opts.headers.hasOwnProperty(k)) {
+ req.setRequestHeader(k, opts.headers[k]);
+ }
+ }
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) d.call(req);
+ };
+ req.send(opts.data || null);
+ d.xhr = req;
+ return d;
+ }
+ http.get = function (url) http({method:"get", url:url});
+ http.post = function (url, data) http({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}});
+
+ Deferred.Deferred = Deferred;
+ Deferred.http = http;
+
+
+ function WSSEUtils(aUserName, aPassword){
+ this._init(aUserName, aPassword);
+ }
+
+ WSSEUtils.prototype = {
+
+ get userName() this._userName,
+ get noce() this._nonce,
+ get created() this._created,
+ get passwordDigest() this._passwordDigest,
+
+ getWSSEHeader: function(){
+ var result = [
+ 'UsernameToken Username="' + this._userName + '", ',
+ 'PasswordDigest="' + this._passwordDigest + '=", ',
+ 'Nonce="' + this._nonce + '", ',
+ 'Created="' + this._created + '"'
+ ].join("");
+
+ return result;
+ },
+
+ _init: function(aUserName, aPassword){
+ var uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
+ var seed = (new Date()).toUTCString() + uuidGenerator.generateUUID().toString();
+
+ this._userName = aUserName;
+ this._nonce = this._getSha1Digest(seed, true);
+ this._created = this._getISO8601String((new Date()));
+ this._passwordDigest = this._getSha1Digest(this._getSha1Digest(seed, false) + this._created + aPassword, true);
+ },
+
+ _getSha1Digest: function(aString, aBase64){
+ var cryptoHash = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
+ cryptoHash.init(Ci.nsICryptoHash.SHA1);
+
+ var inputStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
+ inputStream.setData(aString, aString.length);
+ cryptoHash.updateFromStream(inputStream, -1);
+
+ return cryptoHash.finish(aBase64);
+ },
+
+ _getISO8601String: function(aDate){
+ var result = [
+ zeropad(aDate.getUTCFullYear(), 4), "-",
+ zeropad(aDate.getUTCMonth() + 1, 2), "-",
+ zeropad(aDate.getUTCDate(), 2), "T",
+ zeropad(aDate.getUTCHours(), 2), ":",
+ zeropad(aDate.getUTCMinutes(), 2), ":",
+ zeropad(aDate.getUTCSeconds(), 2), "Z"
+ ].join("");
+ return result;
+
+ function zeropad(s, l){
+ s = String(s);
+ while(s.length < l){
+ s = "0" + s;
+ }
+ return s;
+ }
+ }
+
+ };
+
+ // copied from AutoPagerize (c) id:swdyh
+ function getElementsByXPath(xpath, node){
+ node = node || document;
+ var nodesSnapshot = (node.ownerDocument || node).evaluate(xpath, node, null,
+ XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ var data = [];
+ for(var i = 0, l = nodesSnapshot.snapshotLength; i < l;
+ data.push(nodesSnapshot.snapshotItem(i++)));
+ return (data.length > 0) ? data : null;
+ }
+
+ function getFirstElementByXPath(xpath, node){
+ node = node || document;
+ var result = (node.ownerDocument || node).evaluate(xpath, node, null,
+ XPathResult.FIRST_ORDERED_NODE_TYPE, null);
+ return result.singleNodeValue ? result.singleNodeValue : null;
+ }
+
+ // copied from Pagerization (c) id:ofk
+ function parseHTML(str, ignoreTags) {
+ var exp = "^[\\s\\S]*?<html(?:\\s[^>]*)?>|</html\\s*>[\\S\\s]*$";
+ if (ignoreTags) {
+ if (typeof ignoreTags == "string") ignoreTags = [ignoreTags];
+ var stripTags = [];
+ ignoreTags = ignoreTags.filter(function(tag) tag[tag.length - 1] == "/" || !stripTags.push(tag))
+ .map(function(tag) tag.replace(/\/$/, ""));
+ if (stripTags.length > 0) {
+ stripTags = stripTags.length > 1
+ ? "(?:" + stripTags.join("|") + ")"
+ : String(stripTags);
+ exp += "|<" + stripTags + "(?:\\s[^>]*|/)?>|</" + stripTags + "\\s*>";
+ }
+ }
+ str = str.replace(new RegExp(exp, "ig"), "");
+ var res = document.implementation.createDocument(null, "html", null);
+ var range = document.createRange();
+ range.setStartAfter(window.content.document.body);
+ res.documentElement.appendChild(res.importNode(range.createContextualFragment(str), true));
+ if (ignoreTags) ignoreTags.forEach(function(tag) {
+ var elements = res.getElementsByTagName(tag);
+ for (var i = elements.length, el; el = elements.item(--i); el.parentNode.removeChild(el));
+ });
+ return res;
+ }
+
+ //
+ //
+ //
+ //
+
+ function getNormalizedPermalink(url){
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET","http://api.pathtraq.com/normalize_url?url=" + url,false);
+ xhr.send(null);
+ if(xhr.status != 200){
+ liberator.echoerr("Pathtraq: FAILED to normalize URL!!");
+ return undefined;
+ }
+ return xhr.responseText;
+ }
+
+ function getUserAccount(form,post,arg){
+ var user, password;
+ try{
+ var passwordManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+ var logins = passwordManager.findLogins({}, form, post, arg);
+ if(logins.length > 0){
+ [user, password] = [logins[0].username, logins[0].password];
+ } else {
+ var promptUser = { value : this.loginPrompt.user }, promptPass = { value : this.loginPrompt.password };
+ var promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"]
+ .getService(Ci.nsIPromptService);
+
+ var nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
+ Ci.nsILoginInfo,
+ "init");
+
+ var ret = promptSvc.promptUsernameAndPassword(
+ window, form, this.loginPrompt.description,
+ promptUser, promptPass, null, {}
+ );
+ if(ret){
+ [user, password] = [promptUser.value, promptPass.value];
+ var formLoginInfo = new nsLoginInfo(form,
+ post, null,
+ user, password, '', '');
+ passwordManager.addLogin(formLoginInfo);
+ } else {
+ liberator.echoerr("Direct Social Bookmark: account not found - " + form);
+ }
+ }
+ }
+ catch(ex){
+ liberator.echoerr("Direct Social Bookmark: handled exception during tag extracting");
+ liberator.log(ex);
+ }
+ return [user, password];
+ }
+
+ //
+ //
+ //
+ //
+
+ var services = {
+ 'h': {
+ description:'Hatena bookmark',
+ account:['https://www.hatena.ne.jp', 'https://www.hatena.ne.jp', null],
+ loginPrompt:{ user:'', password:'', description:'Enter username and password.' },
+ entryPage:'http://b.hatena.ne.jp/entry/%URL%',
+ poster:function(user,password,url,title,comment,tags){
+ var tagString = tags.length > 0 ? '[' + tags.join('][') + ']' : "";
+ var request =
+ <entry xmlns="http://purl.org/atom/ns#">
+ <title>dummy</title>
+ <link rel="related" type="text/html" href={url}/>
+ <summary type="text/plain">{tagString + comment}</summary>
+ </entry>;
+ var wsse = new WSSEUtils(user,password);
+
+ return Deferred.http({
+ method: "post",
+ url: "http://b.hatena.ne.jp/atom/post",
+ data: request.toString(),
+ headers: {
+ "X-WSSE": wsse.getWSSEHeader(),
+ "Content-Type": "application/atom+xml",
+ },
+ }).next(function(xhr){
+ if(xhr.status != 201) throw "Hatena Bookmark: faild";
+ });
+ },
+ tags:function(user,password){
+ var xhr = new XMLHttpRequest();
+ var hatena_tags = [];
+
+ xhr.open("GET","http://b.hatena.ne.jp/my",false);
+ xhr.send(null);
+
+ var mypage_html = parseHTML(xhr.responseText, ['img', 'script']);
+ var tags = getElementsByXPath("//ul[@id=\"taglist\"]/li/a",mypage_html);
+
+ tags.forEach(function(tag){
+ hatena_tags.push(tag.innerHTML);
+ });
+ liberator.echo("Hatena Bookmark: Tag parsing is finished. Taglist length: " + tags.length);
+ return hatena_tags;
+ },
+ icon:function(url){
+ return '<img src="http://b.hatena.ne.jp/entry/image/' + url + '" style="vertical-align: middle;" />';
+ },
+ },
+ 'd': {
+ description:'del.icio.us',
+ account:['https://secure.delicious.com', 'https://secure.delicious.com', null],
+ loginPrompt:{ user:'', password:'', description:'Enter username and password.' },
+ entryPage:'http://del.icio.us/url/%URL::MD5%',
+ poster:function(user,password,url,title,comment,tags){
+ var request_url = 'https://api.del.icio.us/v1/posts/add?' + [
+ ['url', url], ['description', title], ['extended', comment], ['tags', tags.join(' ')]
+ ].map(function(p) p[0] + '=' + encodeURIComponent(p[1])).join('&');
+ return Deferred.http({
+ method: "get",
+ url: request_url,
+ user: user,
+ password: password,
+ }).next(function(xhr){
+ if(xhr.status != 200) throw "del.icio.us: faild";
+ });
+ },
+ tags:function(user,password){
+ const feed_url = 'http://feeds.delicious.com/feeds/json/tags/';
+ var returnValue = [];
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", feed_url + user + "?raw", false, user, password);
+ xhr.send(null);
+
+ var tags = evalFunc("(" + xhr.responseText + ")");
+ for(var tag in tags)
+ returnValue.push(tag);
+ liberator.echo("del.icio.us: Tag parsing is finished. Taglist length: " + returnValue.length);
+ return returnValue;
+ },
+ icon:function(url){
+ var url = liberator.buffer.URL;
+ var cryptoHash = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
+ cryptoHash.init(Ci.nsICryptoHash.MD5);
+ var inputStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
+ inputStream.setData(url, url.length);
+ cryptoHash.updateFromStream(inputStream, -1);
+ var hash = cryptoHash.finish(false), ascii = [];
+ const hexchars = '0123456789ABCDEF';
+ var hexrep = new Array(hash.length * 2);
+ for(var i = 0; i < hash.length; i++) {
+ ascii[i * 2] = hexchars.charAt((hash.charCodeAt(i) >> 4) & 0xF);
+ ascii[i * 2 + 1] = hexchars.charAt(hash.charCodeAt(i) & 0xF);
+ }
+ return '<img src="http://del.icio.us/feeds/img/savedcount/' + ascii.join('').toLowerCase() + '?aggregate" style="vertical-align: middle;" />';
+ },
+ },
+ 'l': {
+ description:'livedoor clip',
+ account:['http://api.clip.livedoor.com', 'http://api.clip.livedoor.com', null],
+ loginPrompt:{ user:'', password:'apikey', description:'Enter username and apikey.\nyou can get "api-key" from\n\thttp://clip.livedoor.com/config/api' },
+ entryPage:'http://clip.livedoor.com/page/%URL%',
+ poster:function(user,password,url,title,comment,tags){
+ var request_url = 'http://api.clip.livedoor.com/v1/posts/add?' + [
+ ['url', url], ['description', title], ['extended', comment], ['tags', tags.join(' ')]
+ ].map(function(p) p[0] + '=' + encodeURIComponent(p[1])).join('&');
+ return Deferred.http({
+ method: "get",
+ url: request_url,
+ user: user,
+ password: password,
+ }).next(function(xhr){
+ if(xhr.status != 200) throw "livedoor clip: faild";
+ });
+ },
+ tags:function(user,password){
+ var xhr = new XMLHttpRequest();
+ var ldc_tags = [];
+
+ xhr.open("GET","http://clip.livedoor.com/clip/add?link=http://example.example/",false);
+ xhr.send(null);
+
+ var mypage_html = parseHTML(xhr.responseText, ['img', 'script']);
+ var tags = getElementsByXPath("id(\"tag_list\")/span",mypage_html);
+
+ tags.forEach(function(tag){
+ ldc_tags.push(tag.textContent);
+ });
+ liberator.echo("livedoor clip: Tag parsing is finished. Taglist length: " + tags.length);
+ return ldc_tags;
+ },
+ icon:function(url){
+ return '<img src="http://image.clip.livedoor.com/counter/' + url + '" style="vertical-align: middle;" />';
+ },
+ },
+ 'g': {
+ description:'Google Bookmarks',
+ account:null,
+ loginPrompt:null,
+ entryPage:'%URL%',
+ poster:function(user,password,url,title,comment,tags){
+ var request_url = 'http://www.google.com/bookmarks/mark';
+ var params = [
+ ['bkmk', url], ['title', title], ['labels', tags.join(',')]
+ ].map(function(p) p[0] + '=' + encodeURIComponent(p[1])).join('&');
+ return Deferred.http({
+ method: "post",
+ url: request_url,
+ data: params,
+ headers: {
+ "User-Agent": navigator.userAgent + " GoogleToolbarFF 3.0.20070525",
+ },
+ }).next(function(xhr){
+ if(xhr.status != 200) throw "Google Bookmarks: faild";
+ });
+ },
+ tags:function(user,password) [],
+ },
+ 'f': {
+ description:'foves',
+ account:['https://secure.faves.com', 'https://secure.faves.com', null],
+ loginPrompt:{ user:'', password:'', description:'Enter username and password.' },
+ entryPage:'%URL%',
+ poster:function(user,password,url,title,comment,tags){
+ var request_url = 'https://secure.faves.com/v1/posts/add?' + [
+ ['url', url], ['description', title], ['extended', comment], ['tags', tags.join(' ')]
+ ].map(function(p) p[0] + '=' + encodeURIComponent(p[1])).join('&');
+ return Deferred.http({
+ method: "get",
+ url: request_url,
+ user: user,
+ password: password,
+ }).next(function(xhr){
+ if(xhr.status != 200) throw "foves: faild";
+ });
+ },
+ tags:function(user,password){
+ const feed_url = 'https://secure.faves.com/v1/tags/get';
+ var returnValue = [];
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", feed_url, false, user, password);
+ xhr.send(null);
+
+ var tags = xhr.responseXML.getElementsByTagName('tag');
+ for(var n = 0; n < tags.length; n++)
+ returnValue.push(tags[n].getAttribute('tag'));
+ liberator.echo("foves: Tag parsing is finished. Taglist length: " + returnValue.length);
+ return returnValue;
+ },
+ },
+ 'p': {
+ description:'Places',
+ account:null,
+ loginPrompt:null,
+ entryPage:'%URL%',
+ poster:function(user,password,url,title,comment,tags){
+ const taggingService = Cc["@mozilla.org/browser/tagging-service;1"].getService(Ci.nsITaggingService);
+ var nsUrl = Cc["@mozilla.org/network/standard-url;1"].createInstance(Ci.nsIURL);
+ nsUrl.spec = url;
+ taggingService.tagURI(nsUrl,tags);
+ try{
+ Application.bookmarks.tags.addBookmark(title, nsUrl);
+ }catch(e){
+ throw "Places: faild";
+ }
+ },
+ tags:function(user,password)
+ Application.bookmarks.tags.children.map(function(x) x.title),
+ },
+ };
+ liberator.plugins.direct_bookmark = { services: services, tags: [] };
+
+ function getTags(arg){
+ var d,first;
+ d = first = Deferred();
+
+ useServicesByTag.split(/\s*/).forEach(function(service){
+ var user, password, currentService = services[service] || null;
+ [user,password] = currentService.account ? getUserAccount.apply(currentService,currentService.account) : ["", ""];
+ d = d.next(function(t) t.concat(currentService.tags(user,password)));
+ });
+ d.next(function(tags){liberator.plugins.direct_bookmark.tags = tags.filter(function(e,i,a) a.indexOf(e) == i).sort()})
+ .error(function(e){liberator.echoerr("direct_bookmark.js: Exception throwed! " + e)});
+ return first;
+ }
+ liberator.commands.addUserCommand(['btags'],"Update Social Bookmark Tags",
+ function(arg){setTimeout(function(){getTags().call([])},0)}, {});
+ liberator.commands.addUserCommand(['bentry'],"Goto Bookmark Entry Page",
+ function(service, special){
+ service = service || useServicesByPost.split(/\s*/)[0];
+ var currentService = services[service] || null;
+ if(!currentService || !currentService.entryPage) {
+ return;
+ }
+ liberator.open(currentService.entryPage
+ .replace(/%URL(?:::(ESC|MD5))?%/g, function(x, t){
+ if(!t) return liberator.buffer.URL.replace(/#/, '%23');
+ if(t == "ESC") return encodeURIComponent(liberator.buffer.URL);
+ if(t == "MD5"){
+ var url = liberator.buffer.URL;
+ var cryptoHash = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
+ cryptoHash.init(Ci.nsICryptoHash.MD5);
+ var inputStream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
+ inputStream.setData(url, url.length);
+ cryptoHash.updateFromStream(inputStream, -1);
+ var hash = cryptoHash.finish(false), ascii = [];
+ const hexchars = '0123456789ABCDEF';
+ var hexrep = new Array(hash.length * 2);
+ for(var i = 0; i < hash.length; i++) {
+ ascii[i * 2] = hexchars.charAt((hash.charCodeAt(i) >> 4) & 0xF);
+ ascii[i * 2 + 1] = hexchars.charAt(hash.charCodeAt(i) & 0xF);
+ }
+ return ascii.join('').toLowerCase();
+ }
+ }), special ? liberator.NEW_TAB : CURRENT_TAB);
+ },{
+ completer: function(filter)
+ [0, useServicesByPost.split(/\s*/).map(function(p) [p, services[p].description])]
+ }
+ );
+ liberator.commands.addUserCommand(['bicon'],"Show Bookmark Count as Icon",
+ function(arg){
+ var url = getNormalizedPermalink(liberator.buffer.URL);
+ var html = useServicesByTag.split(/\s*/).map(function(service){
+ var currentService = services[service] || null;
+ return (currentService && typeof currentService.icon === 'function') ?
+ (currentService.description + ': ' + currentService.icon(url)) : null;
+ }).join('<br />');
+ liberator.echo(html, true);
+ }, {});
+ liberator.commands.addUserCommand(['sbm'],"Post to Social Bookmark",
+ function(arg){
+ var comment = "";
+ var targetServices = useServicesByPost;
+
+ for(var opt in arg){
+ switch(opt){
+ case '-s':
+ if (arg[opt]) targetServices = arg[opt];
+ break;
+ case 'arguments':
+ if(arg[opt].length > 0) comment = arg[opt].join(" ");
+ }
+ }
+
+ var tags = [];
+ var re = /\[([^\]]+)\]([^\[].*)?/g;
+
+ var d = new Deferred();
+ var first = d;
+
+ if(/^\[[^\]]+\]/.test(comment)){
+ var tag, text;
+ while((tag = re.exec(comment))){
+ [, tag, text] = tag;
+ tags.push(tag);
+ }
+ comment = text || '';
+ }
+
+ var url = liberator.buffer.URL;
+ var title = liberator.buffer.title;
+
+ targetServices.split(/\s*/).forEach(function(service){
+ var user, password, currentService = services[service] || null;
+ [user,password] = currentService.account ? getUserAccount.apply(currentService,currentService.account) : ["", ""];
+ d = d.next(function() currentService.poster(
+ user,password,
+ isNormalize ? getNormalizedPermalink(url) : url,title,
+ comment,tags
+ ));
+ });
+ d.error(function(e){liberator.echoerr("direct_bookmark.js: Exception throwed! " + e);liberator.log(e);});
+ setTimeout(function(){first.call();},0);
+ },{
+ completer: function(filter){
+ var match_result = filter.match(/((?:\[[^\]]*\])*)\[?(.*)/); //[all, commited, now inputting]
+ var m = new RegExp(XMigemoCore && isUseMigemo ? "^(" + XMigemoCore.getRegExp(match_result[2]) + ")" : "^" + match_result[2],'i');
+ var completionList = [];
+ if(liberator.plugins.direct_bookmark.tags.length == 0)
+ getTags().call([]);
+ return [match_result[1].length, [["[" + tag + "]","Tag"]
+ for each (tag in liberator.plugins.direct_bookmark.tags) if (m.test(tag) && match_result[1].indexOf('[' + tag + ']') < 0)]];
+ },
+ options: [
+ [['-s','-service'], liberator.commands.OPTION_STRING],
+ ]
+ }
+ );
+})();
+// vim:sw=4 ts=4 et:
diff --git a/feedSomeKeys.js b/feedSomeKeys.js
new file mode 100644
index 0000000..ba4419a
--- /dev/null
+++ b/feedSomeKeys.js
@@ -0,0 +1,320 @@
+/**
+ * ==VimperatorPlugin==
+ * @name feedSomeKeys
+ * @description feed some defined key events into the Web content
+ * @description-ja 定義したkeyイベントをWebページ側へ送ってあげます
+ * @author teramako teramako@gmail.com
+ * @version 0.1a
+ * ==/VimperatorPlugin==
+ *
+ * 英語での説明を放棄する
+ *
+ * keyイベント(正確にはkepressイベント)をWebコンテンツ側へ送る事を可能にするプラグイン
+ * Gmailとかlivedoor ReaderとかGreasemonkeyでキーを割り当てている場合に活躍するでしょう。
+ * それ以外の場面ではむしろ邪魔になる諸刃の剣
+ *
+ * :f[eed]map lhs -> lhsのキーマップをそのままWebコンテンツへ
+ * :f[eed]map lhs [num]rhs -> lhsのキーマップをrhsへ変換してWebコンテンツへ
+ * [num]はフレームの番号(省略時はトップウィンドウへイベントが送られる)
+ *
+ * :fmapc
+ * :feedmapclear -> 全てを無に帰して元に戻す
+ *
+ * :f[eed]map! lhs -> "!" をつけると、仮想キーコードでイベントを送るように
+ *
+ * == LDR の場合 ==
+js <<EOF
+autocommands.add('PageLoad,TabSelect',/reader\.livedoor\.com\/reader\//,
+ 'js plugins.feedKey.setup("j k s a p o v c <Space> <S-Space> z b < >".split(/ +/));');
+EOF
+ * とかやると幸せになれるかも。
+ *
+ * == Gmail の場合 ==
+js <<EOF
+autocommands.add('PageLoad,TabSelect',/mail\.google\.com\/mail/,[
+ 'js plugins.feedKey.setup(',
+ '"c / j k n p o u e x s r a # [ ] z ? gi gs gt gd ga gc".split(/ +/).map(function(i) [i, "3" + i])',
+ ');'
+].join(''));
+EOF
+ * とかやると幸せになれるかもしれません。
+ * 頭についている3の意味は3番目のフレームの意味。通常のmapと違い3回の意味ではないので注意
+ *
+ * Greasemonkey LDRizeの場合などにも使用可
+ */
+
+liberator.plugins.feedKey = (function(){
+var origMaps = [];
+var feedMaps = [];
+
+// keyTableの再定義...ひどく不毛...
+const keyTable = [
+ [ KeyEvent.DOM_VK_BACK_SPACE, ["BS"] ],
+ [ KeyEvent.DOM_VK_TAB, ["Tab"] ],
+ [ KeyEvent.DOM_VK_RETURN, ["Return", "CR", "Enter"] ],
+ //[ KeyEvent.DOM_VK_ENTER, ["Enter"] ],
+ [ KeyEvent.DOM_VK_ESCAPE, ["Esc", "Escape"] ],
+ [ KeyEvent.DOM_VK_SPACE, ["Spc", "Space"] ],
+ [ KeyEvent.DOM_VK_PAGE_UP, ["PageUp"] ],
+ [ KeyEvent.DOM_VK_PAGE_DOWN, ["PageDown"] ],
+ [ KeyEvent.DOM_VK_END, ["End"] ],
+ [ KeyEvent.DOM_VK_HOME, ["Home"] ],
+ [ KeyEvent.DOM_VK_LEFT, ["Left"] ],
+ [ KeyEvent.DOM_VK_UP, ["Up"] ],
+ [ KeyEvent.DOM_VK_RIGHT, ["Right"] ],
+ [ KeyEvent.DOM_VK_DOWN, ["Down"] ],
+ [ KeyEvent.DOM_VK_INSERT, ["Ins", "Insert"] ],
+ [ KeyEvent.DOM_VK_DELETE, ["Del", "Delete"] ],
+ [ KeyEvent.DOM_VK_F1, ["F1"] ],
+ [ KeyEvent.DOM_VK_F2, ["F2"] ],
+ [ KeyEvent.DOM_VK_F3, ["F3"] ],
+ [ KeyEvent.DOM_VK_F4, ["F4"] ],
+ [ KeyEvent.DOM_VK_F5, ["F5"] ],
+ [ KeyEvent.DOM_VK_F6, ["F6"] ],
+ [ KeyEvent.DOM_VK_F7, ["F7"] ],
+ [ KeyEvent.DOM_VK_F8, ["F8"] ],
+ [ KeyEvent.DOM_VK_F9, ["F9"] ],
+ [ KeyEvent.DOM_VK_F10, ["F10"] ],
+ [ KeyEvent.DOM_VK_F11, ["F11"] ],
+ [ KeyEvent.DOM_VK_F12, ["F12"] ],
+ [ KeyEvent.DOM_VK_F13, ["F13"] ],
+ [ KeyEvent.DOM_VK_F14, ["F14"] ],
+ [ KeyEvent.DOM_VK_F15, ["F15"] ],
+ [ KeyEvent.DOM_VK_F16, ["F16"] ],
+ [ KeyEvent.DOM_VK_F17, ["F17"] ],
+ [ KeyEvent.DOM_VK_F18, ["F18"] ],
+ [ KeyEvent.DOM_VK_F19, ["F19"] ],
+ [ KeyEvent.DOM_VK_F20, ["F20"] ],
+ [ KeyEvent.DOM_VK_F21, ["F21"] ],
+ [ KeyEvent.DOM_VK_F22, ["F22"] ],
+ [ KeyEvent.DOM_VK_F23, ["F23"] ],
+ [ KeyEvent.DOM_VK_F24, ["F24"] ],
+];
+const vkeyTable = [
+ [ KeyEvent.DOM_VK_0, ['0'] ],
+ [ KeyEvent.DOM_VK_1, ['1'] ],
+ [ KeyEvent.DOM_VK_2, ['2'] ],
+ [ KeyEvent.DOM_VK_3, ['3'] ],
+ [ KeyEvent.DOM_VK_4, ['4'] ],
+ [ KeyEvent.DOM_VK_5, ['5'] ],
+ [ KeyEvent.DOM_VK_6, ['6'] ],
+ [ KeyEvent.DOM_VK_7, ['7'] ],
+ [ KeyEvent.DOM_VK_8, ['8'] ],
+ [ KeyEvent.DOM_VK_9, ['9'] ],
+ [ KeyEvent.DOM_VK_SEMICOLON, [';'] ],
+ [ KeyEvent.DOM_VK_EQUALS, ['='] ],
+ [ KeyEvent.DOM_VK_A, ['a'] ],
+ [ KeyEvent.DOM_VK_B, ['b'] ],
+ [ KeyEvent.DOM_VK_C, ['c'] ],
+ [ KeyEvent.DOM_VK_D, ['d'] ],
+ [ KeyEvent.DOM_VK_E, ['e'] ],
+ [ KeyEvent.DOM_VK_F, ['f'] ],
+ [ KeyEvent.DOM_VK_G, ['g'] ],
+ [ KeyEvent.DOM_VK_H, ['h'] ],
+ [ KeyEvent.DOM_VK_I, ['i'] ],
+ [ KeyEvent.DOM_VK_J, ['j'] ],
+ [ KeyEvent.DOM_VK_K, ['k'] ],
+ [ KeyEvent.DOM_VK_L, ['l'] ],
+ [ KeyEvent.DOM_VK_M, ['m'] ],
+ [ KeyEvent.DOM_VK_N, ['n'] ],
+ [ KeyEvent.DOM_VK_O, ['o'] ],
+ [ KeyEvent.DOM_VK_P, ['p'] ],
+ [ KeyEvent.DOM_VK_Q, ['q'] ],
+ [ KeyEvent.DOM_VK_R, ['r'] ],
+ [ KeyEvent.DOM_VK_S, ['s'] ],
+ [ KeyEvent.DOM_VK_T, ['t'] ],
+ [ KeyEvent.DOM_VK_U, ['u'] ],
+ [ KeyEvent.DOM_VK_V, ['v'] ],
+ [ KeyEvent.DOM_VK_W, ['w'] ],
+ [ KeyEvent.DOM_VK_X, ['x'] ],
+ [ KeyEvent.DOM_VK_Y, ['y'] ],
+ [ KeyEvent.DOM_VK_Z, ['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_BACK_QUOTE, ['`'] ],
+ [ KeyEvent.DOM_VK_OPEN_BRACKET, ['{'] ],
+ [ KeyEvent.DOM_VK_BACK_SLASH, ['\\'] ],
+ [ KeyEvent.DOM_VK_CLOSE_BRACKET, ['}'] ],
+ [ KeyEvent.DOM_VK_QUOTE, ["'"] ],
+];
+
+function getKeyCode(str, vkey) {
+ str = str.toLowerCase();
+ var ret = 0;
+ (vkey ? vkeyTable : keyTable).some(function(i) i[1].some(function(k) k.toLowerCase() == str && (ret = i[0])));
+ return ret;
+}
+function init(keys, useVkey){
+ destroy();
+ keys.forEach(function(key){
+ var origKey, feedKey;
+ if (key instanceof Array){
+ [origKey, feedKey] = key;
+ } else if (typeof(key) == 'string'){
+ [origKey, feedKey] = [key,key];
+ }
+ replaceUserMap(origKey, feedKey, useVkey);
+ });
+}
+function replaceUserMap(origKey, feedKey, useVkey){
+ if (mappings.hasMap(modes.NORMAL, origKey)){
+ var origMap = mappings.get(modes.NORMAL,origKey);
+ if (origMap.description.indexOf(origKey+' -> ') != 0) {
+ // origMapをそのままpushするとオブジェクト内の参照先を消されてしまう
+ // 仕方なく複製を試みる
+ var clone = new Map(origMap.modes.map(function(m) m),
+ origMap.names.map(function(n) n),
+ origMap.description,
+ origMap.action,
+ { flags:origMap.flags, rhs:origMap.rhs, noremap:origMap.noremap });
+ origMaps.push(clone);
+ }
+ }
+ var map = new Map([modes.NORMAL], [origKey], origKey + ' -> ' + feedKey,
+ function(count){
+ count = count > 1 ? count : 1;
+ for (var i=0; i<count; i++){
+ feedKeyIntoContent(feedKey, useVkey);
+ }
+ }, { flags:Mappings.flags.COUNT, rhs:feedKey, noremap:true });
+ addUserMap(map);
+ if (feedMaps.some(function(fmap){
+ if (fmap.names[0] != origKey) return false;
+ for (var key in fmap) fmap[key] = map[key];
+ return true;
+ })) return;
+ feedMaps.push(map);
+}
+function destroy(){
+ try{
+ feedMaps.forEach(function(map){
+ mappings.remove(map.modes[0],map.names[0]);
+ });
+ }catch(e){ log(map); }
+ origMaps.forEach(function(map){
+ addUserMap(map);
+ });
+ origMaps = [];
+ feedMaps = [];
+}
+function addUserMap(map){
+ mappings.addUserMap(map.modes, map.names, map.description, map.action, { flags:map.flags,noremap:map.noremap,rhs:map.rhs });
+}
+function parseKeys(keys){
+ var matches = /^\d+(?=\D)/.exec(keys);
+ if (matches){
+ var num = parseInt(matches[0],10);
+ if (!isNaN(num)) return [keys.substr(matches[0].length), num];
+ }
+ return [keys, 0];
+}
+function getDestinationElement(frameNum){
+ var root = document.commandDispatcher.focusedWindow;
+ if (frameNum > 0){
+ var frames = [];
+ (function(frame){// @see liberator.buffer.shiftFrameFocus
+ if (frame.document.body.localName.toLowerCase() == 'body') {
+ frames.push(frame);
+ }
+ for (var i=0; i<frame.frames.length; i++){
+ arguments.callee(frame.frames[i]);
+ }
+ })(window.content);
+ frames = frames.filter(function(frame){
+ frame.focus();
+ if (document.commandDispatcher.focusedWindow == frame) return frame;
+ });
+ if (frames[frameNum]) return frames[frameNum];
+ }
+ return root;
+}
+function feedKeyIntoContent(keys, useVkey){
+ var frameNum = 0;
+ [keys, frameNum] = parseKeys(keys);
+ var destElem = getDestinationElement(frameNum);
+ destElem.focus();
+ modes.passAllKeys = true;
+ modes.passNextKey = false;
+ for (var i=0; i<keys.length; i++){
+ var keyCode;
+ var shift = false, ctrl = false, alt = false, meta = false;
+ if (useVkey && (keyCode = getKeyCode(keys[i], true))) {
+ var charCode = 0;
+ } else {
+ var charCode = keys.charCodeAt(i);
+ keyCode = 0;
+ }
+ if (keys[i] == '<'){
+ var matches = keys.substr(i + 1).match(/^((?:[ACMSacms]-)*)([^>]+)/);
+ if (matches) {
+ if (matches[1]) {
+ ctrl = /[cC]-/.test(matches[1]);
+ alt = /[aA]-/.test(matches[1]);
+ shift = /[sS]-/.test(matches[1]);
+ meta = /[mM]-/.test(matches[1]);
+ }
+ if (matches[2].length == 1) {
+ if (!ctrl && !alt && !shift && !meta) return false;
+ if (useVkey && (keyCode = getKeyCode(matches[2], true))) {
+ charCode = 0;
+ } else {
+ charCode = matches[2].charCodeAt(0);
+ }
+ } else if (matches[2].toLowerCase() == "space") {
+ if (useVkey) {
+ charCode = 0;
+ keyCode = KeyEvent.DOM_VK_SPACE;
+ } else {
+ charCode = KeyEvent.DOM_VK_SPACE;
+ }
+ } else if (keyCode = getKeyCode(matches[2])) {
+ charCode = 0;
+ } else {
+ return false;
+ }
+ i += matches[0].length + 1;
+ }
+ } else {
+ shift = (keys[i] >= "A" && keys[i] <= "Z");
+ }
+
+ //liberator.log({ctrl:ctrl, alt:alt, shift:shift, meta:meta, keyCode:keyCode, charCode:charCode, useVkey: useVkey});
+ var evt = content.document.createEvent('KeyEvents');
+ evt.initKeyEvent('keypress', true, true, content, ctrl, alt, shift, meta, keyCode, charCode);
+ destElem.document.dispatchEvent(evt);
+ }
+ modes.passAllKeys = false;
+}
+
+// --------------------------
+// Command
+// --------------------------
+commands.addUserCommand(['feedmap','fmap'],'Feed Map a key sequence',
+ function(args, bang){
+ if(!args){
+ echo(feedMaps.map(function(map) map.description.replace(/</g,'&lt;').replace(/>/g,'&gt;')),true);
+ }
+ var [ ,lhs,rhs] = args.match(/(\S+)(?:\s+(.+))?/);
+ if (!rhs){
+ replaceUserMap(lhs,lhs,bang);
+ } else {
+ replaceUserMap(lhs,rhs,bang);
+ }
+ },{
+ bang: true
+ }
+);
+commands.addUserCommand(['feedmapclear','fmapc'],'Clear Feed Maps',destroy);
+var converter = {
+ get origMap() origMaps,
+ get feedMap() feedMaps,
+ setup: init,
+ reset: destroy
+};
+return converter;
+})();
+// vim: fdm=marker sw=4 ts=4 et:
diff --git a/googlekanji.js b/googlekanji.js
new file mode 100644
index 0000000..59eed15
--- /dev/null
+++ b/googlekanji.js
@@ -0,0 +1,82 @@
+// ==VimperatorPlugin==
+// @name Google-Kanji
+// @description-ja グーグルを使って漢字を検索
+// @license Creative Commons 2.1 (Attribution + Share Alike)
+// @version 1.0
+// ==/VimperatorPlugin==
+//
+// Usage:
+// :gkanji うぶめ
+// のようにひらがななどで読みを入力します。
+// すると、
+// :gkcopy
+// が開き、補完が可能になるので、正しそうな漢字を選びます。
+// すると、クリップボードにその漢字がコピーされます。
+
+(function () { try {
+
+ var copycompl = [];
+
+ function getKanji (word) {
+ var re = /[一-龠]+/g;
+ var ignore = /検索|関連/;
+ var req = new XMLHttpRequest();
+ var word = encodeURIComponent(word);
+ liberator.log(word);
+ req.open('GET', 'http://www.google.co.jp/search?hl=ja&q=' + word + '&lr=lang_ja', true);
+ var f = function () {
+ var cnt = {};
+ for each (let it in req.responseText.match(re)) {
+ if (ignore.test(it))
+ continue;
+ if (cnt[it])
+ cnt[it] += 1;
+ else
+ cnt[it] = 1;
+ }
+ var cnta = [];
+ for (let i in cnt) {
+ if (cnt[i] < 3)
+ continue;
+ cnta.push([i, cnt[i]]);
+ }
+ cnta.sort(function (a, b) b[1] - a[1]);
+ copycompl = cnta;
+ liberator.commandline.open(":", "gkcopy ", liberator.modes.EX);
+ };
+ req.onreadystatechange = function (aEvt) {
+ if (req.readyState == 4 && req.status == 200) {
+ f();
+ }
+ };
+ req.send(null);
+ }
+
+ liberator.commands.addUserCommand(
+ ['gkanji', 'googlekanji'],
+ 'Google kanji',
+ getKanji
+ );
+
+ function copyToClipboard (copytext) {
+ const supstr = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+ const clipid = Ci.nsIClipboard;
+ const clip = Cc["@mozilla.org/widget/clipboard;1"].getService(clipid);
+
+ supstr.data = copytext;
+ trans.addDataFlavor("text/unicode");
+ trans.setTransferData("text/unicode", supstr, copytext.length * 2);
+
+ clip.setData(trans, null, clipid.kGlobalClipboard);
+ }
+
+ liberator.commands.addUserCommand(
+ ['gkcopy'],
+ 'Google kanji',
+ copyToClipboard,
+ { completer: function (args) [0, copycompl] }
+ );
+
+
+} catch (e) { liberator.log(e) } })();
diff --git a/googlesuggest.js b/googlesuggest.js
new file mode 100644
index 0000000..ff6da04
--- /dev/null
+++ b/googlesuggest.js
@@ -0,0 +1,34 @@
+// Vimperator plugin: 'Completion by Google Suggest'
+// Last Change: 21-Mar-2008. Jan 2008
+// License: Creative Commons
+// Maintainer: Trapezoid <trapezoid.g@gmail.com> - http://unsigned.g.hatena.ne.jp/Trapezoid
+//
+// search word completion using Google Suggest script for Vimperator 0.6.*
+
+liberator.commands.addUserCommand(['google'],"Search Web sites with Google Suggest",
+ function(arg){
+ const endpoint = "http://www.google.co.jp/search?q=";
+ //liberator.open(endpoint + encodeURIComponent(arg));
+ liberator.open(endpoint + encodeURIComponent(arg),liberator.NEW_TAB);
+ },
+ {
+ completer: function (filter) {
+ const endPoint = "http://suggestqueries.google.com/complete/search?output=firefox&client=firefox"
+ var [lang] = Components.classes["@mozilla.org/network/protocol;1?name=http"]
+ .getService(Components.interfaces.nsIHttpProtocolHandler)
+ .language.split("-", 1);
+ var xhr = new XMLHttpRequest();
+ var completionList = [];
+
+ xhr.open("GET",endPoint + "&hl=" + lang + "&qu=" + encodeURIComponent(filter),false);
+ xhr.send(null);
+ var response = window.eval(xhr.responseText)[1];
+
+ response.forEach(function(r) {
+ completionList.push([r,"Suggests"]);
+ });
+ return [0,completionList];
+ }
+ }
+);
+// vim:sw=4 ts=4 et:
diff --git a/hatenaStar.js b/hatenaStar.js
new file mode 100644
index 0000000..d78aba9
--- /dev/null
+++ b/hatenaStar.js
@@ -0,0 +1,69 @@
+/**
+ * For Vimperator 0.7.0
+ * @author mattn mattn.jp@gmail.com
+ */
+
+(function(){
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+var flasher = null;
+
+function getFlasher(){
+ if (!flasher){
+ flasher = Cc['@mozilla.org/inspector/flasher;1'].createInstance(Ci.inIFlasher);
+ flasher.color = '#FF0000';
+ flasher.thickness = 2;
+ }
+ return flasher;
+}
+
+function blink(aNode){
+ if (!aNode) {
+ liberator.echoerr('start not found');
+ return;
+ }
+ if (aNode.nodeType == 3) aNode = aNode.parentNode;
+ var toggle = true;
+ var flasher = getFlasher();
+ for (var i=1; i<7; ++i){
+ setTimeout(function() {
+ if (toggle) flasher.drawElementOutline(aNode);
+ else flasher.repaintElement(aNode);
+ toggle = !toggle;
+ }, i * 100);
+ }
+}
+
+liberator.commands.addUserCommand(['hatenastar', 'hatenas'], 'add Hatena Star',
+ function (arg, special) {
+ try {
+ var result = liberator.buffer.evaluateXPath('.//img[contains(concat(" ", @class, " "), " hatena-star-add-button ")]');
+ if (arg.match(/^(\d+)\?$/)) {
+ blink(result.snapshotItem(Number(RegExp.$1)-1));
+ return;
+ }
+ for (var i = 0, l = result.snapshotLength; i < l; i++) {
+ if (arg == '' || arg == 'all' || arg == (i+1)) {
+ var s = result.snapshotItem(i);
+ var e = document.createEvent('MouseEvents');
+ e.initMouseEvent('click', true, true, window, 1, 10, 50, 10, 50, 0, 0, 0, 0, 1, s);
+ s.dispatchEvent(e);
+ }
+ }
+ } catch (e) { liberator.echoerr('hatenaStar: ' + e); }
+ }
+);
+
+liberator.mappings.addUserMap([liberator.modes.NORMAL], [',?s'], 'add Hatena Star',
+ function (count) {
+ try {
+ for (var n = 0; n++ < count; liberator.commands.get('hatenastar').execute("all", false, count));
+ } catch (e) { liberator.echoerr('hatenaStar: ' + e); }
+ }, {
+ noremap: true,
+ flags: liberator.Mappings.flags.COUNT
+ }
+);
+
+})();
diff --git a/ime_controller.js b/ime_controller.js
new file mode 100644
index 0000000..96a1f86
--- /dev/null
+++ b/ime_controller.js
@@ -0,0 +1,73 @@
+/**
+ * ==VimperatorPlugin==
+ * @name IME Controller Lite
+ * @description control imput method at into commandline-mode
+ * @description-ja コマンドラインモード移行時にIMEの制御を行う
+ * @author teramako teramako@gmail.com
+ * @namespace http://d.hatena.ne.jp/teramako/
+ * @maxVersion 0.6pre
+ * @minVersion 0.6pre
+ * ==/VimperatorPlugin==
+ *
+ * Please set g:ex_ime_mode and g:textarea_ime_mode value.
+ *
+ * g:ex_ime_mode:
+ * used at into EX mode
+ *
+ * g:textarea_ime_mode:
+ * used at into TEXTAREA mode from INSERT mode and "noinsertmode" is set.
+ *
+ * ex).
+ * :let g:ex_ime_mode = "inactive"
+ * :let g:textarea_ime_mode = "inactive"
+ *
+ * following values are available:
+ * "auto" : No change
+ * "active" : Initially IME on
+ * "inactive" : Initially IME off
+ * "disabled" : Disable IME
+ *
+ * more details: see http://developer.mozilla.org/en/docs/CSS:ime-mode
+ *
+ * if these values are null, "inactive" is used
+ *
+ */
+
+liberator.plugins.imeController = (function(){
+ var inputElement = document.getAnonymousElementByAttribute(
+ document.getElementById('liberator-commandline-command'),'anonid','input'
+ );
+ function getMode(name){
+ return liberator.globalVariables[name] ? liberator.globalVariables[name] : 'inactive';
+ }
+ function preExec(target,name,func){
+ var original = target[name];
+ target[name] = function(){
+ func.apply(this,arguments);
+ return original.apply(target,arguments);
+ }
+ }
+ preExec(commandline,'open',function(){
+ liberator.plugins.imeController.set(inputElement, getMode('ex_ime_mode'));
+ });
+ preExec(events,'onEscape',function(){
+ if (liberator.mode == modes.INSERT && (modes.extended & modes.TEXTAREA) && !options.insertmode){
+ var inputField = buffer.lastInputField;
+ if (liberator.plugins.imeController.set(inputField, getMode('textarea_ime_mode'))){
+ inputField.blur();
+ setTimeout(function(){inputField.focus();},0);
+ }
+ }
+ });
+ return {
+ set: function(elem, mode){
+ if (elem && mode) return elem.style.imeMode = mode;
+ return false;
+ },
+ reset: function(){
+ delete liberator.globalVariables.ex_ime_mode;
+ delete liberator.globalVariables.textarea_ime_mode;
+ }
+ };
+})();
+// vim: sw=4 ts=4 et:
diff --git a/lo.js b/lo.js
new file mode 100644
index 0000000..24b4102
--- /dev/null
+++ b/lo.js
@@ -0,0 +1,150 @@
+// ==VimperatorPlugin==
+// @name Link Opener
+// @description Open filtered link(s).
+// @description-ja リンクをフィルタリングして開く
+// @license Creative Commons 2.1 (Attribution + Share Alike)
+// @version 1.1
+// ==/VimperatorPlugin==
+//
+// Usage:
+// :fo[pen][!] <REGEXP> [-i <INTERVAL_SEC>] [-w <WHERE>]
+// Open filtered links by regexp.
+// When used "!", open links in foreground.
+//
+// :lo[pen][!] URI [-w <WHERE>]
+// Open URI
+//
+// Usage-ja:
+// :fo[pen][!] <ミゲ文字列> [-i <INTERVAL_SEC>] [-w <WHERE>]
+// :fo[pen][!] /<正規表現> [-i <INTERVAL_SEC>] [-w <WHERE>]
+// ミゲ文字列か正規表現でフィルタされたリンクを開く
+//
+// :lo[pen][!] URI [-w <WHERE>]
+// URI を開く
+//
+// ちなみに Migemo はなくても動きます。
+// 無い場合は、 "/" 要らずで正規表現オンリーになります。
+//
+// Variables:
+// let g:fopen_default_interval="<INTERVAL_SEC>"
+
+
+(function () { try {
+
+ let migemo = window.XMigemoCore;
+
+ function isHttpLink (link) {
+ return link.href && link.href.indexOf('http') == 0;
+ }
+
+ function lmatch (re, link) {
+ return isHttpLink(link) && (link.href.match(re) || link.textContent.toString().match(re));
+ }
+
+ function makeRegExp (str) {
+ return migemo ? (str.indexOf('/') == 0) ? new RegExp(str.slice(1), 'i')
+ : migemo.getRegExp(str)
+ : new RegExp(str, 'i');
+ }
+
+ function filteredLinks (word) {
+ if (word.match(/^\s*$/))
+ return [];
+ let re = makeRegExp(word);
+ return [it for each (it in content.document.links) if (lmatch(re, it))];
+ }
+
+ function charToWhere (str, fail) {
+ const table = {
+ f: NEW_TAB,
+ t: NEW_TAB,
+ n: NEW_TAB,
+ b: NEW_BACKGROUND_TAB,
+ c: CURRENT_TAB,
+ w: NEW_WINDOW,
+ };
+ return (str && table[str.charAt(0).toLowerCase()]) || fail;
+ }
+
+ const WHERE_COMPLETIONS = ['f', 't', 'n', 'b', 'c', 'w'];
+
+
+ let (foihandle) {
+
+ liberator.commands.addUserCommand(
+ ['fo[pen]', 'filteropen'],
+ 'Filtered open',
+ function (opts, bang) {
+ let where = charToWhere(opts['-where'], bang ? NEW_TAB : NEW_BACKGROUND_TAB);
+ let [i, links] = [1, filteredLinks(opts.arguments.join(''))];
+ if (!links.length)
+ return;
+ open(links[0].href, where);
+ if (links.length <= 1)
+ return;
+ let interval = (opts['-interval'] || liberator.globalVariables.fopen_default_interval || 1) * 1000;
+ foihandle = setInterval(function () {
+ try {
+ open(links[i].href, where);
+ if ((++i) >= links.length)
+ clearInterval(foihandle);
+ } catch (e) {
+ clearInterval(foihandle);
+ }
+ }, interval);
+ },
+ {
+ bang: true,
+ options: [
+ [['-interval', '-i'], liberator.commands.OPTION_INT],
+ [['-where', '-w'], liberator.commands.OPTION_STRING],
+ ],
+ completer: function (word) {
+ let links = filteredLinks(word);
+ return [0, [[it.href, it.textContent] for each (it in links)]];
+ },
+ }
+ );
+
+ liberator.commands.addUserCommand(
+ ['stopfilteropen', 'stopfo[pen]'],
+ 'Stop filtered open',
+ function () {
+ clearInterval(foihandle);
+ }
+ );
+
+ }
+
+ let (
+ lolinks = [],
+ looptions = [ [['-where', '-w'], liberator.commands.OPTION_STRING, null, WHERE_COMPLETIONS] ]
+ ) {
+
+ liberator.commands.addUserCommand(
+ ['lo[pen]', 'linkopen'],
+ 'Filtered open',
+ function (opts, bang) {
+ let where = charToWhere(opts['-where'], bang ? NEW_TAB : CURRENT_TAB);
+ let arg = opts.arguments[0];
+ let m = arg.match(/^\d+(?=,)/);
+ if (m)
+ liberator.buffer.followLink(lolinks[parseInt(m[0], 10)], where);
+ },
+ {
+ options: looptions,
+ bang: true,
+ completer: function (word) {
+ if (!word || word.match(/\s|\d+,/))
+ return [];
+ lolinks = filteredLinks(word);
+ return [0, [[i + ',' + lolinks[i].href, lolinks[i].textContent] for (i in lolinks || [])]];
+ }
+ }
+ );
+
+ }
+
+} catch (e) { log(e); }})();
+
+// vim:sw=2 ts=2 et:
diff --git a/lookupDictionary.js b/lookupDictionary.js
new file mode 100644
index 0000000..f52b751
--- /dev/null
+++ b/lookupDictionary.js
@@ -0,0 +1,269 @@
+/**
+ * ==VimperatorPlugin==
+ * @name lookup dictionary (Vimperator plugin)
+ * @description Lookup words from Web dictionaries, and show the results in the bottom of the window
+ * @description-ja Web上の辞書を引いた結果をコマンドライン・バッファへ出力します
+ * @author teramako teramako@gmail.com
+ * @version 0.3
+ * ==/VimperatorPlugin==
+ */
+(function(){
+
+const SITE_DEFINITION = [{
+ names: ['eiji[ro]'],
+ url: 'http://eow.alc.co.jp/%s/UTF-8/',
+ shortHelp: 'SPACE ALC (英辞郎 on the Web)',
+ xpath: 'id("resultList")',
+ dictionary: 'en-US',
+},{
+ names: ['goo'],
+ url: 'http://dictionary.goo.ne.jp/search.php?MT=%s&kind=all&mode=0',
+ shortHelp: 'goo 辞書',
+ encode: 'EUC-JP',
+ xpath: '//div[@id="incontents"]/*[@class="ch04" or @class="fs14" or contains(@class,"diclst")]',
+ multi: true,
+ dictionary: 'en-US',
+},{
+ names: ['infokanji'],
+ url: 'http://dictionary.www.infoseek.co.jp/?sc=1&se=on&lp=0&gr=kj&sv=KJ&qt=&qty=%s&qtb=&qtk=0',
+ shorthelp: 'Infoseek 漢字辞書',
+ encode: 'EUC-JP',
+ xpath: '//div[@class="NetDicHead"]',
+},{
+ names: ['answers'],
+ url: 'http://www.answers.com/%s',
+ shortHelp: 'Answers.com(英英辞書)',
+ xpath: 'id("firstDs")',
+ dictionary: 'en-US',
+}];
+
+let (siteDef = liberator.globalVariables.lookupDictionary_site_definition) {
+ if (siteDef) {
+ if (siteDef instanceof String)
+ siteDef = eval(siteDef);
+ if (siteDef.forEach instanceof Function)
+ siteDef.forEach(function (obj) { SITE_DEFINITION.push(obj); });
+ else
+ SITE_DEFINITION.push(siteDef);
+ }
+}
+
+// class definition
+function SpellChecker() {
+ this.initialize.apply(this, arguments);
+}
+SpellChecker.prototype = {
+ initialize: function () {
+ const MYSPELL = "@mozilla.org/spellchecker/myspell;1";
+ const HUNSPELL = "@mozilla.org/spellchecker/hunspell;1";
+ const ENGINE = "@mozilla.org/spellchecker/engine;1";
+
+ var spellclass = MYSPELL;
+ if (HUNSPELL in Components.classes)
+ spellclass = HUNSPELL;
+ if (ENGINE in Components.classes)
+ spellclass = ENGINE;
+
+ this.engine = Components.classes[spellclass]
+ .createInstance(Components.interfaces.mozISpellCheckingEngine);
+ },
+
+ /**
+ * @return {Array}
+ */
+ getDictionaryList: function () {
+ var dictionaries = {};
+ this.engine.getDictionaryList(dictionaries, {});
+ return dictionaries.value;
+ },
+
+ /**
+ * @return {String}
+ */
+ dictionary: function () {
+ var dict;
+ try { dict = this.engine.dictionary; }
+ catch (e) {}
+ return dict ? dict : null;
+ },
+
+ /**
+ * @param {String} dict
+ */
+ setDictionary: function (dict) {
+ var dictionaries = this.getDictionaryList()
+ for (var i=0, max=dictionaries.length ; i<max ; ++i) {
+ if (dictionaries[i] === dict) {
+ this.engine.dictionary = dict;
+ return dict;
+ }
+ }
+ return null;
+ },
+
+ /**
+ * @param {Boolean} isBeginningWith
+ */
+ setBeginningWith: function (isBeginningWith) {
+ this.isBeginningWith = isBeginningWith;
+ },
+
+ /**
+ * @param {String} spell
+ * @return {Boolean}
+ */
+ check: function (spell) {
+ return this.engine.check(spell);
+ },
+
+ /**
+ * @param {String} spell
+ * @return {Array}
+ */
+ suggest: function (spell) {
+ var suggestions = {};
+ this.engine.suggest(spell, suggestions, {});
+ suggestions = suggestions.value;
+
+ if (this.isBeginningWith) {
+ suggestions = suggestions.filter( function (cand) {
+ return (cand.toLowerCase().indexOf(spell) === 0);
+ });
+ }
+
+ return suggestions;
+ },
+};
+
+var spellChecker = buildSpellChecker();
+
+SITE_DEFINITION.forEach(function(dictionary){
+ commands.addUserCommand(
+ dictionary.names,
+ dictionary.shortHelp,
+ function(arg,special){
+ var sel = (window.content.window.getSelection) ?
+ window.content.window.getSelection().toString() : null;
+ if (special && sel) arg = sel;
+ if (!arg) return;
+ var url;
+ if (dictionary.encode){
+ var ttbu = Components.classes['@mozilla.org/intl/texttosuburi;1']
+ .getService( Components.interfaces.nsITextToSubURI);
+ url = dictionary.url.replace(/%s/g, ttbu.ConvertAndEscape(dictionary.encode, arg));
+ } else {
+ url = dictionary.url.replace(/%s/g,encodeURI(arg));
+ }
+ //liberator.log('URL: ' +url);
+ var result;
+ getHTML(url, function(str){
+ var doc = createHTMLDocument(str);
+ var result = getNodeFromXPath(dictionary.xpath, doc, dictionary.multi);
+ if (!result){
+ liberator.echoerr('Nothing to show...');
+ }
+ var xs = new XMLSerializer();
+ liberator.echo('<base href="' + url + '"/>' + xs.serializeToString( result ), true);
+ }, dictionary.encode ? dictionary.encode : 'UTF-8');
+ },
+ {
+ completer: function (arg) {
+ if (!spellChecker ||
+ !dictionary.dictionary ||
+ !spellChecker.setDictionary(dictionary.dictionary))
+ return [0, []];
+
+ var suggestions = spellChecker.suggest(arg);
+ var candidates = [];
+ for (var i=0, max=suggestions.length ; i<max ; ++i) {
+ candidates.push([suggestions[i], 'suggest']);
+ }
+
+ if (!spellChecker.check(arg)) {
+ candidates.unshift(['', 'not exist']);
+ }
+ return [0, candidates];
+ },
+ bang: true
+ }
+ );
+});
+commands.addUserCommand(
+ ['availabledictionaries'],
+ 'display available dictionaries',
+ function () { liberator.echo('available dictionaries: ' + spellChecker.getDictionaryList()); },
+ {}
+);
+/**
+ * @param {String} url
+ * @param {Function} callback
+ * @param {String} charset
+ */
+function getHTML(url, callback, charset){
+ var xhr= new XMLHttpRequest();
+ xhr.onreadystatechange = function(){
+ if (xhr.readyState == 4){
+ if (xhr.status == 200){
+ callback.call(this,xhr.responseText);
+ } else {
+ throw new Error(xhr.statusText);
+ }
+ }
+ };
+ xhr.open('GET',url,true);
+ xhr.overrideMimeType('text/html; charset=' + charset);
+ xhr.send(null);
+}
+/**
+ * @param {String} str
+ * @return {DOMDocument}
+ */
+function createHTMLDocument(str){
+ str = str.replace(/^[\s\S]*?<html(?:\s[^>]+?)?>|<\/html\s*>[\S\s]*$/ig,'').replace(/[\r\n]+/g,' ');
+ var htmlFragment = document.implementation.createDocument(null,'html',null);
+ var range = document.createRange();
+ range.setStartAfter(window.content.document.body);
+ htmlFragment.documentElement.appendChild(htmlFragment.importNode(range.createContextualFragment(str),true));
+ return htmlFragment;
+}
+/**
+ * @param {String} xpath XPath Expression
+ * @param {DOMDocument} doc
+ * @param {Boolean} isMulti
+ * @return {Element}
+ */
+function getNodeFromXPath(xpath,doc,isMulti){
+ if (!xpath || !doc) return;
+ var result;
+ if (isMulti){
+ var nodesSnapshot = doc.evaluate(xpath,doc.documentElement,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
+ if (nodesSnapshot.snapshotLength == 0) return;
+ result = document.createElementNS(null,'div');
+ for (var i=0; i<nodesSnapshot.snapshotLength; result.appendChild(nodesSnapshot.snapshotItem(i++)));
+ } else {
+ var node = doc.evaluate(xpath,doc.documentElement,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
+ if (!node.singleNodeValue) return;
+ result = node.singleNodeValue;
+ }
+ return result;
+}
+
+/**
+ * @return {Object}
+ */
+function buildSpellChecker() {
+ var enable = liberator.globalVariables.lookupDictionary_enableSuggestion;
+ enable = (enable === undefined) ? true : !!parseInt(enable, 10);
+ if (!enable) return;
+
+ var spellChecker = new SpellChecker();
+
+ var isBeginningWith = liberator.globalVariables.lookupDictionary_beginningWith
+ isBeginningWith = (isBeginningWith === undefined) ? false : !!parseInt(isBeginningWith, 10);
+ spellChecker.setBeginningWith(isBeginningWith);
+
+ return spellChecker;
+}
+})();
+
+// vim: fdm=marker sw=4 ts=4 et:
diff --git a/migemized_find.js b/migemized_find.js
new file mode 100644
index 0000000..5c5b461
--- /dev/null
+++ b/migemized_find.js
@@ -0,0 +1,470 @@
+// ==VimperatorPlugin==
+// @name Migemized Find
+// @description-ja デフォルトのドキュメント内検索をミゲマイズする。
+// @license Creative Commons 2.1 (Attribution + Share Alike)
+// @version 2.5
+// ==/VimperatorPlugin==
+//
+// Usage:
+// 検索ワードの一文字目が
+// '/' => 正規表現検索
+// '?' => Migemo検索
+// 以外 => Migemo検索
+//
+// :ml <検索ワード> [-c <色>]
+// :migelight <検索ワード> [-c <色>]
+// 検索ワードを指定色で強調表示する。
+//
+// :ml! <色1> <色2> ... <色N>
+// :migelight! <色1> <色2> ... <色N>
+// 指定の色の強調表示を消す
+//
+// :ml! all
+// :migelight! all
+// 全ての強調表示を消す。
+//
+// let g:migemized_find_language = "cat";
+// ミ言語設定
+//
+// Author:
+// anekos
+//
+// Link:
+// http://d.hatena.ne.jp/nokturnalmortum/20080805#1217941126
+
+(function () { try {
+
+ let XMigemoCore = Components.classes['@piro.sakura.ne.jp/xmigemo/factory;1']
+ .getService(Components.interfaces.pIXMigemoFactory)
+ .getService(liberator.globalVariables.migemized_find_language || 'ja');
+
+ function getPosition (elem) {
+ if (!elem)
+ return {x: 0, y: 0};
+ let parent = getPosition(elem.offsetParent);
+ return { x: (elem.offsetLeft || 0) + parent.x,
+ y: (elem.offsetTop || 0) + parent.y }
+ }
+
+ function slashArray (ary, center) {
+ let head = [], tail = [];
+ let current = head;
+ for (let i = 0; i < ary.length; i++) {
+ let it = ary[i];
+ if (it == center)
+ current = tail;
+ else
+ current.push(it);
+ }
+ return [head, tail];
+ }
+
+ let MF = {
+ // 定数
+ MODE_NORMAL: 0,
+ MODE_REGEXP: 1,
+ MODE_MIGEMO: 2,
+
+ // 全体で共有する変数
+ lastSearchText: null,
+ lastSearchExpr: null,
+ lastDirection: null,
+ lastColor: null,
+ currentSearchText: null,
+ currentSearchExpr: null,
+ currentColor: null,
+
+ // submit の為に使う
+ firstResult: null,
+
+ // --color-- の部分は置換される。
+ style: 'background-color: --color--; color: black; border: dotted 3px blue;',
+ findColor: 'lightblue',
+ highlightColor: 'orange',
+
+ // 手抜き用プロパティ
+ get document function () content.document,
+
+ // タブ毎に状態を保存するために、変数を用意
+ // 初回アクセス時に初期化を行う
+ get storage function () (
+ gBrowser.mCurrentTab.__migemized_find_storage ||
+ (gBrowser.mCurrentTab.__migemized_find_storage = {
+ highlightRemovers: {},
+ })
+ ),
+
+ // 現在のタブのフレームリスト
+ get currentFrames function () {
+ let result = [];
+ (function (frame) {
+ // ボディがない物は検索対象外なので外す
+ if (frame.document.body.localName.toLowerCase() == 'body')
+ result.push(frame);
+ for (let i = 0; i < frame.frames.length; i++)
+ arguments.callee(frame.frames[i]);
+ })(content);
+ return result;
+ },
+
+ // ボディを範囲とした Range を作る
+ makeBodyRange: function (frame) {
+ let range = frame.document.createRange();
+ range.selectNodeContents(frame.document.body);
+ return range;
+ },
+
+ // this.style に色を適用した物を返す
+ coloredStyle: function (color) {
+ return this.style.replace(/--color--/, color);
+ },
+
+ // 検索文字列から検索モードと検索文字列を得る。
+ searchTextToRegExpString: function (str) {
+ let [head, tail] = [str[0], str.slice(1)];
+ switch (head) {
+ case '/':
+ return tail;
+ case '?':
+ return XMigemoCore.getRegExp(tail);
+ }
+ return XMigemoCore.getRegExp(str);
+ },
+
+ // 指定色のハイライト削除
+ removeHighlight: function (color) {
+ (this.storage.highlightRemovers[color] || function () void(0))();
+ delete this.storage.highlightRemovers[color];
+ },
+
+ focusLink: function (range) {
+ let node = range.commonAncestorContainer;
+ while (node && node.parentNode) {
+ if (node.localName.toString().toLowerCase() == 'a')
+ return void(Components.lookupMethod(node, 'focus').call(node));
+ node = node.parentNode;
+ }
+ },
+
+ highlight: function (target, color, doScroll, setRemover) {
+ let span = this.document.createElement('span');
+
+ span.setAttribute('style', this.coloredStyle(color));
+ target.range.surroundContents(span);
+
+ if (doScroll) {
+ let scroll = function () {
+ let pos = getPosition(span);
+ target.frame.scroll(pos.x - (target.frame.innerWidth / 2),
+ pos.y - (target.frame.innerHeight / 2));
+ let sel = target.frame.getSelection();
+ let r = target.range.cloneRange();
+ r.collapse(true);
+ sel.removeAllRanges();
+ sel.addRange(r);
+ };
+ setTimeout(scroll, 0);
+ }
+
+ let remover = function () {
+ let range = this.document.createRange();
+ range.selectNodeContents(span);
+ let content = range.extractContents();
+ range.setStartBefore(span);
+ range.insertNode(content);
+ range.selectNode(span);
+ range.deleteContents();
+ };
+
+ if (setRemover)
+ this.storage.highlightRemovers[color] = remover;
+
+ return remover;
+ },
+
+ find: function (str, backwards, range, start, end) {
+ if (!range)
+ range = this.makeBodyRange(this.currentFrames[0]);
+
+ if (!start) {
+ start = range.startContainer.ownerDocument.createRange();
+ start.setStartBefore(range.startContainer);
+ }
+ if (!end) {
+ end = range.endContainer.ownerDocument.createRange();
+ end.setEndAfter(range.endContainer);
+ }
+
+ // 検索方向に合わせて、開始終了位置を交換
+ if (backwards)
+ [start, end] = [end, start];
+
+ try {
+ return XMigemoCore.regExpFind(str, 'i', range, start, end, backwards);
+ } catch (e) {
+ return false;
+ }
+ },
+
+ findFirst: function (str, backwards, color) {
+ if (!color)
+ color = this.findColor;
+
+ this.lastDirection = backwards;
+ let expr = this.searchTextToRegExpString(str);
+ this.currentSearchText = str;
+ this.currentSearchExpr = expr;
+ this.currentColor = color;
+
+ let result, frames = this.currentFrames;
+ if (backwards)
+ frames = frames.reverse();
+
+ for each (let frame in frames) {
+ let ret = this.find(expr, backwards, this.makeBodyRange(frame));
+ if (ret) {
+ result = this.storage.lastResult = {
+ frame: frame,
+ range: ret,
+ };
+ break;
+ }
+ }
+
+ this.removeHighlight(color);
+
+ if (result)
+ this.highlight(result, color, true, true);
+
+ this.firstResult = result;
+
+ return result;
+ },
+
+ findSubmit: function (str, backwards, color) {
+ this.findFirst(str, backwards, color);
+ return this.submit();
+ },
+
+ findAgain: function (reverse) {
+ let backwards = !!(!this.lastDirection ^ !reverse);
+ let last = this.storage.lastResult;
+ let frames = this.currentFrames;
+
+ // 前回の結果がない場合、(初め|最後)のフレームを対象にする
+ // findFirst と"似た"挙動になる
+ if (last) {
+ if (backwards) {
+ end = last.range.cloneRange();
+ end.setEnd(last.range.startContainer, last.range.startOffset);
+ } else {
+ start = last.range.cloneRange();
+ start.setStart(last.range.endContainer, last.range.endOffset);
+ }
+ } else {
+ let idx = backwards ? frames.length - 1
+ : 0;
+ last = {frame: frames[0], range: this.makeBodyRange(frames[0])};
+ }
+
+ this.removeHighlight(this.lastColor);
+
+ let str = this.lastSearchExpr;
+ let start, end;
+
+ let result;
+ let ret = this.find(str, backwards, this.makeBodyRange(last.frame), start, end);
+
+ if (ret) {
+ result = {frame: last.frame, range: ret};
+ } else {
+ // 見つからなかったので、ほかのフレームから検索
+ let [head, tail] = slashArray(frames, last.frame);
+ let next = backwards ? head.reverse().concat(tail.reverse())
+ : tail.concat(head);
+ for each (let frame in next) {
+ let r = this.find(str, backwards, this.makeBodyRange(frame));
+ if (r) {
+ result = {frame: frame, range: r};
+ break;
+ }
+ }
+ }
+
+ this.storage.lastResult = result;
+
+ if (result) {
+ this.highlight(result, this.lastColor, true, true);
+ this.focusLink(result);
+ }
+
+ return result;
+ },
+
+ submit: function () {
+ this.lastSearchText = this.currentSearchText;
+ this.lastSearchExpr = this.currentSearchExpr;
+ this.lastColor = this.currentColor;
+ if (this.firstResult)
+ this.focusLink(this.firstResult.range);
+ return this.firstResult;
+ },
+
+ cancel: function () {
+ },
+
+ highlightAll: function (str, color) {
+ let expr = this.searchTextToRegExpString(str);
+ this.lastSearchText = str;
+ this.lastSearchExpr = expr;
+
+ if (!color)
+ color = this.highlightColor;
+
+ this.removeHighlight(color);
+
+ let frames = this.currentFrames;
+ let removers = [];
+
+ for each (let frame in frames) {
+ let frameRange = this.makeBodyRange(frame);
+ let ret, start = frameRange;
+ while (ret = this.find(expr, false, frameRange, start)) {
+ removers.push(this.highlight({frame: frame, range: ret}, color, false, false));
+ start = ret.cloneRange();
+ start.setStart(ret.endContainer, ret.endOffset);
+ }
+ }
+
+ this.storage.highlightRemovers[color] = function () { removers.forEach(function (it) it.call()); };
+
+ return removers;
+ },
+ };
+
+
+ // 前のタイマーを削除するために保存しておく
+ let delayCallTimer = null;
+ let delayedFunc = null;
+
+ // Vimp の仕様変更に対応
+ let _backwards;
+ let _findFirst = function (str, backwards) {
+ // 短時間に何回も検索をしないように遅延させる
+ delayedFunc = function () MF.findFirst(str, backwards);
+ if (delayCallTimer) {
+ delayCallTimer = null;
+ clearTimeout(delayCallTimer);
+ }
+ delayCallTimer = setTimeout(function () delayedFunc(), 500);
+ };
+
+ // ミゲモ化セット
+ let migemized = {
+ find: function find (str, backwards) {
+ _backwards = backwards;
+ if (str)
+ _findFirst(str, backwards);
+ },
+
+ findAgain: function findAgain (reverse) {
+ if (!MF.findAgain(reverse))
+ liberator.echoerr('not found: ' + MF.lastSearchText);
+ },
+
+ searchSubmitted: function searchSubmitted (command, forcedBackward) {
+ if (delayCallTimer) {
+ delayCallTimer = null;
+ clearTimeout(delayCallTimer);
+ delayedFunc();
+ }
+ if (!MF.submit())
+ liberator.echoerr('not found: ' + MF.currentSearchText);
+ },
+
+ searchCanceled: function searchCanceled () {
+ MF.cancel();
+ },
+
+ searchKeyPressed: function (str) {
+ _findFirst(str, _backwards);
+ },
+ };
+
+
+ // オリジナルの状態に戻せるように保存しておく
+ let (original = {}) {
+ for (let name in migemized)
+ original[name] = search[name];
+
+ function set (funcs) {
+ for (let name in funcs)
+ search[name] = funcs[name];
+ }
+
+ set(migemized);
+
+ MF.install = function () set(migemized);
+ MF.uninstall = function () set(original);
+ }
+
+
+ // highlight コマンド
+ commands.addUserCommand(
+ ['ml', 'migelight'],
+ 'Migelight matched words',
+ function (opts, bang) {
+ if (bang) {
+ let colors = opts.arguments.join(' ') + ' ' + (opts['-color'] || '');
+ liberator.execute('removemigelight ' + colors);
+ } else {
+ let r = MF.highlightAll(opts.arguments.join(' '), opts['-color']);
+ liberator.echo(r ? r.length + ' words migelighted.'
+ : 'word not found.');
+ }
+ },
+ {
+ bang: true,
+ options: [
+ [['-color', '-c'], commands.OPTION_STRING],
+ ]
+ }
+ );
+
+ // remove highlight コマンド
+ commands.addUserCommand(
+ ['rml', 'removemigelight'],
+ 'Remove migelight',
+ function (args) {
+ // HEAD (2)
+ if (args != undefined)
+ args = args.string;
+ if (!args)
+ return MF.removeHighlight(MF.highlightColor);
+ if (args == 'all')
+ return [f() for each (f in MF.storage.highlightRemovers)];
+ for each (let color in args.split(/\s+/))
+ MF.removeHighlight(color);
+ }
+ );
+
+ // find コマンド
+ commands.addUserCommand(
+ ['mf[ind]'],
+ 'Migemized find',
+ function (opts) {
+ if (!MF.findSubmit(opts.arguments.join(' '), opts['-backward'], opts['-color']))
+ liberator.echoerr('not found: ' + MF.currentSearchText);
+ },
+ {
+ options: [
+ [['-backward', '-b'], commands.OPTION_NOARG],
+ [['-color', '-c'], commands.OPTION_STRING],
+ ]
+ }
+ );
+
+ // 外から使えるように
+ liberator.plugins.migemizedFind = MF;
+
+}catch(e){liberator.log(e);}})();
diff --git a/migemo_completion.js b/migemo_completion.js
new file mode 100644
index 0000000..b3ede9f
--- /dev/null
+++ b/migemo_completion.js
@@ -0,0 +1,51 @@
+/**
+ * ==VimperatorPlugin==
+ * @name migemo_completion.js
+ * @description replace completion function with using Migemo
+ * @description-ja ⊮֐Migemogp̂Ɏւ
+ * @author Trapezoid
+ * @version 0.2
+ * ==/VimperatorPlugin==
+ *
+ * Support commands:
+ * - :buffer
+ * - :sidebar
+ * - :emenu
+ * - :dialog
+ * - :help
+ * - :macros
+ * - :play
+ * and more
+ **/
+var XMigemoCore;
+try{
+ XMigemoCore = Components.classes['@piro.sakura.ne.jp/xmigemo/factory;1']
+ .getService(Components.interfaces.pIXMigemoFactory)
+ .getService("ja");
+}
+catch(ex if ex instanceof TypeError){}
+
+function replaceFunction(target,symbol,f,originalArguments){
+ var oldFunction = target[symbol];
+ target[symbol] = function() f.apply(target,[oldFunction.apply(target,originalArguments || arguments), arguments]);
+}
+
+replaceFunction(liberator.completion,"buffer",function(oldResult,args){
+ var filter = args[0];
+ var migemoPattern = new RegExp(XMigemoCore.getRegExp(filter));
+ return [0, oldResult[1].filter(function([value,label]){
+ return migemoPattern.test(value) || migemoPattern.test(label)
+ })];
+},[""]);
+liberator.completion.filter = function(array,filter,matchFromBeginning){
+ if(!filter) return array;
+
+ var migemoString = XMigemoCore.getRegExp(filter);
+ if(matchFromBeginning)
+ migemoString ="^(" + migemoString + ")";
+ var migemoPattern = new RegExp(migemoString);
+
+ return array.filter(function([value,label]){
+ return migemoPattern.test(value) || migemoPattern.test(label)
+ });
+}
diff --git a/migemo_hint.js b/migemo_hint.js
new file mode 100644
index 0000000..62d7c19
--- /dev/null
+++ b/migemo_hint.js
@@ -0,0 +1,21 @@
+// Vimperator plugin: 'Hint Matching with XUL/Migemo'
+// Last Change: 16-Jun-2008. Jan 2008
+// License: Creative Commons
+// Maintainer: Trapezoid <trapezoid.g@gmail.com> - http://unsigned.g.hatena.ne.jp/Trapezoid
+// Require: XUL/Migemo extension - https://addons.mozilla.org/ja/firefox/addon/5239
+//
+// extended hint matching with migemo for vimperator1.2pre(16-Jun-2008)
+//
+// Usage:
+// :set hintmatching = custom
+liberator.plugins.customHintMatcher = function(inputString){
+ var XMigemoCore;
+ try{
+ XMigemoCore = Components.classes['@piro.sakura.ne.jp/xmigemo/factory;1']
+ .getService(Components.interfaces.pIXMigemoFactory)
+ .getService("ja");
+ }
+ catch(ex if ex instanceof TypeError){}
+ var r = new RegExp(XMigemoCore.getRegExp(inputString));
+ return function(hintString) r.test(hintString);
+}
diff --git a/proxy.js b/proxy.js
new file mode 100644
index 0000000..d99b0f1
--- /dev/null
+++ b/proxy.js
@@ -0,0 +1,133 @@
+/**
+ * ==VimperatorPlugin==
+ * @name proxy.js
+ * @description proxy setting plugin
+ * @description-ja プロクシ設定
+ * @minVersion 0.6pre
+ * @author cho45, halt feits
+ * @version 0.6
+ * ==/VimperatorPlugin==
+ *
+ * Usage:
+ * :proxy {conf_name} -> set proxy setting to conf_name
+ *
+ * The proxy_settings is a string variable which can set on
+ * vimperatorrc as following.
+ *
+ * let proxy_settings = "[{ { conf_name:'disable', conf_usage:'direct connection', settings:[{label:'type', param:0}] } }]"
+ *
+ * or your can set it using inline JavaScript.
+ *
+ * javascript <<EOM
+ * liberator.globalVariables.proxy_settings = [
+ * {
+ * conf_name: 'disable',
+ * conf_usage: 'direct connection',
+ * settings: [
+ * {
+ * label: 'type',
+ * param: 0
+ * }
+ * ]
+ * },
+ * {
+ * conf_name: 'squid',
+ * conf_usage: 'use squid cache proxy',
+ * settings: [
+ * {
+ * label: 'type',
+ * param: 1
+ * },
+ * {
+ * label: 'http',
+ * param: 'squid.example.com'
+ * },
+ * {
+ * label: 'http_port',
+ * param: 3128
+ * }
+ * ]
+ * }
+ * ];
+ * EOM
+ */
+
+(function() {
+ if (!liberator.globalVariables.proxy_settings) {
+ liberator.globalVariables.proxy_settings = [
+ {
+ conf_name: 'disable',
+ conf_usage: 'direct connection',
+ settings: [
+ {
+ label: 'type',
+ param: 0
+ }
+ ]
+ },
+ {
+ conf_name: 'polipo',
+ conf_usage: 'use polipo cache proxy',
+ settings: [
+ {
+ label: 'type',
+ param: 1
+ },
+ {
+ label: 'http',
+ param: 'localhost'
+ },
+ {
+ label: 'http_port',
+ param: 8123
+ }
+ ]
+ }
+ ];
+ };
+
+ var proxy_settings = liberator.globalVariables.proxy_settings;
+
+ liberator.commands.addUserCommand(["proxy"], 'Proxy settings', function (args) {
+ const prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefService);
+ var name = args;
+ if (!name) {
+ liberator.echo("Usage: proxy {setting name}");
+ }
+ proxy_settings.some(function (proxy_setting) {
+ if (proxy_setting.conf_name.toLowerCase() != name.toLowerCase()) {
+ return false;
+ }
+
+ //delete setting
+ ['http', 'ssl', 'ftp', 'gopher'].forEach(function (scheme_name) {
+ prefs.setCharPref("network.proxy." + scheme_name, '');
+ prefs.setIntPref("network.proxy." + scheme_name + "_port", 0);
+ });
+
+ proxy_setting.settings.forEach(function (conf) {
+ liberator.options.setPref('network.proxy.' + conf.label, conf.param);
+ });
+
+ liberator.echo("Set config: " + name);
+ return true;
+ });
+ },
+ {
+ completer: function (filter) {
+ var completions = [];
+ var exp = new RegExp("^" + filter);
+
+ for each (let { conf_name: name, conf_usage: usage } in proxy_settings) {
+ if (exp.test(name)) {
+ completions.push([name, usage]);
+ }
+ }
+
+ return [0, completions];
+ }
+ });
+
+})();
+// vim: set sw=4 ts=4 et:
diff --git a/sbmcommentsviewer.js b/sbmcommentsviewer.js
new file mode 100644
index 0000000..1117eb6
--- /dev/null
+++ b/sbmcommentsviewer.js
@@ -0,0 +1,543 @@
+/**
+ * ==VimperatorPlugin==
+ * @name SBM Comments Viewer
+ * @description List show Social BookMark Comments
+ * @description-ja ソーシャル・ブックマーク・コメントを表示します
+ * @version 0.1b
+ * ==/VimperatorPlugin==
+ *
+ * Usage:
+ *
+ * viewSBMComments [url] [options]
+ * url : 省略時は現在のURL
+ * options:
+ * -f, -format : 出力時のフォーマット(`,'区切りのリスト)
+ * (default: id,timestamp,tags,comment)
+ * let g:def_sbm_format = ... で指定可能
+ * -t, -type : 出力するSBMタイプ
+ * (default: hdl)
+ * let g:def_sbms = ... で指定可能
+ * -c, -count : ブックマーク件数のみ出力
+ * -b, -browser: バッファ・ウィンドウではなくブラウザに開く
+ * TODO:まだ出来てない
+ *
+ * 指定可能フォーマット:
+ * id, timpstamp, tags, comment, tagsAndComment
+ *
+ * SBMタイプ:
+ * h : hatena bookmark
+ * d : del.icio.us bookmark
+ * l : livedoor clip
+ * z : Buzzurl
+ * XXX:今後増やしていきたい
+ *
+ * 例:
+ * :viewSBMComments http://d.hatena.ne.jp/teramako/ -t hdl -f id,comment -c
+ *
+ * 備考:
+ * * 一度取得したものは(30分ほど)キャッシュに貯めてますので何度も見直すことが可能です。
+ * * 粋なコマンド名募集中
+ */
+
+liberator.plugins.sbmCommentsViewer = (function(){
+/**
+ * SBMEntry Container {{{
+ * @param {String} type
+ * @param {Number} count
+ * @param {Object} extra
+ * extra = {
+ * faviconURL,
+ * pageURL
+ * }
+ */
+function SBMContainer(type, count, extra){ //{{{
+ this.type = type;
+ this.count = count || 0;
+ this.entries = [];
+ if (extra){
+ this.faviconURL = extra.faviconURL || '';
+ this.pageURL = extra.pageURL || '';
+ }
+} //}}}
+SBMContainer.prototype = { //{{{
+ add: function(id, timestamp, comment, tags, extra){
+ this.entries.push(new SBMEntry(
+ id, timestamp, comment, tags, extra
+ ));
+ },
+ toHTMLString: function(format, countOnly){
+ var label = (this.faviconURL ? '<img src="' + this.faviconURL + '" width="16" height="16"/>' : '') +
+ manager.type[this.type] + ' ' + this.count + '(' + this.entries.length + ')' +
+ (this.pageURL ? ' <a href="' + this.pageURL + '">' + this.pageURL + '</a>' : '');
+ if (countOnly){
+ return label;
+ } else {
+ var str = [
+ '<table id="liberator-sbmcommentsviewer"><caption style="text-align:left;" class="hl-Title">' + label + '</caption><tr>'
+ ];
+ format.forEach(function(colum){
+ var name = manager.format[colum] || '-';
+ str.push('<th>' + name + '</th>');
+ });
+ str.push('</tr>');
+ this.entries.forEach(function(e){
+ str.push(e.toHTMLString(format));
+ });
+ str.push('</table>');
+ return str.join('');
+ }
+ }
+}; //}}}
+// }}}
+/**
+ * SBM Entry {{{
+ * @param {String} id UserName
+ * @param {String|Date} timestamp
+ * @param {String} comment
+ * @param {String[]} tags
+ * @param {Object} extra
+ * extra = {
+ * userIcon
+ * link
+ * }
+ */
+function SBMEntry(id, timestamp, comment, tags, extra){ //{{{
+ this.id = id || '';
+ this.timeStamp = timestamp instanceof Date ? timestamp : null;
+ this.comment = comment || '';
+ this.tags = tags || [];
+ if (extra){
+ this.userIcon = extra.userIcon || null;
+ this.link = extra.link || null;
+ }
+} //}}}
+SBMEntry.prototype = { //{{{
+ toHTMLString: function(format){
+ // E4X で書く手もあるけど、liberator.echoを使って出力すると
+ // toString後に"\n"が<br/>に変換されてしまうのでStringで
+ var str = ['<tr>'];
+ var self = this;
+ format.forEach(function(colum){
+ switch(colum){
+ case 'id':
+ str.push('<td class="liberator-sbmcommentsviewer-id">' + (self.userIcon ? '<p style="display:table-cell;vertical-align:middle;padding-right:3px;"><img src="'+self.userIcon +'" width="16" height="16"/></p>' : '') +
+ '<p style="display:table-cell;vertical-align:middle;">' + self.id + '</p></td>');
+ break;
+ case 'timestamp':
+ str.push('<td class="liberator-sbmcommentsviewer-timestamp">' + self.formatDate() + '</td>'); break;
+ case 'tags':
+ str.push('<td class="liberator-sbmcommentsviewer-tags">' + self.tags.join(',') + '</td>'); break;
+ case 'comment':
+ str.push('<td class="liberator-sbmcommentsviewer-comment" style="white-space:normal;">' + self.comment + '</td>'); break;
+ case 'tagsAndComment':
+ tagString = self.tags.length ? '[' + self.tags.join('][') + ']':'';
+ str.push('<td class="liberator-sbmcommentsviewer-tagsAndComment" style="white-space:normal;">' + tagString + ' ' + self.comment + '</td>'); break;
+ default:
+ str.push('<td>-</td>');
+ }
+ });
+ str.push('</tr>');
+ return str.join('');
+ },
+ formatDate: function(){
+ if (!this.timeStamp) return '';
+ var [year,month,day,hour,min,sec] = [
+ this.timeStamp.getFullYear(),
+ this.timeStamp.getMonth()+1,
+ this.timeStamp.getDate(),
+ this.timeStamp.getHours(),
+ this.timeStamp.getMinutes(),
+ this.timeStamp.getSeconds()
+ ];
+ return [
+ year, '/',
+ (month < 10 ? '0'+month : month), '/',
+ (day < 10 ? '0'+day : day), ' ',
+ (hour < 10 ? '0'+hour : hour), ':',
+ (min < 10 ? '0'+min : min), ':',
+ (sec < 10 ? '0'+sec : sec)
+ ].join('');
+ }
+}; //}}}
+//}}}
+/**
+ * openSBM {{{
+ * @param {String} url
+ * @param {String} type
+ * @param {String[]} format
+ * @param {Boolean} countOnly
+ * @param {Boolean} openToBrowser
+ */
+function openSBM(url, type, format, countOnly, openToBrowser){
+ var sbmLabel = manager.type[type];
+ var sbmURL = SBM[sbmLabel].getURL(url);
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', sbmURL, true);
+ xhr.onreadystatechange = function(){
+ if (xhr.readyState == 4){
+ if (xhr.status == 200){
+ var sbmContainer = SBM[sbmLabel].parser.call(this, xhr);
+ if (!sbmContainer) return;
+ cacheManager.add(sbmContainer, url, type);
+ if (openToBrowser)
+ manager.open(sbmContainer.toHTMLString(format,false));
+ else
+ liberator.echo(sbmContainer.toHTMLString(format,countOnly), true);
+ } else {
+ liberator.echoerr(sbmURL + ' ' + xhr.status, true);
+ }
+ }
+ }
+ xhr.send(null);
+} //}}}
+/**
+ * getURL と parser メソッドを供えること
+ * getURL は 取得先のURLを返すこと
+ * parser は SBMContainer オブジェクトを返すこと
+ */
+var SBM = { //{{{
+ hatena: { //{{{
+ getURL: function(url){
+ var urlPrefix = 'http://b.hatena.ne.jp/entry/json/?url=';
+ return urlPrefix + encodeURIComponent(url.replace(/%23/g,'#'));
+ },
+ parser: function(xhr){
+ //var json = window.eval(xhr.responseText);
+ var json = jsonDecode(xhr.responseText, true);
+ var count = json.bookmarks.length;
+ var c = new SBMContainer('h', json.count, {
+ faviconURL:'http://b.hatena.ne.jp/favicon.ico',
+ pageURL: 'http://b.hatena.ne.jp/entry/' + json.url
+ });
+ json.bookmarks.forEach(function(bm){
+ c.add(bm.user, new Date(bm.timestamp), bm.comment, bm.tags, {
+ userIcon: 'http://www.hatena.ne.jp/users/' + bm.user.substring(0,2) + '/' + bm.user +'/profile_s.gif'
+ });
+ });
+ return c;
+ }
+ }, //}}}
+ delicious: { //{{{
+ getURL: function(url){
+ //var urlPrefix = 'http://del.icio.us/rss/url/';
+ var urlPrefix = 'http://feeds.delicious.com/rss/url/';
+ return urlPrefix + getMD5Hash(url);
+ },
+ parser: function(xhr){
+ var rss = xhr.responseXML;
+ if (!rss){
+ liberator.echoerr('del.icio.us feed is none',true);
+ return;
+ }
+ try {
+ var pageURL = evaluateXPath(rss, '//rss:channel/rss:link')[0].textContent;
+ var items = evaluateXPath(rss, '//rss:item');
+ } catch(e){
+ liberator.log(e);
+ }
+ var c = new SBMContainer('d', items.length, {
+ faviconURL: 'http://del.icio.us/favicon.ico',
+ pageURL: pageURL
+ });
+ items.forEach(function(item){
+ var children = item.childNodes;
+ var [id,date,tags,comment,link] = ['','',[],'',''];
+ for (var i=0; i<children.length; i++){
+ var node = children[i];
+ if (node.nodeType == 1){
+ switch (node.localName){
+ case 'creator': id = node.textContent; break;
+ case 'link': link = node.textContent; break;
+ case 'date':
+ date = window.eval('new Date(' + node.textContent.split(/[-T:Z]/,6).join(',') + ')');
+ break;
+ case 'description': comment = node.textContent; break;
+ case 'subject': tags = node.textContent.split(/\s+/); break;
+ }
+ }
+ }
+ c.add(id, date, comment, tags, {link: link});
+ });
+ return c;
+ }
+ }, //}}}
+ livedoorclip: { //{{{
+ getURL: function(url){
+ var urlPrefix = 'http://api.clip.livedoor.com/json/comments?link=';
+ return urlPrefix + encodeURIComponent(url.replace(/%23/g,'#')) + '&all=0';
+ },
+ parser: function(xhr){
+ /*
+ var json = Components.classes['@mozilla.org/dom/json;1'].
+ getService(Components.interfaces.nsIJSON).
+ decode(xhr.responseText);
+ */
+ var json = jsonDecode(xhr.reponseText);
+ if (json && json.isSuccess){
+ var c = new SBMContainer('l', json.total_clip_count, {
+ faviconURL: 'http://clip.livedoor.com/favicon.ico',
+ pageURL: 'http://clip.livedoor.com/page/' + json.link
+ });
+ json.Comments.forEach(function(clip){
+ c.add( clip.livedoor_id, new Date(clip.created_on * 1000),
+ clip.notes ? clip.notes.replace(/</g,'&lt;').replace(/>/g,'&gt;') : '',
+ clip.tags,
+ {
+ userIcon: 'http://image.clip.livedoor.com/profile/' +
+ '?viewer_id=[%%20member.livedoor_id%20Z%]&target_id=' +
+ clip.livedoor_id,
+ link: 'http://clip.livedoor.com/clips/' + clip.livedoor_id
+ }
+ );
+ });
+ return c;
+ } else {
+ liverator.log('Faild: LivedoorClip');
+ }
+ }
+ }, //}}}
+ buzzurl: { //{{{
+ getURL: function(url){
+ var urlPrefix = 'http://api.buzzurl.jp/api/posts/get/v1/json/?url=';
+ return urlPrefix + encodeURIComponent(url.replace(/%23/g,'#'));
+ },
+ parser: function(xhr){
+ var url = 'http://buzzurl.jp/user/';
+ var json = jsonDecode(xhr.responseText);
+ if (json && json[0] && json[0].user_num){
+ var c = new SBMContainer('buzzurl', json[0].user_num, {
+ faviconURL: 'http://buzzurl.jp/favicon.ico',
+ pageURL: 'http://buzzurl.jp/entry/' + json[0].url
+ });
+ json[0].posts.forEach(function(entry){
+ c.add( entry.user_name, window.eval('new Date(' + entry.date.split(/[-\s:]/,6).join(',') + ')'),
+ entry.comment ? entry.comment : '', entry.keywords.split(','),
+ {
+ userIcon: url + entry.user_name + '/photo',
+ link: url + '/' + entry.user_name
+ }
+ );
+ });
+ return c;
+ } else {
+ liverator.log('Faild: Buzzurl');
+ }
+ }
+ }, //}}}
+}; //}}}
+
+
+/**
+ * jsonDecode {{{
+ * @param {String} str JSON String
+ * @param {Boolean} toRemove はてなブックマークのJSONの様に
+ * 前後に()が付いている場合に取り除くためのフラグ
+ */
+function jsonDecode(str, toRemove){
+ var 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);
+}
+//}}}
+/**
+ * getMD5Hash {{{
+ * @param {String} str
+ * @return {String} MD5HashString
+ */
+function getMD5Hash(str){
+ var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"].
+ createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
+ converter.charset = 'UTF-8';
+ var result = {};
+ var data = converter.convertToByteArray(str, result);
+ var ch = Components.classes['@mozilla.org/security/hash;1'].createInstance(Components.interfaces.nsICryptoHash);
+ ch.init(ch.MD5);
+ ch.update(data, data.length);
+ var hash = ch.finish(false);
+ function toHexString(charCode){
+ return ('0' + charCode.toString(16)).slice(-2);
+ }
+ var s = [i < hash.length ? toHexString(hash.charCodeAt(i)) : '' for (i in hash)].join('');
+ return s;
+} //}}}
+/**
+ * evaluateXPath {{{
+ * @param {Element} aNode
+ * @param {String} aExpr XPath Expression
+ * @return {Element[]}
+ * @see http://developer.mozilla.org/ja/docs/Using_XPath
+ */
+function evaluateXPath(aNode, aExpr){
+ var xpe = new XPathEvaluator();
+ function nsResolver(prefix){
+ var ns = {
+ xhtml: 'http://www.w3.org/1999/xhtml',
+ rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ dc: 'http://purl.org/dc/elements/1.1/',
+ rss: 'http://purl.org/rss/1.0/',
+ taxo: 'http://purl.org/rss/1.0/modules/taxonomy/',
+ content: 'http://purl.org/rss/1.0/modules/content/',
+ syn: 'http://purl.org/rss/1.0/modules/syndication/',
+ admin: 'http://webns.net/mvcb/'
+ };
+ return ns[prefix] || null;
+ }
+ var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
+ var found = [];
+ var res;
+ while (res = result.iterateNext())
+ found.push(res);
+ return found;
+} //}}}
+/**
+ * sbmCommentsView manager {{{
+ * @alias liberator.plugins.sbmCommentsViewer
+ */
+var manager = {
+ type: {
+ h: 'hatena',
+ d: 'delicious',
+ l: 'livedoorclip',
+ z: 'buzzurl'
+ },
+ format: {
+ id: 'ID',
+ comment: 'Comment',
+ timestamp: 'TimeStamp',
+ tags: 'Tags',
+ tagsAndComment: 'Tags&Comment'
+ },
+ // for debug
+ convertMD5: function(str){
+ return getMD5Hash(str);
+ },
+ // for debug
+ getXML: function(url){
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET',url,false);
+ xhr.send(null);
+ return xhr;
+ },
+ // for debug
+ get cache(){
+ return cacheManager;
+ },
+ /**
+ * @param {String} str
+ * @param {Number} where
+ * TODO
+ */
+ open: function(str, where){
+ /*
+ getBrowser().addTab('data:text/html,'+str, null,null,null);
+ */
+ }
+}; //}}}
+
+commands.addUserCommand(['viewSBMComments'], 'SBM Comments Viewer', //{{{
+ function(arg){ //{{{
+ var types = liberator.globalVariables.def_sbms || 'hdlz';
+ var format = (liberator.globalVariables.def_sbm_format || 'id,timestamp,tags,comment').split(',');
+ var countOnly = false, openToBrowser = false;
+ var url = buffer.URL;
+ for (var opt in arg){
+ switch(opt){
+ case '-c':
+ case '-count':
+ countOnly = true;
+ break;
+ case '-b':
+ case '-browser':
+ openToBrowser = true;
+ break;
+ case '-t':
+ if (arg[opt]) types = arg[opt];
+ break;
+ case '-f':
+ if (arg[opt]) format = arg[opt];
+ break;
+ case "arguments":
+ if (arg[opt].length > 0) url = arg[opt][0];
+ break;
+ }
+ }
+
+ for (var i=0; i<types.length; i++){
+ var type = types.charAt(i);
+ if ( manager.type[type] ){
+ if ( cacheManager.isAvailable(url, type) ){
+ liberator.log('cache avairable');
+ if (openToBrowser)
+ // TODO
+ manager.open(cacheManager.get(url,type).toHTMLString(format,false), liberator.forceNewTab);
+ else
+ liberator.echo(cacheManager.get(url, type).toHTMLString(format,countOnly), true);
+ } else {
+ try {
+ openSBM(url, type, format, countOnly, openToBrowser);
+ } catch(e){
+ liberator.log(e);
+ }
+ }
+ }
+ }
+ }, //}}}
+ {
+ options: [
+ [['-t','-type'], commands.OPTION_STRING],
+ [['-f','-format'], commands.OPTION_LIST],
+ [['-c','-count'], commands.OPTION_NOARG],
+ [['-b','-browser'],commands.OPTION_NORARG]
+ ]
+ }
+); //}}}
+
+var cacheManager = (function(){
+ var cache = {};
+ // min sec millisec
+ var threshold = 30 * 60 * 1000;
+ var interval = 10 * 60 * 1000;
+ var c_manager = {
+ get raw(){
+ return cache;
+ },
+ has: function(url){
+ if (cache[url])
+ return true;
+ else
+ return false;
+ },
+ add: function(sbmComments, url, type){
+ if (!cache[url]) cache[url] = {};
+ cache[url][type] = [new Date(), sbmComments];
+ },
+ get: function(url, type){
+ return cache[url][type][1];
+ },
+ delete: function(url, type) {
+ if (!cache[url]) return true;
+ if (!type) return delete cache[url];
+ return delete cache[url][type];
+ },
+ garbage: function(){
+ var date = new Date();
+ for (var url in cache){
+ for (var type in cache[url]){
+ if (date - cache[url][type][0] > threshold) delete cache[url][type];
+ }
+ }
+ },
+ isAvailable: function(url, type){
+ if (cache[url] && cache[url][type] && new Date() - cache[url][type][0] < threshold)
+ return true;
+
+ return false;
+ }
+ };
+ return c_manager;
+})();
+
+return manager;
+})();
+// vim: sw=4 ts=4 sts=0 et fdm=marker:
diff --git a/tombloo.js b/tombloo.js
new file mode 100644
index 0000000..085287d
--- /dev/null
+++ b/tombloo.js
@@ -0,0 +1,72 @@
+/**
+ * ==VimperatorPlugin==
+ * @name tombloo.js
+ * @description Tombloo integrate plugin
+ * @description-ja Tombloo経由で選択領域などをpostする
+ * @author Trapezoid
+ * @version 0.1d
+ * ==/VimperatorPlugin==
+ *
+ * Usage:
+ * :tombloo arg -> post by Tombloo (don't use prompt)
+ * :tombloo! arg -> post by Tombloo (use prompt)
+ * :tomblooAction arg -> execute Tombloo's action in tool menu
+ **/
+var TomblooService = Components.classes['@brasil.to/tombloo-service;1'].getService().wrappedJSObject;
+function update(target,src,keys){
+ if(keys){
+ keys.forEach(function(key){
+ target[key] = src[key];
+ });
+ } else {
+ for(let key in src)
+ target[key] = src[key];
+ }
+
+ return target;
+}
+
+function getContext(){
+ var doc = window.content.document;
+ var win = window.content.wrappedJSObject;
+ return update(update({
+ document : doc,
+ window : win,
+ title : ''+doc.title || '',
+ selection : ''+win.getSelection(),
+ target : doc,
+ //event : event,
+ //mouse : mouse,
+ //menu : gContextMenu,
+ },{}),win.location);
+}
+
+liberator.commands.addUserCommand(['tomblooAction'],'Execute Tombloo actions',
+ function(arg){
+ TomblooService.Tombloo.Service.actions[arg].execute();
+ },{
+ completer: function(filter){
+ var completionList = new Array();
+ for(let name in TomblooService.Tombloo.Service.actions)
+ if(name.indexOf(filter) > -1)
+ completionList.push([name,name]);
+ return [0,completionList];
+ }
+ }
+);
+
+liberator.commands.addUserCommand(['tombloo'],'Post by Tombloo',
+ function(arg,special){
+ TomblooService.Tombloo.Service.share(getContext(),TomblooService.Tombloo.Service.extractors[arg],special);
+ },{
+ bang: true,
+ completer: function(filter){
+ var completionList = new Array();
+ var exts = TomblooService.Tombloo.Service.check(getContext());
+ for(let i=0,l=exts.length; i < l; i++)
+ if(exts[i].name.indexOf(filter) > -1)
+ completionList.push([exts[i].name,exts[i].name]);
+ return [0,completionList];
+ }
+ }
+);
diff --git a/uaSwitch.js b/uaSwitch.js
new file mode 100644
index 0000000..f759fd1
--- /dev/null
+++ b/uaSwitch.js
@@ -0,0 +1,37 @@
+// Vimperator plugin: uaSwitch
+// Maintainer: mattn <mattn.jp@gmail.com> - http://mattn.kaoriya.net
+// Require: User Agent Switcher - https://addons.mozilla.org/firefox/addon/59
+// Usage:
+// :ua MyUserAgent - set user agent named MyUserAgent.
+// :ua Default - reset user agent to default.
+// :ua! - open user agent switcher setting dialog.
+// :ua - show current user agent.
+
+(function() {
+ if (typeof useragentswitcher_reset != 'function') return;
+
+ // activate user agent siwtcher
+ useragentswitcher_displayUserAgentSwitcherMenu(document.getElementById('useragentswitcher-popup-menu'), 'menu');
+
+ // return user agent list
+ function getItems()
+ Array.map(document.getElementById('useragentswitcher-menu')
+ .getElementsByAttribute('type', 'radio'),
+ function(n) {
+ return {
+ label : n.getAttribute('label'),
+ oncommand : n.getAttribute('oncommand'),
+ checked : n.getAttribute('checked')
+ }
+ });
+
+ // register Vimperator command
+ liberator.commands.addUserCommand(['ua'], 'Switch User Agent', function(arg, special){
+ if (special) useragentswitcher_options();
+ else if (!arg) liberator.echo('UserAgent: ' + getItems().filter(function(n) n.checked)[0].label);
+ else window.eval(getItems().filter(function(n) n.label == arg)[0].oncommand);
+ }, {
+ completer: function(filter, special)
+ [0, getItems().map(function(n) [n.label, n.label])]
+ });
+})();
diff --git a/ubiquity.js b/ubiquity.js
new file mode 100644
index 0000000..ab3881e
--- /dev/null
+++ b/ubiquity.js
@@ -0,0 +1,144 @@
+/**
+ * ==VimperatorPlugin==
+ * @name Ubiquity Glue
+ * @description viperator-plugin for Ubiquity
+ * @depend Ubiquity (ubiquity@labs.mozilla.com)
+ * @version 0.1.1a
+ * ==/VimperatorPlugin==
+ *
+ * USAGE:
+ *
+ * :ubi[quity] {ubiquityCommand}
+ * {ubiquityCommand}をUbiquityに渡して実行
+ *
+ * :ubi[quity]
+ * ランチャを起動
+ *
+ * - ランチャ起動キーでコマンドラインに":ubiquity "がでます(ランチャはポップアップしない)
+ * 気に入らない場合は、XXX:の辺りのコードをコメントアウトしてください
+ * - Ubiquityコマンド名の補完が効きます。
+ * - Ubiquityコマンド入力後、引数を入力してのタブ補完をするとUbiquityのプレビューがでる(はず)
+ *
+ * FIXME:
+ * - プレビュー時の選択範囲の取得がイマイチ出来てない
+ * - プレビュー後の操作はマウス必須になってしまう(これはどうしようもない?)
+ *
+ */
+
+liberator.plugins.ubiquity = (function(){
+
+var ubiquityID = 'ubiquity@labs.mozilla.com';
+if (!Application.extensions.has(ubiquityID) || !Application.extensions.get(ubiquityID).enabled){
+ Components.utils.reportError('Vimperator: UbiquityGlue: Ubiquity is not installed');
+ return null;
+}
+function preExec(target,name,func){
+ var original = target[name];
+ target[name] = function(){
+ var result = func.apply(this,arguments);
+ var tmp = null;
+ if (result != false) tmp = original.apply(target,arguments);
+ return tmp;
+ }
+}
+
+preExec(events, 'onEscape', function(){
+ if (ubiquityManager.panel.state == 'open') gUbiquity.closeWindow();
+});
+var focusedWindow = null;
+var focusedElement = null;
+preExec(commandline, 'open', function(){
+ focusedWindow = document.commandDispatcher.focusedWindow;
+ focusedElement = document.commandDispatcher.focusedElement;
+});
+
+// XXX:選択範囲が必要な操作が現状上手く動かない.不便であればコメントアウトしてください.
+preExec(gUbiquity, 'openWindow', function(anchor, flag){
+ if(!flag) {
+ liberator.commandline.open(':', 'ubiquity ', liberator.modes.EX);
+ return false;
+ }
+});
+
+// -------------------------------------------------
+// Command
+// -------------------------------------------------
+commands.addUserCommand(['ubi[quity]'],'Vimperator Ubiquity Glue',
+ function(args){
+ if (!args){
+ gUbiquity.openWindow(getBrowser(), true);
+ return;
+ }
+ ubiquityManager.execute(args);
+ },{
+ completer: function(filter){
+ return ubiquityManager.completer(filter);
+ }
+ },
+ true
+);
+
+// -------------------------------------------------
+// Public Section
+// -------------------------------------------------
+var ubiquityManager = {
+ get panel(){
+ return gUbiquity.__msgPanel;
+ },
+ get cmdManager(){
+ return gUbiquity.__cmdManager;
+ },
+ get nlParser(){
+ return this.cmdManager.__nlParser;
+ },
+ get commands(){
+ return this.cmdManager.__cmdSource.getAllCommands();
+ },
+ execute: function(cmds){
+ var context = this.getContext();
+ this.nlParser.updateSuggestionList(cmds, context);
+ if (this.nlParser.getNumSuggestions() == 0){
+ liberator.echoerr('No such command');
+ return false;
+ }
+ var parsedSentence = this.nlParser.getSentence(0);
+ try {
+ parsedSentence.execute(context);
+ } catch (e) {
+ liberator.echoerr(e);
+ }
+ },
+ completer: function(args){
+ var matches = args.match(/([^\s]+)(?:\s+(.+)$)?/);
+ var suggestions = [];
+ for (var cmd in this.commands){
+ suggestions.push([cmd , this.commands[cmd].description]);
+ }
+ if (!matches){
+ return [0, suggestions];
+ }
+ var [cmd, arg] = [matches[1], matches[2]];
+ if (arg || (cmd && cmd in this.commands) ){
+ if ( (cmd in this.commands) && this.commands[cmd].preview){
+ this.getContext();
+ gUbiquity.__textBox.value = args;
+ if (this.panel.state == 'closed') {
+ gUbiquity.openWindow(getBrowser(), true);
+ }
+ gUbiquity.__updatePreview();
+ }
+ } else if (cmd){
+ return [0, suggestions.filter(function(command){return command[0].indexOf(cmd) == 0;}) ];
+ }
+ return [0, []];
+ },
+ getContext: function(){
+ gUbiquity.__focusedWindow = focusedWindow;
+ gUbiquity.__focusedElement = focusedElement;
+ return gUbiquity.__makeContext();
+ }
+};
+
+return ubiquityManager;
+})();
+// vim:ts=4 sw=4 et: