From c4489d32e21b8548976fec780eff4df613697a53 Mon Sep 17 00:00:00 2001 From: retlet Date: Wed, 5 Nov 2008 06:04:44 +0000 Subject: 手元の環境で動いているものをtags/2.0pre_1016からコピー git-svn-id: http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/branches/1.2@22751 d0d07461-0603-4401-acd4-de1884942a52 --- appendAnchor.js | 41 ++++ copy.js | 193 +++++++++++++++ direct_bookmark.js | 648 +++++++++++++++++++++++++++++++++++++++++++++++++++ feedSomeKeys.js | 320 +++++++++++++++++++++++++ googlekanji.js | 82 +++++++ googlesuggest.js | 34 +++ hatenaStar.js | 69 ++++++ ime_controller.js | 73 ++++++ lo.js | 150 ++++++++++++ lookupDictionary.js | 269 +++++++++++++++++++++ migemized_find.js | 470 +++++++++++++++++++++++++++++++++++++ migemo_completion.js | 51 ++++ migemo_hint.js | 21 ++ proxy.js | 133 +++++++++++ sbmcommentsviewer.js | 543 ++++++++++++++++++++++++++++++++++++++++++ tombloo.js | 72 ++++++ uaSwitch.js | 37 +++ ubiquity.js | 144 ++++++++++++ 18 files changed, 3350 insertions(+) create mode 100644 appendAnchor.js create mode 100644 copy.js create mode 100644 direct_bookmark.js create mode 100644 feedSomeKeys.js create mode 100644 googlekanji.js create mode 100644 googlesuggest.js create mode 100644 hatenaStar.js create mode 100644 ime_controller.js create mode 100644 lo.js create mode 100644 lookupDictionary.js create mode 100644 migemized_find.js create mode 100644 migemo_completion.js create mode 100644 migemo_hint.js create mode 100644 proxy.js create mode 100644 sbmcommentsviewer.js create mode 100644 tombloo.js create mode 100644 uaSwitch.js create mode 100644 ubiquity.js diff --git a/appendAnchor.js b/appendAnchor.js new file mode 100644 index 0000000..9dc410d --- /dev/null +++ b/appendAnchor.js @@ -0,0 +1,41 @@ +/** + * == VimperatorPlugin== + * @name appendAnchor + * @description append anchors to texts look like url. + * @author SAKAI, Kazuaki + * @version 0.02 + * == VimperatorPlugin== + */ + +(function(){ + + liberator.commands.addUserCommand(['anc'], 'append anchors to texts look like url', + function(arg, special) { + var doc = window.content.document; + var nodes = liberator.buffer.evaluateXPath( + '/descendant::*[not(contains(" TITLE STYLE SCRIPT TEXTAREA XMP A ", concat(" ", translate(local-name(), "aceilmprstxy", "ACEILMPRSTXY"), " ")))]/child::text()' + ); + var regex = new RegExp("h?(ttps?):/+([a-zA-Z0-9][-_.!~*'()a-zA-Z0-9;/?:@&=+$,%#]+[-_~*(a-zA-Z0-9;/?@&=+$%#])"); + + var range = doc.createRange(); + var last; + var href; + for (let i = 0, l = nodes.snapshotLength; i < l; i++) { + let node = nodes.snapshotItem(i); + range.selectNode(node); + while (node && (last = range.toString().search(regex)) > -1) { + range.setStart(node, last); + range.setEnd(node, last + RegExp.lastMatch.length); + href = 'h' + RegExp.$1 + '://' + RegExp.$2; + let anchor = doc.createElement('a'); + range.insertNode(anchor); + anchor.setAttribute('href', href); + range.surroundContents(anchor); + node = node.nextSibling.nextSibling; + range.selectNode(node); + } + } + range.detach(); + },{} + ); +})(); diff --git a/copy.js b/copy.js new file mode 100644 index 0000000..4bcd3e3 --- /dev/null +++ b/copy.js @@ -0,0 +1,193 @@ +/** + * ==VimperatorPlugin== + * @name copy.js + * @description enable to copy strings from a template (like CopyURL+) + * @description-ja テンプレートから文字列のコピーを可能にします(CopyURL+みたなもの) + * @minVersion 1.1 + * @author teramako teramako@gmail.com + * @version 0.5a + * ==/VimperatorPlugin== + * + * Usage: + * :copy {copyString} -> copy the argument replaced some certain string + * :copy! {expr} -> evaluate the argument and copy the result + * + * e.g.) + * :copy %TITLE% -> copied the title of the current page + * :copy title -> same as `:copy %TITLE%' by default + * :copy! liberator.version -> copy the value of liberator.version + * + * If non-argument, used `default' + * + * label: template name which is command argument + * value: copy string + * the certian string is replace to ... + * %TITTLE% -> to the title of the current page + * %URL% -> to the URL of the current page + * %SEL% -> to the string of selection + * %HTMLSEL% -> to the html string of selection + * + * map: key map (optional) + * + * custom: {function} or {Array} (optional) + * {function}: + * execute the function and copy return value, if specified. + * + * {Array}: + * replaced to the {value} by normal way at first. + * and replace words matched {Array}[0] in the replaced string to {Array}[1]. + * {Array}[0] is string or regexp + * {Array}[1] is string or function + * see http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:String:replace + * + * The copy_templates is a string variable which can set on + * vimperatorrc as following. + * + * let copy_templates = "[{ label: 'titleAndURL', value: '%TITLE%\n%URL%' }, { label: 'title', value: '%TITLE%' }]" + * + * or your can set it using inline JavaScript. + * + * javascript <%TITLE%' }, + * { label: 'selanchor', value: '%SEL%' }, + * { label: 'htmlblockquote', value: '
%HTMLSEL%
' } + * { 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: '%TITLE%' }, + { label: 'selanchor', value: '%SEL%' }, + { label: 'htmlblockquote', value: '
%HTMLSEL%
' } + ]; +} + +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 - 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]*?]*)?>|[\\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[^>]*|/)?>|"; + } + } + 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 = + + dummy + + {tagString + comment} + ; + 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 ''; + }, + }, + '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 ''; + }, + }, + '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 ''; + }, + }, + '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('
'); + 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 < z b < >".split(/ +/));'); +EOF + * とかやると幸せになれるかも。 + * + * == Gmail の場合 == +js < ') != 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 0){ + var frames = []; + (function(frame){// @see liberator.buffer.shiftFrameFocus + if (frame.document.body.localName.toLowerCase() == 'body') { + frames.push(frame); + } + for (var i=0; i]+)/); + 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,'>')),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 - http://unsigned.g.hatena.ne.jp/Trapezoid +// +// search word completion using Google Suggest script for Vimperator 0.6.* + +liberator.commands.addUserCommand(['google'],"Search Web sites with Google Suggest", + function(arg){ + const endpoint = "http://www.google.co.jp/search?q="; + //liberator.open(endpoint + encodeURIComponent(arg)); + liberator.open(endpoint + encodeURIComponent(arg),liberator.NEW_TAB); + }, + { + completer: function (filter) { + const endPoint = "http://suggestqueries.google.com/complete/search?output=firefox&client=firefox" + var [lang] = Components.classes["@mozilla.org/network/protocol;1?name=http"] + .getService(Components.interfaces.nsIHttpProtocolHandler) + .language.split("-", 1); + var xhr = new XMLHttpRequest(); + var completionList = []; + + xhr.open("GET",endPoint + "&hl=" + lang + "&qu=" + encodeURIComponent(filter),false); + xhr.send(null); + var response = window.eval(xhr.responseText)[1]; + + response.forEach(function(r) { + completionList.push([r,"Suggests"]); + }); + return [0,completionList]; + } + } +); +// vim:sw=4 ts=4 et: diff --git a/hatenaStar.js b/hatenaStar.js new file mode 100644 index 0000000..d78aba9 --- /dev/null +++ b/hatenaStar.js @@ -0,0 +1,69 @@ +/** + * For Vimperator 0.7.0 + * @author mattn mattn.jp@gmail.com + */ + +(function(){ + +const Cc = Components.classes; +const Ci = Components.interfaces; +var flasher = null; + +function getFlasher(){ + if (!flasher){ + flasher = Cc['@mozilla.org/inspector/flasher;1'].createInstance(Ci.inIFlasher); + flasher.color = '#FF0000'; + flasher.thickness = 2; + } + return flasher; +} + +function blink(aNode){ + if (!aNode) { + liberator.echoerr('start not found'); + return; + } + if (aNode.nodeType == 3) aNode = aNode.parentNode; + var toggle = true; + var flasher = getFlasher(); + for (var i=1; i<7; ++i){ + setTimeout(function() { + if (toggle) flasher.drawElementOutline(aNode); + else flasher.repaintElement(aNode); + toggle = !toggle; + }, i * 100); + } +} + +liberator.commands.addUserCommand(['hatenastar', 'hatenas'], 'add Hatena Star', + function (arg, special) { + try { + var result = liberator.buffer.evaluateXPath('.//img[contains(concat(" ", @class, " "), " hatena-star-add-button ")]'); + if (arg.match(/^(\d+)\?$/)) { + blink(result.snapshotItem(Number(RegExp.$1)-1)); + return; + } + for (var i = 0, l = result.snapshotLength; i < l; i++) { + if (arg == '' || arg == 'all' || arg == (i+1)) { + var s = result.snapshotItem(i); + var e = document.createEvent('MouseEvents'); + e.initMouseEvent('click', true, true, window, 1, 10, 50, 10, 50, 0, 0, 0, 0, 1, s); + s.dispatchEvent(e); + } + } + } catch (e) { liberator.echoerr('hatenaStar: ' + e); } + } +); + +liberator.mappings.addUserMap([liberator.modes.NORMAL], [',?s'], 'add Hatena Star', + function (count) { + try { + for (var n = 0; n++ < count; liberator.commands.get('hatenastar').execute("all", false, count)); + } catch (e) { liberator.echoerr('hatenaStar: ' + e); } + }, { + noremap: true, + flags: liberator.Mappings.flags.COUNT + } +); + +})(); diff --git a/ime_controller.js b/ime_controller.js new file mode 100644 index 0000000..96a1f86 --- /dev/null +++ b/ime_controller.js @@ -0,0 +1,73 @@ +/** + * ==VimperatorPlugin== + * @name IME Controller Lite + * @description control imput method at into commandline-mode + * @description-ja コマンドラインモード移行時にIMEの制御を行う + * @author teramako teramako@gmail.com + * @namespace http://d.hatena.ne.jp/teramako/ + * @maxVersion 0.6pre + * @minVersion 0.6pre + * ==/VimperatorPlugin== + * + * Please set g:ex_ime_mode and g:textarea_ime_mode value. + * + * g:ex_ime_mode: + * used at into EX mode + * + * g:textarea_ime_mode: + * used at into TEXTAREA mode from INSERT mode and "noinsertmode" is set. + * + * ex). + * :let g:ex_ime_mode = "inactive" + * :let g:textarea_ime_mode = "inactive" + * + * following values are available: + * "auto" : No change + * "active" : Initially IME on + * "inactive" : Initially IME off + * "disabled" : Disable IME + * + * more details: see http://developer.mozilla.org/en/docs/CSS:ime-mode + * + * if these values are null, "inactive" is used + * + */ + +liberator.plugins.imeController = (function(){ + var inputElement = document.getAnonymousElementByAttribute( + document.getElementById('liberator-commandline-command'),'anonid','input' + ); + function getMode(name){ + return liberator.globalVariables[name] ? liberator.globalVariables[name] : 'inactive'; + } + function preExec(target,name,func){ + var original = target[name]; + target[name] = function(){ + func.apply(this,arguments); + return original.apply(target,arguments); + } + } + preExec(commandline,'open',function(){ + liberator.plugins.imeController.set(inputElement, getMode('ex_ime_mode')); + }); + preExec(events,'onEscape',function(){ + if (liberator.mode == modes.INSERT && (modes.extended & modes.TEXTAREA) && !options.insertmode){ + var inputField = buffer.lastInputField; + if (liberator.plugins.imeController.set(inputField, getMode('textarea_ime_mode'))){ + inputField.blur(); + setTimeout(function(){inputField.focus();},0); + } + } + }); + return { + set: function(elem, mode){ + if (elem && mode) return elem.style.imeMode = mode; + return false; + }, + reset: function(){ + delete liberator.globalVariables.ex_ime_mode; + delete liberator.globalVariables.textarea_ime_mode; + } + }; +})(); +// vim: sw=4 ts=4 et: diff --git a/lo.js b/lo.js new file mode 100644 index 0000000..24b4102 --- /dev/null +++ b/lo.js @@ -0,0 +1,150 @@ +// ==VimperatorPlugin== +// @name Link Opener +// @description Open filtered link(s). +// @description-ja リンクをフィルタリングして開く +// @license Creative Commons 2.1 (Attribution + Share Alike) +// @version 1.1 +// ==/VimperatorPlugin== +// +// Usage: +// :fo[pen][!] [-i ] [-w ] +// Open filtered links by regexp. +// When used "!", open links in foreground. +// +// :lo[pen][!] URI [-w ] +// Open URI +// +// Usage-ja: +// :fo[pen][!] <ミゲ文字列> [-i ] [-w ] +// :fo[pen][!] /<正規表現> [-i ] [-w ] +// ミゲ文字列か正規表現でフィルタされたリンクを開く +// +// :lo[pen][!] URI [-w ] +// URI を開く +// +// ちなみに Migemo はなくても動きます。 +// 無い場合は、 "/" 要らずで正規表現オンリーになります。 +// +// Variables: +// let g:fopen_default_interval="" + + +(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' + 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 正規表現検索 +// '?' => 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 - 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 <' : '') + + manager.type[this.type] + ' ' + this.count + '(' + this.entries.length + ')' + + (this.pageURL ? ' ' + this.pageURL + '' : ''); + if (countOnly){ + return label; + } else { + var str = [ + '' + ]; + format.forEach(function(colum){ + var name = manager.format[colum] || '-'; + str.push(''); + }); + str.push(''); + this.entries.forEach(function(e){ + str.push(e.toHTMLString(format)); + }); + str.push('
' + label + '
' + name + '
'); + 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"が
に変換されてしまうのでStringで + var str = ['']; + var self = this; + format.forEach(function(colum){ + switch(colum){ + case 'id': + str.push('' + (self.userIcon ? '

' : '') + + '

' + self.id + '

'); + break; + case 'timestamp': + str.push('' + self.formatDate() + ''); break; + case 'tags': + str.push('' + self.tags.join(',') + ''); break; + case 'comment': + str.push('' + self.comment + ''); break; + case 'tagsAndComment': + tagString = self.tags.length ? '[' + self.tags.join('][') + ']':''; + str.push('' + tagString + ' ' + self.comment + ''); break; + default: + str.push('-'); + } + }); + str.push(''); + 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/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 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 - 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: -- cgit v1.2.3