/* ほぼ、マウス前提なので、Vimperatorらしからぬプラグインですが... == Settings == g:twittperator_plugin_twlist_panel = 1 $RUNTIMEDIR/plugin/twittperator に入れている場合は設定してください。 g:twittperator_screen_name = "" g:twlist_auto_poup = {0 | 1} ツイートがあったときに自動でポップアップするか g:twlist_popup_time = second 自動ポップアップ時から閉じるまでの秒数 == Options == set [no]showtwlist[!] ポップアップを永続的にするかどうか */ let ID_PANEL = "twlist-panel", ID_TIMELINE = "twlist-timeline", ID_MENTION = "twlist-mentions", ID_DM = "twlist-dm", ID_ROOT = "twlist-box", ID_SPLITTER = "twlist-splitter"; let rows = 6; let timelineBox = null, mentionsBox = null, dmBox = null, panel = null, screenNameReg = null; let contextPath = 'liberator.plugins.contexts["'+PATH.replace("\\","\\\\","g")+'"]'; let panelXML = ; function setStyleSheet() { highlight.loadCSS(<>tab,chrome://browser/content/browser.xul { text-shadow: rgba(240,240,240,0.8) 3px 3px 2px; background-color: rgba(224,2224,224,0.8) !important; padding: 2px 5px; margin: 0 2px; } TwlistTab[selected],#twlist-tabs>tab[selected=true] border-bottom: 2px solid rgba(240,128,128,0.8); TwlistPanels,#twlist-panels,chrome://browser/content/browser.xul { background-color: transparent !important; border: none !important; padding: 0 !important; } TwlistItemContent,.twlist-item-content,chrome://browser/content/browser.xul { -moz-user-select: -moz-all; padding: 2px; border: solid thin silver; -moz-border-radius: 5px; margin: 0 0 1em 0; background-color: rgba(0,0,0,0.8); color: white; } TwlistItemContent[selected],.twlist-item-content[selected=true],chrome://browser/content/browser.xul { background-color: black !important; } TwlistItemRT,.twlist-item-rt,chrome://browser/content/browser.xul TwlistRTMark,.twlist-rt-mark,chrome://browser/content/browser.xul { color: white; font-weight: bold; background-color: gray; padding: 2px 5px; margin: 0; -moz-border-radius: 4px; } TwlistProfileImage,.twlist-profile-image,chrome://browser/content/browser.xul { border: thin solid white; -moz-border-radius: 2px; } TwlistContent,.twlist-content,chrome://browser/content/browser.xul TwlistText,.twlist-text,chrome://browser/content/browser.xul margin: 2px 1em; TwlistTextLabel,.twlist-text>label,chrome://browser/content/browser.xul margin: 1px 2px 2px 2px !important; TwlistMetaInfo,.twlist-metainfo,chrome://browser/content/browser.xul TwlistUserName,.twlist-username,chrome://browser/content/browser.xul TwlistScreenName,.twlist-screenname,chrome://browser/content/browser.xul font-weight: bold; TwlistLink,.twlist-link,chrome://browser/content/browser.xul color: cyan; TwlistLink:hover,.twlist-link:hover,chrome://browser/content/browser.xul cursor: pointer !important; TwlistLinkHash,.twlist-hash,chrome://browser/content/browser.xul color: lightgreen !important; TwlistLinkUser,.twlist-user,chrome://browser/content/browser.xul TwlistLinkURL,.twlist-url,chrome://browser/content/browser.xul TwlistImage,.twlist-image,chrome://browser/content/browser.xul max-height: 300px; border:thin solid ]]>.toString()); styles.addSheet(true, "twlist-styles", "chrome://browser/content/browser.xul", <> spacer { border: none !important; } #twlist-tabs > tab { -moz-appearance: none !important; border: none !important; } #twlist-panels richlistbox { -moz-appearance: none !important; background-color: transparent !important; } .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; } ]]>.toString()); } function add (msg, target) { if (!target) target = timelineBox; //let isRT = ("retweeted_status" in msg); //let domContent = formatText(isRT ? msg.retweeted_status.text : msg.text); XML.ignoreWhitespace = true; let xml; if ("direct_message" in msg) { xml = {formatText(msg.direct_message.text.replace(/[\01-\10\14\16-\37]/g,""))} ; } else if ("retweeted_status" in msg) { xml = {formatText(msg.retweeted_status.text.replace(/[\01-\10\14\16-\37]/g,""))} ; } else { xml = {formatText(msg.text.replace(/[\01-\10\14\16-\37]/g,""))} ; } let dom = xmlToDom(xml, XUL); //dom.querySelector(".twlist-content").appendChild(domContent); target.insertBefore(dom, target.firstChild); if (target.getRowCount() > 50) { target.removeChild(target.lastChild); } if (autoPopup){ popup(true); } } function onLoad () { let gv = liberator.globalVariables; __context__.__defineGetter__("autoPopup", function() ("twlist_auto_popup" in gv) ? !!gv.twlist_auto_poup : true); __context__.__defineGetter__("popupTime", function() gv.twlist_popup_time || 20); __context__.__defineGetter__("screenName", function() gv.twittperator_screen_name || ""); if (screenName) screenNameReg = new RegExp("@" + screenName + "\\b"); setStyleSheet(); let (app = document.getElementById("liberator-visualbell")) { app.parentNode.appendChild(xmlToDom(panelXML)); } panel = document.getElementById(ID_PANEL); timelineBox = document.getElementById(ID_TIMELINE); mentionsBox = document.getElementById(ID_MENTION); dmBox = document.getElementById(ID_DM); splitter = document.getElementById(ID_SPLITTER); plugins.twittperator.ChirpUserStream.addListener(streamListener); options.add(["showtwpanel"], "popup/hide twittperator panel", "boolean", false, { setter: function (value) { if (value) { popup(); } else { //panel.hidePopup(); panel.style.opacity = 0; } return value; }, }); } let t = null; function popup(autoHide) { if (panel.state != "open") { panel.openPopup(document.getElementById("browser-bottombox"), "after_end", 0, 0, false, true); } panel.style.opacity = 1; if (t) { clearTimeout(t); t = null; } if (autoHide && !options["showtwpanel"]) t = setTimeout(function(){ panel.style.opacity = 0; }, popupTime * 1000); } function onUnload () { let elm = document.getElementById(ID_PANEL); if (elm) elm.parentNode.removeChild(elm); plugins.twittperator.ChirpUserStream.removeListener(streamListener); } function xmlToDom(xml, xmlns) { XML.prettyPrinting = false; var doc = (new DOMParser).parseFromString( '' + xml.toXMLString() + "", "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 streamListener(msg, raw) { if (msg.text && msg.user) { add(msg, timelineBox); if (screenNameReg && screenNameReg.test(msg.text)){ add(msg, mentionsBox); } } } function formatText (str) { str = str.trim(); let reg = /https?:\/\/[^\s]+|[#@]\w+/g; XML.ignoreWhitespace = false; let m, i = 0, buf = "", x = ; while((m=reg.exec(str))){ buf = str.substring(i, m.index); if (buf) x.appendChild(buf); let class = "twlist-link", href = ""; switch (m[0].charAt(0)){ case "@": class += " twlist-user"; href = "http://twitter.com/" + m[0].substr(1); break; case "#": class += " twlist-hash"; href = "http://twitter.com/search?q=%23" + m[0].substr(1); break; default: class += " twlist-url"; href = m[0]; } x.appendChild({m[0]}); i=reg.lastIndex; } buf = str.substr(i); if (buf) x.appendChild(buf); //return xmlToDom(x, "http://www.w3.org/1999/xhtml"); return x; } function getMedia (uri) { if (/\.gif$|\.jpe?g$|\.pi?ng$/.test(uri.path)) return ["image", uri.spec]; switch (uri.host) { case "twitpic.com": return ["image", "http://twitpic.com/show/thumb" + uri.path + ".jpg"]; case "movapic.com": return ["image", "http://image.movapic.com/pic/m_" + uri.path.substr(uri.path.lastIndexOf("/")+1) + ".jpeg"]; case "gyazo.com": return ["image", uri.spec]; case "twittgoo.com": let elm = util.httpGet(uri.spec + "/?format=atom").responseXML.getElementsByTagName("icon")[0]; return ["image", elm.textContent]; case "www.flickr.com": case "f.hatena.ne.jp": default: return []; } } function isShortenURL (uri) { switch (uri.host) { case "bit.ly": case "is.gd": case "j.mp": case "goo.gl": case "htn.to": case "tinyurl.com": case "ff.im": case "youtu.be": 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.call(aElement, x.channel.URI); } }; x.send(null); } else { aCallback.call(aElement, aURI); } } function onSelect (evt) { let item = evt.target.selectedItem; let links = item.querySelectorAll("a.twlist-url"); function detectMedia (uri) { this.setAttribute("href", uri.spec); this.textContent = uri.spec; let [type, src] = getMedia(uri); if (type && src) { switch (type) { case "image": if (this.hasAttribute("shown") && this.getAttribute("shown") == "true") break; let img = document.createElementNS(XHTML, "img"); img.setAttribute("src", src); img.setAttribute("class", "twlist-image"); img.setAttribute("align", "right"); this.parentNode.appendChild(img); this.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 onPopupShown() { let elm = document.commandDispatcher.focusedElement; if (elm) { elm.focus(); } else { liberator.focusContent(); } } 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/statues/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"; }); } } onLoad(); // vim: sw=2 ts=2 et filetype=javascript: