]> gitweb @ CieloNegro.org - sugar.git/blob - dot-files/_vimperator/plugin/multi_requester_js
Auto commit by The Sugar System.
[sugar.git] / dot-files / _vimperator / plugin / multi_requester_js
1 /*** BEGIN LICENSE BLOCK {{{
2   Copyright (c) 2008 suVene<suvene@zeromemory.info>
3
4   distributable under the terms of an MIT-style license.
5   http://www.opensource.jp/licenses/mit-license.html
6 }}}  END LICENSE BLOCK ***/
7 // PLUGIN_INFO//{{{
8 var INFO = xml`
9 <plugin name="multi_requester" version="0.4.16"
10         href="https://github.com/vimpr/vimperator-plugins/raw/master/multi_requester.js"
11         summary="request, and the result is displayed to the buffer."
12         xmlns="http://vimperator.org/namespaces/liberator">
13     <author href="http://zeromemory.sblo.jp/">suVene</author>
14     <license>MIT</license>
15     <project name="Vimperator" minVersion="3.6"/>
16     <p>
17       <code><![CDATA[
18 == Needs Library ==
19 - _libly.js(ver.0.1.19)
20   @see http://coderepos.org/share/browser/lang/javascript/vimperator-plugins/trunk/_libly.js
21
22 == Usage ==
23 >||
24 command[!] subcommand [ANY_TEXT]
25 ||<
26 - !        create new tab.
27 - ANY_TEXT     your input text
28
29 e.g.)
30 >||
31 :mr  alc[,goo,any1,any2…] ANY_TEXT       -> request by the input text, and display to the buffer.
32 :mr! goo[,any1,any2,…]  {window.selection} -> request by the selected text, and display to the new tab.
33 ||<
34
35 == Custumize .vimperatorrc ==
36 === Command(default [ mr ]) ===
37 >||
38 let g:multi_requester_command = "ANY1, ANY2, ……"
39 or
40 liberator.globalVariables.multi_requester_command = [ ANY1, ANY2, …… ];
41 ||<
42
43 === Default Sites (default undefined) ===
44 >||
45 liberator.globalVariables.multi_requester_default_sites = "alc,goo"
46 ||<
47 These sites(subcommands) will be used, if this variable has been defined and you do not specify subcommands.
48
49 === SITEINFO ===
50 e.g.)
51 >||
52 javascript <<EOM
53 liberator.globalVariables.multi_requester_siteinfo = [
54   {
55     map:            ",me",              // optional: keymap for this siteinfo call
56     bang:           true,               // optional:
57     args:           "any"               // optional:
58     name:           "ex",               // required: subcommand name
59     description:    "example",          // required: commandline short help
60     url:            "http://example.com/?%s",     // required: %s <-- replace string
61     xpath:          "//*",              // optional: default all
62     srcEncode:      "SHIFT_JIS",        // optional: default UTF-8
63     urlEncode:      "SHIFT_JIS",        // optional: default srcEncode
64     ignoreTags:     "img",              // optional: default script, syntax "tag1,tag2,……"
65     extractLink:    "//xpath"           // optional: extract permalink
66   },
67 ];
68 EOM
69 ||<
70
71 === other siteinfo by wedata. ===
72   @see http://wedata.net/databases/Multi%20Requester/items
73
74 === Mappings ===
75 e.g.)
76 >||
77 javascript <<EOM
78 liberator.globalVariables.multi_requester_mappings = [
79   [ ",ml", "ex" ],              // == :mr  ex
80   [ ",mg", "goo", "!" ],        // == :mr! goo
81   [ ",ma", "alc",  , "args" ],  // == :mr  alc args
82 ];
83 EOM
84 ||<
85
86 === Other Options ===
87 >||
88 let g:multi_requester_use_wedata = "false"       // true by default
89 ||<
90 wedata を利用しない場合は false を設定してください。
91 >||
92 let g:multi_requester_default_sites = 'alc';
93 ||<
94 subcommand を省略した場合に利用されるサイトを設定します。
95 >||
96 let g:multi_requester_order = 'count'; // date by default
97 ||<
98 補完の順番を設定します。(大きい順に並びます)
99 "count" または "date" を設定してください。
100       ]]></code>
101     </p>
102 </plugin>`;
103 //}}}
104 (function() {
105 if (!liberator.plugins.libly) {
106   liberator.log("multi_requester: needs _libly.js");
107   return;
108 }
109
110 // global variables {{{
111 var DEFAULT_COMMAND = [ "mr" ];
112 var SITEINFO = [
113   {
114     name: "alc",
115     description: "SPACE ALC (\u82F1\u8F9E\u6717 on the Web)",
116     url: "http://eow.alc.co.jp/%s/UTF-8/",
117     xpath: 'id("resultsList")'
118   }
119 ];
120 var libly = liberator.plugins.libly;
121 var $U = libly.$U;
122 var logger = $U.getLogger("multi_requester");
123 var mergedSiteinfo = {};
124 var store = storage.newMap('plugins-multi_requester', {store: true});
125 //}}}
126
127 // Vimperator plugin command register {{{
128 var CommandRegister = {
129   register: function(cmdClass, siteinfo) {
130     cmdClass.siteinfo = siteinfo;
131
132     commands.addUserCommand(
133       cmdClass.name,
134       cmdClass.description,
135       $U.bind(cmdClass, cmdClass.cmdAction),
136       {
137         completer: cmdClass.cmdCompleter || function(context, arg) {
138           if (arg.length > 1)
139             return;
140           context.title = [ "Name", "Descprition" ];
141           var sorted = siteinfo.sort(function(a, b)
142                          typeof liberator.globalVariables.multi_requester_order == "undefined" ||
143                          liberator.globalVariables.multi_requester_order == "date" ? store.get(b.name).lastPostTime - store.get(a.name).lastPostTime :
144                          liberator.globalVariables.multi_requester_order == "count" ? store.get(b.name).count - store.get(a.name).count :
145                          store.get(b.name).lastPostTime - store.get(a.name).lastPostTime);
146           var filters = context.filter.split(",");
147           var prefilters = filters.slice(0, filters.length - 1);
148           var prefilter = !prefilters.length ? "" : prefilters.join(",") + ",";
149           var subfilters = sorted.filter(function(s) prefilters.every(function(p) s.name != p));
150           var allSuggestions = subfilters.map(function(s) [prefilter + s.name, s.description]);
151           context.completions = context.filter
152             ? allSuggestions.filter(function(s) s[0].indexOf(context.filter) == 0)
153             : allSuggestions;
154         },
155         options: cmdClass.cmdOptions,
156         argCount: cmdClass.argCount || undefined,
157         bang: cmdClass.bang || true,
158         count: cmdClass.count || false
159       },
160       true // replace
161     );
162
163   },
164   addUserMaps: function(prefix, mapdef) {
165     mapdef.forEach(function([ key, command, bang, args ]) {
166       var cmd = prefix + (bang ? "! " : " ") + command + " ";
167       mappings.addUserMap(
168         [ modes.NORMAL, modes.VISUAL ],
169         [ key ],
170         "user defined mapping",
171         function() {
172           if (args) {
173             liberator.execute(cmd + args);
174           } else {
175             let sel = $U.getSelectedString();
176             if (sel.length) {
177               liberator.execute(cmd + sel);
178             } else {
179               commandline.open(":", cmd, modes.EX);
180             }
181           }
182         },
183         {
184           rhs: ":" + cmd,
185           norremap: true
186         }
187       );
188     });
189   }
190 };
191 //}}}
192
193 // initial data access class {{{
194 var DataAccess = {
195   getCommand: function() {
196     var c = liberator.globalVariables.multi_requester_command;
197     var ret;
198     if (typeof c == "string") {
199       ret = [ c ];
200     } else if (typeof c == "Array") {
201       ret = check;
202     } else {
203       ret = DEFAULT_COMMAND;
204     }
205     return ret;
206   },
207   getSiteInfo: function() {
208
209     var self = this;
210     var useWedata = typeof liberator.globalVariables.multi_requester_use_wedata == "undefined" ?
211                     true : $U.eval(liberator.globalVariables.multi_requester_use_wedata);
212
213     if (liberator.globalVariables.multi_requester_siteinfo) {
214       liberator.globalVariables.multi_requester_siteinfo.forEach(function(site) {
215         if (!mergedSiteinfo[site.name]) mergedSiteinfo[site.name] = {};
216         $U.extend(mergedSiteinfo[site.name], site);
217         if (!store.get(site.name)) {
218             store.set(site.name, { count: 0, lastPostTime: (new Date()).getTime() });
219             store.save();
220         }
221         if (site.map) {
222           CommandRegister.addUserMaps(MultiRequester.name[0],
223             [[ site.map, site.name, site.bang, site.args ]]);
224         }
225       });
226     }
227
228     SITEINFO.forEach(function(site) {
229       if (!mergedSiteinfo[site.name]) mergedSiteinfo[site.name] = {};
230       $U.extend(mergedSiteinfo[site.name], site);
231       if (!store.get(site.name)) {
232         store.set(site.name, { count: 0, lastPostTime: (new Date()).getTime() });
233         store.save();
234       }
235       if (site.map) {
236         CommandRegister.addUserMaps(MultiRequester.name[0],
237           [[ site.map, site.name, site.bang, site.args ]]);
238       }
239     });
240
241     if (useWedata) {
242       logger.log("use wedata");
243       var wedata = new libly.Wedata("Multi Requester");
244       wedata.getItems(24 * 60 * 60 * 1000,
245         function(item) {
246           var site = item.data;
247           if (mergedSiteinfo[site.name]) return;
248           mergedSiteinfo[site.name] = {};
249           $U.extend(mergedSiteinfo[site.name], site);
250           if (!store.get(site.name)) {
251             store.set(site.name, { count: 0, lastPostTime: (new Date()).getTime() });
252             store.save();
253           }
254         },
255         function(isSuccess, data) {
256           if (!isSuccess) return;
257           CommandRegister.register(MultiRequester, $U.A(mergedSiteinfo));
258         }
259       );
260     }
261
262     return $U.A(mergedSiteinfo);
263   }
264 };
265 //}}}
266
267 // main controller {{{
268 var MultiRequester = {
269   name: DataAccess.getCommand(),
270   description: "request, and display to the buffer",
271   defaultSites: liberator.globalVariables.multi_requester_default_sites,
272   doProcess: false,
273   requestNames: "",
274   requestCount: 0,
275   echoHash: {},
276   cmdAction: function(args) { //{{{
277
278     if (MultiRequester.doProcess) return;
279
280     var bang = args.bang;
281     var count = args.count;
282
283     var parsedArgs = this.parseArgs(args);
284     if (parsedArgs.count == 0) { return; } // do nothing
285
286     MultiRequester.doProcess = true;
287     MultiRequester.requestNames = parsedArgs.names;
288     MultiRequester.requestCount = 0;
289     MultiRequester.echoHash = {};
290     var siteinfo = parsedArgs.siteinfo;
291     for (let i = 0, len = parsedArgs.count; i < len; i++) {
292
293       let info = siteinfo[i];
294       let name = info.name;
295
296       let history = store.get(name);
297       history.count++;
298       history.lastPostTime = (new Date()).getTime();
299       store.set(name, history);
300       store.save();
301
302       let url = info.url;
303       // see: http://fifnel.com/2008/11/14/1980/
304       let srcEncode = info.srcEncode || "UTF-8";
305       let urlEncode = info.urlEncode || srcEncode;
306
307       let m = url.match(/%s/g);
308       let repStrCount = m && m.length;
309       if (repStrCount && !parsedArgs.strs.length) continue;
310
311       // via. lookupDictionary.js
312       let ttbu = Components.classes["@mozilla.org/intl/texttosuburi;1"]
313                  .getService(Components.interfaces.nsITextToSubURI);
314
315       let cnt = 0;
316       url = url.replace(/%s/g, function(m, i) ttbu.ConvertAndEscape(urlEncode,
317             (cnt >= parsedArgs.strs.length ? parsedArgs.strs[cnt - 1] :
318              cnt >= (repStrCount - 1) ? parsedArgs.strs.splice(cnt).join(' ') :
319              parsedArgs.strs[cnt++])));
320       logger.log(url + "[" + srcEncode + "][" + urlEncode + "]::" + info.xpath);
321
322       if (bang) {
323         liberator.open(url, liberator.NEW_TAB);
324       } else {
325         let req = new libly.Request(url, null, {
326           encoding: srcEncode,
327           siteinfo: info,
328           args: {
329             args: args,
330             bang: bang,
331             count: count
332           }
333         });
334         req.addEventListener("exception", $U.bind(this, this.onException));
335         req.addEventListener("success", $U.bind(this, this.onSuccess));
336         req.addEventListener("failure", $U.bind(this, this.onFailure));
337         req.get();
338         MultiRequester.requestCount++;
339       }
340     }
341
342     if (MultiRequester.requestCount) {
343       logger.echo("Loading " + parsedArgs.names + " ...", commandline.FORCE_SINGLELINE);
344     } else {
345       MultiRequester.doProcess = false;
346     }
347   },
348   // return {names: "", strs: [""], count: 0, siteinfo: [{}]}
349   parseArgs: function(args) {
350
351     var self = this;
352     var ret = {};
353     ret.names = "";
354     ret.strs = [];
355     ret.count = 0;
356     var sel = $U.getSelectedString();
357
358     if (args.length < 1 && !sel.length) return ret;
359
360     function parse(args, names) {
361       args = Array.concat(args);
362       ret.siteinfo = [];
363       ret.names = names || args.shift() || "";
364       ret.strs = (args.length < 1 ? [ sel.replace(/[\n\r]+/g, "") ] : args);
365
366       ret.names.split(",").forEach(function(name) {
367         var site = self.getSite(name);
368         if (site) {
369           ret.count++;
370           ret.siteinfo.push(site);
371         }
372       });
373     }
374
375     parse(args);
376
377     if (!ret.siteinfo.length && this.defaultSites)
378       parse(args, this.defaultSites);
379
380     return ret;
381   },
382   getSite: function(name) {
383     if (!name) this.siteinfo[0];
384     var ret = null;
385     this.siteinfo.forEach(function(s) {
386       if (s.name == name) ret = s;
387     });
388     return ret;
389   },//}}}
390   extractLink: function(res, extractLink) { //{{{
391
392     var el = res.getHTMLDocument(extractLink);
393     if (!el) throw "extract link failed.: extractLink -> " + extractLink;
394     var url = $U.pathToURL(el[0], res.req.url);
395     var req = new libly.Request(url, null, $U.extend(res.req.options, { extractLink: true }));
396     req.addEventListener("exception", $U.bind(this, this.onException));
397     req.addEventListener("success", $U.bind(this, this.onSuccess));
398     req.addEventListener("failure", $U.bind(this, this.onFailure));
399     req.get();
400     MultiRequester.requestCount++;
401     MultiRequester.doProcess = true;
402
403   },//}}}
404   onSuccess: function(res) { //{{{
405
406     if (!MultiRequester.doProcess) {
407       MultiRequester.requestCount = 0;
408       return;
409     }
410
411     logger.log("success!!: " + res.req.url);
412     MultiRequester.requestCount--;
413     if (MultiRequester.requestCount == 0) {
414       MultiRequester.doProcess = false;
415     }
416
417     var url, escapedUrl, xpath, doc, html, extractLink, ignoreTags;
418
419     try {
420
421       if (!res.isSuccess() || res.responseText == "") throw "response is fail or null";
422
423       url = res.req.url;
424       escapedUrl = util.escapeHTML(url);
425       xpath = res.req.options.siteinfo.xpath;
426       extractLink = res.req.options.siteinfo.extractLink;
427
428       if (extractLink && !res.req.options.extractLink) {
429         this.extractLink(res, extractLink);
430         return;
431       }
432       ignoreTags = [ "script" ].concat(libly.$U.A(res.req.options.siteinfo.ignoreTags));
433       doc = document.createElementNS(null, "div");
434       res.getHTMLDocument(xpath, null, ignoreTags, function(node, i) {
435         if (node.tagName.toLowerCase() != "html")
436           doc.appendChild(node);
437       });
438       if (!doc || !doc.childNodes.length) throw "XPath result is undefined or null.: XPath -> " + xpath;
439
440       $U.getNodesFromXPath("descendant-or-self::a | descendant-or-self::img", doc, function(node) {
441         var tagName = node.tagName.toLowerCase();
442         if (tagName == "a") {
443           node.href = $U.pathToURL(node, url, res.doc);
444         } else if (tagName == "img") {
445           node.src = $U.pathToURL(node, url, res.doc);
446         }
447       });
448
449       html = '<a href="' + escapedUrl + '" class="hl-Title" target="_self">' + escapedUrl + '</a>' +
450            $U.xmlSerialize(doc);
451
452       MultiRequester.echoHash[res.req.options.siteinfo.name] = html;
453
454     } catch (e) {
455       logger.log("error!!: " + e);
456       MultiRequester.echoHash[res.req.options.siteinfo.name] =
457               '<span style="color: red;">error!!: ' + e + '</span>';
458     }
459
460     if (MultiRequester.requestCount == 0) {
461       let echoList = [];
462       MultiRequester.requestNames.split(",").forEach(function(name) {
463         echoList.push(MultiRequester.echoHash[name]);
464       });
465       html = '<div style="white-space:normal;"><base href="' + escapedUrl + '"/>' +
466              echoList.join("") +
467              '</div>';
468       try { logger.echo(new XMLList(html)); } catch (e) { logger.log(e); logger.echo(html); }
469     }
470
471   },
472   onFailure: function(res) {
473     MultiRequester.doProcess = false;
474     logger.echoerr("request failure!!: " + res.statusText);
475   },
476   onException: function(e) {
477     MultiRequester.doProcess = false;
478     logger.echoerr("exception!!: " + e);
479   }//}}}
480 };
481 //}}}
482
483 // boot strap {{{
484 CommandRegister.register(MultiRequester, DataAccess.getSiteInfo());
485 if (liberator.globalVariables.multi_requester_mappings) {
486   CommandRegister.addUserMaps(MultiRequester.name[0], liberator.globalVariables.multi_requester_mappings);
487 }
488 //}}}
489
490 return MultiRequester;
491
492 })();
493 // vim: set fdm=marker sw=2 ts=2 sts=0 et:
494