]> gitweb @ CieloNegro.org - sugar.git/blob - dot-files/_vimperator/plugin/multi_requester_js
90ca321e355ceac175fd4c6f4424318714e41ecc
[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 repStrCount = let (m = url.match(/%s/g)) (m && m.length);
308       if (repStrCount && !parsedArgs.strs.length) continue;
309
310       // via. lookupDictionary.js
311       let ttbu = Components.classes["@mozilla.org/intl/texttosuburi;1"]
312                  .getService(Components.interfaces.nsITextToSubURI);
313
314       let cnt = 0;
315       url = url.replace(/%s/g, function(m, i) ttbu.ConvertAndEscape(urlEncode,
316             (cnt >= parsedArgs.strs.length ? parsedArgs.strs[cnt - 1] :
317              cnt >= (repStrCount - 1) ? parsedArgs.strs.splice(cnt).join(' ') :
318              parsedArgs.strs[cnt++])));
319       logger.log(url + "[" + srcEncode + "][" + urlEncode + "]::" + info.xpath);
320
321       if (bang) {
322         liberator.open(url, liberator.NEW_TAB);
323       } else {
324         let req = new libly.Request(url, null, {
325           encoding: srcEncode,
326           siteinfo: info,
327           args: {
328             args: args,
329             bang: bang,
330             count: count
331           }
332         });
333         req.addEventListener("exception", $U.bind(this, this.onException));
334         req.addEventListener("success", $U.bind(this, this.onSuccess));
335         req.addEventListener("failure", $U.bind(this, this.onFailure));
336         req.get();
337         MultiRequester.requestCount++;
338       }
339     }
340
341     if (MultiRequester.requestCount) {
342       logger.echo("Loading " + parsedArgs.names + " ...", commandline.FORCE_SINGLELINE);
343     } else {
344       MultiRequester.doProcess = false;
345     }
346   },
347   // return {names: "", strs: [""], count: 0, siteinfo: [{}]}
348   parseArgs: function(args) {
349
350     var self = this;
351     var ret = {};
352     ret.names = "";
353     ret.strs = [];
354     ret.count = 0;
355     var sel = $U.getSelectedString();
356
357     if (args.length < 1 && !sel.length) return ret;
358
359     function parse(args, names) {
360       args = Array.concat(args);
361       ret.siteinfo = [];
362       ret.names = names || args.shift() || "";
363       ret.strs = (args.length < 1 ? [ sel.replace(/[\n\r]+/g, "") ] : args);
364
365       ret.names.split(",").forEach(function(name) {
366         var site = self.getSite(name);
367         if (site) {
368           ret.count++;
369           ret.siteinfo.push(site);
370         }
371       });
372     }
373
374     parse(args);
375
376     if (!ret.siteinfo.length && this.defaultSites)
377       parse(args, this.defaultSites);
378
379     return ret;
380   },
381   getSite: function(name) {
382     if (!name) this.siteinfo[0];
383     var ret = null;
384     this.siteinfo.forEach(function(s) {
385       if (s.name == name) ret = s;
386     });
387     return ret;
388   },//}}}
389   extractLink: function(res, extractLink) { //{{{
390
391     var el = res.getHTMLDocument(extractLink);
392     if (!el) throw "extract link failed.: extractLink -> " + extractLink;
393     var url = $U.pathToURL(el[0], res.req.url);
394     var req = new libly.Request(url, null, $U.extend(res.req.options, { extractLink: true }));
395     req.addEventListener("exception", $U.bind(this, this.onException));
396     req.addEventListener("success", $U.bind(this, this.onSuccess));
397     req.addEventListener("failure", $U.bind(this, this.onFailure));
398     req.get();
399     MultiRequester.requestCount++;
400     MultiRequester.doProcess = true;
401
402   },//}}}
403   onSuccess: function(res) { //{{{
404
405     if (!MultiRequester.doProcess) {
406       MultiRequester.requestCount = 0;
407       return;
408     }
409
410     logger.log("success!!: " + res.req.url);
411     MultiRequester.requestCount--;
412     if (MultiRequester.requestCount == 0) {
413       MultiRequester.doProcess = false;
414     }
415
416     var url, escapedUrl, xpath, doc, html, extractLink, ignoreTags;
417
418     try {
419
420       if (!res.isSuccess() || res.responseText == "") throw "response is fail or null";
421
422       url = res.req.url;
423       escapedUrl = util.escapeHTML(url);
424       xpath = res.req.options.siteinfo.xpath;
425       extractLink = res.req.options.siteinfo.extractLink;
426
427       if (extractLink && !res.req.options.extractLink) {
428         this.extractLink(res, extractLink);
429         return;
430       }
431       ignoreTags = [ "script" ].concat(libly.$U.A(res.req.options.siteinfo.ignoreTags));
432       doc = document.createElementNS(null, "div");
433       res.getHTMLDocument(xpath, null, ignoreTags, function(node, i) {
434         if (node.tagName.toLowerCase() != "html")
435           doc.appendChild(node);
436       });
437       if (!doc || !doc.childNodes.length) throw "XPath result is undefined or null.: XPath -> " + xpath;
438
439       $U.getNodesFromXPath("descendant-or-self::a | descendant-or-self::img", doc, function(node) {
440         var tagName = node.tagName.toLowerCase();
441         if (tagName == "a") {
442           node.href = $U.pathToURL(node, url, res.doc);
443         } else if (tagName == "img") {
444           node.src = $U.pathToURL(node, url, res.doc);
445         }
446       });
447
448       html = '<a href="' + escapedUrl + '" class="hl-Title" target="_self">' + escapedUrl + '</a>' +
449            $U.xmlSerialize(doc);
450
451       MultiRequester.echoHash[res.req.options.siteinfo.name] = html;
452
453     } catch (e) {
454       logger.log("error!!: " + e);
455       MultiRequester.echoHash[res.req.options.siteinfo.name] =
456               '<span style="color: red;">error!!: ' + e + '</span>';
457     }
458
459     if (MultiRequester.requestCount == 0) {
460       let echoList = [];
461       MultiRequester.requestNames.split(",").forEach(function(name) {
462         echoList.push(MultiRequester.echoHash[name]);
463       });
464       html = '<div style="white-space:normal;"><base href="' + escapedUrl + '"/>' +
465              echoList.join("") +
466              '</div>';
467       try { logger.echo(new XMLList(html)); } catch (e) { logger.log(e); logger.echo(html); }
468     }
469
470   },
471   onFailure: function(res) {
472     MultiRequester.doProcess = false;
473     logger.echoerr("request failure!!: " + res.statusText);
474   },
475   onException: function(e) {
476     MultiRequester.doProcess = false;
477     logger.echoerr("exception!!: " + e);
478   }//}}}
479 };
480 //}}}
481
482 // boot strap {{{
483 CommandRegister.register(MultiRequester, DataAccess.getSiteInfo());
484 if (liberator.globalVariables.multi_requester_mappings) {
485   CommandRegister.addUserMaps(MultiRequester.name[0], liberator.globalVariables.multi_requester_mappings);
486 }
487 //}}}
488
489 return MultiRequester;
490
491 })();
492 // vim: set fdm=marker sw=2 ts=2 sts=0 et:
493