diff options
-rw-r--r-- | appendAnchor.js | 41 | ||||
-rw-r--r-- | copy.js | 193 | ||||
-rw-r--r-- | direct_bookmark.js | 648 | ||||
-rw-r--r-- | feedSomeKeys.js | 320 | ||||
-rw-r--r-- | googlekanji.js | 82 | ||||
-rw-r--r-- | googlesuggest.js | 34 | ||||
-rw-r--r-- | hatenaStar.js | 69 | ||||
-rw-r--r-- | ime_controller.js | 73 | ||||
-rw-r--r-- | lo.js | 150 | ||||
-rw-r--r-- | lookupDictionary.js | 269 | ||||
-rw-r--r-- | migemized_find.js | 470 | ||||
-rw-r--r-- | migemo_completion.js | 51 | ||||
-rw-r--r-- | migemo_hint.js | 21 | ||||
-rw-r--r-- | proxy.js | 133 | ||||
-rw-r--r-- | sbmcommentsviewer.js | 543 | ||||
-rw-r--r-- | tombloo.js | 72 | ||||
-rw-r--r-- | uaSwitch.js | 37 | ||||
-rw-r--r-- | ubiquity.js | 144 |
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(); + },{} + ); +})(); @@ -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,'<').replace(/>/g,'>')),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: @@ -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,'<').replace(/>/g,'>') : '', + 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: |