Skip to content

Instantly share code, notes, and snippets.

@958
Last active September 25, 2015 15:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 958/945711 to your computer and use it in GitHub Desktop.
Save 958/945711 to your computer and use it in GitHub Desktop.
[keysnail]Multi Requester from KeySnail
// original script license
/*** BEGIN LICENSE BLOCK {{{
Copyright (c) 2008 suVene<suvene@zeromemory.info>
distributable under the terms of an MIT-style license.
http://www.opensource.jp/licenses/mit-license.html
}}} END LICENSE BLOCK ***/
var PLUGIN_INFO =
<KeySnailPlugin>
<name>Multi Requester from KeySnail</name>
<description>request, and the result is displayed to the buffer.</description>
<description lang="ja">リクエストの結果をバッファに出力する。</description>
<updateURL>https://gist.github.com/958/945711/raw/multi_request.ks.js</updateURL>
<version>0.0.8</version>
<author>958</author>
<license>MIT</license>
<minVersion>1.8.0</minVersion>
<detail lang="ja"><![CDATA[
=== 使い方 ===
>||
multi-request
subcommand [ANY_TEXT]
||<
- ANY_TEXT 任意の文字列
>||
alc[,goo,any1,any2…] ANY_TEXT -> 入力文字に対するリクエストを送信し、結果を表示します
goo[,any1,any2,…] {window.selection} -> 選択テキストに対するリクエストを送信し、結果を表示します
||<
=== ショートカットキー ===
以下のような感じでショートカットキーを割り当てるとよいです
>||
key.setGlobalKey(['C-t', 'j'], function (ev, arg) {
// Google 翻訳 と So-net 翻訳で日本語に翻訳
ext.exec("multi-request", "googletrans-ja,so-net-e2j", ev);
}, 'Multi request', true);
key.setGlobalKey(['C-t', 'e'], function (ev, arg) {
// Google 翻訳 と So-net 翻訳で英語に翻訳
ext.exec("multi-request", "googletrans-en,so-net-j2e", ev);
});
key.setGlobalKey(['C-t', 'a'], function (ev, arg) {
// alc と goo で単語翻訳
ext.exec("multi-request", "alc,goo", ev);
});
||<
=== SITEINFO ===
SITEINFO を任意に追加できます
>||
plugins.options['multi_requester.siteinfo'] = [
{
name: "ex", // required: subcommand name
description: "example", // required: commandline short help
url: "http://example.com/?%s", // required: %s <-- replace string
xpath: "//*", // optional: default all
srcEncode: "SHIFT_JIS", // optional: default UTF-8
urlEncode: "SHIFT_JIS", // optional: default srcEncode
ignoreTags: "img", // optional: default script, syntax "tag1,tag2,……"
extractLink: "//xpath" // optional: extract permalink
},
];
||<
デフォルトで、So-net 翻訳を追加しています
wedata の SITEINFO に追加しようと思ったのですが、本家 Vimplerator プラグインではうまく動作しなかったので、ローカルに追加しました
=== Wedata ===
Wedata にある Vimperator Multi Requester Plugin の SITEINFO を使用します
http://wedata.net/databases/Multi%20Requester/items
=== 流用元 ===
multi_requester.js at master from vimpr/vimperator-plugins - GitHub
https://github.com/vimpr/vimperator-plugins/blob/master/multi_requester.js
]]></detail>
</KeySnailPlugin>
const KEY = 'multi_requester'
let pOptions = plugins.setupOptions(KEY, {
'style': {
preset:'* { font-size: 10pt; } \
.mr_site { display: block; margin: 1em 0; } \
.mr_title { display: block; white-space: nowrap; overflow: hidden; margin-bottom: 0.5em; } \
.mr_detail { margin: 0 1em; }',
description: M({ja: 'echo エリアのcss', en: 'css of echo area'})
},
'siteinfo': {
preset: [
{
name: 'so-net-e2j',
description: L('So-net 翻訳(英日)'),
url: 'http://www.so-net.ne.jp/translation/cgi-bin/text.cgi?text=%s&language=EJ&requestTranslate.x=60&requestTranslate.y=30',
xpath: 'id("textTextResult")/text()'
},
{
name: 'so-net-j2e',
description: L('So-net 翻訳(日英)'),
url: 'http://www.so-net.ne.jp/translation/cgi-bin/text.cgi?text=%s&language=JE&requestTranslate.x=60&requestTranslate.y=30',
xpath: 'id("textTextResult")/text()'
}
],
description: M({ja: '追加で使用する siteinfo', en: 'local siteinfo'})
}
}, PLUGIN_INFO);
(function (callback){
let store = persist.restore(KEY);
let isCache = false;
if (store) {
isCache = true;
callback(store.siteinfo);
}
if (!store || new Date(store.expire) > new Date()) {
util.requestGet('http://wedata.net/databases/Multi%20Requester/items.json', {
callback: function(xhr) {
if (xhr.status != 200) return;
if (xhr.responseText) {
store = {};
store.expire = new Date();
store.siteinfo = JSON.parse(xhr.responseText);
persist.preserve(store, KEY);
if (!isCache)
callback(store.siteinfo);
}
}
});
}
})(function(siteinfo) {
let info = siteinfo.map((i) => i.data);
pOptions['siteinfo'].forEach((i) => info.push(i));
info = info.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase());
plugins.withProvides(function (provide) {
provide('multi-request',
function(ev, arg) {
MultiRequester.siteinfo = info;
let reader = function(str) {
prompt.reader({
message : 'Select request site:',
group : "multi_requester",
initialInput : str,
cursorEnd : true,
completer : function (left, whole) {
var filters = left.split(",");
var prefilters = filters.slice(0, filters.length - 1);
var prefilter = !prefilters.length ? "" : prefilters.join(",") + ",";
var subfilters = info.filter((s) => prefilters.every((p) => s.name != p));
var allSuggestions = subfilters.map((s) => [prefilter + s.name, s.description]);
var completions = left
? allSuggestions.filter((s) => s[0].indexOf(left) == 0)
: allSuggestions;
return {
collection : completions,
origin : 0,
query : left,
flags : [0, 0],
header : ['Name', 'Description'],
width : [30, 70],
};
},
callback : (str) => MultiRequester.cmdAction(str)
});
};
if (typeof arg === 'string') {
if (MultiRequester.cmdAction(arg.replace(/^\s+|\s+$/g, "")) == false)
reader(arg);
} else {
reader('');
}
},
'Multi request'
);
}, PLUGIN_INFO);
});
// main controller {{{
var MultiRequester = {
description: "request, and display to the buffer",
defaultSites: 'alc',
doProcess: false,
requestNames: "",
requestCount: 0,
echoHash: {},
cmdAction: function(args) { //{{{
var self = this;
args = args.split(' ');
if (MultiRequester.doProcess) return undefined;
var parsedArgs = this.parseArgs(args);
if (parsedArgs.count == 0) { return false; } // do nothing
if (parsedArgs.strs.length === 0 ||
(parsedArgs.strs.length === 1 && parsedArgs.strs[0].length === 0))
{ return false; } // do nothing
MultiRequester.doProcess = true;
MultiRequester.requestNames = parsedArgs.names;
MultiRequester.requestCount = 0;
MultiRequester.echoHash = {};
var siteinfo = parsedArgs.siteinfo;
for (let i = 0, len = parsedArgs.count; i < len; i++) {
let info = siteinfo[i];
let name = info.name;
let url = info.url;
// see: http://fifnel.com/2008/11/14/1980/
let srcEncode = info.srcEncode || "UTF-8";
let urlEncode = info.urlEncode || srcEncode;
let m = url.match(/%s/g);
let repStrCount = (m && m.length);
if (repStrCount && !parsedArgs.strs.length) continue;
// via. lookupDictionary.js
let ttbu = Components.classes["@mozilla.org/intl/texttosuburi;1"]
.getService(Components.interfaces.nsITextToSubURI);
let cnt = 0;
let strs = _.clone(parsedArgs.strs);
url = url.replace(/%s/g, (m, i) => ttbu.ConvertAndEscape(urlEncode,
(cnt >= strs.length ? strs[cnt - 1] :
cnt >= (repStrCount - 1) ? strs.splice(cnt).join(' ') :
strs[cnt++])));
util.message(url + "[" + srcEncode + "][" + urlEncode + "]::" + info.xpath);
var opt = {
mimeType: 'text/html; charset=' + srcEncode,
callback: function(xhr){
self.onSuccess(xhr, url, info);
},
header: {
Referer: url.split('?')[0],
},
};
util.requestGet(url, opt);
MultiRequester.requestCount++;
}
if (MultiRequester.requestCount) {
display.echoStatusBar("Loading " + parsedArgs.names + " ...");
} else {
MultiRequester.doProcess = false;
}
return true;
},
// return {names: "", strs: [""], count: 0, siteinfo: [{}]}
parseArgs: function(args) {
var self = this;
var ret = {};
ret.names = "";
ret.strs = [];
ret.count = 0;
var sel = (document.commandDispatcher.focusedWindow || gBrowser.contentWindow).getSelection().toString();
if (args.length < 1 && !sel.length) return ret;
function parse(args, names) {
args = Array.concat(args);
ret.siteinfo = [];
ret.names = names || args.shift() || "";
ret.strs = (args.length < 1 ? [ sel.replace(/[\n\r]+/g, "") ] : args);
ret.names.split(",").forEach(function(name) {
var site = self.getSite(name);
if (site) {
ret.count++;
ret.siteinfo.push(site);
}
});
}
parse(args);
if (!ret.siteinfo.length && this.defaultSites)
parse(args, this.defaultSites);
return ret;
},
getSite: function(name) {
if (!name) this.siteinfo[0];
var ret = null;
this.siteinfo.forEach(function(s) {
if (s.name == name) ret = s;
});
return ret;
},//}}}
extractLink: function(res, baseUrl, info, extractLink) { //{{{
var self = this;
var el = getHTMLDocument(res.responseText, extractLink);
if (!el) throw "extract link failed.: extractLink -> " + extractLink;
var url = pathToURL(el[0], baseUrl);
util.requestGet(url, {
callback: function(xhr){
let temp = {};
_.extend(temp, info);
temp.extractLink = false;
self.onSuccess(xhr, url, temp);
}
});
MultiRequester.requestCount++;
MultiRequester.doProcess = true;
},//}}}
onSuccess: function(res, url, siteinfo) { //{{{
if (!MultiRequester.doProcess) {
MultiRequester.requestCount = 0;
return;
}
display.echoStatusBar("success!!: " + url, 3000);
MultiRequester.requestCount--;
if (MultiRequester.requestCount == 0) {
MultiRequester.doProcess = false;
}
var url, escapedUrl, xpath, doc, source, extractLink, ignoreTags;
try {
if (res.responseText == "") throw "response is fail or null";
escapedUrl = url.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
xpath = siteinfo.xpath;
extractLink = siteinfo.extractLink;
if (extractLink) {
this.extractLink(res, url, siteinfo, extractLink);
return;
}
ignoreTags = [ "script" ].concat((siteinfo.ignoreTags) ? siteinfo.ignoreTags.split(',') : []);
doc = document.createElementNS(null, "div");
getHTMLDocument(res.responseText, xpath, null, ignoreTags, function(node, i) {
if (!node.tagName || node.tagName.toLowerCase() != "html") {
doc.appendChild(node);
}
}).doc;
if (!doc || !doc.childNodes.length) throw "XPath result is undefined or null.: XPath -> " + xpath;
source = '<div class="mr_site" style="white-space:normal;"><base href="' + escapedUrl + '"/>' +
'<a href="' + escapedUrl + '" class="mr_title" target="_blank">' + siteinfo.description + '</a>' +
'<div class="mr_detail">' +
xmlSerialize(doc) +
'</div></div>';
MultiRequester.echoHash[siteinfo.name] = source;
} catch (e) {
display.echoStatusBar(e, 3000);
MultiRequester.echoHash[siteinfo.name] =
'<div class="mr_site" style="white-space:normal;"><base href="' + escapedUrl + '"/>' +
'<a href="' + escapedUrl + '" class="mr_title" target="_blank">' + siteinfo.description + '</a>' +
'<div class="mr_detail">' +
'<span style="color: red;">error!!: ' + e + '</span>' +
'</div></div>';
}
if (MultiRequester.requestCount == 0) {
let echoList = [];
MultiRequester.requestNames.split(",").forEach(function(name) {
echoList.push(MultiRequester.echoHash[name]);
});
source = echoList.join("");
try {
display.echo.html(source);
Array.slice(display.echo.document.querySelectorAll("a,img")).forEach(function(node){
let tagName = node.nodeName.toLowerCase();
if (tagName == "a") {
node.href = pathToURL(node, url, display.echo.document);
node.addEventListener('click', function(e){ openUILinkIn(e.target.href, "tab"); e.preventDefault(); }, false);
} else if (tagName == "img") {
node.src = pathToURL(node, url, display.echo.document);
}
});
if (!display.echo.document.getElementById('ml_style')) {
let css = display.echo.document.createElement('style');
css.type = "text/css";
css.id = 'ml_style';
css.innerHTML = pOptions.style;
display.echo.document.getElementsByTagName('head')[0].appendChild(css);
}
display.echo.open();
display.echo.document.defaultView.scrollTo(0,0);
let onblur = function(e){
display.echo.document.defaultView.removeEventListener('blur', onblur, false);
display.echo.close();
};
display.echo.document.defaultView.addEventListener('blur', onblur, false);
} catch (e) {
display.echoStatusBar(e, 3000);
util.message(source);
}
}
},
onFailure: function(res) {
MultiRequester.doProcess = false;
util.message("request failure!!: " + res.statusText);
},
onException: function(e) {
MultiRequester.doProcess = false;
util.message("exception!!: " + e);
}
//}}}
//}}}
};
// via https://github.com/azu/KeySnail-Plugins/raw/master/JSReference/js-referrence.ks.js
function createHTMLDocument(source) {
var processor = new XSLTProcessor();
var sheet = new DOMParser().parseFromString(
'<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">' +
'<xsl:output method="html"/>' +
'<xsl:template match="/">' +
'<html><head><title></title></head><body></body></html>' +
'</xsl:template>' +
'</xsl:stylesheet>',
'application/xml'
);
processor.importStylesheet(sheet);
var doc = processor.transformToDocument(sheet);
var range = doc.createRange();
range.selectNodeContents(doc.documentElement);
range.deleteContents();
doc.documentElement.appendChild(range.createContextualFragment(source));
return doc;
}
function getHTMLDocument(text, xpath, xmlns, ignoreTags, callback, thisObj) {
let doc = createHTMLDocument(text, xmlns);
if (!xpath) xpath = '//*';
return getNodesFromXPath(xpath, doc, callback, thisObj);
}
function getNodesFromXPath(xpath, context, callback, thisObj) {
var ret = [];
if (!xpath) return ret;
context = context || window.content.document;
var nodesSnapshot = (context.ownerDocument || context).evaluate(xpath, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0, l = nodesSnapshot.snapshotLength; i < l; i++) {
if (typeof callback == 'function') callback.call(thisObj, nodesSnapshot.snapshotItem(i), i);
ret.push(nodesSnapshot.snapshotItem(i));
}
return ret;
}
function pathToURL(a, baseURL, doc) {
if (!a) return '';
var XHTML_NS = "http://www.w3.org/1999/xhtml";
var XML_NS = "http://www.w3.org/XML/1998/namespace";
//var path = (a.href || a.getAttribute('src') || a.action || a.value || a);
var path = (a.getAttribute('href') || a.getAttribute('src') || a.action || a.value || a);
if (/^https?:\/\//.test(path)) return path;
var link = (doc || window.content.documtent).createElementNS(XHTML_NS, 'a');
link.setAttributeNS(XML_NS, 'xml:base', baseURL);
link.href = path;
return link.href;
}
function xmlSerialize(xml) {
try {
return (new XMLSerializer()).serializeToString(xml)
.replace(/<!--(?:[^-]|-(?!->))*-->/g, '')
.replace(/<\s*\/?\s*\w+/g, (all) => all.toLowerCase());
} catch (e) { return '' }
}
//}}}
// vim: fenc=utf-8 sw=2 sts=2:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment