// Vimperator plugin: "Update Twitter" // Last Change: 21-Jan-2009. Jan 2008 // License: Creative Commons // Maintainer: Trapezoid - http://unsigned.g.hatena.ne.jp/Trapezoid // // The script allows you to update Twitter status from Vimperator 0.6.*. // // Commands: // :twitter some thing text // post "some thing text" to Twitter. // :twitter! someone // show someone's statuses. // :twitter!? someword // show search result of 'someword' from "http://search.twitter.com/". // :twitter!@ // show mentions. // :twitter!+ someone // fav someone's last status.. // :twitter!- someone // un-fav someone's last status.. let PLUGIN_INFO = {NAME} The script allows you to update Twitter status from Vimperator 1.2.0 http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/twitter.js Trapezoid Creative Commons ; liberator.modules.twitter = (function(){ var statuses = null; var expiredStatus = false; var autoStatusUpdate = !!parseInt(liberator.globalVariables.twitter_auto_status_update || 0); var statusValidDuration = parseInt(liberator.globalVariables.twitter_status_valid_duration || 90); var statusRefreshTimer; var passwordManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); var evalFunc = window.eval; try { var sandbox = new Components.utils.Sandbox("about:blank"); if (Components.utils.evalInSandbox("true", sandbox) === true){ evalFunc = function(text){ return Components.utils.evalInSandbox(text, sandbox); }; } } catch (e){ liberator.log("warning: twitter.js is working with unsafe sandbox."); } function sprintf(format){ var i = 1, re = /%s/, result = "" + format; while (re.test(result) && i < arguments.length) result = result.replace(re, arguments[i++]); return result; } function getElementsByXPath(xpath, node){ node = node || document; var nodesSnapshot = (node.ownerDocument || node).evaluate(xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); var data = []; for(var i = 0, l = nodesSnapshot.snapshotLength; i < l; data.push(nodesSnapshot.snapshotItem(i++))); return (data.length > 0) ? data : null; } function getFirstElementByXPath(xpath, node){ node = node || document; var doc = node.ownerDocument; var result = (node.ownerDocument || node).evaluate(xpath, node, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null); return result.singleNodeValue ? result.singleNodeValue : null; } function sayTwitter(username, password, stat){ var sendData = ''; if (stat.match(/^(.*)@([^\s#]+)(?:#(\d+))(.*)$/)){ var [replyUser, replyID] = [RegExp.$2, RegExp.$3]; stat = RegExp.$1 + "@" + replyUser + RegExp.$4; sendData = "status=" + encodeURIComponent(stat) + "&in_reply_to_status_id=" + replyID; } else { sendData = "status=" + encodeURIComponent(stat); } sendData += "&source=Vimperator"; var xhr = new XMLHttpRequest(); xhr.open("POST", "https://twitter.com/statuses/update.json", false, username, password); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.send(sendData); liberator.echo("[Twitter] Your post " + '"' + stat + '" (' + stat.length + " characters) was sent. " ); } function favTwitter(username, password, user){ var xhr = new XMLHttpRequest(); xhr.open("GET", "https://twitter.com/statuses/user_timeline/" + user + ".json?count=1", false, username, password); xhr.send(null); xhr.open("POST", "https://twitter.com/favourings/create/" + window.eval(xhr.responseText)[0].id + ".json", false, username, password); xhr.send(null); } function unfavTwitter(username, password, user){ var xhr = new XMLHttpRequest(); xhr.open("GET", "https://twitter.com/statuses/user_timeline/" + user + ".json?count=1", false, username, password); xhr.send(null); xhr.open("DELETE", "https://twitter.com/favourings/destroy/" + window.eval(xhr.responseText)[0].id + ".json", false, username, password); xhr.send(null); } function showTwitterMentions(username, password){ var xhr = new XMLHttpRequest(); xhr.open("GET", "https://twitter.com/statuses/mentions.json", false, username, password); xhr.send(null); statuses = evalFunc(xhr.responseText); var html = .toSource() .replace(/(?:\r\n|[\r\n])[ \t]*/g, " ") + statuses.map(function(status) <> {status.user.screen_name} {status.user.name}‬ .toSource() .replace(/(?:\r\n|[\r\n])[ \t]*/g, " ") + sprintf(': %s‬', status.text)) .join("
"); //liberator.log(html); liberator.echo(html, true); } function getFollowersStatus(username, password, target, onComplete){ // for debug //target = "otsune" function setRefresher(){ expiredStatus = false; if (statusRefreshTimer) clearTimeout(statusRefreshTimer); statusRefreshTimer = setTimeout(function () expiredStatus = true, statusValidDuration * 1000); } var xhr = new XMLHttpRequest(); var endPoint = target ? "https://twitter.com/statuses/user_timeline/" + target + ".json" : "https://twitter.com/statuses/friends_timeline.json"; xhr.open("GET", endPoint, onComplete, username, password); liberator.log('get!'); if (onComplete) { xhr.onreadystatechange = function () { if (xhr.readyState == 4 && xhr.status == 200) { liberator.log('got!'); setRefresher(); onComplete(statuses = evalFunc(xhr.responseText) || []); } } } xhr.send(null); if (onComplete) return; setRefresher(); statuses = evalFunc(xhr.responseText) || []; } function showFollowersStatus(username, password, target){ // for debug //target = "otsune" getFollowersStatus.apply(null, arguments); var html = .toSource() .replace(/(?:\r\n|[\r\n])[ \t]*/g, " ") + statuses.map(function(status) <> {status.user.screen_name} {status.user.name}‬ : {detectLink(status.text)} .toSource() .replace(/(?:\r\n|[\r\n])[ \t]*/g, " ")) .join("
"); //liberator.log(html); liberator.echo(html, true); } function detectLink(str){ let m = str.match(/https?:\/\/\S+/); if (m) { let left = str.substr(0, m.index); let url = m[0]; let right = str.substring(m.index + m[0].length); return <>{detectLink(left)} {url} {detectLink(right)}; } return str; } function getAccount(){ try { var logins = passwordManager.findLogins({}, "http://twitter.com", "https://twitter.com", null); if (logins.length) return [logins[0].username, logins[0].password]; else throw "Twitter: account not found"; } catch (ex){ liberator.echoerr(ex); } } function showTwitterSearchResult(word){ var xhr = new XMLHttpRequest(); xhr.open("GET", "http://search.twitter.com/search.json?q=" + encodeURIComponent(word), false); xhr.send(null); var results = (evalFunc("("+xhr.responseText+")") || {"results":[]}).results; var html = .toSource() .replace(/(?:\r\n|[\r\n])[ \t]*/g, " ") + results.map(function(result) <> {result.from_user} {result.from_user}‬ : {result.text} .toSource() .replace(/(?:\r\n|[\r\n])[ \t]*/g, " ")) .join("
"); //liberator.log(html); liberator.echo(html, true); } liberator.modules.commands.addUserCommand(["twitter"], "Change Twitter status", function(arg){ var special = arg.bang; var [username, password] = getAccount(); arg = arg.string.replace(/%URL%/g, liberator.modules.buffer.URL) .replace(/%TITLE%/g, liberator.modules.buffer.title); if (special && arg.match(/^\?\s*(.*)/)) showTwitterSearchResult(RegExp.$1); else if (special && arg.match(/^\+\s*(.*)/)) favTwitter(username, password, RegExp.$1); else if (special && arg.match(/^-\s*(.*)/)) unfavTwitter(username, password, RegExp.$1); else if (special && arg.match(/^@/)) showTwitterMentions(username, password); else if (special || arg.length == 0) showFollowersStatus(username, password, arg); else sayTwitter(username, password, arg); },{ bang: true, literal: 0, completer: let (getting, targetContext) function(context, args){ function compl(){ if (args.bang){ targetContext.title = ["Name","Entry"]; list = statuses.map(function(s) ["@" + s.user.screen_name, s.text]); } else if (/RT\s+@\w*$/.test(args[0])){ targetContext.title = ["Name + Text"]; list = statuses.map(function(s) ["@" + s.user.screen_name + ": " + s.text, "-"]); } else { targetContext.title = ["Name#ID","Entry"]; list = statuses.map(function(s) ["@" + s.user.screen_name+ "#" + s.id + " ", s.text]); } if (target){ list = list.filter(function($_) $_[0].indexOf(target) >= 0); } targetContext.completions = list; targetContext.incomplete = false; targetContext = getting = null; } var matches= context.filter.match(/@(\w*)$/); if (!matches) return; var list = []; var target = matches[1]; var doGet = (expiredStatus || !(statuses && statuses.length)) && autoStatusUpdate; context.offset += matches.index; context.incomplete = doGet; context.hasitems = !doGet; targetContext = context; if (doGet) { if (!getting) { getting = true; var [username, password] = getAccount(); getFollowersStatus(username, password, null, compl); } } else { compl(); } } }, true ); let self = { get statuses(){ return statuses; }, }; return self; })(); // vim:sw=4 ts=4 et: lass="nx">refcontrol_enabled = "true" javascript <<EOM liberator.globalVariables.refcontrol={ [domain] : [param], '@DEFAULT' : '@FORGE', 'tumblr.com' : '@FORGE', 'del.icio.us': '@NORMAL', }; EOM ||< domain: '@DEFAULT' はデフォルト設定を指します param: @NORMAL: 通常の動作です @FORGE: ドメインのルートをリファラにセットします '': リファラーを送信しません url: 指定したURLでリファラーを送信します ]]></detail> </VimperatorPlugin>; liberator.plugins.RefControl = (function() { const Cc = Components.classes; const Ci = Components.interfaces; const ENABLE_ICON = 'data:image/png;base64,' + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMA/wAAAP+JwC+QAAAB' + 'qklEQVR4nI1RPWgUYRSct/stexc3xvX8udV4BhPUiI1RUsnVglgJCh52aaxs0llrpZVNWhVr' + 'wdZCFGwsohCRJKAmqLnoheDd5X52v2+/sUiEzfoTh6mGmfeY94RgVK3tGd537MKV8plJ1/Ow' + 'HbTpt7k3S8+eNBaX6i8fS7l6bWL60d6DSbC74DoAc36IICV6Hf39qzt797rqfZnjj8Xg6Enb' + 'h7Ug824A4qCoPM+s6PVl5YfRQCkyCWjzs7ctEQyWDvhhpEQUqEySn/07jBHHKyhLGk2jd3AD' + 'MJokFUmjrf7PgKUCJE2Z7hQgkRpYQqW60++2/cIuEedXx5wXAEmbJInpt5QfDm+sfx7aH5m+' + '2fqCiEAgApLg5jVc3201PvVWPwrBofHzp6cfhpURagcAjEVs0kRDBBAn8N0BadZX3t+/sfb6' + 'qRAEMDh6Lhg9G4xMQOT4xaluzKKTmk7cWquXxsbeztzc+DDbXHgFYCuQRVStxZ125ertsDLe' + 'XV2en5lqzj/PVuIfWTx86kjt3uSDuHzpVlb/a2CTwYnqoct3soqDf6K98MI23mWVn59g+pcs' + 'KKOSAAAAAElFTkSuQmCC'; const DISABLE_ICON = 'data:image/png;base64,' + 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMA/wAAAP+JwC+QAAAB' + 'kklEQVR4nIVSzWoiQRCu/hkdHRh7dfxZNmGH5CasV0/eosx75OL7+AI+wZK8hDcvySQkgouQ' + 'Q0RwMpdo07ZdOTRpREP2o/noqvq+ruqmCQLyYvn3r5+9Xi+KomKxSAhhjJVKJc/zdrvder3O' + 'smwymcxfXtV7ToCQ/tXVcDgMw5BSaow5ZQCQUo7H4783t/z87GwwGPi+r5SytSM4Z5IkaZpS' + 'IUSr1TLGSCkBQCl1xDZvjBFCCCG4LZzqvmRCCLVuKaWU0m6+YQCgxhg3vc26SZzOCpRSiEjd' + 'O7gbu8McnGe73XL5CUqpUxyF1q+U0lrz/X6/Wq2q1ap9DYtCoXAYAoDv+8vlkjHGsrfs+uU6' + 'juN6vc45Z4x5nqe11lojIiISQoIg4JyPRqOn5yeCgADQ+dMRQlQqlXK5jIi1Wq3ZbIZhuNls' + 'ZrPZYrHI8/w+TRENtx3v7u9c98uLyyAIkiSJoijP8+l0+vD44Kr89C/M/80BoAOdOI673W6j' + '0YDHgzICfrPa7Xa/3z/M/MeAgCz4cRh+AOxEaXXUX/5aAAAAAElFTkSuQmCC'; var sites; var _isEnable = false; const completer_params = [['', 'send referrer:nothing'], ['@FORGE', 'send referrer:top domain URL'], ['@NORMAL', 'send referrer:normal']]; // icon manager object var Class = function() function() {this.initialize.apply(this, arguments);}; var RefControl = new Class(); RefControl.prototype = { initialize : function() { this.panel = this.createPanel(); this.isEnable = eval(liberator.globalVariables.refcontrol_enabled) || false; }, createPanel: function() { var self = this; var panel = document.getElementById('refcontrol-status-panel'); if (panel) { let parent = panel.parentNode; parent.removeChild(panel); } panel = document.createElement('statusbarpanel'); panel.setAttribute('id', 'refcontrol-status-panel'); panel.setAttribute('class', 'statusbarpanel-iconic'); panel.setAttribute('src', self.isEnable ? ENABLE_ICON : DISABLE_ICON); panel.addEventListener('click', function(e) { self.isEnable = !self.isEnable; }, false); document.getElementById('status-bar').insertBefore( panel, document.getElementById('security-button').nextSibling); return panel; }, get isEnable() _isEnable, set isEnable(val) { this.panel.setAttribute('src', val ? ENABLE_ICON : DISABLE_ICON); _isEnable = val; }, }; // some utilities var init = function() { // read settings sites = liberator.globalVariables.refcontrol; if (typeof sites == 'undefined') sites = new Object(); if (typeof sites['@DEFAULT'] == 'undefined') sites['@DEFAULT'] = '@NORMAL'; }; var dump = function(obj) { var m = ''; for (let key in obj) { m+=key+':'+obj[key]+'\n'; } return m; }; init(); var manager = new RefControl(); // add user command commands.addUserCommand(['addref'], 'add referrer control setting', function(args) { var domain = args[0]; var perf = args[1] || ''; if (!domain || /[:\/]/.test(domain)) { liberator.echo(dump(sites)+'usage: addref [domain] [@NORMAL or @FORGE or empty]'); return; } sites[domain] = perf; }, { completer: function(context, args) { //var last = context.contextList.slice(-1)[0]; var list; var pos = 0; if (args.length == 2) { context.title = ['Params', 'Description']; list = completer_params; //pos = 1; } else if (args.length <= 1) { context.title = ['URL', 'Description']; list = [['@DEFAULT', 'default preference'], [window.content.location.host, '']]; } context.completions = list; context.advance(pos); } } ); commands.addUserCommand(['togglerefcontrol'], 'toggle referrer control on/off', function() { manager.isEnable = !manager.isEnable; }, {} ); // register refcontrol var adjustRef = function(http, site) { var sRef, refAction; try { refAction = sites[site]; if (refAction == undefined) return false; if (refAction.charAt(0) == '@') { switch (refAction) { case '@NORMAL': return true; case '@FORGE': sRef = http.URI.scheme + '://' + http.URI.hostPort + '/'; break; default: return false; } } else if (refAction.length > 0) sRef = refAction; http.setRequestHeader('Referer', sRef, false); if (http.referrer) http.referrer.spec = sRef; return true; } catch (e) {} return false; }; Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService).addObserver({ observe: function(subject, topic, data) { if (topic != 'http-on-modify-request') return; if (!_isEnable) return; var http = subject.QueryInterface(Ci.nsIHttpChannel); for (let s = http.URI.host; s != ''; s = s.replace(/^[^.]*(?:\.|$)/, '')) if (adjustRef(http, s)) return; adjustRef( http, '@DEFAULT'); } }, 'http-on-modify-request', false); return manager; })();