From 827d36be905b07e85608d0d5547796e514466f6d Mon Sep 17 00:00:00 2001 From: teramako Date: Sun, 4 May 2008 21:41:22 +0000 Subject: new add git-svn-id: http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk@11139 d0d07461-0603-4401-acd4-de1884942a52 --- sbmcommentsviewer.js | 508 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 sbmcommentsviewer.js (limited to 'sbmcommentsviewer.js') diff --git a/sbmcommentsviewer.js b/sbmcommentsviewer.js new file mode 100644 index 0000000..fe91579 --- /dev/null +++ b/sbmcommentsviewer.js @@ -0,0 +1,508 @@ +/** + * ==VimperatorPlugin== + * @name SBM Comments Viewer + * @description List show Social BookMark Comments + * @description-ja ソーシャル・ブックマーク・コメントを表示します + * @version 0.1a + * ==/VimperatorPlugin== + * + * Usage: + * + * viewSBMComments [url] [options] + * url : 省略時は現在のURL + * options: + * -f, -format : 出力時のフォーマット(`,'区切りのリスト) + * (default: id,timestamp,tags,comment) + * let g:def_sbm_format = ... で指定可能 + * -t, -type : 出力するSBMタイプ + * (default: hdl) + * let g:def_sbms = ... で指定可能 + * -c, -count : ブックマーク件数のみ出力 + * -b, -browser: バッファ・ウィンドウではなくブラウザに開く + * TODO:まだ出来てない + * + * 指定可能フォーマット: + * id, timpstamp, tags, comment + * + * SBMタイプ: + * h : hatena bookmark + * d : del.icio.us bookmark + * l : livedoor clip + * XXX:今後増やしていきたい + * + * 例: + * :viewSBMComments http://d.hatena.ne.jp/teramako/ -t hdl -f id,comment -c + * + * 備考: + * * 一度取得したものは(30分ほど)キャッシュに貯めてますので何度も見直すことが可能です。 + * * 粋なコマンド名募集中 + */ + +liberator.plugins.sbmCommentsViewer = (function(){ +/** + * SBMEntry Container {{{ + * @param {String} type + * @param {Number} count + * @param {Object} extra + * extra = { + * faviconURL, + * pageURL + * } + */ +function SBMContainer(type, count, extra){ //{{{ + this.type = type; + this.count = count || 0; + this.entries = []; + if (extra){ + this.faviconURL = extra.faviconURL || ''; + this.pageURL = extra.pageURL || ''; + } +} //}}} +SBMContainer.prototype = { //{{{ + add: function(id, timestamp, comment, tags, extra){ + this.entries.push(new SBMEntry( + id, timestamp, comment, tags, extra + )); + }, + toHTMLString: function(format, countOnly){ + var label = (this.faviconURL ? '' : '') + + manager.type[this.type] + ' ' + this.count + '(' + this.entries.length + ')' + + (this.pageURL ? ' ' + this.pageURL + '' : ''); + if (countOnly){ + return label; + } else { + var str = [ + '' + ]; + format.forEach(function(colum){ + var name = manager.format[colum] || '-'; + str.push(''); + }); + str.push(''); + this.entries.forEach(function(e){ + str.push(e.toHTMLString(format)); + }); + str.push('
' + label + '
' + name + '
'); + return str.join(''); + } + } +}; //}}} +// }}} +/** + * SBM Entry {{{ + * @param {String} id UserName + * @param {String|Date} timestamp + * @param {String} comment + * @param {String[]} tags + * @param {Object} extra + * extra = { + * userIcon + * link + * } + */ +function SBMEntry(id, timestamp, comment, tags, extra){ //{{{ + this.id = id || ''; + this.timeStamp = timestamp instanceof Date ? timestamp : null; + this.comment = comment || ''; + this.tags = tags || []; + if (extra){ + this.userIcon = extra.userIcon || null; + this.link = extra.link || null; + } +} //}}} +SBMEntry.prototype = { //{{{ + toHTMLString: function(format){ + // E4X で書く手もあるけど、liberator.echoを使って出力すると + // toString後に"\n"が
に変換されてしまうのでStringで + var str = ['']; + var self = this; + format.forEach(function(colum){ + switch(colum){ + case 'id': + str.push('' + (self.userIcon ? '' : '') + + self.id + ''); + break; + case 'timestamp': + str.push('' + self.formatDate() + ''); break; + case 'tags': + str.push('' + self.tags.join(',') + ''); break; + case 'comment': + str.push('' + self.comment + ''); break; + default: + str.push('-'); + } + }); + str.push(''); + return str.join(''); + }, + formatDate: function(){ + if (!this.timeStamp) return ''; + var [year,month,day,hour,min,sec] = [ + this.timeStamp.getFullYear(), + this.timeStamp.getMonth()+1, + this.timeStamp.getDate(), + this.timeStamp.getHours(), + this.timeStamp.getMinutes(), + this.timeStamp.getSeconds() + ]; + return [ + year, '/', + (month < 10 ? '0'+month : month), '/', + (day < 10 ? '0'+day : day), ' ', + (hour < 10 ? '0'+hour : hour), ':', + (min < 10 ? '0'+min : min), ':', + (sec < 10 ? '0'+sec : sec) + ].join(''); + } +}; //}}} +//}}} +/** + * openSBM {{{ + * @param {String} url + * @param {String} type + * @param {String[]} format + * @param {Boolean} countOnly + * @param {Boolean} openToBrowser + */ +function openSBM(url, type, format, countOnly, openToBrowser){ + var sbmLabel = manager.type[type]; + var sbmURL = SBM[sbmLabel].getURL(url); + var xhr = new XMLHttpRequest(); + xhr.open('GET', sbmURL, true); + xhr.onreadystatechange = function(){ + if (xhr.readyState == 4){ + if (xhr.status == 200){ + var sbmContainer = SBM[sbmLabel].parser.call(this, xhr); + if (!sbmContainer) return; + cacheManager.add(sbmContainer, url, type); + if (openToBrowser) + manager.open(sbmContainer.toHTMLString(format,false)); + else + liberator.echo(sbmContainer.toHTMLString(format,countOnly), true); + } else { + liberator.echoerr(sbmURL + ' ' + xhr.status, true); + } + } + } + xhr.send(null); +} //}}} +/** + * getURL と parser メソッドを供えること + * getURL は 取得先のURLを返すこと + * parser は SBMContainer オブジェクトを返すこと + */ +var SBM = { //{{{ + hatena: { //{{{ + getURL: function(url){ + var urlPrefix = 'http://b.hatena.ne.jp/entry/json/?url='; + return urlPrefix + encodeURIComponent(url.replace(/%23/g,'#')); + }, + parser: function(xhr){ + var json = window.eval(xhr.responseText); + var count = json.bookmarks.length; + var c = new SBMContainer('h', json.count, { + faviconURL:'http://b.hatena.ne.jp/favicon.ico', + pageURL: 'http://b.hatena.ne.jp/entry/' + json.url + }); + json.bookmarks.forEach(function(bm){ + c.add(bm.user, new Date(bm.timestamp), bm.comment, bm.tags, { + userIcon: 'http://www.hatena.ne.jp/users/' + bm.user.substring(0,2) + '/' + bm.user +'/profile_s.gif' + }); + }); + return c; + } + }, //}}} + delicious: { //{{{ + getURL: function(url){ + //var urlPrefix = 'http://del.icio.us/rss/url/'; + var urlPrefix = 'http://feeds.delicious.com/rss/url/'; + return urlPrefix + getMD5Hash(url); + }, + parser: function(xhr){ + var rss = xhr.responseXML; + if (!rss){ + liberator.echoerr('del.icio.us feed is none',true); + return; + } + try { + var pageURL = evaluateXPath(rss, '//rss:channel/rss:link')[0].textContent; + var items = evaluateXPath(rss, '//rss:item'); + } catch(e){ + liberator.log(e); + } + var c = new SBMContainer('d', items.length, { + faviconURL: 'http://del.icio.us/favicon.ico', + pageURL: pageURL + }); + items.forEach(function(item){ + var children = item.childNodes; + var [id,date,tags,comment,link] = ['','',[],'','']; + for (var i=0; i/g,'>') : '', + clip.tags, + { + userIcon: 'http://image.clip.livedoor.com/profile/' + + '?viewer_id=[%%20member.livedoor_id%20Z%]&target_id=' + + clip.livedoor_id, + link: 'http://clip.livedoor.com/clips/' + clip.livedoor_id + } + ); + }); + return c; + } else { + liverator.log('Faild: LivedoorClip'); + } + } + }, //}}} +}; //}}} + +/** + * getMD5Hash {{{ + * @param {String} str + * @return {String} MD5HashString + */ +function getMD5Hash(str){ + var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Components.interfaces.nsIScriptableUnicodeConverter); + converter.charset = 'UTF-8'; + var result = {}; + var data = converter.convertToByteArray(str, result); + var ch = Components.classes['@mozilla.org/security/hash;1'].createInstance(Components.interfaces.nsICryptoHash); + ch.init(ch.MD5); + ch.update(data, data.length); + var hash = ch.finish(false); + function toHexString(charCode){ + return ('0' + charCode.toString(16)).slice(-2); + } + var s = [toHexString(hash.charCodeAt(i)) for (i in hash)].join(''); + return s; +} //}}} +/** + * evaluateXPath {{{ + * @param {Element} aNode + * @param {String} aExpr XPath Expression + * @return {Element[]} + * @see http://developer.mozilla.org/ja/docs/Using_XPath + */ +function evaluateXPath(aNode, aExpr){ + var xpe = new XPathEvaluator(); + function nsResolver(prefix){ + var ns = { + xhtml: 'http://www.w3.org/1999/xhtml', + rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + dc: 'http://purl.org/dc/elements/1.1/', + rss: 'http://purl.org/rss/1.0/', + taxo: 'http://purl.org/rss/1.0/modules/taxonomy/', + content: 'http://purl.org/rss/1.0/modules/content/', + syn: 'http://purl.org/rss/1.0/modules/syndication/', + admin: 'http://webns.net/mvcb/' + }; + return ns[prefix] || null; + } + var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null); + var found = []; + var res; + while (res = result.iterateNext()) + found.push(res); + return found; +} //}}} +/** + * sbmCommentsView manager {{{ + * @alias liberator.plugins.sbmCommentsViewer + */ +var manager = { + type: { + h: 'hatena', + d: 'delicious', + l: 'livedoorclip' + }, + format: { + id: 'ID', + comment: 'Comment', + timestamp: 'TimeStamp', + tags: 'Tags' + }, + // for debug + convertMD5: function(str){ + return getMD5Hash(str); + }, + // for debug + getXML: function(url){ + var xhr = new XMLHttpRequest(); + xhr.open('GET',url,false); + xhr.send(null); + return xhr; + }, + // for debug + get cache(){ + return cacheManager; + }, + /** + * @param {String} str + * @param {Number} where + * TODO + */ + open: function(str, where){ + /* + getBrowser().addTab('data:text/html,'+str, null,null,null); + */ + } +}; //}}} + +commands.addUserCommand(['viewSBMComments'], 'SBM Comments Viewer', //{{{ + function(arg){ //{{{ + var types = liberator.globalVariables.def_sbms || 'hdl'; + var format = (liberator.globalVariables.def_sbm_format || 'id,timestamp,tags,comment').split(','); + var countOnly = false, openToBrowser = false; + var url = buffer.URL; + var res = liberator.commands.parseArgs(arg, this.args); + if (res){ + if (res.args.length > 0){ + res.args.forEach(function(arg){ + switch(arg){ + case '-c': + case '-count': + countOnly = true; + break; + case '-b': + case '-browser': + openToBrowser = true; + break; + default: + url = arg; + } + }); + } + if (res.opts.length > 0){ + res.opts.forEach(function(opt){ + switch(opt[0]){ + case '-t': + if (opt[1]) types = opt[1]; + break; + case '-f': + if (opt[1]) format = opt[1]; + break; + case '-c': + countOnly = true; + break; + case '-b': + openToBrowser = true; + break; + } + }); + } + if (res.args[0]) url = res.args[0]; + } + + for (var i=0; i threshold) delete cache[url][type]; + } + } + }, + isAvailable: function(url, type){ + if (cache[url] && cache[url][type] && new Date() - cache[url][type][0] < threshold) + return true; + + return false; + } + }; + return c_manager; +})(); + +return manager; +})(); +// vim: sw=4 ts=4 sts=0 et fdm=marker: -- cgit v1.2.3