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