From 96c7cfce51237dc7b0843ed0c1da40e2895f919f Mon Sep 17 00:00:00 2001 From: teramako Date: Sat, 7 Feb 2009 17:36:56 +0000 Subject: new add relatedBlogSearch.js git-svn-id: http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk@29688 d0d07461-0603-4401-acd4-de1884942a52 --- relatedBlogSearch.js | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 249 insertions(+) create mode 100644 relatedBlogSearch.js (limited to 'relatedBlogSearch.js') diff --git a/relatedBlogSearch.js b/relatedBlogSearch.js new file mode 100644 index 0000000..d1b9fa6 --- /dev/null +++ b/relatedBlogSearch.js @@ -0,0 +1,249 @@ +let PLUGIN_INFO = + +{NAME} +Show/Open related blog +現在ページへリンクしているブログを表示または開くプラグイン +teramako +1.0 +MPL 1.1/GPL 2.0/LGPL 2.1 +2.0pre +2.0 +http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/relatedBlogSearch.js +|| +tab relatedblog {URL} +||< +とすることで新規タブに開けます。 +また、リストを表示した後、";o" などのヒントからも開けるでしょう。(historyコマンドに似ている) + +== 変数 == +let コマンドで設定してください(しない場合はデフォルト値が使用されます) + +:g:blogSearchHeaderTemplate: + リストを表示する時のヘッダとなるXML文字列 + 省略時は以下が使用されます。 + >|| + '
TitleAuthorContent
'; + ||< +:g:blogSearchBodyTemplate: + リストを表示する時のコンテンツとなるXML文字列 + 省略時は以下が使用されます。 + >|| + '%title%%author%%content%'; + ||< + +また、%で囲まれた特定の文字列が変数として使用されます。 +%link%: + 対象ページのURL +%title%: + 対象ページのタイトル +%author%: + 対象ページの著者 +%content%: + 対象ページの内容(一部) +%rootURI%: + 対象ページのトップページのURL +%id%: + 不明(ドメイン、日付、パスが含まれる) +%published%: + 投稿された日時(%Y-%m-%dT%H:%M:%SZ) +%updated%: + 更新された日時(%Y-%m-%dT%H:%M:%SZ) +]]>
+
; + +liberator.plugins.relatedBlogSearch = (function(){ +const LANG = window.navigator.language; +const BLOGSEARCH_URL = 'http://blogsearch.google.com/blogsearch_feeds?output=atom&hl=' + LANG + '&q='; + +/** + * Serach with Google blogsearch and get entries + * @param {String} name query string + */ +function BlogSearch() { this._init.apply(this, arguments); } +BlogSearch.prototype = { + _init: function(name){ + if (!name) + throw Components.results.NS_ERROR_XPC_NOT_ENOUGH_ARGS; + + this.date = new Date(); + this.name = name; + this.query = BLOGSEARCH_URL + this.name; + let self = this; + this.items = Array.map( + util.httpGet(this.query).responseXML.getElementsByTagName("entry"), + function(entry) self._parse(entry) + ); + }, + /** + * @param {Element} entryElm Atom entry element + * @return {Object} + */ + _parse: function(entryElm){ + let entry = {}; + Array.forEach(entryElm.childNodes, function(elm){ + let tagName = elm.localName; + switch(tagName){ + case 'link': + entry.link = elm.getAttribute('href'); break; + case 'author': + entry.author = elm.childNodes[0].textContent; + entry.rootURI = elm.childNodes[1].textContent; + break; + default: + entry[tagName] = elm.textContent; + } + }); + return entry; + }, + /** + * @param {RegExp} reg + * @param {String[]} itemNames item's property names + * @return {Object[]} item list + */ + search: function(reg, itemNames){ + if (!itemNames) itemNames = [name for (name in this.items[0])]; + return this.items.filter(function(item) itemNames.some(function(name) reg.test(item[name]))); + }, + /** + * @param {String} template + * @param {XMLList} xml header XML + * @param {Object[]} items if omitted, used all items + * template e.g.) + * '%title%%author%%content%' + */ + toXMLByTemplate: function(template, xml, items){ + if (!items) items = this.items; + + function entryToXML(item){ + function replacer(all, name) name in item ? item[name] : all; + return new XMLList(template.replace(/%(\w+?)%/g, replacer).replace(/&/g,"&")); + } + items.forEach(function(item){xml.* += entryToXML(item);}); + return xml; + }, +}; + +/** + * -query option completion list + * e.g.) + * return [ + * ["link:https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects", "-"], + * ["link:https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference", "-"], + * ["link:https://developer.mozilla.org/en", "-"], + * ["link:https://developer.mozilla.org", "-"], + * ] + * when current url is "https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects" + * @return {String[][]} + */ +function getQueryList(){ + return buffer.URL.split(/\/(?!\/)/).reduce(function(p,c){ + p.length == 0 ? p.push(c) : p.push(p[p.length-1]+"/"+c); + return p; + },[]).splice(1).map(function(url) ["link:" + url, "-"]).reverse(); +} + +// ---------------------------------------------- +// command +// ---------------------------------------------- +commands.addUserCommand(["relatedblog"], "search linked blog from google blogsearch", +function(args){ + let query = args["-query"] ? args["-query"] : "link:" + buffer.URL; + let entry = manager.getCache(query); + let header = new XMLList(manager.headerTemplate); + if (args.length == 0){ + let xml = entry.toXMLByTemplate(manager.bodyTemplate, header); + liberator.echo(xml, true); + } else { + let reg = new RegExp(args[0], "i"); + let items = entry.search(reg); + if (items.length > 1){ + liberator.echo(entry.toXMLByTemplate(manager.bodyTemplate, header, items),true); + } else if (items.length == 1){ + let url = items[0].link; + liberator.open(url); + } else { + liberator.echomsg("related blog is none",5); + } + } +},{ + options: [ + [["-query", "-q"], commands.OPTION_STRING, null, getQueryList] + ], + argCount: "?", + completer: function(context, args){ + manager.completer(context, args); + }, +},true); + +// ---------------------------------------------- +// public +// ---------------------------------------------- +var manager = { + get bodyTemplate(){ + return liberator.globalVariables.blogSearchBodyTemplate ? + liberator.globalVariables.blogSearchBodyTemplate : + '%title%%author%%content%'; + }, + get headerTemplate(){ + return liberator.globalVariables.blogSearchHeaderTemplate ? + liberator.globalVariables.blogSearchHeaderTemplate : + '
TitleAuthorContent
'; + }, + /** + * @type {BlogSerach[]} + */ + cache: [], + /** + * キャッシュからエントリを取得 + * キャッシュがないまたは期限切れの場合は新たに取得 + * キャッシュのリストは10個まで XXX: 外だししたほうが良いかもしれない + * XXX: 有効期限を変数に持たせた方が良いかもしれない + * @param {String} name query string + * @return {BlogSearch} + */ + getCache: function(name){ + let cache = this.cache.filter(function(entry) entry.name == name); + if (cache.length == 0){ + let entry = new BlogSearch(name); + if(this.cache.push(entry) > 10) + this.cache.shift(); + + return entry; + } else if (cache[0].date < Date.now() - 5*60*1000){ + cache[0]._init(name); + } + return cache[0]; + }, + /** + * relatedblog コマンド用の補完関数 + * @param {CompletionContext} context + * @param {String[]} args + */ + completer: function(context, args){ + let query = args["-query"] ? args["-query"] : "link:" + buffer.URL; + let entry = this.getCache(query) + context.title = ["URL", "Title"]; + if (args.length == 0){ + context.completions = entry.items.map(function(item) [item.link, item.title]); + } else { + let reg = new RegExp(context.filter, "i"); + context.completions = entry.search(reg).map(function(item) [item.link, item.title]); + } + }, +}; +return manager; +})(); +// vim: sw=2 ts=2 et: -- cgit v1.2.3