/**
* ==VimperatorPlugin==
* @name SBM Comments Viewer
* @description List show Social Bookmark Comments
* @description-ja ソーシャル・ブックマーク・コメントを表示します
* @version 0.1c
* ==/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, tagsAndComment
*
* SBMタイプ:
* h : hatena bookmark
* d : Delicious
* l : livedoor clip
* z : Buzzurl
* 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
));
},
toHTML: function(format, countOnly){
var label = <>
{this.faviconURL ? <img src={this.faviconURL} width="16" height="16"/> : <></>}
{manager.type[this.type] + ' ' + this.count + '(' + this.entries.length + ')'}
{this.pageURL ? <a href="#">{this.pageURL}</a> : <></>}
</>;
if (countOnly){
return label;
} else {
let xml = <table id="liberator-sbmcommentsviewer">
<caption style="text-align:left" class="hl-Title">{label}</caption>
</table>;
let self = this;
xml.* += (function(){
var thead = <tr/>;
format.forEach(function(colum){ thead.* += <th>{manager.format[colum] || '-'}</th>; });
var tbody = <></>;
self.entries.forEach(function(e){ tbody += e.toHTML(format); });
return thead + tbody;
})();
return xml;
}
}
}; //}}}
// }}}
/**
* 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</*
ほぼ、マウス前提なので、Vimperatorらしからぬプラグインですが...
短縮URLはアイテムを選択すると展開されるはず、
あと、画像っぽいURLも展開する(まだ出来るものが少ない)
ToDo: YouTubeとかも展開出来るとイイね!
== Settings ==
g:twittperator_plugin_twlist_win = 1
$RUNTIMEDIR/plugin/twittperator に入れている場合は設定してください。
g:twittperator_screen_name = "<your screen name>"
g:twlist_max_rows = num
表示するアイテム数 (default: 50)
:js liberator.globalVariables.twlist_track_words = [words]
通常は g:twittperator_track_words = "words" で OK だが、
vimp,vimperator など一つのタブにまとめたい時に、
["vimp,vimperator", "firefox", "javascript"] などとする
当然ながら g:twittperator_track_words の値は必須
== Command ==
:showtwin
ウィンドウの表示/非表示
ToDo: 表示位置と幅、高さを維持したい
== Tips ==
:js plugins.twlistWin.addTrack (word)
新たな word を検索対象にする
:js plugins.twlistWin.win.TrackTabs.remove(word)
word を検索対象としているタブを削除
Todo: コマンド or オプションで追加削除出来るようにしろ!
*/
XML.ignoreProcessingInstructions = false;
let winXML = <>
<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
<window id="twlist-window"
sizemode="normal"
title="Twlist - Twittperator"
width="500"
height="600"
onload="init()"
onunload="twlist.onClose()"
windowtype="twittperator:twlist"
xmlns={XUL}
xmlns:xhtml={XHTML}>
<script type="application/javascript; version=1.8"><![CDATA[
const XUL = new Namespace("xul", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
XHTML = new Namespace("xhtml", "http://www.w3.org/1999/xhtml");
var liberator, twlist, timelineBox, mentionsBox, dmBox, tabBox, cmdBox;
function $(id) document.getElementById(id);
function init(){
liberator = window.arguments[0];
twlist = window.arguments[1];
timelineBox = $("twlist-timeline");
mentionsBox = $("twlist-mentions");
dmBox = $("twlist-dm");
tabBox = $("twlist-tabbox");
cmdBox = $("twlist-command");
liberator.plugins.twittperator.Tweets.slice(0,twlist.maxRows).reverse().forEach(function(m) add(m));
if (twlist.trackWords){
if (twlist.twlistTrack){
TrackTabs.init(twlist.twlistTrack);
} else {
let words = twlist.trackWords.split(",");
TrackTabs.init(words);
}
}
}
function TrackTab (word, index) {
this.word = word;
this.index = index;
this.tab = null;
this.panel = null;
this.listbox = null;
this.matchReg = new RegExp("\\b" + word.split(",").join("\\b|\\b") + "\\b", "i");
}
TrackTab.prototype = {
setTabAndPanel: function(tab, panel){
this.tab = tab;
this.panel = panel;
this.listbox = panel.querySelector("richlistbox");
},
}
var TrackTabs = {
init: function(words) {
for (let i=0; i < words.length; i++){
this.add(words[i]);
}
},
lastIndex: 0,
list: [],
add: function (word){
let tab = tabBox.tabs.appendChild(xmlToDom(<tab label={"Track - " + word} crop="end" maxwidth="150"/>,XUL));
this.lastIndex++;
let panel = tabBox.tabpanels.appendChild(xmlToDom(
<tabpanel flex="1" xmlns={XUL}>
<richlistbox id={"twlist-track-"+this.lastIndex} flex="1" disableKeyNavigation="true"
onselect="twlist.onSelect(event)"/>
</tabpanel>, XUL)
);
let trackTab = new TrackTab(word, this.lastIndex);
trackTab.setTabAndPanel(tab, panel);
this.list.push(trackTab);
},
remove: function(word){
for (let i=0,t; t = this.list[i]; i++){
if (t.word == word){
this.list.splice(i, 1);
t.panel.parentNode.removeChild(t.panel);
t.tab.parentNode.removeChild(t.tab);
break;
}
}
}
};
function keepMaxRows(box) {
if (box.getRowCount() > twlist.maxRows){
box.removeChild(box.lastChild);
}
}
function add (msg, isTrack) {
let xml = twlist.getItemXML(msg);
let dom = xmlToDom(xml, XUL);
function addMsgToTrack(){
TrackTabs.list.forEach(function(tab) {
if (tab.matchReg.test(msg.text))
addMsg(tab.listbox, dom.cloneNode(true), true);
});
}
if (isTrack){
addMsgToTrack();
} else if ("direct_message" in msg){
addMsg(dmBox, dom, true);
} else {
addMsg(timelineBox, dom, false);
if (twlist.screenName && twlist.screenName.test(msg.text)) {
let repDom = dom.cloneNode(true);
addMsg(mentionsBox, repDom, true);
}
}
}
function addMsg(box, node, doSetSymbol){
box.insertBefore(node, box.firstChild);
keepMaxRows(box);
if (doSetSymbol)
setNewSymbol(box);
if (box.selectedIndex >= 0)
box.ensureIndexIsVisible(box.selectedIndex);
}
function xmlToDom(xml, xmlns) {
XML.prettyPrinting = false;
XML.ignoreWhitespace = true;
var doc = (new DOMParser).parseFromString(
'<root xmlns="' + xmlns + '">' + xml.toXMLString() + "</root>",
"application/xml");
var imported = document.importNode(doc.documentElement, true);
var range = document.createRange();
range.selectNodeContents(imported);
var fragment = range.extractContents();
range.detach();
return fragment.childNodes.length > 1 ? fragment : fragment.firstChild;
}
function setNewSymbol(box){
let panels = tabBox.tabpanels.childNodes;
let index = 0;
for (let i=0; i < panels.length; i++){
if (box.parentNode == panels[i]){
index = i;
break;
}
}
let tab = tabBox.tabs.getItemAtIndex(index);
if (index != tabBox.selectedIndex && tab.label.indexOf("*") != 0){
tab.label = "* " + tab.label;
}
}
function onTabSelect(evt){
let tab = $("twlist-tabbox").tabs.selectedItem;
if (tab.label.indexOf("* ") == 0){
tab.label = tab.label.substr(2);
}
}
function getCurrentListBox(){
return tabBox.tabpanels.selectedPanel.firstChild;
}
function getParent(node, class){
let elm = node;
while (elm != document.documentElement){
if (elm instanceof class)
return elm;
elm = elm.parentNode;
}
return null;
}
var gActions = (function(){
function getItemIndex(listbox, arg){
let index = 0, currentIndex = listbox.selectedIndex;
switch (arg) {
case "0":
return 0;
case "$":
return listbox.itemCount - 1;
case "+1":
if (currentIndex == listbox.itemCount - 1)
return null;
return currentIndex + 1;
case "-1":
if (currentIndex == -1)
return null;
return currentIndex - 1;
default:
return null;
}
}
function getCopyItem(target){
let listbox = getCurrentListBox();
let item = listbox.selectedItem;
if (!item)
return null;
switch (target){
case "TWEETURL":
return [
"http://twitter.com",
item.querySelector(".twlist-screenname").textContent,
"status",
item.value
].join("/");
case "ID":
return item.value;
case "SCREENNAME":
return item.querySelector(".twlist-screenname").textContent;
case "TEXT":
return item.querySelector(".twlist-text").textContent;
case "SELECTION":
return window.getSelection().toString();
case "URL":
let node = document.popupNode;
if (node) {
let elm = getParent(node, HTMLAnchorElement);
if (elm)
return elm.getAttribute("href");
}
}
return null;
}
var self = {
copy: function(target){
let text = getCopyItem(target);
if (text) {
liberator.modules.util.copyToClipboard(text, true);
}
},
select: function(arg){
let listbox = getCurrentListBox();
let index = getItemIndex(listbox, arg);
listbox.ensureIndexIsVisible( listbox.selectedIndex = index );
listbox.timedSelect(listbox.getItemAtIndex(index), 0.1);
},
reply: function(){
let listbox = getCurrentListBox();
let item = listbox.selectedItem;
if (!item)
return;
cmdBox.value = (listbox == dmBox ? "D @" : "@") + item.getAttribute("searchlabel") + " ";
cmdBox.focus();
},
retweet: function(){
let listbox = getCurrentListBox();
if (listbox == dmBox)
return;
let item = listbox.selectedItem;
if (!item)
return;
twlist.onRetweet(item.querySelector(".twlist-retweet"));
},
fav: function(){
let listbox = getCurrentListBox();
if (listbox == dmBox)
return;
let item = listbox.selectedItem;
if (!item)
return;
twlist.onFav(item.querySelector(".twlist-fav"));
},
resetFocus: function(){
cmdBox.blur();
cmdBox.value = "";
}
};
return self;
})();
var gContext = (function(){
var popupNode = null, anchor;
function showItem (item, show, text){
if (show){
if (item.hasAttribute("hidden"))
item.removeAttribute("hidden");
if (text)
item.setAttribute("tooltiptext", text);
} else {
item.setAttribute("hidden", true);
if (item.hasAttribute("tooltiptext"))
item.removeAttribute("tooltiptext");
}
}
function getSelText(text){
if (text.length > 20)
return text.substr(20) + "...";
return text;
}
var self = {
showing: function gContextShowing(){
let listbox = getCurrentListBox();
if (!listbox.selectedItem)
return false;
popupNode = document.popupNode;
let openLinkMenu = $("twlist-menuitem-openlink"),
openLinkTabMenu = $("twlist-menuitem-openlinktab"),
copyURLMenu = $("twlist-menuitem-copy-url"),
isAnchor = false,
href = null;
anchor = getParent(popupNode, HTMLAnchorElement);
if (anchor){
isAnchor = true;
href = anchor.getAttribute("href");
}
showItem(openLinkMenu, isAnchor, href);
showItem(openLinkTabMenu, isAnchor, href);
showItem(copyURLMenu, isAnchor, href);
let sel = window.getSelection(),
selectMenu = $("twlist-menuitem-copy-selection");
showItem(selectMenu, (sel.toString() != ""), getSelText(sel.toString()));
return true;
},
hiding: function gContextHiding(){
anchor = null;
},
openLink: function(newTab) {
if (popupNode instanceof HTMLAnchorElement) {
liberator.open(popupNode.getAttribute("href"),
{where: (newTab ? liberator.NEW_TAB : liberator.CURRENT_TAB) });
}
}
};
return self;
})();
]]></script>
<commandset id="twlist-commandset">
<command id="cmd_select_next" oncommand="gActions.select('+1')"/>
<command id="cmd_select_prev" oncommand="gActions.select('-1')"/>
<command id="cmd_select_first" oncommand="gActions.select('0')"/>
<command id="cmd_select_last" oncommand="gActions.select('$')"/>
<command id="cmd_reply" oncommand="gActions.reply()"/>
<command id="cmd_retweet" oncommand="gActions.retweet()"/>
<command id="cmd_fav" oncommand="gActions.fav()"/>
</commandset>
<keyset id="twlist-keyset">
<key id="key_select_next" command="cmd_select_next" key="J"/>
<key id="key_select_prev" command="cmd_select_prev" key="K"/>
<key id="key_select_first" command="cmd_select_first" key="G"/>
<key id="key_select_last" command="cmd_select_last" key="G" modifiers="shift"/>
<key id="key_reply" command="cmd_reply" key="R"/>
<key id="key_retweet" command="cmd_retweet" key="R" modifiers="shift"/>
<key id="key_fav" command="cmd_fav" key="F"/>
<key id="key_esd" keycode="VK_ESCAPE" oncommand="gActions.resetFocus()"/>
<key id="key_command" key=":" oncommand="cmdBox.focus()"/>
</keyset>
<popupset>
<popup id="twlist-context"
onpopupshowing="if(event.target!=this) return true; return gContext.showing();"
onpopuphiding="if(event.target==this){gContext.hiding();}">
<menuitem id="twlist-menuitem-openlink" label="Open" oncommand="gContext.openLink()"/>
<menuitem id="twlist-menuitem-openlinktab" label="Open in a new tab" oncommand="gContext.openLink(true)"/>
<menu id="twlist-menu-copy" label="Copy" accesskey="C">
<menupopup>
<menuitem id="twlist-menuitem-copy-tweeturl" label="TweetURL" accesskey="W" oncommand="gActions.copy('TWEETURL')"/>
<menuitem id="twlist-menuitem-copy-text" label="Text" accesskey="T" oncommand="gActions.copy('TEXT')"/>
<menuitem id="twlist-menuitem-copy-id" label="ID" accesskey="I" oncommand="gActions.copy('ID')"/>
<menuitem id="twlist-menuitem-copy-name" label="ScreenName" accesskey="S" oncommand="gActions.copy('SCREENNAME')"/>
<menuitem id="twlist-menuitem-copy-selection" label="Copy Selection" oncommand="gActions.copy('SELECTION')"/>
<menuitem id="twlist-menuitem-copy-url" label="URL" accesskey="U" oncommand="gActions.copy('URL')"/>
</menupopup>
</menu>
<menuitem id="twlist-menuitem-rt" label="RT" accesskey="T" oncommand="gActions.retweet()"/>
<menuitem id="twlist-menuitem-reply" label="Reply" accesskey="R" oncommand="gActions.reply()"/>
<menuitem id="twlist-menuitem-fav" label="Fav" accesskey="F" oncommand="gActions.fav()"/>
</popup>
</popupset>
<vbox id="twlist-box" flex="1">
<tabbox id="twlist-tabbox" flex="1">
<tabs id="twlist-tabs" onselect="onTabSelect(event)">
<tab label="TimeLine" accesskey="T"/>
<tab label="Mentions" accesskey="M"/>
<tab label="DM" accesskey="D"/>
</tabs>
<tabpanels id="twlist-panels" flex="1" style="background: transparent;" contextmenu="twlist-context">
<tabpanel flex="1">
<richlistbox id="twlist-timeline" disableKeyNavigation="true"
flex="1" onselect="twlist.onSelect(event)"/>
</tabpanel>
<tabpanel flex="1">
<richlistbox id="twlist-mentions" flex="1" disableKeyNavigation="true"
onselect="twlist.onSelect(event)"/>
</tabpanel>
<tabpanel flex="1">
<richlistbox id="twlist-dm" flex="1" disableKeyNavigation="true"
onselect="twlist.onSelect(event)"/>
</tabpanel>
</tabpanels>
</tabbox>
</vbox>
<statusbar id="status-bar">
<statusbarpanel flex="1">
<textbox id="twlist-command" flex="1" emptytext="Tweet" onkeydown="twlist.onKeyDown(event)"/>
</statusbarpanel>
</statusbar>
</window>
</>.toXMLString();
function setStyleSheet() {
styles.addSheet(true, "twlist-styles", "data:*",
<><![CDATA[
#twlist-panels {
background-color: transparent !important;
border: none !important;
padding: 0 !important;
}
#twlist-tabs > tab {
max-width: 150px !important;
}
#twlist-tabs > tab[selected=true] {
max-width: 300px !important;
}
#twlist-command { background-color: white; }
.twlist-item-content {
-moz-user-select: -moz-all;
border-bottom: solid thin silver;
}
.twlist-item-content[selected=true] {
background-color: rgb(240,240,240) !important;
color: -moz-fieldtext !important;
}
.twlist-item-mentioned {
background-color: #F0FFFF;
}
.twlist-rt-mark {
color: white; font-weight: bold; background-color: gray;
padding: 2px 5px; margin: 0;
-moz-border-radius: 4px;
}
.twlist-reply, .twlist-retweet, .twlist-fav {
color: white; font-weight: bold; background-color: gray;
padding: 2px; margin:0;
-moz-border-radius: 2px;
}
.twlist-fav {
color: yellow;
}
.twlist-text { margin: 2px 1em; }
.twlist-text>label { margin: 1px 2px 2px 2px !important; }
.twlist-screenname { font-weight: bold; }
.twlist-link { color: -moz-hyperlinktext; }
.twlist-link:hover { chrome://browser/content/browser.xul cursor: pointer !important; }
.twlist-hash { color: DarkGreen !important; }
.twlist-image { max-height: 300px; border:thin solid; }
]]></>.toString());
}
function addTrack(word){
if (!win) return;
win.TrackTabs.add(word);
let words = trackWords.split(","),
flag = false;
word.split(",").forEach(function(w) {
if (words.indexOf(w) == -1){
words.push(w);
flag = true;
}
});
if (flag) {
plugins.twittperator.TrackingStream.start({track: words.join(",")});
}
}
function getItemXML(msg) {
XML.prettyPrinting = true;
XML.ignoreWhitespace = true;
let xml;
if ("direct_message" in msg) {
xml = <richlistitem value={msg.direct_message.id}
searchlabel={msg.direct_message.sender_screen_name}
xmlns={XUL} class="twlist-item-content twlist-item-dm">
<vbox class="twlist-profile-image">
<image src={msg.direct_message.sender.profile_image_url} width="48" height="48"/>
<spacer flex="1"/>
</vbox>
<vbox flex="1" class="twlist-content">
<hbox>
<label class="twlist-screenname">{msg.direct_message.sender.screen_name}</label>
<hbox class="twlist-matainfo">
<label class="twlist-username">{"(" + msg.direct_message.sender.name + ")"}</label>
<label>{(new Date(msg.direct_message.sender.created_at)).toLocaleFormat()}</label>
</hbox>
</hbox>
{formatText(msg.direct_message.text.replace(/[\01-\10\14\16-\37]/g,""))}
</vbox>
<vbox>
<spacer flex="1"/>
<label value={"\u21A9"} class="twlist-reply" onclick="twlist.onReply(this, true)"/>
<spacer flex="1"/>
</vbox>
</richlistitem>;
} else if ("retweeted_status" in msg) {
xml =
<richlistitem value={msg.retweeted_status.id}
searchlabel={msg.retweeted_status.user.screen_name+"#"+msg.retweeted_status.id}
xmlns={XUL} class="twlist-item-content twlist-item-rt">
<vbox class="twlist-profile-image">
<image src={msg.retweeted_status.user.profile_image_url} width="48" height="48"/>
<spacer flex="1"/>
</vbox>
<vbox flex="1" class="twlist-content">
<hbox>
<label value={"\u21BB"} class="twlist-rt-mark"/>
<label class="twlist-screenname">{msg.retweeted_status.user.screen_name}</label>
<hbox class="twlist-metainfo">
<label class="twlist-username">{"(" + msg.retweeted_status.user.name + ")"}</label>
<label>{(new Date(msg.created_at)).toLocaleFormat()}</label>
<label>{"By " + msg.user.screen_name}</label>
</hbox>
</hbox>
{formatText(msg.retweeted_status.text.replace(/[\01-\10\14\16-\37]/g,""))}
</vbox>
<vbox>
<spacer flex="1"/>
<label value={"\u21A9"} class="twlist-reply" onclick="twlist.onReply(this)"/>
<label value={msg.favorited ? "\u2605" : "\u2606"} class="twlist-fav" onclick="twlist.onFav(this)"/>
<label value={"\u21BB"} class="twlist-retweet" onclick="twlist.onRetweet(this)"/>
<spacer flex="1"/>
</vbox>
</richlistitem>;
} else {
xml =
<richlistitem value={msg.id} searchlabel={msg.user.screen_name+"#"+msg.id}
xmlns={XUL} class={"twlist-item-content" + (screenName.test(msg.text) ? " twlist-item-mentioned" : "")}>
<vbox class="twlist-profile-image">
<image src={msg.user.profile_image_url} width="48" height="48"/>
<spacer flex="1"/>
</vbox>
<vbox flex="1" class="twlist-content">
<hbox>
<label class="twlist-screenname">{msg.user.screen_name}</label>
<hbox class="twlist-metainfo">
<label class="twlist-username">{"(" + msg.user.name + ")"}</label>
<label>{(new Date(msg.created_at)).toLocaleFormat()}</label>
</hbox>
</hbox>
{formatText(msg.text.replace(/[\01-\10\14\16-\37]/g,""))}
</vbox>
<vbox>
<spacer flex="1"/>
<label value={"\u21A9"} class="twlist-reply" onclick="twlist.onReply(this)"/>
<label value={msg.favorited ? "\u2605" : "\u2606"} class="twlist-fav" onclick="twlist.onFav(this)"/>
<label value={"\u21BB"} class="twlist-retweet" onclick="twlist.onRetweet(this)"/>
<spacer flex="1"/>
</vbox>
</richlistitem>;
}
return xml;
}
let listener = {
userStream: function userStreamListener(msg, raw) {
if (!win)
return;
if ((msg.text && msg.user) || ("direct_message" in msg)) {
win.add(msg);
}
},
trackStream: function trackStreamListener(msg, raw){
if (!win)
return;
if (msg.text && msg.user){
win.add(msg, true);
}
}
}
function onLoad () {
let gv = liberator.globalVariables;
__context__.__defineGetter__("maxRows", function() gv.twlist_max_rows || 50);
__context__.__defineGetter__("trackWords", function() gv.twittperator_track_words || "");
__context__.__defineGetter__("twlistTrack", function() gv.twlist_track_words);
__context__.__defineGetter__("win", function() services.get("windowMediator").getMostRecentWindow("twittperator:twlist"));
__context__.screenName = gv.twittperator_screen_name ? new RegExp("\\b@" + gv.twittperator_screen_name +"\\b") : null;
setStyleSheet();
plugins.twittperator.ChirpUserStream.addListener(listener.userStream);
if (trackWords)
plugins.twittperator.TrackingStream.addListener(listener.trackStream);
commands.addUserCommand(["showtwin"], "popup/hide twittperator window",
function(arg){
if (!win) {
open()
} else {
win.close();
}
},{
bang: true
}, true);
plugins.twlistWin = __context__;
}
function open(){
let URL = "data:application/vnd.mozilla.xul+xml;base64," + btoa(winXML);
openDialog(URL, null, "chrome,resizable=yes", liberator, __context__ );
}
function onClose(){
}
function onUnload () {
if (win)
win.close();
plugins.twittperator.ChirpUserStream.removeListener(listener.userStream);
plugins.twittperator.TrackingStream.removeListener(listener.trackStream);
}
function getMedia (uri, elm, callback) {
var media = { anchor: uri.spec };
if (/\.gif$|\.jpe?g$|\.pi?ng$/.test(uri.path)){
media.image = uri.spec;
} else {
switch (uri.host) {
case "twitpic.com":
media.image = "http://twitpic.com/show/thumb" + uri.path + ".jpg";
break;
case "movapic.com":
media.image = "http://image.movapic.com/pic/m_" + uri.path.substr(uri.path.lastIndexOf("/")+1) + ".jpeg";
break;
case "gyazo.com":
media.image = uri.spec;
break;
case "tweetphoto.com":
media.image = "http://TweetPhotoAPI.com/api/TPAPI.svc/imagefromurl?size=thumbnail&url=" + uri.spec;
break;
case "twittgoo.com":
util.httpGet(uri.spec + "/?format=atom", function(xhr){
media.image = xhr.responseXML.getElementsByTagName("icon")[0].textContent;
callback(elm, media);
});
return;
case "yfrog.com": {
let x = new XMLHttpRequest;
x.open("HEAD", uri.spec + ".th.jpg", true);
x.onreadystatechange = function(){
if (x.readyState == 4) {
media.image = x.channel.URI.spec;
callback(elm, media);
}
}
x.send(null);
}
case "plixi.com":
util.httpGet("http://api.plixi.com/api/tpapi.svc/photos/" + uri.path.substr(uri.path.lastIndexOf("/")+1), function(xhr){
media.image = xhr.responseXML.getElementsByTagName("ThumbnailUrl")[0].textContent;
callback(elm, media);
});
return;
case "www.youtube.com": {
let query = uri.path.substr(1).split("?")[1];
if (!query) break;
let hash = query.split("&").filter(function(param) param.indexOf("v=")==0)[0];
if (!hash) break;
media.image = "http://i.ytimg.com/vi/" + hash + "/1.jpg";
break;
}
case "f.hatena.ne.jp": {
let [, userid, date] = uri.path.split("/");
media.image = ["http://img.f.hatena.ne.jp/images/fotolife/",
userid.charAt(0), "/", userid, "/", date.substr(0, 8),
"/", date, "_m.jpg"].join("");
break;
}
case "photomemo.jp": {
let [, user, num] = uri.path.split("/");
util.httpGet(uri.prePath + "/" + user + "/rss.xml", function(xhr){
let items = xhr.responseXML.querySelectorAll("item");
for (let i=0, item; item = items[i]; i++){
if (item.querySelector("link").textContent == uri.spec) {
media.image = item.querySelector("content").getAttribute("url");
callback(elm, media);
return;
}
}
});
return;
}
case "www.flickr.com": {
let [,,user, id, id2, setId] = uri.path.split("/");
let imgReg;
if (id == "sets" && id2) {
imgReg = /<img id="primary_photo_img" src="([^"]+)"/;
} else if ( (id2 == "in" && setId && setId.indexOf("set-") == 0) || (user && id) ) {
imgReg = /<link rel="image_src" href="([^"]+)"/;
} else {
return;
}
util.httpGet(uri.spec, function(xhr){
let matches = xhr.responseText.match(imgReg);
if (matches && matches[1]){
media.image = matches[1];
callback(elm, media);
return;
}
});
return;
}
case "photozou.jp": {
util.httpGet("http://api.photozou.jp/rest/photo_info?photo_id=" + uri.path.substr(uri.path.lastIndexOf("/")+1), function(xhr){
let urlElm = xhr.responseXML.getElementsByTagName("thumbnail_image_url")[0];
if (urlElm) {
media.image = urlElm.textContent;
callback(elm, media);
}
});
return;
}
default:
return;
}
}
if (callback){
callback(elm, media);
} else {
return media;
}
}
function isShortenURL (uri) {
switch (uri.host) {
case "bit.ly":
case "ow.ly":
case "is.gd":
case "j.mp":
case "goo.gl":
case "htn.to":
case "tinyurl.com":
case "ff.im":
case "p.tl":
case "youtu.be":
case "ustre.am":
case "t.co":
case "flic.kr":
case "4sq.com":
return true;
}
return false;
}
function getRedirectedURL (aURI, aElement, aCallback){
if (!aURI.schemeIs("http") && !aURI.schemeIs("https"))
return;
if (isShortenURL(aURI)){
let x = new XMLHttpRequest;
x.open("HEAD", aURI.spec, true);
x.onreadystatechange = function(){
if (x.readyState == 4){
aCallback(aElement, x.channel.URI);
}
};
x.send(null);
} else {
aCallback(aElement, aURI);
}
}
function onSelect (evt) {
let item = evt.target.selectedItem;
if (!item) return;
let links = item.querySelectorAll("a.twlist-url");
function setMedia (elm, media) {
if ("image" in media) {
if (elm.hasAttribute("shown") && elm.getAttribute("shown") == "true")
return;
let a = null;
let m = document.createElementNS(XHTML, "img");
m.setAttribute("src", media.image);
m.setAttribute("class", "twlist-image");
m.setAttribute("align", "right");
if ("anchor" in media){
a = document.createElementNS(XHTML, "a");
a.setAttribute("href", media.anchor);
a.appendChild(m);
elm.parentNode.appendChild(a);
} else {
elm.parentNode.appendChild(m);
}
elm.setAttribute("shown", "true");
}
}
function detectMedia (elm, uri) {
elm.setAttribute("href", uri.spec);
elm.textContent = uri.spec;
getMedia(uri, elm, setMedia);
/*
let [type, src] = getMedia(uri);
if (type && src) {
switch (type) {
case "image":
if (elm.hasAttribute("shown") && elm.getAttribute("shown") == "true")
break;
let img = document.createElementNS(XHTML, "img");
img.setAttribute("src", src);
img.setAttribute("class", "twlist-image");
img.setAttribute("align", "right");
elm.parentNode.appendChild(img);
elm.setAttribute("shown", "true");
break;
default:
}
}
*/
}
for (let i=0; i < links.length; i++) {
let elm = links[i];
let uri = util.newURI(elm.getAttribute("href"));
getRedirectedURL(uri, elm, detectMedia);
}
}
function formatText (str) {
str = str.trim();
let reg = /(?:https?:\/\/[\x21-\x7e]+)|(?:@\w{1,15})|(?:#\S+)/g;
XML.ignoreWhitespace = false;
let m, i = 0, buf = "", x = <xhtml:p class="twlist-text" xmlns:xhtml={XHTML}/>;
while((m=reg.exec(str))){
buf = str.substring(i, m.index);
if (buf)
x.appendChild(buf);
let classValue = "twlist-link", href = "";
switch (m[0].charAt(0)){
case "@":
classValue += " twlist-user";
href = "http://twitter.com/" + m[0].substr(1);
break;
case "#":
classValue += " twlist-hash";
href = "http://twitter.com/search?q=%23" + m[0].substr(1);
break;
default:
classValue += " twlist-url";
href = m[0];
}
x.appendChild(<xhtml:a class={classValue} href={href}
onclick="twlist.onClick(event)" xmlns:xhtml={XHTML}>{m[0]}</xhtml:a>);
i=reg.lastIndex;
}
buf = str.substr(i);
if (buf)
x.appendChild(buf);
return x;
}
function onClick (evt) {
if (evt.button == 2)
return;
evt.preventDefault();
evt.stopPropagation();
let where = (evt.ctrlKey || evt.button == 1) ? liberator.NEW_TAB : liberator.CURRENT_TAB;
let url = evt.target.getAttribute("href");
liberator.open(url, {where: where});
}
function onReply (elm, isDirectMessage) {
let item = elm.parentNode.parentNode;
let label = item.getAttribute("searchlabel");
let cmd = "tw " + (isDirectMessage ? "D @" : "@") + label + " ";
commandline.open(":", cmd, modes.EX);
window.focus();
}
function onRetweet(elm){
let id = elm.parentNode.parentNode.value;
plugins.twittperator.OAuth.post("http://api.twitter.com/1/statuses/retweet/" + id + ".json",
null, function(text){
});
}
function onFav (elm) {
let id = elm.parentNode.parentNode.value;
let fav = elm.value;
if (fav == "\u2605") {
plugins.twittperator.OAuth.post("http://api.twitter.com/1/favorites/destroy/" + id + ".json",
null, function(text){
elm.value = "\u2606";
});
} else {
plugins.twittperator.OAuth.post("http://api.twitter.com/1/favorites/create/" + id + ".json",
null, function(text){
elm.value = "\u2605";
});
}
}
function onKeyDown(event){
switch (event.keyCode){
case event.DOM_VK_RETURN:
case event.DOM_VK_ENTER:
let txt = event.target.value;
if (txt){
if (/^@\w+#\d+$/.test(txt.trim()))
return;
liberator.execute('tw ' + txt, null, true);
event.target.value = "";
event.target.blur();
}
break;
}
}
onLoad();
// vim: sw=2 ts=2 et filetype=javascript: