root/lang/javascript/vimperator-plugins/branches/2.1/relatedBlogSearch.js

Revision 29688, 8.3 kB (checked in by teramako, 19 months ago)

new add relatedBlogSearch.js

Line 
1let PLUGIN_INFO =
2<VimperatorPlugin>
3<name>{NAME}</name>
4<description>Show/Open related blog</description>
5<description lang="ja">現在ページへリンクしているブログを表示または開くプラグイン</description>
6<author email="teramako@gmail.com" homepage="http://vimperator.g.hatena.ne.jp/teramako/">teramako</author>
7<version>1.0</version>
8<license>MPL 1.1/GPL 2.0/LGPL 2.1</license>
9<minVersion>2.0pre</minVersion>
10<maxVersion>2.0</maxVersion>
11<updateURL>http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/relatedBlogSearch.js</updateURL>
12<detail lang="ja"><![CDATA[
13現在開いているページへリンクしているブログを探しだし、表示または開くことを目的とするプラグイン
14
15== Command ==
16
17:relatedblog:
18  現在ページにリンクしているブログをリストします。
19:relatedblog -q[uery] {searchTerm}:
20  {searchTerm} で検索してブログをりすとします。
21:relatedblog {URL}:
22  {URL} を開きます。
23  補完から取ってくると良いと思います。
24
25>||
26tab relatedblog {URL}
27||<
28とすることで新規タブに開けます。
29また、リストを表示した後、";o" などのヒントからも開けるでしょう。(historyコマンドに似ている)
30
31== 変数 ==
32let コマンドで設定してください(しない場合はデフォルト値が使用されます)
33
34:g:blogSearchHeaderTemplate:
35  リストを表示する時のヘッダとなるXML文字列
36  省略時は以下が使用されます。
37  >||
38  '<table><tr><th>Title</th><th>Author</th><th>Content</th></tr></table>';
39  ||<
40:g:blogSearchBodyTemplate:
41  リストを表示する時のコンテンツとなるXML文字列
42  省略時は以下が使用されます。
43  >||
44  '<tr><td><a highlight="URL" href="%link%">%title%</a></td><td>%author%</td><td>%content%</td></tr>';
45  ||<
46
47また、%で囲まれた特定の文字列が変数として使用されます。
48%link%:
49  対象ページのURL
50%title%:
51  対象ページのタイトル
52%author%:
53  対象ページの著者
54%content%:
55  対象ページの内容(一部)
56%rootURI%:
57  対象ページのトップページのURL
58%id%:
59  不明(ドメイン、日付、パスが含まれる)
60%published%:
61  投稿された日時(%Y-%m-%dT%H:%M:%SZ)
62%updated%:
63  更新された日時(%Y-%m-%dT%H:%M:%SZ)
64]]></detail>
65</VimperatorPlugin>;
66
67liberator.plugins.relatedBlogSearch = (function(){
68const LANG = window.navigator.language;
69const BLOGSEARCH_URL = 'http://blogsearch.google.com/blogsearch_feeds?output=atom&hl=' + LANG + '&q=';
70
71/**
72 * Serach with Google blogsearch and get entries
73 * @param {String} name query string
74 */
75function BlogSearch() { this._init.apply(this, arguments); }
76BlogSearch.prototype = {
77  _init: function(name){
78    if (!name)
79      throw Components.results.NS_ERROR_XPC_NOT_ENOUGH_ARGS;
80
81    this.date = new Date();
82    this.name = name;
83    this.query = BLOGSEARCH_URL + this.name;
84    let self = this;
85    this.items = Array.map(
86      util.httpGet(this.query).responseXML.getElementsByTagName("entry"),
87      function(entry) self._parse(entry)
88    );
89  },
90  /**
91   * @param {Element} entryElm Atom entry element
92   * @return {Object}
93   */
94  _parse: function(entryElm){
95    let entry = {};
96    Array.forEach(entryElm.childNodes, function(elm){
97      let tagName = elm.localName;
98      switch(tagName){
99        case 'link':
100          entry.link = elm.getAttribute('href'); break;
101        case 'author':
102          entry.author = elm.childNodes[0].textContent;
103          entry.rootURI = elm.childNodes[1].textContent;
104          break;
105        default:
106          entry[tagName] = elm.textContent;
107      }
108    });
109    return entry;
110  },
111  /**
112   * @param {RegExp} reg
113   * @param {String[]} itemNames item's property names
114   * @return {Object[]} item list
115   */
116  search: function(reg, itemNames){
117    if (!itemNames) itemNames = [name for (name in this.items[0])];
118    return this.items.filter(function(item) itemNames.some(function(name) reg.test(item[name])));
119  },
120  /**
121   * @param {String} template
122   * @param {XMLList} xml header XML
123   * @param {Object[]} items if omitted, used all items
124   * template e.g.)
125   *  '<tr><td><a href="link">%title%</a></td><td>%author%</td><td>%content%</td></tr>'
126   */
127  toXMLByTemplate: function(template, xml, items){
128    if (!items) items = this.items;
129
130    function entryToXML(item){
131      function replacer(all, name) name in item ? item[name] : all;
132      return new XMLList(template.replace(/%(\w+?)%/g, replacer).replace(/&/g,"&amp;"));
133    }
134    items.forEach(function(item){xml.* += entryToXML(item);});
135    return xml;
136  },
137};
138
139/**
140 * -query option completion list
141 * e.g.)
142 * return [
143 *  ["link:https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects", "-"],
144 *  ["link:https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference", "-"],
145 *  ["link:https://developer.mozilla.org/en", "-"],
146 *  ["link:https://developer.mozilla.org", "-"],
147 * ]
148 * when current url is "https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects"
149 * @return {String[][]}
150 */
151function getQueryList(){
152  return buffer.URL.split(/\/(?!\/)/).reduce(function(p,c){
153    p.length == 0 ? p.push(c) : p.push(p[p.length-1]+"/"+c);
154    return p;
155  },[]).splice(1).map(function(url) ["link:" + url, "-"]).reverse();
156}
157
158// ----------------------------------------------
159// command
160// ----------------------------------------------
161commands.addUserCommand(["relatedblog"], "search linked blog from google blogsearch",
162function(args){
163  let query = args["-query"] ? args["-query"] : "link:" + buffer.URL;
164  let entry = manager.getCache(query);
165  let header = new XMLList(manager.headerTemplate);
166  if (args.length == 0){
167    let xml  = entry.toXMLByTemplate(manager.bodyTemplate, header);
168    liberator.echo(xml, true);
169  } else {
170    let reg = new RegExp(args[0], "i");
171    let items = entry.search(reg);
172    if (items.length > 1){
173      liberator.echo(entry.toXMLByTemplate(manager.bodyTemplate, header, items),true);
174    } else if (items.length == 1){
175      let url = items[0].link;
176      liberator.open(url);
177    } else {
178      liberator.echomsg("related blog is none",5);
179    }
180  }
181},{
182  options: [
183    [["-query", "-q"], commands.OPTION_STRING, null, getQueryList]
184  ],
185  argCount: "?",
186  completer: function(context, args){
187    manager.completer(context, args);
188  },
189},true);
190
191// ----------------------------------------------
192// public
193// ----------------------------------------------
194var manager = {
195  get bodyTemplate(){
196    return liberator.globalVariables.blogSearchBodyTemplate ?
197      liberator.globalVariables.blogSearchBodyTemplate :
198      '<tr><td><a highlight="URL" href="%link%">%title%</a></td><td>%author%</td><td>%content%</td></tr>';
199  },
200  get headerTemplate(){
201    return liberator.globalVariables.blogSearchHeaderTemplate ?
202      liberator.globalVariables.blogSearchHeaderTemplate :
203      '<table><tr><th>Title</th><th>Author</th><th>Content</th></tr></table>';
204  },
205  /**
206   * @type {BlogSerach[]}
207   */
208  cache: [],
209  /**
210   * キャッシュからエントリを取得
211   * キャッシュがないまたは期限切れの場合は新たに取得
212   * キャッシュのリストは10個まで XXX: 外だししたほうが良いかもしれない
213   * XXX: 有効期限を変数に持たせた方が良いかもしれない
214   * @param {String} name query string
215   * @return {BlogSearch}
216   */
217  getCache: function(name){
218    let cache = this.cache.filter(function(entry) entry.name == name);
219    if (cache.length == 0){
220      let entry = new BlogSearch(name);
221      if(this.cache.push(entry) > 10)
222        this.cache.shift();
223
224      return entry;
225    } else if (cache[0].date < Date.now() - 5*60*1000){
226      cache[0]._init(name);
227    }
228    return cache[0];
229  },
230  /**
231   * relatedblog コマンド用の補完関数
232   * @param {CompletionContext} context
233   * @param {String[]} args
234   */
235  completer: function(context, args){
236    let query = args["-query"] ? args["-query"] : "link:" + buffer.URL;
237    let entry = this.getCache(query)
238    context.title = ["URL", "Title"];
239    if (args.length == 0){
240      context.completions = entry.items.map(function(item) [item.link, item.title]);
241    } else {
242      let reg = new RegExp(context.filter, "i");
243      context.completions = entry.search(reg).map(function(item) [item.link, item.title]);
244    }
245  },
246};
247return manager;
248})();
249// vim: sw=2 ts=2 et:
Note: See TracBrowser for help on using the browser.