diff options
author | anekos | 2010-07-31 10:05:51 +0000 |
---|---|---|
committer | anekos | 2010-07-31 10:05:51 +0000 |
commit | c8ab80046950d97dabf196d5eaac0c2d1753358a (patch) | |
tree | 0cfd64423de442d0ba8c0ed568f40e4b83e7cf9c | |
parent | c3a75e1d2a1412134a1a564d18cd4c04fff36238 (diff) | |
download | vimperator-plugins-c8ab80046950d97dabf196d5eaac0c2d1753358a.tar.bz2 |
変数を外に出しすぎないようにする
git-svn-id: http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk@38081 d0d07461-0603-4401-acd4-de1884942a52
-rwxr-xr-x | twittperator.js | 2798 |
1 files changed, 1400 insertions, 1398 deletions
diff --git a/twittperator.js b/twittperator.js index 8392b93..f21c34c 100755 --- a/twittperator.js +++ b/twittperator.js @@ -27,1490 +27,1492 @@ * @see http://twitter.com/oauth_clients/details/197565 */ -// TwitterOauth for Greasemonkey -function TwitterOauth() { - this.initialize.apply(this, arguments); -} - -// OAuth {{{ -TwitterOauth.prototype = (function() { - - // {{{2 oauth.js - /* - OAuth.js - SHA-1.js - TwitterOauth for Greasemonkey - */ - - /* - * Copyright 2008 Netflix, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - /* Here's some JavaScript software for implementing OAuth. - - This isn't as useful as you might hope. OAuth is based around - allowing tools and websites to talk to each other. However, - JavaScript running in web browsers is hampered by security - restrictions that prevent code running on one website from - accessing data stored or served on another. - - Before you start hacking, make sure you understand the limitations - posed by cross-domain XMLHttpRequest. - - On the bright side, some platforms use JavaScript as their - language, but enable the programmer to access other web sites. - Examples include Google Gadgets, and Microsoft Vista Sidebar. - For those platforms, this library should come in handy. - */ - - // The HMAC-SHA1 signature method calls b64_hmac_sha1, defined by - // http://pajhome.org.uk/crypt/md5/sha1.js - - /* An OAuth message is represented as an object like this: - {method: "GET", action: "http://server.com/path", parameters: ...} - - The parameters may be either a map {name: value, name2: value2} - or an Array of name-value pairs [[name, value], [name2, value2]]. - The latter representation is more powerful: it supports parameters - in a specific sequence, or several parameters with the same name; - for example [["a", 1], ["b", 2], ["a", 3]]. - - Parameter names and values are NOT percent-encoded in an object. - They must be encoded before transmission and decoded after reception. - For example, this message object: - {method: "GET", action: "http://server/path", parameters: {p: "x y"}} - ... can be transmitted as an HTTP request that begins: - GET /path?p=x%20y HTTP/1.0 - (This isn't a valid OAuth request, since it lacks a signature etc.) - Note that the object "x y" is transmitted as x%20y. To encode - parameters, you can call OAuth.addToURL, OAuth.formEncode or - OAuth.getAuthorization. - - This message object model harmonizes with the browser object model for - input elements of an form, whose value property isn't percent encoded. - The browser encodes each value before transmitting it. For example, - see consumer.setInputs in example/consumer.js. - */ - - /* This script needs to know what time it is. By default, it uses the local - clock (new Date), which is apt to be inaccurate in browsers. To do - better, you can load this script from a URL whose query string contains - an oauth_timestamp parameter, whose value is a current Unix timestamp. - For example, when generating the enclosing document using PHP: - - <script src="oauth.js?oauth_timestamp=<?=time()?>" ... - - Another option is to call OAuth.correctTimestamp with a Unix timestamp. - */ - - - var OAuth; if (OAuth == null) OAuth = {}; - OAuth.setProperties = function setProperties(into, from) { - if (into != null && from != null) { - for (var key in from) { - into[key] = from[key]; - } - } - return into; +(function() { + // TwitterOauth for Greasemonkey + function TwitterOauth() { + this.initialize.apply(this, arguments); } - OAuth.setProperties(OAuth, // utility functions - { - percentEncode: function percentEncode(s) { - if (s == null) { - return ""; - } - if (s instanceof Array) { - var e = ""; - for (var i = 0; i < s.length; ++s) { - if (e != "") e += "&"; - e += OAuth.percentEncode(s[i]); - } - return e; - } - s = encodeURIComponent(s); - // Now replace the values which encodeURIComponent doesn't do - // encodeURIComponent ignores: - _ . ! ~ * ' ( ) - // OAuth dictates the only ones you can ignore are: - _ . ~ - // Source: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURIComponent - s = s.replace(/!/g, "%21"); - s = s.replace(/\*/g, "%2A"); - s = s.replace(/'/g, "%27"); - s = s.replace(/\(/g, "%28"); - s = s.replace(/\)/g, "%29"); - return s; - } - , - decodePercent: function decodePercent(s) { - if (s != null) { - // Handle application/x-www-form-urlencoded, which is defined by - // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 - s = s.replace(/\+/g, " "); - } - return decodeURIComponent(s); - } - , - /** Convert the given parameters to an Array of name-value pairs. */ - getParameterList: function getParameterList(parameters) { - if (parameters == null) { - return []; - } - if (typeof parameters != "object") { - return OAuth.decodeForm(parameters + ""); - } - if (parameters instanceof Array) { - return parameters; - } - var list = []; - for (var p in parameters) { - list.push([p, parameters[p]]); - } - return list; - } - , - /** Convert the given parameters to a map from name to value. */ - getParameterMap: function getParameterMap(parameters) { - if (parameters == null) { - return {}; - } - if (typeof parameters != "object") { - return OAuth.getParameterMap(OAuth.decodeForm(parameters + "")); - } - if (parameters instanceof Array) { - var map = {}; - for (var p = 0; p < parameters.length; ++p) { - var key = parameters[p][0]; - if (map[key] === undefined) { // first value wins - map[key] = parameters[p][1]; - } - } - return map; - } - return parameters; - } - , - getParameter: function getParameter(parameters, name) { - if (parameters instanceof Array) { - for (var p = 0; p < parameters.length; ++p) { - if (parameters[p][0] == name) { - return parameters[p][1]; // first value wins - } - } - } else { - return OAuth.getParameterMap(parameters)[name]; - } - return null; - } - , - formEncode: function formEncode(parameters) { - var form = ""; - var list = OAuth.getParameterList(parameters); - for (var p = 0; p < list.length; ++p) { - var value = list[p][1]; - if (value == null) value = ""; - if (form != "") form += "&"; - form += OAuth.percentEncode(list[p][0]) - +"="+ OAuth.percentEncode(value); - } - return form; - } - , - decodeForm: function decodeForm(form) { - var list = []; - var nvps = form.split("&"); - for (var n = 0; n < nvps.length; ++n) { - var nvp = nvps[n]; - if (nvp == "") { - continue; - } - var equals = nvp.indexOf("="); - var name; - var value; - if (equals < 0) { - name = OAuth.decodePercent(nvp); - value = null; - } else { - name = OAuth.decodePercent(nvp.substring(0, equals)); - value = OAuth.decodePercent(nvp.substring(equals + 1)); - } - list.push([name, value]); - } - return list; - } - , - setParameter: function setParameter(message, name, value) { - var parameters = message.parameters; - if (parameters instanceof Array) { - for (var p = 0; p < parameters.length; ++p) { - if (parameters[p][0] == name) { - if (value === undefined) { - parameters.splice(p, 1); - } else { - parameters[p][1] = value; - value = undefined; - } - } - } - if (value !== undefined) { - parameters.push([name, value]); - } - } else { - parameters = OAuth.getParameterMap(parameters); - parameters[name] = value; - message.parameters = parameters; - } - } - , - setParameters: function setParameters(message, parameters) { - var list = OAuth.getParameterList(parameters); - for (var i = 0; i < list.length; ++i) { - OAuth.setParameter(message, list[i][0], list[i][1]); - } - } - , - /** Fill in parameters to help construct a request message. - This function doesn't fill in every parameter. - The accessor object should be like: - {consumerKey:"foo", consumerSecret:"bar", accessorSecret:"nurn", token:"krelm", tokenSecret:"blah"} - The accessorSecret property is optional. - */ - completeRequest: function completeRequest(message, accessor) { - if (message.method == null) { - message.method = "GET"; - } - var map = OAuth.getParameterMap(message.parameters); - if (map.oauth_consumer_key == null) { - OAuth.setParameter(message, "oauth_consumer_key", accessor.consumerKey || ""); - } - if (map.oauth_token == null && accessor.token != null) { - OAuth.setParameter(message, "oauth_token", accessor.token); - } - if (map.oauth_version == null) { - OAuth.setParameter(message, "oauth_version", "1.0"); - } - if (map.oauth_timestamp == null) { - OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); - } - if (map.oauth_nonce == null) { - OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); - } - OAuth.SignatureMethod.sign(message, accessor); - } - , - setTimestampAndNonce: function setTimestampAndNonce(message) { - OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); - OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); - } - , - addToURL: function addToURL(url, parameters) { - newURL = url; - if (parameters != null) { - var toAdd = OAuth.formEncode(parameters); - if (toAdd.length > 0) { - var q = url.indexOf("?"); - if (q < 0) newURL += "?"; - else newURL += "&"; - newURL += toAdd; - } - } - return newURL; - } - , - /** Construct the value of the Authorization header for an HTTP request. */ - getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) { - var header = 'OAuth realm="' + OAuth.percentEncode(realm) + '"'; - var list = OAuth.getParameterList(parameters); - for (var p = 0; p < list.length; ++p) { - var parameter = list[p]; - var name = parameter[0]; - if (name.indexOf("oauth_") == 0) { - header += "," + OAuth.percentEncode(name) + '="' + OAuth.percentEncode(parameter[1]) + '"'; - } - } - return header; - } - , - /** Correct the time using a parameter from the URL from which the last script was loaded. */ - correctTimestampFromSrc: function correctTimestampFromSrc(parameterName) { - parameterName = parameterName || "oauth_timestamp"; - var scripts = document.getElementsByTagName("script"); - if (scripts == null || !scripts.length) return; - var src = scripts[scripts.length-1].src; - if (!src) return; - var q = src.indexOf("?"); - if (q < 0) return; - parameters = OAuth.getParameterMap(OAuth.decodeForm(src.substring(q+1))); - var t = parameters[parameterName]; - if (t == null) return; - OAuth.correctTimestamp(t); - } - , - /** Generate timestamps starting with the given value. */ - correctTimestamp: function correctTimestamp(timestamp) { - OAuth.timeCorrectionMsec = (timestamp * 1000) - (new Date()).getTime(); - } - , - /** The difference between the correct time and my clock. */ - timeCorrectionMsec: 0 - , - timestamp: function timestamp() { - var t = (new Date()).getTime() + OAuth.timeCorrectionMsec; - return Math.floor(t / 1000); - } - , - nonce: function nonce(length) { - var chars = OAuth.nonce.CHARS; - var result = ""; - for (var i = 0; i < length; ++i) { - var rnum = Math.floor(Math.random() * chars.length); - result += chars.substring(rnum, rnum+1); - } - return result; - } - }); - - OAuth.nonce.CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; - - /** Define a constructor function, - without causing trouble to anyone who was using it as a namespace. - That is, if parent[name] already existed and had properties, - copy those properties into the new constructor. - */ - OAuth.declareClass = function declareClass(parent, name, newConstructor) { - var previous = parent[name]; - parent[name] = newConstructor; - if (newConstructor != null && previous != null) { - for (var key in previous) { - if (key != "prototype") { - newConstructor[key] = previous[key]; - } - } - } - return newConstructor; - } + // OAuth {{{ + TwitterOauth.prototype = (function() { + + // {{{2 oauth.js + /* + OAuth.js + SHA-1.js + TwitterOauth for Greasemonkey + */ + + /* + * Copyright 2008 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + /* Here's some JavaScript software for implementing OAuth. + + This isn't as useful as you might hope. OAuth is based around + allowing tools and websites to talk to each other. However, + JavaScript running in web browsers is hampered by security + restrictions that prevent code running on one website from + accessing data stored or served on another. + + Before you start hacking, make sure you understand the limitations + posed by cross-domain XMLHttpRequest. + + On the bright side, some platforms use JavaScript as their + language, but enable the programmer to access other web sites. + Examples include Google Gadgets, and Microsoft Vista Sidebar. + For those platforms, this library should come in handy. + */ + + // The HMAC-SHA1 signature method calls b64_hmac_sha1, defined by + // http://pajhome.org.uk/crypt/md5/sha1.js + + /* An OAuth message is represented as an object like this: + {method: "GET", action: "http://server.com/path", parameters: ...} + + The parameters may be either a map {name: value, name2: value2} + or an Array of name-value pairs [[name, value], [name2, value2]]. + The latter representation is more powerful: it supports parameters + in a specific sequence, or several parameters with the same name; + for example [["a", 1], ["b", 2], ["a", 3]]. + + Parameter names and values are NOT percent-encoded in an object. + They must be encoded before transmission and decoded after reception. + For example, this message object: + {method: "GET", action: "http://server/path", parameters: {p: "x y"}} + ... can be transmitted as an HTTP request that begins: + GET /path?p=x%20y HTTP/1.0 + (This isn't a valid OAuth request, since it lacks a signature etc.) + Note that the object "x y" is transmitted as x%20y. To encode + parameters, you can call OAuth.addToURL, OAuth.formEncode or + OAuth.getAuthorization. + + This message object model harmonizes with the browser object model for + input elements of an form, whose value property isn't percent encoded. + The browser encodes each value before transmitting it. For example, + see consumer.setInputs in example/consumer.js. + */ + + /* This script needs to know what time it is. By default, it uses the local + clock (new Date), which is apt to be inaccurate in browsers. To do + better, you can load this script from a URL whose query string contains + an oauth_timestamp parameter, whose value is a current Unix timestamp. + For example, when generating the enclosing document using PHP: + + <script src="oauth.js?oauth_timestamp=<?=time()?>" ... + + Another option is to call OAuth.correctTimestamp with a Unix timestamp. + */ + + + var OAuth; if (OAuth == null) OAuth = {}; + OAuth.setProperties = function setProperties(into, from) { + if (into != null && from != null) { + for (var key in from) { + into[key] = from[key]; + } + } + return into; + } - /** An abstract algorithm for signing messages. */ - OAuth.declareClass(OAuth, "SignatureMethod", function OAuthSignatureMethod() {}); - - OAuth.setProperties(OAuth.SignatureMethod.prototype, // instance members - { - /** Add a signature to the message. */ - sign: function sign(message) { - var baseString = OAuth.SignatureMethod.getBaseString(message); - var signature = this.getSignature(baseString); - OAuth.setParameter(message, "oauth_signature", signature); - return signature; // just in case someone's interested - } - , - /** Set the key string for signing. */ - initialize: function initialize(name, accessor) { - var consumerSecret; - if (accessor.accessorSecret != null - && name.length > 9 - && name.substring(name.length-9) == "-Accessor") - { - consumerSecret = accessor.accessorSecret; - } else { - consumerSecret = accessor.consumerSecret; - } - this.key = OAuth.percentEncode(consumerSecret) - +"&"+ OAuth.percentEncode(accessor.tokenSecret); - } - }); - - /* SignatureMethod expects an accessor object to be like this: - {tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."} - The accessorSecret property is optional. - */ - // Class members: - OAuth.setProperties(OAuth.SignatureMethod, // class members - { - sign: function sign(message, accessor) { - var name = OAuth.getParameterMap(message.parameters).oauth_signature_method; - if (name == null || name == "") { - name = "HMAC-SHA1"; - OAuth.setParameter(message, "oauth_signature_method", name); - } - OAuth.SignatureMethod.newMethod(name, accessor).sign(message); - } - , - /** Instantiate a SignatureMethod for the given method name. */ - newMethod: function newMethod(name, accessor) { - var impl = OAuth.SignatureMethod.REGISTERED[name]; - if (impl != null) { - var method = new impl(); - method.initialize(name, accessor); - return method; - } - var err = new Error("signature_method_rejected"); - var acceptable = ""; - for (var r in OAuth.SignatureMethod.REGISTERED) { - if (acceptable != "") acceptable += "&"; - acceptable += OAuth.percentEncode(r); - } - err.oauth_acceptable_signature_methods = acceptable; - throw err; - } - , - /** A map from signature method name to constructor. */ - REGISTERED : {} - , - /** Subsequently, the given constructor will be used for the named methods. - The constructor will be called with no parameters. - The resulting object should usually implement getSignature(baseString). - You can easily define such a constructor by calling makeSubclass, below. - */ - registerMethodClass: function registerMethodClass(names, classConstructor) { - for (var n = 0; n < names.length; ++n) { - OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor; - } - } - , - /** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */ - makeSubclass: function makeSubclass(getSignatureFunction) { - var superClass = OAuth.SignatureMethod; - var subClass = function() { - superClass.call(this); - } - subClass.prototype = new superClass(); - // Delete instance variables from prototype: - // delete subclass.prototype... There aren't any. - subClass.prototype.getSignature = getSignatureFunction; - subClass.prototype.constructor = subClass; - return subClass; - } - , - getBaseString: function getBaseString(message) { - var URL = message.action; - var q = URL.indexOf("?"); - var parameters; - if (q < 0) { - parameters = message.parameters; - } else { - // Combine the URL query string with the other parameters: - parameters = OAuth.decodeForm(URL.substring(q + 1)); - var toAdd = OAuth.getParameterList(message.parameters); - for (var a = 0; a < toAdd.length; ++a) { - parameters.push(toAdd[a]); - } - } - return OAuth.percentEncode(message.method.toUpperCase()) - +"&"+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL)) - +"&"+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters)); - } - , - normalizeUrl: function normalizeUrl(url) { - var uri = OAuth.SignatureMethod.parseUri(url); - var scheme = uri.protocol.toLowerCase(); - var authority = uri.authority.toLowerCase(); - var dropPort = (scheme == "http" && uri.port == 80) - || (scheme == "https" && uri.port == 443); - if (dropPort) { - // find the last : in the authority - var index = authority.lastIndexOf(":"); - if (index >= 0) { - authority = authority.substring(0, index); - } - } - var path = uri.path; - if (!path) { - path = "/"; // conforms to RFC 2616 section 3.2.2 - } - // we know that there is no query and no fragment here. - return scheme + "://" + authority + path; - } - , - parseUri: function parseUri(str) { - /* This function was adapted from parseUri 1.2.1 - http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js - */ - var o = {key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], - parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }}; - var m = o.parser.strict.exec(str); - var uri = {}; - var i = 14; - while (i--) uri[o.key[i]] = m[i] || ""; - return uri; - } - , - normalizeParameters: function normalizeParameters(parameters) { - if (parameters == null) { - return ""; - } - var list = OAuth.getParameterList(parameters); - var sortable = []; - for (var p = 0; p < list.length; ++p) { - var nvp = list[p]; - if (nvp[0] != "oauth_signature") { - sortable.push([ OAuth.percentEncode(nvp[0]) - + " " // because it comes before any character that can appear in a percentEncoded string. - + OAuth.percentEncode(nvp[1]) - , nvp]); - } - } - sortable.sort(function(a,b) { - if (a[0] < b[0]) return -1; - if (a[0] > b[0]) return 1; - return 0; - }); - var sorted = []; - for (var s = 0; s < sortable.length; ++s) { - sorted.push(sortable[s][1]); - } - return OAuth.formEncode(sorted); - } - }); + OAuth.setProperties(OAuth, // utility functions + { + percentEncode: function percentEncode(s) { + if (s == null) { + return ""; + } + if (s instanceof Array) { + var e = ""; + for (var i = 0; i < s.length; ++s) { + if (e != "") e += "&"; + e += OAuth.percentEncode(s[i]); + } + return e; + } + s = encodeURIComponent(s); + // Now replace the values which encodeURIComponent doesn't do + // encodeURIComponent ignores: - _ . ! ~ * ' ( ) + // OAuth dictates the only ones you can ignore are: - _ . ~ + // Source: http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Functions:encodeURIComponent + s = s.replace(/!/g, "%21"); + s = s.replace(/\*/g, "%2A"); + s = s.replace(/'/g, "%27"); + s = s.replace(/\(/g, "%28"); + s = s.replace(/\)/g, "%29"); + return s; + } + , + decodePercent: function decodePercent(s) { + if (s != null) { + // Handle application/x-www-form-urlencoded, which is defined by + // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 + s = s.replace(/\+/g, " "); + } + return decodeURIComponent(s); + } + , + /** Convert the given parameters to an Array of name-value pairs. */ + getParameterList: function getParameterList(parameters) { + if (parameters == null) { + return []; + } + if (typeof parameters != "object") { + return OAuth.decodeForm(parameters + ""); + } + if (parameters instanceof Array) { + return parameters; + } + var list = []; + for (var p in parameters) { + list.push([p, parameters[p]]); + } + return list; + } + , + /** Convert the given parameters to a map from name to value. */ + getParameterMap: function getParameterMap(parameters) { + if (parameters == null) { + return {}; + } + if (typeof parameters != "object") { + return OAuth.getParameterMap(OAuth.decodeForm(parameters + "")); + } + if (parameters instanceof Array) { + var map = {}; + for (var p = 0; p < parameters.length; ++p) { + var key = parameters[p][0]; + if (map[key] === undefined) { // first value wins + map[key] = parameters[p][1]; + } + } + return map; + } + return parameters; + } + , + getParameter: function getParameter(parameters, name) { + if (parameters instanceof Array) { + for (var p = 0; p < parameters.length; ++p) { + if (parameters[p][0] == name) { + return parameters[p][1]; // first value wins + } + } + } else { + return OAuth.getParameterMap(parameters)[name]; + } + return null; + } + , + formEncode: function formEncode(parameters) { + var form = ""; + var list = OAuth.getParameterList(parameters); + for (var p = 0; p < list.length; ++p) { + var value = list[p][1]; + if (value == null) value = ""; + if (form != "") form += "&"; + form += OAuth.percentEncode(list[p][0]) + +"="+ OAuth.percentEncode(value); + } + return form; + } + , + decodeForm: function decodeForm(form) { + var list = []; + var nvps = form.split("&"); + for (var n = 0; n < nvps.length; ++n) { + var nvp = nvps[n]; + if (nvp == "") { + continue; + } + var equals = nvp.indexOf("="); + var name; + var value; + if (equals < 0) { + name = OAuth.decodePercent(nvp); + value = null; + } else { + name = OAuth.decodePercent(nvp.substring(0, equals)); + value = OAuth.decodePercent(nvp.substring(equals + 1)); + } + list.push([name, value]); + } + return list; + } + , + setParameter: function setParameter(message, name, value) { + var parameters = message.parameters; + if (parameters instanceof Array) { + for (var p = 0; p < parameters.length; ++p) { + if (parameters[p][0] == name) { + if (value === undefined) { + parameters.splice(p, 1); + } else { + parameters[p][1] = value; + value = undefined; + } + } + } + if (value !== undefined) { + parameters.push([name, value]); + } + } else { + parameters = OAuth.getParameterMap(parameters); + parameters[name] = value; + message.parameters = parameters; + } + } + , + setParameters: function setParameters(message, parameters) { + var list = OAuth.getParameterList(parameters); + for (var i = 0; i < list.length; ++i) { + OAuth.setParameter(message, list[i][0], list[i][1]); + } + } + , + /** Fill in parameters to help construct a request message. + This function doesn't fill in every parameter. + The accessor object should be like: + {consumerKey:"foo", consumerSecret:"bar", accessorSecret:"nurn", token:"krelm", tokenSecret:"blah"} + The accessorSecret property is optional. + */ + completeRequest: function completeRequest(message, accessor) { + if (message.method == null) { + message.method = "GET"; + } + var map = OAuth.getParameterMap(message.parameters); + if (map.oauth_consumer_key == null) { + OAuth.setParameter(message, "oauth_consumer_key", accessor.consumerKey || ""); + } + if (map.oauth_token == null && accessor.token != null) { + OAuth.setParameter(message, "oauth_token", accessor.token); + } + if (map.oauth_version == null) { + OAuth.setParameter(message, "oauth_version", "1.0"); + } + if (map.oauth_timestamp == null) { + OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); + } + if (map.oauth_nonce == null) { + OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); + } + OAuth.SignatureMethod.sign(message, accessor); + } + , + setTimestampAndNonce: function setTimestampAndNonce(message) { + OAuth.setParameter(message, "oauth_timestamp", OAuth.timestamp()); + OAuth.setParameter(message, "oauth_nonce", OAuth.nonce(6)); + } + , + addToURL: function addToURL(url, parameters) { + newURL = url; + if (parameters != null) { + var toAdd = OAuth.formEncode(parameters); + if (toAdd.length > 0) { + var q = url.indexOf("?"); + if (q < 0) newURL += "?"; + else newURL += "&"; + newURL += toAdd; + } + } + return newURL; + } + , + /** Construct the value of the Authorization header for an HTTP request. */ + getAuthorizationHeader: function getAuthorizationHeader(realm, parameters) { + var header = 'OAuth realm="' + OAuth.percentEncode(realm) + '"'; + var list = OAuth.getParameterList(parameters); + for (var p = 0; p < list.length; ++p) { + var parameter = list[p]; + var name = parameter[0]; + if (name.indexOf("oauth_") == 0) { + header += "," + OAuth.percentEncode(name) + '="' + OAuth.percentEncode(parameter[1]) + '"'; + } + } + return header; + } + , + /** Correct the time using a parameter from the URL from which the last script was loaded. */ + correctTimestampFromSrc: function correctTimestampFromSrc(parameterName) { + parameterName = parameterName || "oauth_timestamp"; + var scripts = document.getElementsByTagName("script"); + if (scripts == null || !scripts.length) return; + var src = scripts[scripts.length-1].src; + if (!src) return; + var q = src.indexOf("?"); + if (q < 0) return; + parameters = OAuth.getParameterMap(OAuth.decodeForm(src.substring(q+1))); + var t = parameters[parameterName]; + if (t == null) return; + OAuth.correctTimestamp(t); + } + , + /** Generate timestamps starting with the given value. */ + correctTimestamp: function correctTimestamp(timestamp) { + OAuth.timeCorrectionMsec = (timestamp * 1000) - (new Date()).getTime(); + } + , + /** The difference between the correct time and my clock. */ + timeCorrectionMsec: 0 + , + timestamp: function timestamp() { + var t = (new Date()).getTime() + OAuth.timeCorrectionMsec; + return Math.floor(t / 1000); + } + , + nonce: function nonce(length) { + var chars = OAuth.nonce.CHARS; + var result = ""; + for (var i = 0; i < length; ++i) { + var rnum = Math.floor(Math.random() * chars.length); + result += chars.substring(rnum, rnum+1); + } + return result; + } + }); - OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"], - OAuth.SignatureMethod.makeSubclass( - function getSignature(baseString) { - return this.key; - } - )); - - OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"], - OAuth.SignatureMethod.makeSubclass( - function getSignature(baseString) { - b64pad = "="; - var signature = b64_hmac_sha1(this.key, baseString); - return signature; - } - )); - - OAuth.correctTimestamp(new Date()); - // oauth.js 2}}} - // {{{2 sha1.js - /* - * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined - * in FIPS PUB 180-1 - * Version 2.1a Copyright Paul Johnston 2000 - 2002. - * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet - * Distributed under the BSD License - * See http://pajhome.org.uk/crypt/md5 for details. - */ - - /* - * Configurable variables. You may need to tweak these to be compatible with - * the server-side, but the defaults work in most cases. - */ - var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ - var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ - var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ - - /* - * These are the functions you'll usually want to call - * They take string arguments and return either hex or base-64 encoded strings - */ - function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} - function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} - function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} - function hex_hmac_sha1(key, data){return binb2hex(core_hmac_sha1(key, data));} - function b64_hmac_sha1(key, data){return binb2b64(core_hmac_sha1(key, data));} - function str_hmac_sha1(key, data){return binb2str(core_hmac_sha1(key, data));} - - /* - * Perform a simple self-test to see if the VM is working - */ - function sha1_vm_test() - { - return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; - } + OAuth.nonce.CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz"; + + /** Define a constructor function, + without causing trouble to anyone who was using it as a namespace. + That is, if parent[name] already existed and had properties, + copy those properties into the new constructor. + */ + OAuth.declareClass = function declareClass(parent, name, newConstructor) { + var previous = parent[name]; + parent[name] = newConstructor; + if (newConstructor != null && previous != null) { + for (var key in previous) { + if (key != "prototype") { + newConstructor[key] = previous[key]; + } + } + } + return newConstructor; + } + + /** An abstract algorithm for signing messages. */ + OAuth.declareClass(OAuth, "SignatureMethod", function OAuthSignatureMethod() {}); - /* - * Calculate the SHA-1 of an array of big-endian words, and a bit length - */ - function core_sha1(x, len) - { - /* append padding */ - x[len >> 5] |= 0x80 << (24 - len % 32); - x[((len + 64 >> 9) << 4) + 15] = len; - - var w = Array(80); - var a = 1732584193; - var b = -271733879; - var c = -1732584194; - var d = 271733878; - var e = -1009589776; - - for (var i = 0; i < x.length; i += 16) + OAuth.setProperties(OAuth.SignatureMethod.prototype, // instance members { - var olda = a; - var oldb = b; - var oldc = c; - var oldd = d; - var olde = e; + /** Add a signature to the message. */ + sign: function sign(message) { + var baseString = OAuth.SignatureMethod.getBaseString(message); + var signature = this.getSignature(baseString); + OAuth.setParameter(message, "oauth_signature", signature); + return signature; // just in case someone's interested + } + , + /** Set the key string for signing. */ + initialize: function initialize(name, accessor) { + var consumerSecret; + if (accessor.accessorSecret != null + && name.length > 9 + && name.substring(name.length-9) == "-Accessor") + { + consumerSecret = accessor.accessorSecret; + } else { + consumerSecret = accessor.consumerSecret; + } + this.key = OAuth.percentEncode(consumerSecret) + +"&"+ OAuth.percentEncode(accessor.tokenSecret); + } + }); - for (var j = 0; j < 80; j++) - { - if (j < 16) w[j] = x[i + j]; - else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); - var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), - safe_add(safe_add(e, w[j]), sha1_kt(j))); - e = d; - d = c; - c = rol(b, 30); - b = a; - a = t; - } + /* SignatureMethod expects an accessor object to be like this: + {tokenSecret: "lakjsdflkj...", consumerSecret: "QOUEWRI..", accessorSecret: "xcmvzc..."} + The accessorSecret property is optional. + */ + // Class members: + OAuth.setProperties(OAuth.SignatureMethod, // class members + { + sign: function sign(message, accessor) { + var name = OAuth.getParameterMap(message.parameters).oauth_signature_method; + if (name == null || name == "") { + name = "HMAC-SHA1"; + OAuth.setParameter(message, "oauth_signature_method", name); + } + OAuth.SignatureMethod.newMethod(name, accessor).sign(message); + } + , + /** Instantiate a SignatureMethod for the given method name. */ + newMethod: function newMethod(name, accessor) { + var impl = OAuth.SignatureMethod.REGISTERED[name]; + if (impl != null) { + var method = new impl(); + method.initialize(name, accessor); + return method; + } + var err = new Error("signature_method_rejected"); + var acceptable = ""; + for (var r in OAuth.SignatureMethod.REGISTERED) { + if (acceptable != "") acceptable += "&"; + acceptable += OAuth.percentEncode(r); + } + err.oauth_acceptable_signature_methods = acceptable; + throw err; + } + , + /** A map from signature method name to constructor. */ + REGISTERED : {} + , + /** Subsequently, the given constructor will be used for the named methods. + The constructor will be called with no parameters. + The resulting object should usually implement getSignature(baseString). + You can easily define such a constructor by calling makeSubclass, below. + */ + registerMethodClass: function registerMethodClass(names, classConstructor) { + for (var n = 0; n < names.length; ++n) { + OAuth.SignatureMethod.REGISTERED[names[n]] = classConstructor; + } + } + , + /** Create a subclass of OAuth.SignatureMethod, with the given getSignature function. */ + makeSubclass: function makeSubclass(getSignatureFunction) { + var superClass = OAuth.SignatureMethod; + var subClass = function() { + superClass.call(this); + } + subClass.prototype = new superClass(); + // Delete instance variables from prototype: + // delete subclass.prototype... There aren't any. + subClass.prototype.getSignature = getSignatureFunction; + subClass.prototype.constructor = subClass; + return subClass; + } + , + getBaseString: function getBaseString(message) { + var URL = message.action; + var q = URL.indexOf("?"); + var parameters; + if (q < 0) { + parameters = message.parameters; + } else { + // Combine the URL query string with the other parameters: + parameters = OAuth.decodeForm(URL.substring(q + 1)); + var toAdd = OAuth.getParameterList(message.parameters); + for (var a = 0; a < toAdd.length; ++a) { + parameters.push(toAdd[a]); + } + } + return OAuth.percentEncode(message.method.toUpperCase()) + +"&"+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeUrl(URL)) + +"&"+ OAuth.percentEncode(OAuth.SignatureMethod.normalizeParameters(parameters)); + } + , + normalizeUrl: function normalizeUrl(url) { + var uri = OAuth.SignatureMethod.parseUri(url); + var scheme = uri.protocol.toLowerCase(); + var authority = uri.authority.toLowerCase(); + var dropPort = (scheme == "http" && uri.port == 80) + || (scheme == "https" && uri.port == 443); + if (dropPort) { + // find the last : in the authority + var index = authority.lastIndexOf(":"); + if (index >= 0) { + authority = authority.substring(0, index); + } + } + var path = uri.path; + if (!path) { + path = "/"; // conforms to RFC 2616 section 3.2.2 + } + // we know that there is no query and no fragment here. + return scheme + "://" + authority + path; + } + , + parseUri: function parseUri(str) { + /* This function was adapted from parseUri 1.2.1 + http://stevenlevithan.com/demo/parseuri/js/assets/parseuri.js + */ + var o = {key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], + parser: {strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/ }}; + var m = o.parser.strict.exec(str); + var uri = {}; + var i = 14; + while (i--) uri[o.key[i]] = m[i] || ""; + return uri; + } + , + normalizeParameters: function normalizeParameters(parameters) { + if (parameters == null) { + return ""; + } + var list = OAuth.getParameterList(parameters); + var sortable = []; + for (var p = 0; p < list.length; ++p) { + var nvp = list[p]; + if (nvp[0] != "oauth_signature") { + sortable.push([ OAuth.percentEncode(nvp[0]) + + " " // because it comes before any character that can appear in a percentEncoded string. + + OAuth.percentEncode(nvp[1]) + , nvp]); + } + } + sortable.sort(function(a,b) { + if (a[0] < b[0]) return -1; + if (a[0] > b[0]) return 1; + return 0; + }); + var sorted = []; + for (var s = 0; s < sortable.length; ++s) { + sorted.push(sortable[s][1]); + } + return OAuth.formEncode(sorted); + } + }); - a = safe_add(a, olda); - b = safe_add(b, oldb); - c = safe_add(c, oldc); - d = safe_add(d, oldd); - e = safe_add(e, olde); + OAuth.SignatureMethod.registerMethodClass(["PLAINTEXT", "PLAINTEXT-Accessor"], + OAuth.SignatureMethod.makeSubclass( + function getSignature(baseString) { + return this.key; + } + )); + + OAuth.SignatureMethod.registerMethodClass(["HMAC-SHA1", "HMAC-SHA1-Accessor"], + OAuth.SignatureMethod.makeSubclass( + function getSignature(baseString) { + b64pad = "="; + var signature = b64_hmac_sha1(this.key, baseString); + return signature; + } + )); + + OAuth.correctTimestamp(new Date()); + // oauth.js 2}}} + // {{{2 sha1.js + /* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1a Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + + /* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ + var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ + var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ + var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + + /* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ + function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} + function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} + function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} + function hex_hmac_sha1(key, data){return binb2hex(core_hmac_sha1(key, data));} + function b64_hmac_sha1(key, data){return binb2b64(core_hmac_sha1(key, data));} + function str_hmac_sha1(key, data){return binb2str(core_hmac_sha1(key, data));} + + /* + * Perform a simple self-test to see if the VM is working + */ + function sha1_vm_test() + { + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; } - return Array(a, b, c, d, e); - } + /* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ + function core_sha1(x, len) + { + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for (var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for (var j = 0; j < 80; j++) + { + if (j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } - /* - * Perform the appropriate triplet combination function for the current - * iteration - */ - function sha1_ft(t, b, c, d) - { - if (t < 20) return (b & c) | ((~b) & d); - if (t < 40) return b ^ c ^ d; - if (t < 60) return (b & c) | (b & d) | (c & d); - return b ^ c ^ d; - } + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); - /* - * Determine the appropriate additive constant for the current iteration - */ - function sha1_kt(t) - { - return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : - (t < 60) ? -1894007588 : -899497514; - } + } - /* - * Calculate the HMAC-SHA1 of a key and some data - */ - function core_hmac_sha1(key, data) - { - var bkey = str2binb(key); - if (bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + /* + * Perform the appropriate triplet combination function for the current + * iteration + */ + function sha1_ft(t, b, c, d) + { + if (t < 20) return (b & c) | ((~b) & d); + if (t < 40) return b ^ c ^ d; + if (t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; + } - var ipad = Array(16), opad = Array(16); - for (var i = 0; i < 16; i++) + /* + * Determine the appropriate additive constant for the current iteration + */ + function sha1_kt(t) { - ipad[i] = bkey[i] ^ 0x36363636; - opad[i] = bkey[i] ^ 0x5C5C5C5C; + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; } - var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); - return core_sha1(opad.concat(hash), 512 + 160); - } + /* + * Calculate the HMAC-SHA1 of a key and some data + */ + function core_hmac_sha1(key, data) + { + var bkey = str2binb(key); + if (bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); - /* - * Add integers, wrapping at 2^32. This uses 16-bit operations internally - * to work around bugs in some JS interpreters. - */ - function safe_add(x, y) - { - var lsw = (x & 0xFFFF) + (y & 0xFFFF); - var msw = (x >> 16) + (y >> 16) + (lsw >> 16); - return (msw << 16) | (lsw & 0xFFFF); - } + var ipad = Array(16), opad = Array(16); + for (var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } - /* - * Bitwise rotate a 32-bit number to the left. - */ - function rol(num, cnt) - { - return (num << cnt) | (num >>> (32 - cnt)); - } + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); + } - /* - * Convert an 8-bit or 16-bit string to an array of big-endian words - * In 8-bit function, characters >255 have their hi-byte silently ignored. - */ - function str2binb(str) - { - var bin = Array(); - var mask = (1 << chrsz) - 1; - for (var i = 0; i < str.length * chrsz; i += chrsz) - bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); - return bin; - } + /* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ + function safe_add(x, y) + { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } - /* - * Convert an array of big-endian words to a string - */ - function binb2str(bin) - { - var str = ""; - var mask = (1 << chrsz) - 1; - for (var i = 0; i < bin.length * 32; i += chrsz) - str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); - return str; - } + /* + * Bitwise rotate a 32-bit number to the left. + */ + function rol(num, cnt) + { + return (num << cnt) | (num >>> (32 - cnt)); + } - /* - * Convert an array of big-endian words to a hex string. - */ - function binb2hex(binarray) - { - var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; - var str = ""; - for (var i = 0; i < binarray.length * 4; i++) + /* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ + function str2binb(str) { - str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + - hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + var bin = Array(); + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); + return bin; + } + + /* + * Convert an array of big-endian words to a string + */ + function binb2str(bin) + { + var str = ""; + var mask = (1 << chrsz) - 1; + for (var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); + return str; } - return str; - } - /* - * Convert an array of big-endian words to a base-64 string - */ - function binb2b64(binarray) - { - var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - var str = ""; - for (var i = 0; i < binarray.length * 4; i += 3) + /* + * Convert an array of big-endian words to a hex string. + */ + function binb2hex(binarray) { - var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) - | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) - | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); - for (var j = 0; j < 4; j++) + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i++) { - if (i * 8 + j * 6 > binarray.length * 32) str += b64pad; - else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); } + return str; } - return str; - } - // sha1.js 2}}} - - let p = { - initialize: function(accessor) { - if (accessor) - this.accessor = accessor; - if (this.isAuthorize()) { - setup(); - } else { - preSetup(); - } - }, - // temp for request - request : { - token :"",// response oauth_token - tokenSecret: ""// response oauth_token_secret - }, - // トークンが取得済みかの真偽値を返す - isAuthorize : function() { - let accessor = this.getAccessor(); - if (accessor.consumerKey && accessor.consumerSecret && - accessor.token && accessor.tokenSecret) { - return true; - }else{ - return false; + + /* + * Convert an array of big-endian words to a base-64 string + */ + function binb2b64(binarray) + { + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for (var j = 0; j < 4; j++) + { + if (i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } } - }, - getAccessor: function() { - return { - consumerKey: this.accessor.get("consumerKey",""), - consumerSecret: this.accessor.get("consumerSecret",""), - token: this.accessor.get("token",""), - tokenSecret: this.accessor.get("tokenSecret","") - }; - }, - deleteAccessor : function() { - var clientInfo = { - clientName: this.accessor.get("clientName", ""), - consumerKey: this.accessor.get("consumerKey",""), - consumerSecret: this.accessor.get("consumerSecret",""), - }; - this.accessor.clear(); - this.accessor.set("clientName", clientInfo.clientName); - this.accessor.set("consumerKey", clientInfo.consumerKey); - this.accessor.set("consumerSecret", clientInfo.consumerSecret); - }, - // 認証ページのURLを取得 - getRequestToken : function(callback) { - let message = { - method: "GET", - action: "https://twitter.com/oauth/request_token", - parameters: { - oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey","") + return str; + } + // sha1.js 2}}} + + let p = { + initialize: function(accessor) { + if (accessor) + this.accessor = accessor; + if (this.isAuthorize()) { + setup(); + } else { + preSetup(); } - }; - OAuth.setTimestampAndNonce(message); - OAuth.SignatureMethod.sign(message, this.getAccessor()); - var target = OAuth.addToURL(message.action, message.parameters); - var self = this; - var options = { - method: message.method, - url: target, - onload: function(d) { - if (d.status == 200) { - var res = d.responseText; - var parameter = self.getParameter(res); - self.request.token = parameter["oauth_token"]; - self.request.tokenSecret = parameter["oauth_token_secret"]; - // requestURLを引数にcallback - if (callback) { - callback("https://twitter.com/oauth/authorize?oauth_token="+self.request.token); + }, + // temp for request + request : { + token :"",// response oauth_token + tokenSecret: ""// response oauth_token_secret + }, + // トークンが取得済みかの真偽値を返す + isAuthorize : function() { + let accessor = this.getAccessor(); + if (accessor.consumerKey && accessor.consumerSecret && + accessor.token && accessor.tokenSecret) { + return true; + }else{ + return false; + } + }, + getAccessor: function() { + return { + consumerKey: this.accessor.get("consumerKey",""), + consumerSecret: this.accessor.get("consumerSecret",""), + token: this.accessor.get("token",""), + tokenSecret: this.accessor.get("tokenSecret","") + }; + }, + deleteAccessor : function() { + var clientInfo = { + clientName: this.accessor.get("clientName", ""), + consumerKey: this.accessor.get("consumerKey",""), + consumerSecret: this.accessor.get("consumerSecret",""), + }; + this.accessor.clear(); + this.accessor.set("clientName", clientInfo.clientName); + this.accessor.set("consumerKey", clientInfo.consumerKey); + this.accessor.set("consumerSecret", clientInfo.consumerSecret); + }, + // 認証ページのURLを取得 + getRequestToken : function(callback) { + let message = { + method: "GET", + action: "https://twitter.com/oauth/request_token", + parameters: { + oauth_signature_method: "HMAC-SHA1", + oauth_consumer_key: this.accessor.get("consumerKey","") + } + }; + OAuth.setTimestampAndNonce(message); + OAuth.SignatureMethod.sign(message, this.getAccessor()); + var target = OAuth.addToURL(message.action, message.parameters); + var self = this; + var options = { + method: message.method, + url: target, + onload: function(d) { + if (d.status == 200) { + var res = d.responseText; + var parameter = self.getParameter(res); + self.request.token = parameter["oauth_token"]; + self.request.tokenSecret = parameter["oauth_token_secret"]; + // requestURLを引数にcallback + if (callback) { + callback("https://twitter.com/oauth/authorize?oauth_token="+self.request.token); + } + }else{ + alert(d.statusText); } - }else{ - alert(d.statusText); + }, + }; + xmlhttpRequest(options); + + }, + setPin: function(pin) { + let self = this; + this.getAccessToken(pin, function() { + liberator.echo("Twittperator: getting access token is success.", true); + self.initialize(); + }); + }, + // pinを元にAccess Tokenを取得して保存、callbackにはaccessorオブジェクトを渡す + getAccessToken: function(pin, callback) { + var message = { + method: "GET", + action: "https://twitter.com/oauth/access_token", + parameters: { + oauth_signature_method: "HMAC-SHA1", + oauth_consumer_key: this.accessor.get("consumerKey",""), + oauth_token: this.request.token, // Request Token + oauth_verifier: pin } - }, - }; - xmlhttpRequest(options); - - }, - setPin: function(pin) { - let self = this; - this.getAccessToken(pin, function() { - liberator.echo("Twittperator: getting access token is success.", true); - self.initialize(); - }); - }, - // pinを元にAccess Tokenを取得して保存、callbackにはaccessorオブジェクトを渡す - getAccessToken: function(pin, callback) { - var message = { - method: "GET", - action: "https://twitter.com/oauth/access_token", - parameters: { - oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey",""), - oauth_token: this.request.token, // Request Token - oauth_verifier: pin - } - }; - OAuth.setTimestampAndNonce(message); - OAuth.SignatureMethod.sign(message, this.request); - var target = OAuth.addToURL(message.action, message.parameters); - var self = this; - var options = { - method: message.method, - url: target, - onload: function(d) { - if (d.status == 200) { - /* 返り値からAccess Token/Access Token Secretを取り出す */ - var res = d.responseText; - var parameter = self.getParameter(res); - self.accessor.set("token", parameter["oauth_token"]); - self.accessor.set("tokenSecret", parameter["oauth_token_secret"]); - // Accessorの保存 - //self.saveAccessor(); - if (callback) { - callback(self.accessor); + }; + OAuth.setTimestampAndNonce(message); + OAuth.SignatureMethod.sign(message, this.request); + var target = OAuth.addToURL(message.action, message.parameters); + var self = this; + var options = { + method: message.method, + url: target, + onload: function(d) { + if (d.status == 200) { + /* 返り値からAccess Token/Access Token Secretを取り出す */ + var res = d.responseText; + var parameter = self.getParameter(res); + self.accessor.set("token", parameter["oauth_token"]); + self.accessor.set("tokenSecret", parameter["oauth_token_secret"]); + // Accessorの保存 + //self.saveAccessor(); + if (callback) { + callback(self.accessor); + } + }else{ + alert(d.statusText); } - }else{ - alert(d.statusText); + }, + }; + + xmlhttpRequest(options); // 送信 + }, + // api+?+query にアクセスした結果をcallbackに渡す + get: function(api, query, callback) { + var btquery = (query)? "?"+this.buildQuery(query) : ""; + var message = { + method: "GET", + action: api + btquery, + parameters: { + oauth_signature_method: "HMAC-SHA1", + oauth_consumer_key: this.accessor.get("consumerKey",""),// queryの構築 + oauth_token: this.accessor.get("token","") // Access Token } - }, - }; - - xmlhttpRequest(options); // 送信 - }, - // api+?+query にアクセスした結果をcallbackに渡す - get: function(api, query, callback) { - var btquery = (query)? "?"+this.buildQuery(query) : ""; - var message = { - method: "GET", - action: api + btquery, - parameters: { - oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey",""),// queryの構築 - oauth_token: this.accessor.get("token","") // Access Token - } - }; - OAuth.setTimestampAndNonce(message); - OAuth.SignatureMethod.sign(message, this.getAccessor()); - var target = OAuth.addToURL(message.action, message.parameters); - var options = { - method: message.method, - url: target, - onload: function(d) { - if (d.status == 200) { - if (callback) { - callback(d.responseText); + }; + OAuth.setTimestampAndNonce(message); + OAuth.SignatureMethod.sign(message, this.getAccessor()); + var target = OAuth.addToURL(message.action, message.parameters); + var options = { + method: message.method, + url: target, + onload: function(d) { + if (d.status == 200) { + if (callback) { + callback(d.responseText); + } + }else{ + callback(d.statusText); } - }else{ - callback(d.statusText); + }, + }; + xmlhttpRequest(options); // 送信 + }, + post: function(api, content, callback) { + var message = { + method: "POST", + action: api, + parameters: { + oauth_signature_method: "HMAC-SHA1", + oauth_consumer_key: this.accessor.get("consumerKey",""), + oauth_token: this.accessor.get("token","") // Access Token } - }, - }; - xmlhttpRequest(options); // 送信 - }, - post: function(api, content, callback) { - var message = { - method: "POST", - action: api, - parameters: { - oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey",""), - oauth_token: this.accessor.get("token","") // Access Token + }; + // 送信するデータをパラメータに追加する + for ( var key in content ) { + message.parameters[key] = content[key]; } - }; - // 送信するデータをパラメータに追加する - for ( var key in content ) { - message.parameters[key] = content[key]; - } - OAuth.setTimestampAndNonce(message); - OAuth.SignatureMethod.sign(message, this.getAccessor()); - var target = OAuth.addToURL(message.action, message.parameters); - var options = { - method: message.method, - url: target, - onload: function(d) { - if (d.status == 200) { - if (callback) { - callback(d.responseText); + OAuth.setTimestampAndNonce(message); + OAuth.SignatureMethod.sign(message, this.getAccessor()); + var target = OAuth.addToURL(message.action, message.parameters); + var options = { + method: message.method, + url: target, + onload: function(d) { + if (d.status == 200) { + if (callback) { + callback(d.responseText); + } + } else { + // typeof d == object + callback(d); + } + } + }; + xmlhttpRequest(options); // 送信 + }, + getUrl: function (api) { + var message = { + method: "GET", + action: api, + parameters: { + oauth_signature_method: "HMAC-SHA1", + oauth_consumer_key: this.accessor.get("consumerKey",""),// queryの構築 + oauth_token: this.accessor.get("token","") // Access Token + } + }; + OAuth.setTimestampAndNonce(message); + OAuth.SignatureMethod.sign(message, this.getAccessor()); + return OAuth.addToURL(message.action, message.parameters); + }, + // utility関数 + // http://kevin.vanzonneveld.net + urlEncode : function(str) { + str = (str+"").toString(); + return encodeURIComponent(str).replace(/!/g, "%21").replace(/"/g, "%27").replace(/\(/g, "%28") + .replace(/\)/g, "%29").replace(/\*/g, "%2A").replace(/%20/g, "+"); + }, + // オブジェクトからクエリを生成 + buildQuery : function(formdata, numeric_prefix, arg_separator) { + // * example 1: http_build_query({foo: "bar", php: "hypertext processor", baz: "boom", cow: "milk"}, "", "&"); + // * returns 1: "foo=bar&php=hypertext+processor&baz=boom&cow=milk" + // * example 2: http_build_query({"php": "hypertext processor", 0: "foo", 1: "bar", 2: "baz", 3: "boom", "cow": "milk"}, "myvar_"); + // * returns 2: "php=hypertext+processor&myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_3=boom&cow=milk" + var value, key, tmp = []; + var self = this; + var _http_build_query_helper = function(key, val, arg_separator) { + var k, tmp = []; + if (val === true) { + val = "1"; + } else if (val === false) { + val = "0"; + } + if (val !== null && typeof val === "object") { + for (k in val) { + if (val[k] !== null) { + tmp.push(_http_build_query_helper(key + "[" + k + "]", val[k], arg_separator)); + } } + return tmp.join(arg_separator); + } else if (typeof val !== "function") { + return self.urlEncode(key) + "=" + self.urlEncode(val); } else { - // typeof d == object - callback(d); + throw new Error("There was an error processing for http_build_query()."); } } - }; - xmlhttpRequest(options); // 送信 - }, - getUrl: function (api) { - var message = { - method: "GET", - action: api, - parameters: { - oauth_signature_method: "HMAC-SHA1", - oauth_consumer_key: this.accessor.get("consumerKey",""),// queryの構築 - oauth_token: this.accessor.get("token","") // Access Token - } - }; - OAuth.setTimestampAndNonce(message); - OAuth.SignatureMethod.sign(message, this.getAccessor()); - return OAuth.addToURL(message.action, message.parameters); - }, - // utility関数 - // http://kevin.vanzonneveld.net - urlEncode : function(str) { - str = (str+"").toString(); - return encodeURIComponent(str).replace(/!/g, "%21").replace(/"/g, "%27").replace(/\(/g, "%28") - .replace(/\)/g, "%29").replace(/\*/g, "%2A").replace(/%20/g, "+"); - }, - // オブジェクトからクエリを生成 - buildQuery : function(formdata, numeric_prefix, arg_separator) { - // * example 1: http_build_query({foo: "bar", php: "hypertext processor", baz: "boom", cow: "milk"}, "", "&"); - // * returns 1: "foo=bar&php=hypertext+processor&baz=boom&cow=milk" - // * example 2: http_build_query({"php": "hypertext processor", 0: "foo", 1: "bar", 2: "baz", 3: "boom", "cow": "milk"}, "myvar_"); - // * returns 2: "php=hypertext+processor&myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_3=boom&cow=milk" - var value, key, tmp = []; - var self = this; - var _http_build_query_helper = function(key, val, arg_separator) { - var k, tmp = []; - if (val === true) { - val = "1"; - } else if (val === false) { - val = "0"; + + if (!arg_separator) { + arg_separator = "&"; } - if (val !== null && typeof val === "object") { - for (k in val) { - if (val[k] !== null) { - tmp.push(_http_build_query_helper(key + "[" + k + "]", val[k], arg_separator)); - } + for (key in formdata) { + value = formdata[key]; + if (numeric_prefix && !isNaN(key)) { + key = String(numeric_prefix) + key; } - return tmp.join(arg_separator); - } else if (typeof val !== "function") { - return self.urlEncode(key) + "=" + self.urlEncode(val); - } else { - throw new Error("There was an error processing for http_build_query()."); + tmp.push(_http_build_query_helper(key, value, arg_separator)); } - } - if (!arg_separator) { - arg_separator = "&"; - } - for (key in formdata) { - value = formdata[key]; - if (numeric_prefix && !isNaN(key)) { - key = String(numeric_prefix) + key; + return tmp.join(arg_separator); + }, + // Query String から 連想配列を返す + getParameter: function(str) { + var dec = decodeURIComponent; + var par = {}, itm; + if (typeof str == "undefined") return par; + if (str.indexOf("?", 0) > -1) str = str.split("?")[1]; + str = str.split("&"); + for (var i = 0; str.length > i; i++){ + itm = str[i].split("="); + if (itm[0] != "") { + par[itm[0]] = typeof itm[1] == "undefined" ? true : dec(itm[1]); + } } - tmp.push(_http_build_query_helper(key, value, arg_separator)); - } + return par; + } + }; + return p; + })(); + // }}} + + // ChirpUserStream // {{{ + // XXX if (0) の部分は認証に対するテストコード + let ChirpUserStream = (function() { + function getUserInfo() { + let host = ["http://twitter.com", "https://twitter.com"]; + let loginManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); + let login = loginManager.findLogins({}, host[0], host[1], null)[0]; + return login; + } - return tmp.join(arg_separator); - }, - // Query String から 連想配列を返す - getParameter: function(str) { - var dec = decodeURIComponent; - var par = {}, itm; - if (typeof str == "undefined") return par; - if (str.indexOf("?", 0) > -1) str = str.split("?")[1]; - str = str.split("&"); - for (var i = 0; str.length > i; i++){ - itm = str[i].split("="); - if (itm[0] != "") { - par[itm[0]] = typeof itm[1] == "undefined" ? true : dec(itm[1]); - } - } - return par; - } - }; - return p; -})(); -// }}} - -// ChirpUserStream // {{{ -// XXX if (0) の部分は認証に対するテストコード -let ChirpUserStream = (function() { - function getUserInfo() { - let host = ["http://twitter.com", "https://twitter.com"]; - let loginManager = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); - let login = loginManager.findLogins({}, host[0], host[1], null)[0]; - return login; - } + function extractURL(s) + let (m = s.match(/https?:\/\/[\S]+/)) + (m && m[0]); - function extractURL(s) - let (m = s.match(/https?:\/\/[\S]+/)) - (m && m[0]); + function stop() { + let prev = __context__.prev; - function stop() { - let prev = __context__.prev; + if (!prev) + return; - if (!prev) - return; + clearInterval(prev.interval); + prev.sos.close(); + prev.sis.close(); - clearInterval(prev.interval); - prev.sos.close(); - prev.sis.close(); + delete __context__.prev; + } - delete __context__.prev; - } + function start() { + stop(); + + let host = "chirpstream.twitter.com"; + let path = "/2b/user.json"; - function start() { - stop(); + if (0) { + host = "api.twitter.com"; + host = "/1/statuses/mentions.json"; + } - let host = "chirpstream.twitter.com"; - let path = "/2b/user.json"; + let {username, password} = getUserInfo() || {}; + if (!(username && password)) + return liberator.echoerr('Not found basic authorization setting in Firefox'); + + let socketService = + let (stsvc = Cc["@mozilla.org/network/socket-transport-service;1"]) + let (svc = stsvc.getService()) + svc.QueryInterface(Ci["nsISocketTransportService"]); + + let transport = socketService.createTransport(null, 0, host, 80, null); + let os = transport.openOutputStream(0, 0, 0); + let is = transport.openInputStream(0, 0, 0); + let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream); + let sos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); + + sis.init(is); + sos.setOutputStream(os); + + let params = ["Authorization: Basic " + btoa(username + ":" + password)]; + if (0) { + let param = tw.getUrl("http://" + host + path); + let params = param.split(/\?/)[1].split(/&/).map(function (it) + let ([n, v] = it.split(/=/)) + n + ": " + decodeURIComponent(v) + ); + } - if (0) { - host = "api.twitter.com"; - host = "/1/statuses/mentions.json"; - } + let get = [ + "GET " + path + " HTTP/1.1", + "Host: " + host, + params.join("\n"), + "" + ].join("\n"); + get += "\n\n"; + + sos.write(get, get.length); + + let buf = ""; + let interval = setInterval(function () { + let len = sis.available(); + if (len <= 0) + return; + let data = sis.read(len); + let lines = data.split(/\n/); + if (lines.length > 2) { + lines[0] = buf + lines[0]; + for (let [, line] in Iterator(lines.slice(0, -1))) { + try { + onMsg(JSON.parse(line), line); + } catch (e) {} + } + buf = lines.slice(-1)[0]; + } else { + buf += data; + } + }, 500); - let {username, password} = getUserInfo() || {}; - if (!(username && password)) - return liberator.echoerr('Not found basic authorization setting in Firefox'); - - let socketService = - let (stsvc = Cc["@mozilla.org/network/socket-transport-service;1"]) - let (svc = stsvc.getService()) - svc.QueryInterface(Ci["nsISocketTransportService"]); - - let transport = socketService.createTransport(null, 0, host, 80, null); - let os = transport.openOutputStream(0, 0, 0); - let is = transport.openInputStream(0, 0, 0); - let sis = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream); - let sos = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream); - - sis.init(is); - sos.setOutputStream(os); - - let params = ["Authorization: Basic " + btoa(username + ":" + password)]; - if (0) { - let param = tw.getUrl("http://" + host + path); - let params = param.split(/\?/)[1].split(/&/).map(function (it) - let ([n, v] = it.split(/=/)) - n + ": " + decodeURIComponent(v) - ); + __context__.prev = { + sos: sos, + sis: sis, + interval: interval, + }; } - let get = [ - "GET " + path + " HTTP/1.1", - "Host: " + host, - params.join("\n"), - "" - ].join("\n"); - get += "\n\n"; + function onMsg (msg, raw) { + if (msg.text) { + let talk = msg.user.screen_name + ": " + msg.text; - sos.write(get, get.length); + liberator.echo(talk, commandline.FORCE_SINGLELINE); - let buf = ""; - let interval = setInterval(function () { - let len = sis.available(); - if (len <= 0) - return; - let data = sis.read(len); - let lines = data.split(/\n/); - if (lines.length > 2) { - lines[0] = buf + lines[0]; - for (let [, line] in Iterator(lines.slice(0, -1))) { - try { - onMsg(JSON.parse(line), line); - } catch (e) {} - } - buf = lines.slice(-1)[0]; + history.unshift(msg); + if (history.length > 1000) + history = history.slice(0, 1000); + + if (plugins.namakubi) + plugins.namakubi.talk(talk); } else { - buf += data; + let s = []; + for (let [n, v] in Iterator(msg)) + s.push(n + ": " + (n == "user" ? v.screen_name : v)); + liberator.log(s.join("\n")); } - }, 500); + } - __context__.prev = { - sos: sos, - sis: sis, - interval: interval, + return { + start: start, + stop: stop }; - } - - function onMsg (msg, raw) { - if (msg.text) { - let talk = msg.user.screen_name + ": " + msg.text; + })(); // }}} + + // Twittperator + function xmlhttpRequest(options) { // {{{ + let xhr = new XMLHttpRequest(); + xhr.open(options.method, options.url, true); + if (typeof options.onload == "function") { + xhr.onload = function() { + options.onload(xhr); + } + } + xhr.send(null); + } // }}} - liberator.echo(talk, commandline.FORCE_SINGLELINE); + // Variables {{{ + let setting = { + useChirp: liberator.globalVariables.twittperator_use_chirp + }; - history.unshift(msg); - if (history.length > 1000) - history = history.slice(0, 1000); + let accessor = storage.newMap("twittperator", { store: true }); + accessor.set("clientName", "Twittperator"); + accessor.set("consumerKey", "GQWob4E5tCHVQnEVPvmorQ"); + accessor.set("consumerSecret", "gVwj45GaW6Sp7gdua6UFyiF910ffIety0sD1dv36Cz8"); - if (plugins.namakubi) - plugins.namakubi.talk(talk); - } else { - let s = []; - for (let [n, v] in Iterator(msg)) - s.push(n + ": " + (n == "user" ? v.screen_name : v)); - liberator.log(s.join("\n")); - } + let history; + if (__context__.hasOwnProperty('history')) { + history = __context__.history; + liberator.registerObserver('exit', function () accessor.set("history", history)); + } else { + history = __context__.history = accessor.get("history", []); } - return { - start: start, - stop: stop - }; -})(); // }}} - -// Twittperator -function xmlhttpRequest(options) { // {{{ - let xhr = new XMLHttpRequest(); - xhr.open(options.method, options.url, true); - if (typeof options.onload == "function") { - xhr.onload = function() { - options.onload(xhr); + let tw = new TwitterOauth(accessor); + + let expiredStatus = true; + let autoStatusUpdate = !!parseInt(liberator.globalVariables.twittperator_auto_status_update || 0); + let statusValidDuration = parseInt(liberator.globalVariables.twitperator_status_valid_duration || 90); + let statusRefreshTimer; + // }}} + + function showTL(s) { // {{{ + function unescapeBrakets(str) + str.replace(/</g, "<").replace(/>/g, ">"); + + let html = <style type="text/css"><![CDATA[ + .twitter.user { vertical-align: top; } + .twitter.entry-content { white-space: normal !important; } + .twitter.entry-content a { text-decoration: none; } + .twitter.entry-content.rt:before { content: "RT "; color: silver; } + img.twitter.photo { border; 0px; width: 16px; height: 16px; vertical-align: baseline; margin: 1px; } + ]]></style>.toSource() + .replace(/(?:\r\n|[\r\n])[ \t]*/g, " ") + + s.reduce(function(table, status) { + return table.appendChild( + ("retweeted_status" in status) ? + let (rt = status.retweeted_status) + <tr> + <td class="twitter user"> + <img src={rt.user.profile_image_url} alt={rt.user.screen_name} class="twitter photo"/> + <strong>{rt.user.screen_name}‬</strong> + <img src={status.user.profile_image_url} alt={status.user.screen_name} class="twitter photo"/> + </td><td class="twitter entry-content rt"> + {detectLink(unescapeBrakets(rt.text))} + </td> + </tr> : + <tr> + <td class="twitter user"> + <img src={status.user.profile_image_url} alt={status.user.screen_name} class="twitter photo"/> + <strong title={status.user.name}>{status.user.screen_name}‬</strong> + </td><td class="twitter entry-content"> + {detectLink(unescapeBrakets(status.text))} + </td> + </tr> + ); + + }, <table/>) + .toSource().replace(/(?:\r\n|[\r\n])[ \t]*/g, " "); + + //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)}<a highlight="URL" href={url}> {url} </a>{detectLink(right)}</>; + } + return str; + } // }}} + function showTwitterSearchResult(word) { // {{{ + tw.get("http://search.twitter.com/search.json", { q: word }, function(text) { + let results = JSON.parse(text); + showTL(results); + }); + } // }}} + function getFollowersStatus(target, force, onload) { // {{{ + function setRefresher(){ + expiredStatus = false; + if (statusRefreshTimer) + clearTimeout(statusRefreshTimer); + statusRefreshTimer = setTimeout(function() expiredStatus = true, statusValidDuration * 1000); } - } - xhr.send(null); -} // }}} - -// Variables {{{ -let setting = { - useChirp: liberator.globalVariables.twittperator_use_chirp -}; - -let accessor = storage.newMap("twittperator", { store: true }); -accessor.set("clientName", "Twittperator"); -accessor.set("consumerKey", "GQWob4E5tCHVQnEVPvmorQ"); -accessor.set("consumerSecret", "gVwj45GaW6Sp7gdua6UFyiF910ffIety0sD1dv36Cz8"); - -let history; -if (__context__.hasOwnProperty('history')) { - history = __context__.history; - liberator.registerObserver('exit', function () accessor.set("history", history)); -} else { - history = __context__.history = accessor.get("history", []); -} - -let tw = new TwitterOauth(accessor); -plugins.twittperator = tw; - -let expiredStatus = true; -let autoStatusUpdate = !!parseInt(liberator.globalVariables.twittperator_auto_status_update || 0); -let statusValidDuration = parseInt(liberator.globalVariables.twitperator_status_valid_duration || 90); -let statusRefreshTimer; -// }}} - -function showTL(s) { // {{{ - function unescapeBrakets(str) - str.replace(/</g, "<").replace(/>/g, ">"); - - let html = <style type="text/css"><![CDATA[ - .twitter.user { vertical-align: top; } - .twitter.entry-content { white-space: normal !important; } - .twitter.entry-content a { text-decoration: none; } - .twitter.entry-content.rt:before { content: "RT "; color: silver; } - img.twitter.photo { border; 0px; width: 16px; height: 16px; vertical-align: baseline; margin: 1px; } - ]]></style>.toSource() - .replace(/(?:\r\n|[\r\n])[ \t]*/g, " ") + - s.reduce(function(table, status) { - return table.appendChild( - ("retweeted_status" in status) ? - let (rt = status.retweeted_status) - <tr> - <td class="twitter user"> - <img src={rt.user.profile_image_url} alt={rt.user.screen_name} class="twitter photo"/> - <strong>{rt.user.screen_name}‬</strong> - <img src={status.user.profile_image_url} alt={status.user.screen_name} class="twitter photo"/> - </td><td class="twitter entry-content rt"> - {detectLink(unescapeBrakets(rt.text))} - </td> - </tr> : - <tr> - <td class="twitter user"> - <img src={status.user.profile_image_url} alt={status.user.screen_name} class="twitter photo"/> - <strong title={status.user.name}>{status.user.screen_name}‬</strong> - </td><td class="twitter entry-content"> - {detectLink(unescapeBrakets(status.text))} - </td> - </tr> - ); - - }, <table/>) - .toSource().replace(/(?:\r\n|[\r\n])[ \t]*/g, " "); - - //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)}<a highlight="URL" href={url}> {url} </a>{detectLink(right)}</>; - } - return str; -} // }}} -function showTwitterSearchResult(word) { // {{{ - tw.get("http://search.twitter.com/search.json", { q: word }, function(text) { - let results = JSON.parse(text); - showTL(results); - }); -} // }}} -function getFollowersStatus(target, force, onload) { // {{{ - function setRefresher(){ - expiredStatus = false; - if (statusRefreshTimer) - clearTimeout(statusRefreshTimer); - statusRefreshTimer = setTimeout(function() expiredStatus = true, statusValidDuration * 1000); - } - - if (!force && !expiredStatus && history.length > 0) { - onload(history); - } else { - let api = "http://api.twitter.com/1/statuses/home_timeline.json", query = {}; - if (target) { - api = "http://api.twitter.com/1/statuses/user_timeline.json"; - query.screen_name = target; + if (!force && !expiredStatus && history.length > 0) { + onload(history); } else { - query = null; - if (setting.useChirp) { - onload(history); - return; + let api = "http://api.twitter.com/1/statuses/home_timeline.json", query = {}; + + if (target) { + api = "http://api.twitter.com/1/statuses/user_timeline.json"; + query.screen_name = target; + } else { + query = null; + if (setting.useChirp) { + onload(history); + return; + } } - } - tw.get(api, query, function(text) { - setRefresher(); - // TODO 履歴をちゃんと "追記" するようにするようにするべき - onload(history = JSON.parse(text)); + tw.get(api, query, function(text) { + setRefresher(); + // TODO 履歴をちゃんと "追記" するようにするようにするべき + onload(history = JSON.parse(text)); + }); + } + } // }}} + function showFollowersStatus(arg, force) { // {{{ + getFollowersStatus(arg, force, function(statuses) { + showTL(statuses); }); - } -} // }}} -function showFollowersStatus(arg, force) { // {{{ - getFollowersStatus(arg, force, function(statuses) { - showTL(statuses); - }); -} // }}} - -// XXX 引数には何の意味が? -function showTwitterMentions(arg) { // {{{ - if (/^@/.test(arg)) - arg = arg.substr(1); - tw.get("http://api.twitter.com/1/statuses/mentions.json", null, function(text) { - showTL(JSON.parse(text)); - }); -} // }}} -function favTwitter(id) { // {{{ - tw.post("http://api.twitter.com/1/favorites/create/" + id + ".json", null, function(text) { - let res = JSON.parse(text); - liberator.echo("[Twittperator] fav: " + res.user.name + " " + res.text, true); - }); -} // }}} -function unfavTwitter(id) { // {{{ - tw.post("http://api.twitter.com/1/favorites/destroy/" + id + ".json", null, function(text) { - let res = JSON.parse(text); - liberator.echo("[Twittperator] unfav: " + res.user.name + " " + res.text, true); - }); -} // }}} -function sayTwitter(stat) { // {{{ - let sendData = {}; - if (stat.match(/^(.*)@([^\s#]+)(?:#(\d+))(.*)$/)) { - let [prefix, replyUser, replyID, postfix] = [RegExp.$1, RegExp.$2, RegExp.$3, RegExp.$4]; - if (stat.indexOf("RT @" + replyUser + "#" + replyID) == 0) { - ReTweet(replyID); - return; + } // }}} + + // XXX 引数には何の意味が? + function showTwitterMentions(arg) { // {{{ + if (/^@/.test(arg)) + arg = arg.substr(1); + tw.get("http://api.twitter.com/1/statuses/mentions.json", null, function(text) { + showTL(JSON.parse(text)); + }); + } // }}} + function favTwitter(id) { // {{{ + tw.post("http://api.twitter.com/1/favorites/create/" + id + ".json", null, function(text) { + let res = JSON.parse(text); + liberator.echo("[Twittperator] fav: " + res.user.name + " " + res.text, true); + }); + } // }}} + function unfavTwitter(id) { // {{{ + tw.post("http://api.twitter.com/1/favorites/destroy/" + id + ".json", null, function(text) { + let res = JSON.parse(text); + liberator.echo("[Twittperator] unfav: " + res.user.name + " " + res.text, true); + }); + } // }}} + function sayTwitter(stat) { // {{{ + let sendData = {}; + if (stat.match(/^(.*)@([^\s#]+)(?:#(\d+))(.*)$/)) { + let [prefix, replyUser, replyID, postfix] = [RegExp.$1, RegExp.$2, RegExp.$3, RegExp.$4]; + if (stat.indexOf("RT @" + replyUser + "#" + replyID) == 0) { + ReTweet(replyID); + return; + } + stat = prefix + "@" + replyUser + postfix; + if (replyID && !prefix) + sendData.in_reply_to_status_id = replyID; } - stat = prefix + "@" + replyUser + postfix; - if (replyID && !prefix) - sendData.in_reply_to_status_id = replyID; - } - sendData.status = stat; - sendData.source = "Twittperator"; - tw.post("http://api.twitter.com/1/statuses/update.json", sendData, function(text) { - let result = JSON.parse(text || "{}"); - let t = result.text; - liberator.echo("[Twittperator] Your post " + '"' + t + '" (' + t.length + " characters) was sent.", true); - }); -} // }}} -function ReTweet(id) { // {{{ - let url = "http://api.twitter.com/1/statuses/retweet/" + id + ".json"; - tw.post(url, null, function(text) { - let res = JSON.parse(text); - liberator.log(res.toSource(), 0); - liberator.echo("[Twittperator] ReTweet: " + res.retweeted_status.text, true); - }); -} // }}} -function setup() { // {{{ - function commandCompelter(context, args) { - function statusObjectFilter(item) - let (desc = item.description) - (this.match(desc.user.screen_name) || this.match(desc.text)); - - context.createRow = function(item, highlightGroup) { - let desc = item[1] || this.process[1].call(this, item, item.description); - - if (desc && desc.user) { + sendData.status = stat; + sendData.source = "Twittperator"; + tw.post("http://api.twitter.com/1/statuses/update.json", sendData, function(text) { + let result = JSON.parse(text || "{}"); + let t = result.text; + liberator.echo("[Twittperator] Your post " + '"' + t + '" (' + t.length + " characters) was sent.", true); + }); + } // }}} + function ReTweet(id) { // {{{ + let url = "http://api.twitter.com/1/statuses/retweet/" + id + ".json"; + tw.post(url, null, function(text) { + let res = JSON.parse(text); + liberator.log(res.toSource(), 0); + liberator.echo("[Twittperator] ReTweet: " + res.retweeted_status.text, true); + }); + } // }}} + function setup() { // {{{ + function commandCompelter(context, args) { + function statusObjectFilter(item) + let (desc = item.description) + (this.match(desc.user.screen_name) || this.match(desc.text)); + + context.createRow = function(item, highlightGroup) { + let desc = item[1] || this.process[1].call(this, item, item.description); + + if (desc && desc.user) { + return <div highlight={highlightGroup || "CompItem"} style="white-space: nowrap"> + <li highlight="CompDesc"> + <img src={desc.user.profile_image_url} style="max-width: 24px; max-height: 24px"/> +  {desc.user.screen_name}: {desc.text} + </li> + </div>; + } + return <div highlight={highlightGroup || "CompItem"} style="white-space: nowrap"> - <li highlight="CompDesc"> - <img src={desc.user.profile_image_url} style="max-width: 24px; max-height: 24px"/> -  {desc.user.screen_name}: {desc.text} - </li> + <li highlight="CompDesc">{desc} </li> </div>; - } + }; - return <div highlight={highlightGroup || "CompItem"} style="white-space: nowrap"> - <li highlight="CompDesc">{desc} </li> - </div>; - }; + if (args.bang && !/^[-+]/.test(args[0])) { + context.title = ["Name","Entry"]; + list = history.map(function(s) ("retweeted_status" in s) ? + ["@" + s.retweeted_status.user.screen_name, s] : + ["@" + s.user.screen_name, s]); + } else if (/(?:^|\b)RT\s+@.*$/.test(args[0])) { + context.title = ["Name + Text"]; + list = history.map(function(s) ("retweeted_status" in s) ? + ["@" + s.retweeted_status.user.screen_name + "#" + s.retweeted_status.id + + ": " + s.retweeted_status.text, s] : + ["@" + s.user.screen_name + "#" + s.id + ": " + s.text, s]); + } else { + context.title = ["Name#ID","Entry"]; + list = history.map(function(s) ("retweeted_status" in s) ? + ["@" + s.retweeted_status.user.screen_name + "#" + s.retweeted_status.id + " ", s] : + ["@" + s.user.screen_name+"#" + s.id + " ", s]); + } - if (args.bang && !/^[-+]/.test(args[0])) { - context.title = ["Name","Entry"]; - list = history.map(function(s) ("retweeted_status" in s) ? - ["@" + s.retweeted_status.user.screen_name, s] : - ["@" + s.user.screen_name, s]); - } else if (/(?:^|\b)RT\s+@.*$/.test(args[0])) { - context.title = ["Name + Text"]; - list = history.map(function(s) ("retweeted_status" in s) ? - ["@" + s.retweeted_status.user.screen_name + "#" + s.retweeted_status.id + - ": " + s.retweeted_status.text, s] : - ["@" + s.user.screen_name + "#" + s.id + ": " + s.text, s]); - } else { - context.title = ["Name#ID","Entry"]; - list = history.map(function(s) ("retweeted_status" in s) ? - ["@" + s.retweeted_status.user.screen_name + "#" + s.retweeted_status.id + " ", s] : - ["@" + s.user.screen_name+"#" + s.id + " ", s]); + // 本文でも検索できるように、@ はなかったことにする + context.filter = context.filter.replace(/^@/, ''); + context.completions = list; + context.filters = [statusObjectFilter]; + context.incomplete = false; } - // 本文でも検索できるように、@ はなかったことにする - context.filter = context.filter.replace(/^@/, ''); - context.completions = list; - context.filters = [statusObjectFilter]; - context.incomplete = false; - } + function subCommandCompleter(context, args) { + if (!args.bang || context.filter.length > 0) + return; - function subCommandCompleter(context, args) { - if (!args.bang || context.filter.length > 0) - return; - - context.title = ["Sub command", "Description"]; - context.completions = [ - ["@", "Show mentions"], - ["?", "Twitter search"], - ["+", "Fav a tweet"], - ["-", "Unfav a tweet"], - ]; - } + context.title = ["Sub command", "Description"]; + context.completions = [ + ["@", "Show mentions"], + ["?", "Twitter search"], + ["+", "Fav a tweet"], + ["-", "Unfav a tweet"], + ]; + } - commands.addUserCommand(["tw[ittperator]"], "Twittperator command", - function(args) { - let bang = args.bang; - let arg = args.literalArg.replace(/%URL%/g, liberator.modules.buffer.URL) - .replace(/%TITLE%/g, liberator.modules.buffer.title); - - if (bang && arg.match(/^\?\s*(.*)/)) - showTwitterSearchResult(RegExp.$1); - else - if (bang && arg.match(/^\+.*#(\d+)/)) - favTwitter(RegExp.$1); - else - if (bang && arg.match(/^-.*#(\d+)/)) - unfavTwitter(RegExp.$1); - else - if (bang && arg.match(/^@/)) - showTwitterMentions(arg); - else - if (bang || arg.length == 0) - showFollowersStatus(arg, bang); - else - sayTwitter(arg); - }, { - bang: true, - literal: 0, - completer: let (getting) function(context, args) { - context.fork('File', 0, context, function (context) subCommandCompleter(context, args)); - - let list = []; - let doGet = (expiredStatus || !(history && history.length)) && autoStatusUpdate; - - let matches = args.bang ? args.literalArg.match(/([-+?])/) - : args.literalArg.match(/(RT\s|)@/); - if (!args.bang && !matches) - return; - context.offset += matches ? matches.index + matches[1].length : 0; - context.incomplete = doGet; - context.hasitems = !doGet; - targetContext = context; - if (doGet) { - if (!getting) { - getting = true; - getFollowersStatus(null, function() { - getting = false; - context.fork('Twittperator', 0, context, function (context) commandCompelter(context, args)); - }); + commands.addUserCommand(["tw[ittperator]"], "Twittperator command", + function(args) { + let bang = args.bang; + let arg = args.literalArg.replace(/%URL%/g, liberator.modules.buffer.URL) + .replace(/%TITLE%/g, liberator.modules.buffer.title); + + if (bang && arg.match(/^\?\s*(.*)/)) + showTwitterSearchResult(RegExp.$1); + else + if (bang && arg.match(/^\+.*#(\d+)/)) + favTwitter(RegExp.$1); + else + if (bang && arg.match(/^-.*#(\d+)/)) + unfavTwitter(RegExp.$1); + else + if (bang && arg.match(/^@/)) + showTwitterMentions(arg); + else + if (bang || arg.length == 0) + showFollowersStatus(arg, bang); + else + sayTwitter(arg); + }, { + bang: true, + literal: 0, + completer: let (getting) function(context, args) { + context.fork('File', 0, context, function (context) subCommandCompleter(context, args)); + + let list = []; + let doGet = (expiredStatus || !(history && history.length)) && autoStatusUpdate; + + let matches = args.bang ? args.literalArg.match(/([-+?])/) + : args.literalArg.match(/(RT\s|)@/); + if (!args.bang && !matches) + return; + context.offset += matches ? matches.index + matches[1].length : 0; + context.incomplete = doGet; + context.hasitems = !doGet; + targetContext = context; + if (doGet) { + if (!getting) { + getting = true; + getFollowersStatus(null, function() { + getting = false; + context.fork('Twittperator', 0, context, function (context) commandCompelter(context, args)); + }); + } + } else { + context.fork('Twittperator', 0, context, function (context) commandCompelter(context, args)); } - } else { - context.fork('Twittperator', 0, context, function (context) commandCompelter(context, args)); } - } - }, true); - - if (setting.useChirp) - ChirpUserStream.start(); -} // }}} -// PIN code を取得して AccessToken を得る前 {{{ -function preSetup() { - commands.addUserCommand(["tw[ittperator]"], "Twittperator setup command", - function(args) { - if (args["-getPIN"]) { - tw.getRequestToken(function(url) { - liberator.open(url, { where: liberator.NEW_TAB }); - }); - liberator.echo("Twittperator", "Please get PIN code and execute\n :tw -setPIN {PINcode}"); - } else if (args["-setPIN"]) { - tw.setPin(args["-setPIN"]); - } - }, { - options: [ - [["-getPIN"], commands.OPTION_NOARG], - [["-setPIN"], commands.OPTION_STRING, null, null] - ], - }, true); -} // }}} + }, true); + + if (setting.useChirp) + ChirpUserStream.start(); + } // }}} + // PIN code を取得して AccessToken を得る前 {{{ + function preSetup() { + commands.addUserCommand(["tw[ittperator]"], "Twittperator setup command", + function(args) { + if (args["-getPIN"]) { + tw.getRequestToken(function(url) { + liberator.open(url, { where: liberator.NEW_TAB }); + }); + liberator.echo("Twittperator", "Please get PIN code and execute\n :tw -setPIN {PINcode}"); + } else if (args["-setPIN"]) { + tw.setPin(args["-setPIN"]); + } + }, { + options: [ + [["-getPIN"], commands.OPTION_NOARG], + [["-setPIN"], commands.OPTION_STRING, null, null] + ], + }, true); + } // }}} + +})(); // vim: sw=2 ts=2 et fdm=marker: |