PageSourceSearch

https://janetwheeler.co.uk/wp-content/plugins/arrow-twitter-feed/b…/codebird-js/codebird.js?ver=6.8.10

js janetwheeler.co.uk collected 2026-09-25 02:02:13 UTC 50,611 bytes, 1,546 lines download raw bytes

1/**
2 * A Twitter library in JavaScript
3 *
4 * @package   codebird
5 * @version   2.5.0
6 * @author    Jublo Solutions <[email protected]>
7 * @copyright 2010-2014 Jublo Solutions <[email protected]>
8 * @license   http://opensource.org/licenses/GPL-3.0 GNU Public License 3.0
9 * @link      https://github.com/jublonet/codebird-php
10 */
11
12/* jshint curly: true,
13          eqeqeq: true,
14          latedef: true,
15          quotmark: double,
16          undef: true,
17          unused: true,
18          trailing: true,
19          laxbreak: true */
20/* global window,
21          document,
22          navigator,
23          console,
24          Ti,
25          ActiveXObject,
26          module,
27          define,
28          require */
29(function (undefined) {
30"use strict";
31
32/**
33 * Array.indexOf polyfill
34 */
35if (! Array.prototype.indexOf) {
36    Array.prototype.indexOf = function (obj, start) {
37        for (var i = (start || 0); i < this.length; i++) {
38            if (this[i] === obj) {
39                return i;
40            }
41        }
42        return -1;
43    };
44}
45
46/**
47 * A Twitter library in JavaScript
48 *
49 * @package codebird
50 * @subpackage codebird-js
51 */
52/* jshint -W098 */
53var Codebird = function () {
54/* jshint +W098 */
55
56    /**
57     * The OAuth consumer key of your registered app
58     */
59    var _oauth_consumer_key = null;
60
61    /**
62     * The corresponding consumer secret
63     */
64    var _oauth_consumer_secret = null;
65
66    /**
67     * The app-only bearer token. Used to authorize app-only requests
68     */
69    var _oauth_bearer_token = null;
70
71    /**
72     * The API endpoint base to use
73     */
74    var _endpoint_base = "https://api.twitter.com/";
75
76    /**
77     * The media API endpoint base to use
78     */
79    var _endpoint_base_media = "https://upload.twitter.com/";
80
81    /**
82     * The API endpoint to use
83     */
84    var _endpoint = _endpoint_base + "1.1/";
85
86    /**
87     * The media API endpoint to use
88     */
89    var _endpoint_media = _endpoint_base_media + "1.1/";
90
91    /**
92     * The API endpoint base to use
93     */
94    var _endpoint_oauth = _endpoint_base;
95
96    /**
97     * API proxy endpoint
98     */
99    var _endpoint_proxy = "https://api.jublo.net/codebird/";
100
101    /**
102     * The API endpoint to use for old requests
103     */
104    var _endpoint_old = _endpoint_base + "1/";
105
106    /**
107     * Use JSONP for GET requests in IE7-9
108     */
109    var _use_jsonp = (typeof navigator !== "undefined"
110        && typeof navigator.userAgent !== "undefined"
111        && (navigator.userAgent.indexOf("Trident/4") > -1
112            || navigator.userAgent.indexOf("Trident/5") > -1
113            || navigator.userAgent.indexOf("MSIE 7.0") > -1
114        )
115    );
116
117    /**
118     * Whether to access the API via a proxy that is allowed by CORS
119     * Assume that CORS is only necessary in browsers
120     */
121    var _use_proxy = (typeof navigator !== "undefined"
122        && typeof navigator.userAgent !== "undefined"
123    );
124
125    /**
126     * The Request or access token. Used to sign requests
127     */
128    var _oauth_token = null;
129
130    /**
131     * The corresponding request or access token secret
132     */
133    var _oauth_token_secret = null;
134
135    /**
136     * The current Codebird version
137     */
138    var _version = "2.5.0";
139
140    /**
141     * Sets the OAuth consumer key and secret (App key)
142     *
143     * @param string key    OAuth consumer key
144     * @param string secret OAuth consumer secret
145     *
146     * @return void
147     */
148    var setConsumerKey = function (key, secret) {
149        _oauth_consumer_key = key;
150        _oauth_consumer_secret = secret;
151    };
152
153    /**
154     * Sets the OAuth2 app-only auth bearer token
155     *
156     * @param string token OAuth2 bearer token
157     *
158     * @return void
159     */
160    var setBearerToken = function (token) {
161        _oauth_bearer_token = token;
162    };
163
164    /**
165     * Gets the current Codebird version
166     *
167     * @return string The version number
168     */
169    var getVersion = function () {
170        return _version;
171    };
172
173    /**
174     * Sets the OAuth request or access token and secret (User key)
175     *
176     * @param string token  OAuth request or access token
177     * @param string secret OAuth request or access token secret
178     *
179     * @return void
180     */
181    var setToken = function (token, secret) {
182        _oauth_token = token;
183        _oauth_token_secret = secret;
184    };
185
186    /**
187     * Enables or disables CORS proxy
188     *
189     * @param bool use_proxy Whether to use CORS proxy or not
190     *
191     * @return void
192     */
193    var setUseProxy = function (use_proxy) {
194        _use_proxy = !! use_proxy;
195    };
196
197    /**
198     * Sets custom CORS proxy server
199     *
200     * @param string proxy Address of proxy server to use
201     *
202     * @return void
203     */
204    var setProxy = function (proxy) {
205        // add trailing slash if missing
206        if (! proxy.match(/\/$/)) {
207            proxy += "/";
208        }
209        _endpoint_proxy = proxy;
210    };
211
212    /**
213     * Parse URL-style parameters into object
214     *
215     * version: 1109.2015
216     * discuss at: http://phpjs.org/functions/parse_str
217     * +   original by: Cagri Ekin
218     * +   improved by: Michael White (http://getsprink.com)
219     * +    tweaked by: Jack
220     * +   bugfixed by: Onno Marsman
221     * +   reimplemented by: stag019
222     * +   bugfixed by: Brett Zamir (http://brett-zamir.me)
223     * +   bugfixed by: stag019
224     * -    depends on: urldecode
225     * +   input by: Dreamer
226     * +   bugfixed by: Brett Zamir (http://brett-zamir.me)
227     * %        note 1: When no argument is specified, will put variables in global scope.
228     *
229     * @param string str String to parse
230     * @param array array to load data into
231     *
232     * @return object
233     */
234    var _parse_str = function (str, array) {
235        var glue1 = "=",
236            glue2 = "&",
237            array2 = String(str).replace(/^&?([\s\S]*?)&?$/, "$1").split(glue2),
238            i, j, chr, tmp, key, value, bracket, keys, evalStr,
239            fixStr = function (str) {
240                return decodeURIComponent(str).replace(/([\\"'])/g, "\\$1").replace(/\n/g, "\\n").replace(/\r/g, "\\r");
241            };
242        if (! array) {
243            array = this.window;
244        }
245
246        for (i = 0; i < array2.length; i++) {
247            tmp = array2[i].split(glue1);
248            if (tmp.length < 2) {
249                tmp = [tmp, ""];
250            }
251            key = fixStr(tmp[0]);
252            value = fixStr(tmp[1]);
253            while (key.charAt(0) === " ") {
254                key = key.substr(1);
255            }
256            if (key.indexOf("\0") !== -1) {
257                key = key.substr(0, key.indexOf("\0"));
258            }
259            if (key && key.charAt(0) !== "[") {
260                keys = [];
261                bracket = 0;
262                for (j = 0; j < key.length; j++) {
263                    if (key.charAt(j) === "[" && !bracket) {
264                        bracket = j + 1;
265                    } else if (key.charAt(j) === "]") {
266                        if (bracket) {
267                            if (!keys.length) {
268                                keys.push(key.substr(0, bracket - 1));
269                            }
270                            keys.push(key.substr(bracket, j - bracket));
271                            bracket = 0;
272                            if (key.charAt(j + 1) !== "[") {
273                                break;
274                            }
275                        }
276                    }
277                }
278                if (!keys.length) {
279                    keys = [key];
280                }
281                for (j = 0; j < keys[0].length; j++) {
282                    chr = keys[0].charAt(j);
283                    if (chr === " " || chr === "." || chr === "[") {
284                        keys[0] = keys[0].substr(0, j) + "_" + keys[0].substr(j + 1);
285                    }
286                    if (chr === "[") {
287                        break;
288                    }
289                }
290                /* jshint -W061 */
291                evalStr = "array";
292                for (j = 0; j < keys.length; j++) {
293                    key = keys[j];
294                    if ((key !== "" && key !== " ") || j === 0) {
295                        key = "'" + key + "'";
296                    } else {
297                        key = eval(evalStr + ".push([]);") - 1;
298                    }
299                    evalStr += "[" + key + "]";
300                    if (j !== keys.length - 1 && eval("typeof " + evalStr) === "undefined") {
301                        eval(evalStr + " = [];");
302                    }
303                }
304                evalStr += " = '" + value + "';\n";
305                eval(evalStr);
306                /* jshint +W061 */
307            }
308        }
309    };
310
311    /**
312     * Main API handler working on any requests you issue
313     *
314     * @param string   fn            The member function you called
315     * @param array    params        The parameters you sent along
316     * @param function callback      The callback to call with the reply
317     * @param bool     app_only_auth Whether to use app-only auth
318     *
319     * @return mixed The API reply encoded in the set return_format
320     */
321
322    var __call = function (fn, params, callback, app_only_auth) {
323        if (typeof params === "undefined") {
324            params = {};
325        }
326        if (typeof app_only_auth === "undefined") {
327            app_only_auth = false;
328        }
329        if (typeof callback !== "function" && typeof params === "function") {
330            callback = params;
331            params = {};
332            if (typeof callback === "boolean") {
333                app_only_auth = callback;
334            }
335        } else if (typeof callback === "undefined") {
336            callback = function () {};
337        }
338        switch (fn) {
339        case "oauth_authenticate":
340        case "oauth_authorize":
341            return this[fn](params, callback);
342
343        case "oauth2_token":
344            return this[fn](callback);
345        }
346        // reset token when requesting a new token (causes 401 for signature error on 2nd+ requests)
347        if (fn === "oauth_requestToken") {
348            setToken(null, null);
349        }
350        // parse parameters
351        var apiparams = {};
352        if (typeof params === "object") {
353            apiparams = params;
354        } else {
355            _parse_str(params, apiparams); //TODO
356        }
357
358        // map function name to API method
359        var method = "";
360        var param, i, j;
361
362        // replace _ by /
363        var path = fn.split("_");
364        for (i = 0; i < path.length; i++) {
365            if (i > 0) {
366                method += "/";
367            }
368            method += path[i];
369        }
370
371        // undo replacement for URL parameters
372        var url_parameters_with_underscore = ["screen_name", "place_id"];
373        for (i = 0; i < url_parameters_with_underscore.length; i++) {
374            param = url_parameters_with_underscore[i].toUpperCase();
375            var replacement_was = param.split("_").join("/");
376            method = method.split(replacement_was).join(param);
377        }
378
379        // replace AA by URL parameters
380        var method_template = method;
381        var match = method.match(/[A-Z_]{2,}/);
382        if (match) {
383            for (i = 0; i < match.length; i++) {
384                param = match[i];
385                var param_l = param.toLowerCase();
386                method_template = method_template.split(param).join(":" + param_l);
387                if (typeof apiparams[param_l] === "undefined") {
388                    for (j = 0; j < 26; j++) {
389                        method_template = method_template.split(String.fromCharCode(65 + j)).join("_" + String.fromCharCode(97 + j));
390                    }
391                    console.warn("To call the templated method \"" + method_template + "\", specify the parameter value for \"" + param_l + "\".");
392                }
393                method = method.split(param).join(apiparams[param_l]);
394                delete apiparams[param_l];
395            }
396        }
397
398        // replace A-Z by _a-z
399        for (i = 0; i < 26; i++) {
400            method = method.split(String.fromCharCode(65 + i)).join("_" + String.fromCharCode(97 + i));
401            method_template = method_template.split(String.fromCharCode(65 + i)).join("_" + String.fromCharCode(97 + i));
402        }
403
404        var httpmethod = _detectMethod(method_template, apiparams);
405        var multipart = _detectMultipart(method_template);
406        var internal = _detectInternal(method_template);
407
408        return _callApi(
409            httpmethod,
410            method,
411            apiparams,
412            multipart,
413            app_only_auth,
414            internal,
415            callback
416        );
417    };
418
419    /**
420     * Gets the OAuth authenticate URL for the current request token
421     *
422     * @return string The OAuth authenticate URL
423     */
424    var oauth_authenticate = function (params, callback) {
425        if (typeof params.force_login === "undefined") {
426            params.force_login = null;
427        }
428        if (typeof params.screen_name === "undefined") {
429            params.screen_name = null;
430        }
431        if (_oauth_token === null) {
432            console.warn("To get the authenticate URL, the OAuth token must be set.");
433        }
434        var url = _endpoint_oauth + "oauth/authenticate?oauth_token=" + _url(_oauth_token);
435        if (params.force_login === true) {
436            url += "?force_login=1";
437            if (params.screen_name !== null) {
438                url += "&screen_name=" + params.screen_name;
439            }
440        }
441        callback(url);
442        return true;
443    };
444
445    /**
446     * Gets the OAuth authorize URL for the current request token
447     *
448     * @return string The OAuth authorize URL
449     */
450    var oauth_authorize = function (params, callback) {
451        if (typeof params.force_login === "undefined") {
452            params.force_login = null;
453        }
454        if (typeof params.screen_name === "undefined") {
455            params.screen_name = null;
456        }
457        if (_oauth_token === null) {
458            console.warn("To get the authorize URL, the OAuth token must be set.");
459        }
460        var url = _endpoint_oauth + "oauth/authorize?oauth_token=" + _url(_oauth_token);
461        if (params.force_login === true) {
462            url += "?force_login=1";
463            if (params.screen_name !== null) {
464                url += "&screen_name=" + params.screen_name;
465            }
466        }
467        callback(url);
468        return true;
469    };
470
471    /**
472     * Gets the OAuth bearer token
473     *
474     * @return string The OAuth bearer token
475     */
476
477    var oauth2_token = function (callback) {
478        if (_oauth_consumer_key === null) {
479            console.warn("To obtain a bearer token, the consumer key must be set.");
480        }
481
482        if (typeof callback === "undefined") {
483            callback = function () {};
484        }
485
486        var post_fields = "grant_type=client_credentials";
487        var url = _endpoint_oauth + "oauth2/token";
488
489        if (_use_proxy) {
490            url = url.replace(
491                _endpoint_base,
492                _endpoint_proxy
493            );
494        }
495
496        var xml = _getXmlRequestObject();
497        if (xml === null) {
498            return;
499        }
500        xml.open("POST", url, true);
501        xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
502        xml.setRequestHeader(
503            (_use_proxy ? "X-" : "") + "Authorization",
504            "Basic " + _base64_encode(_oauth_consumer_key + ":" + _oauth_consumer_secret)
505        );
506
507        xml.onreadystatechange = function () {
508            if (xml.readyState >= 4) {
509                var httpstatus = 12027;
510                try {
511                    httpstatus = xml.status;
512                } catch (e) {}
513                var response = "";
514                try {
515                    response = xml.responseText;
516                } catch (e) {}
517                var reply = _parseApiReply(response);
518                reply.httpstatus = httpstatus;
519                if (httpstatus === 200) {
520                    setBearerToken(reply.access_token);
521                }
522                callback(reply);
523            }
524        };
525        xml.send(post_fields);
526
527    };
528
529    /**
530     * Signing helpers
531     */
532
533    /**
534     * URL-encodes the given data
535     *
536     * @param mixed data
537     *
538     * @return mixed The encoded data
539     */
540    var _url = function (data) {
541        if ((/boolean|number|string/).test(typeof data)) {
542            return encodeURIComponent(data).replace(/!/g, "%21").replace(/'/g, "%27").replace(/\(/g, "%28").replace(/\)/g, "%29").replace(/\*/g, "%2A");
543        } else {
544            return "";
545        }
546    };
547
548    /**
549     * Gets the base64-encoded SHA1 hash for the given data
550     *
551     * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
552     * in FIPS PUB 180-1
553     * Based on version 2.1 Copyright Paul Johnston 2000 - 2002.
554     * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
555     * Distributed under the BSD License
556     * See http://pajhome.org.uk/crypt/md5 for details.
557     *
558     * @param string data The data to calculate the hash from
559     *
560     * @return string The hash
561     */
562    var _sha1 = function () {
563        function n(e, b) {
564            e[b >> 5] |= 128 << 24 - b % 32;
565            e[(b + 64 >> 9 << 4) + 15] = b;
566            for (var c = new Array(80), a = 1732584193, d = -271733879, h = -1732584194,
567                    k = 271733878, g = -1009589776, p = 0; p < e.length; p += 16) {
568                for (var o = a, q = d, r = h, s = k, t = g, f = 0; 80 > f; f++) {
569                    var m;
570
571                    if (f < 16) {
572                        m = e[p + f];
573                    } else {
574                        m = c[f - 3] ^ c[f - 8] ^ c[f - 14] ^ c[f - 16];
575                        m = m << 1 | m >>> 31;
576                    }
577
578                    c[f] = m;
579                    m = l(l(a << 5 | a >>> 27, 20 > f ? d & h | ~d & k : 40 > f ? d ^
580                        h ^ k : 60 > f ? d & h | d & k | h & k : d ^ h ^ k), l(
581                        l(g, c[f]), 20 > f ? 1518500249 : 40 > f ? 1859775393 :
582                        60 > f ? -1894007588 : -899497514));
583                    g = k;
584                    k = h;
585                    h = d << 30 | d >>> 2;
586                    d = a;
587                    a = m;
588                }
589                a = l(a, o);
590                d = l(d, q);
591                h = l(h, r);
592                k = l(k, s);
593                g = l(g, t);
594            }
595            return [a, d, h, k, g];
596        }
597
598        function l(e, b) {
599            var c = (e & 65535) + (b & 65535);
600            return (e >> 16) + (b >> 16) + (c >> 16) << 16 | c & 65535;
601        }
602
603        function q(e) {
604            for (var b = [], c = (1 << g) - 1, a = 0; a < e.length * g; a += g) {
605                b[a >> 5] |= (e.charCodeAt(a / g) & c) << 24 - a % 32;
606            }
607            return b;
608        }
609        var g = 8;
610        return function (e) {
611            var b = _oauth_consumer_secret + "&" + (null !== _oauth_token_secret ?
612                _oauth_token_secret : "");
613            if (_oauth_consumer_secret === null) {
614                console.warn("To generate a hash, the consumer secret must be set.");
615            }
616            var c = q(b);
617            if (c.length > 16) {
618                c = n(c, b.length * g);
619            }
620            b = new Array(16);
621            for (var a = new Array(16), d = 0; d < 16; d++) {
622                a[d] = c[d] ^ 909522486;
623                b[d] = c[d] ^ 1549556828;
624            }
625            c = n(a.concat(q(e)), 512 + e.length * g);
626            b = n(b.concat(c), 672);
627            c = "";
628            for (a = 0; a < 4 * b.length; a += 3) {
629                for (d = (b[a >> 2] >> 8 * (3 - a % 4) & 255) << 16 | (b[a + 1 >> 2] >>
630                    8 * (3 - (a + 1) % 4) & 255) << 8 | b[a + 2 >> 2] >> 8 * (3 -
631                    (a + 2) % 4) & 255, e = 0; 4 > e; e++) {
632                    c = 8 * a + 6 * e > 32 * b.length ? c + "=" : c +
633                        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
633+/"
634                        .charAt(d >> 6 * (3 - e) & 63);
635                }
636            }
637            return c;
638        };
639    }();
640
641    /*
642     * Gets the base64 representation for the given data
643     *
644     * http://phpjs.org
645     * +   original by: Tyler Akins (http://rumkin.com)
646     * +   improved by: Bayron Guevara
647     * +   improved by: Thunder.m
648     * +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
649     * +   bugfixed by: Pellentesque Malesuada
650     * +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
651     * +   improved by: Rafał Kukawski (http://kukawski.pl)
652     *
653     * @param string data The data to calculate the base64 representation from
654     *
655     * @return string The base64 representation
656     */
657    var _base64_encode = function (a) {
658        var d, e, f, b, g = 0,
659            h = 0,
660            i = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
661            c = [];
662        if (!a) {
663            return a;
664        }
665        do {
666            d = a.charCodeAt(g++);
667            e = a.charCodeAt(g++);
668            f = a.charCodeAt(g++);
669            b = d << 16 | e << 8 | f;
670            d = b >> 18 & 63;
671            e = b >> 12 & 63;
672            f = b >> 6 & 63;
673            b &= 63;
674            c[h++] = i.charAt(d) + i.charAt(e) + i.charAt(f) + i.charAt(b);
675        } while (g < a.length);
676        c = c.join("");
677        a = a.length % 3;
678        return (a ? c.slice(0, a - 3) : c) + "===".slice(a || 3);
679    };
680
681    /*
682     * Builds a HTTP query string from the given data
683     *
684     * http://phpjs.org
685     * +     original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
686     * +     improved by: Legaev Andrey
687     * +     improved by: Michael White (http://getsprink.com)
688     * +     improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
689     * +     improved by: Brett Zamir (http://brett-zamir.me)
690     * +        revised by: stag019
691     * +     input by: Dreamer
692     * +     bugfixed by: Brett Zamir (http://brett-zamir.me)
693     * +     bugfixed by: MIO_KODUKI (http://mio-koduki.blogspot.com/)
694     *
695     * @param string data The data to concatenate
696     *
697     * @return string The HTTP query
698     */
699    var _http_build_query = function (e, f, b) {
700        function g(c, a, d) {
701            var b, e = [];
702            if (a === true) {
703                a = "1";
704            } else if (a === false) {
705                a = "0";
706            }
707            if (null !== a) {
708                if (typeof a === "object") {
709                    for (b in a) {
710                        if (a[b] !== null) {
711                            e.push(g(c + "[" + b + "]", a[b], d));
712                        }
713                    }
714                    return e.join(d);
715                }
716                if (typeof a !== "function") {
717                    return _url(c) + "=" + _url(a);
718                }
719                console.warn("There was an error processing for http_build_query().");
720            } else {
721                return "";
722            }
723        }
724        var d, c, h = [];
725        if (! b) {
726            b = "&";
727        }
728        for (c in e) {
729            d = e[c];
730            if (f && ! isNaN(c)) {
731                c = String(f) + c;
732            }
733            d = g(c, d, b);
734            if (d !== "") {
735                h.push(d);
736            }
737        }
738        return h.join(b);
739    };
740
741    /**
742     * Generates a (hopefully) unique random string
743     *
744     * @param int optional length The length of the string to generate
745     *
746     * @return string The random string
747     */
748    var _nonce = function (length) {
749        if (typeof length === "undefined") {
750            length = 8;
751        }
752        if (length < 1) {
753            console.warn("Invalid nonce length.");
754        }
755        var nonce = "";
756        for (var i = 0; i < length; i++) {
757            var character = Math.floor(Math.random() * 61);
758            nonce += "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".substring(character, character + 1);
759        }
760        return nonce;
761    };
762
763    /**
764     * Sort array elements by key
765     *
766     * @param array input_arr The array to sort
767     *
768     * @return array The sorted keys
769     */
770    var _ksort = function (input_arr) {
771        var keys = [], sorter, k;
772
773        sorter = function (a, b) {
774            var a_float = parseFloat(a),
775            b_float = parseFloat(b),
776            a_numeric = a_float + "" === a,
777            b_numeric = b_float + "" === b;
778            if (a_numeric && b_numeric) {
779                return a_float > b_float ? 1 : a_float < b_float ? -1 : 0;
780            } else if (a_numeric && !b_numeric) {
781                return 1;
782            } else if (!a_numeric && b_numeric) {
783                return -1;
784            }
785            return a > b ? 1 : a < b ? -1 : 0;
786        };
787
788        // Make a list of key names
789        for (k in input_arr) {
790            if (input_arr.hasOwnProperty(k)) {
791                keys.push(k);
792            }
793        }
794        keys.sort(sorter);
795        return keys;
796    };
797
798    /**
799     * Clone objects
800     *
801     * @param object obj    The object to clone
802     *
803     * @return object clone The cloned object
804     */
805    var _clone = function (obj) {
806        var clone = {};
807        for (var i in obj) {
808            if (typeof(obj[i]) === "object") {
809                clone[i] = _clone(obj[i]);
810            } else {
811                clone[i] = obj[i];
812            }
813        }
814        return clone;
815    };
816
817    /**
818     * Generates an OAuth signature
819     *
820     * @param string          httpmethod    Usually either 'GET' or 'POST' or 'DELETE'
821     * @param string          method        The API method to call
822     * @param array  optional params        The API call parameters, associative
823     * @param bool   optional append_to_get Whether to append the OAuth params to GET
824     *
825     * @return string Authorization HTTP header
826     */
827    var _sign = function (httpmethod, method, params, append_to_get) {
828        if (typeof params === "undefined") {
829            params = {};
830        }
831        if (typeof append_to_get === "undefined") {
832            append_to_get = false;
833        }
834        if (_oauth_consumer_key === null) {
835            console.warn("To generate a signature, the consumer key must be set.");
836        }
837        var sign_params = {
838            consumer_key:     _oauth_consumer_key,
839            version:          "1.0",
840            timestamp:        Math.round(new Date().getTime() / 1000),
841            nonce:            _nonce(),
842            signature_method: "HMAC-SHA1"
843        };
844        var sign_base_params = {};
845        var value;
846        for (var key in sign_params) {
847            value = sign_params[key];
848            sign_base_params["oauth_" + key] = _url(value);
849        }
850        if (_oauth_token !== null) {
851            sign_base_params.oauth_token = _url(_oauth_token);
852        }
853        var oauth_params = _clone(sign_base_params);
854        for (key in params) {
855            value = params[key];
856            sign_base_params[key] = value;
857        }
858        var keys = _ksort(sign_base_params);
859        var sign_base_string = "";
860        for (var i = 0; i < keys.length; i++) {
861            key = keys[i];
862            value = sign_base_params[key];
863            sign_base_string += key + "=" + _url(value) + "&";
864        }
865        sign_base_string = sign_base_string.substring(0, sign_base_string.length - 1);
866        var signature    = _sha1(httpmethod + "&" + _url(method) + "&" + _url(sign_base_string));
867
868        params = append_to_get ? sign_base_params : oauth_params;
869        params.oauth_signature = signature;
870        keys = _ksort(params);
871        var authorization = "";
872        if (append_to_get) {
873            for(i = 0; i < keys.length; i++) {
874                key = keys[i];
875                value = params[key];
876                authorization += key + "=" + _url(value) + "&";
877            }
878            return authorization.substring(0, authorization.length - 1);
879        }
880        authorization = "OAuth ";
881        for (i = 0; i < keys.length; i++) {
882            key = keys[i];
883            value = params[key];
884            authorization += key + "=\"" + _url(value) + "\", ";
885        }
886        return authorization.substring(0, authorization.length - 2);
887    };
888
889    /**
890     * Detects HTTP method to use for API call
891     *
892     * @param string method The API method to call
893     * @param array  params The parameters to send along
894     *
895     * @return string The HTTP method that should be used
896     */
897    var _detectMethod = function (method, params) {
898        // multi-HTTP method endpoints
899        switch (method) {
900        case "account/settings":
901        case "account/login_verification_enrollment":
902        case "account/login_verification_request":
903            method = params.length ? method + "__post" : method;
904            break;
905        }
906
907        var httpmethods = {};
908        httpmethods.GET = [
909            // Timelines
910            "statuses/mentions_timeline",
911            "statuses/user_timeline",
912            "statuses/home_timeline",
913            "statuses/retweets_of_me",
914
915            // Tweets
916            "statuses/retweets/:id",
917            "statuses/show/:id",
918            "statuses/oembed",
919            "statuses/retweeters/ids",
920
921            // Search
922            "search/tweets",
923
924            // Direct Messages
925            "direct_messages",
926            "direct_messages/sent",
927            "direct_messages/show",
928
929            // Friends & Followers
930            "friendships/no_retweets/ids",
931            "friends/ids",
932            "followers/ids",
933            "friendships/lookup",
934            "friendships/incoming",
935            "friendships/outgoing",
936            "friendships/show",
937            "friends/list",
938            "followers/list",
939            "friendships/lookup",
940
941            // Users
942            "account/settings",
943            "account/verify_credentials",
944            "blocks/list",
945            "blocks/ids",
946            "users/lookup",
947            "users/show",
948            "users/search",
949            "users/contributees",
950            "users/contributors",
951            "users/profile_banner",
952            "mutes/users/ids",
953            "mutes/users/list",
954
955            // Suggested Users
956            "users/suggestions/:slug",
957            "users/suggestions",
958            "users/suggestions/:slug/members",
959
960            // Favorites
961            "favorites/list",
962
963            // Lists
964            "lists/list",
965            "lists/statuses",
966            "lists/memberships",
967            "lists/subscribers",
968            "lists/subscribers/show",
969            "lists/members/show",
970            "lists/members",
971            "lists/show",
972            "lists/subscriptions",
973            "lists/ownerships",
974
975            // Saved searches
976            "saved_searches/list",
977            "saved_searches/show/:id",
978
979            // Places & Geo
980            "geo/id/:place_id",
981            "geo/reverse_geocode",
982            "geo/search",
983            "geo/similar_places",
984
985            // Trends
986            "trends/place",
987            "trends/available",
988            "trends/closest",
989
990            // OAuth
991            "oauth/authenticate",
992            "oauth/authorize",
993
994            // Help
995            "help/configuration",
996            "help/languages",
997            "help/privacy",
998            "help/tos",
999            "application/rate_limit_status",
1000
1001            // Tweets
1002            "statuses/lookup",
1003
1004            // Internal
1005            "users/recommendations",
1006            "account/push_destinations/device",
1007            "activity/about_me",
1008            "activity/by_friends",
1009            "statuses/media_timeline",
1010            "timeline/home",
1011            "help/experiments",
1012            "search/typeahead",
1013            "search/universal",
1014            "discover/universal",
1015            "conversation/show",
1016            "statuses/:id/activity/summary",
1017            "account/login_verification_enrollment",
1018            "account/login_verification_request",
1019            "prompts/suggest",
1020
1021            "beta/timelines/custom/list",
1022            "beta/timelines/timeline",
1023            "beta/timelines/custom/show"
1024        ];
1025        httpmethods.POST = [
1026            // Tweets
1027            "statuses/destroy/:id",
1028            "statuses/update",
1029            "statuses/retweet/:id",
1030            "statuses/update_with_media",
1031            "media/upload",
1032
1033            // Direct Messages
1034            "direct_messages/destroy",
1035            "direct_messages/new",
1036
1037            // Friends & Followers
1038            "friendships/create",
1039            "friendships/destroy",
1040            "friendships/update",
1041
1042            // Users
1043            "account/settings__post",
1044            "account/update_delivery_device",
1045            "account/update_profile",
1046            "account/update_profile_background_image",
1047            "account/update_profile_colors",
1048            "account/update_profile_image",
1049            "blocks/create",
1050            "blocks/destroy",
1051            "account/update_profile_banner",
1052            "account/remove_profile_banner",
1053            "mutes/users/create",
1054            "mutes/users/destroy",
1055
1056            // Favorites
1057            "favorites/destroy",
1058            "favorites/create",
1059
1060            // Lists
1061            "lists/members/destroy",
1062            "lists/subscribers/create",
1063            "lists/subscribers/destroy",
1064            "lists/members/create_all",
1065            "lists/members/create",
1066            "lists/destroy",
1067            "lists/update",
1068            "lists/create",
1069            "lists/members/destroy_all",
1070
1071            // Saved Searches
1072            "saved_searches/create",
1073            "saved_searches/destroy/:id",
1074
1075            // Spam Reporting
1076            "users/report_spam",
1077
1078            // OAuth
1079            "oauth/access_token",
1080            "oauth/request_token",
1081            "oauth2/token",
1082            "oauth2/invalidate_token",
1083
1084            // Internal
1085            "direct_messages/read",
1086            "account/login_verification_enrollment__post",
1087            "push_destinations/enable_login_verification",
1088            "account/login_verification_request__post",
1089
1090            "beta/timelines/custom/create",
1091            "beta/timelines/custom/update",
1092            "beta/timelines/custom/destroy",
1093            "beta/timelines/custom/add",
1094            "beta/timelines/custom/remove"
1095        ];
1096        for (var httpmethod in httpmethods) {
1097            if (httpmethods[httpmethod].indexOf(method) > -1) {
1098                return httpmethod;
1099            }
1100        }
1101        console.warn("Can't find HTTP method to use for \"" + method + "\".");
1102    };
1103
1104    /**
1105     * Detects if API call should use multipart/form-data
1106     *
1107     * @param string method The API method to call
1108     *
1109     * @return bool Whether the method should be sent as multipart
1110     */
1111    var _detectMultipart = function (method) {
1112        var multiparts = [
1113            // Tweets
1114            "statuses/update_with_media",
1115
1116            // Users
1117            "account/update_profile_background_image",
1118            "account/update_profile_image",
1119            "account/update_profile_banner"
1120        ];
1121        return multiparts.indexOf(method) > -1;
1122    };
1123
1124    /**
1125     * Build multipart request from upload params
1126     *
1127     * @param string method  The API method to call
1128     * @param array  params  The parameters to send along
1129     *
1130     * @return null|string The built multipart request body
1131     */
1132    var _buildMultipart = function (method, params) {
1133        // well, files will only work in multipart methods
1134        if (! _detectMultipart(method)) {
1135            return;
1136        }
1137
1138        // only check specific parameters
1139        var possible_methods = [
1140            // Tweets
1141            "statuses/update_with_media",
1142            // Accounts
1143            "account/update_profile_background_image",
1144            "account/update_profile_image",
1145            "account/update_profile_banner"
1146        ];
1147        var possible_files = {
1148            // Tweets
1149            "statuses/update_with_media": "media[]",
1150            // Accounts
1151            "account/update_profile_background_image": "image",
1152            "account/update_profile_image": "image",
1153            "account/update_profile_banner": "banner"
1154        };
1155        // method might have files?
1156        if (possible_methods.indexOf(method) === -1) {
1157            return;
1158        }
1159
1160        // check for filenames
1161        possible_files = possible_files[method].split(" ");
1162
1163        var multipart_border = "--------------------" + _nonce();
1164        var multipart_request = "";
1165        for (var key in params) {
1166            multipart_request +=
1167                "--" + multipart_border + "\r\n"
1168                + "Content-Disposition: form-data; name=\"" + key + "\"";
1169            if (possible_files.indexOf(key) > -1) {
1170                multipart_request +=
1171                    "\r\nContent-Transfer-Encoding: base64";
1172            }
1173            multipart_request +=
1174                "\r\n\r\n" + params[key] + "\r\n";
1175        }
1176        multipart_request += "--" + multipart_border + "--";
1177        return multipart_request;
1178    };
1179
1180    /**
1181     * Detects if API call is internal
1182     *
1183     * @param string method The API method to call
1184     *
1185     * @return bool Whether the method is defined in internal API
1186     */
1187    var _detectInternal = function (method) {
1188        var internals = [
1189            "users/recommendations"
1190        ];
1191        return internals.join(" ").indexOf(method) > -1;
1192    };
1193
1194    /**
1195     * Detects if API call should use media endpoint
1196     *
1197     * @param string method The API method to call
1198     *
1199     * @return bool Whether the method is defined in media API
1200     */
1201    var _detectMedia = function (method) {
1202        var medias = [
1203            "media/upload"
1204        ];
1205        return medias.join(" ").indexOf(method) > -1;
1206    };
1207
1208    /**
1209     * Detects if API call should use old endpoint
1210     *
1211     * @param string method The API method to call
1212     *
1213     * @return bool Whether the method is defined in old API
1214     */
1215    var _detectOld = function (method) {
1216        var olds = [
1217            "account/push_destinations/device"
1218        ];
1219        return olds.join(" ").indexOf(method) > -1;
1220    };
1221
1222    /**
1223     * Builds the complete API endpoint url
1224     *
1225     * @param string method The API method to call
1226     *
1227     * @return string The URL to send the request to
1228     */
1229    var _getEndpoint = function (method) {
1230        var url;
1231        if (method.substring(0, 5) === "oauth") {
1232            url = _endpoint_oauth + method;
1233        } else if (_detectMedia(method)) {
1234            url = _endpoint_media + method + ".json";
1235        } else if (_detectOld(method)) {
1236            url = _endpoint_old + method + ".json";
1237        } else {
1238            url = _endpoint + method + ".json";
1239        }
1240        return url;
1241    };
1242
1243    /**
1244     * Gets the XML HTTP Request object, trying to load it in various ways
1245     *
1246     * @return object The XMLHttpRequest object instance
1247     */
1248    var _getXmlRequestObject = function () {
1249        var xml = null;
1250        // first, try the W3-standard object
1251        if (typeof window === "object"
1252            && window
1253            && typeof window.XMLHttpRequest !== "undefined"
1254        ) {
1255            xml = new window.XMLHttpRequest();
1256        // then, try Titanium framework object
1257        } else if (typeof Ti === "object"
1258            && Ti
1259            && typeof Ti.Network.createHTTPClient !== "undefined"
1260        ) {
1261            xml = Ti.Network.createHTTPClient();
1262        // are we in an old Internet Explorer?
1263        } else if (typeof ActiveXObject !== "undefined"
1264        ) {
1265            try {
1266                xml = new ActiveXObject("Microsoft.XMLHTTP");
1267            } catch (e) {
1268                console.error("ActiveXObject object not defined.");
1269            }
1270        // now, consider RequireJS and/or Node.js objects
1271        } else if (typeof require === "function"
1272            && require
1273        ) {
1274            // look for xmlhttprequest module
1275            try {
1276                var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
1277                xml = new XMLHttpRequest();
1278            } catch (e1) {
1279                // or maybe the user is using xhr2
1280                try {
1281                    var XMLHttpRequest = require("xhr2");
1282                    xml = new XMLHttpRequest();
1283                } catch (e2) {
1284                    console.error("xhr2 object not defined, cancelling.");
1285                }
1286            }
1287        }
1288        return xml;
1289    };
1290
1291    /**
1292     * Calls the API using cURL
1293     *
1294     * @param string          httpmethod    The HTTP method to use for making the request
1295     * @param string          method        The API method to call
1296     * @param array  optional params        The parameters to send along
1297     * @param bool   optional multipart     Whether to use multipart/form-data
1298     * @param bool   optional app_only_auth Whether to use app-only bearer authentication
1299     * @param bool   optional internal      Whether to use internal call
1300     * @param function        callback      The function to call with the API call result
1301     *
1302     * @return mixed The API reply, encoded in the set return_format
1303     */
1304
1305    var _callApi = function (httpmethod, method, params, multipart, app_only_auth, internal, callback) {
1306        if (typeof params === "undefined") {
1307            params = {};
1308        }
1309        if (typeof multipart === "undefined") {
1310            multipart = false;
1311        }
1312        if (typeof app_only_auth === "undefined") {
1313            app_only_auth = false;
1314        }
1315        if (typeof callback !== "function") {
1316            callback = function () {};
1317        }
1318        if (internal) {
1319            params.adc            = "phone";
1320            params.application_id = 333903271;
1321        }
1322
1323        var url           = _getEndpoint(method);
1324        var authorization = null;
1325
1326        var xml = _getXmlRequestObject();
1327        if (xml === null) {
1328            return;
1329        }
1330        var post_fields;
1331
1332        if (httpmethod === "GET") {
1333            var url_with_params = url;
1334            if (JSON.stringify(params) !== "{}") {
1335                url_with_params += "?" + _http_build_query(params);
1336            }
1337            if (! app_only_auth) {
1338                authorization = _sign(httpmethod, url, params);
1339            }
1340
1341            // append auth params to GET url for IE7-9, to send via JSONP
1342            if (_use_jsonp) {
1343                if (JSON.stringify(params) !== "{}") {
1344                    url_with_params += "&";
1345                } else {
1346                    url_with_params += "?";
1347                }
1348                var callback_name = _nonce();
1349                window[callback_name] = function (reply) {
1350                    reply.httpstatus = 200;
1351
1352                    var rate = null;
1353                    if (typeof xml.getResponseHeader !== "undefined"
1354                        && xml.getResponseHeader("x-rate-limit-limit") !== ""
1355                    ) {
1356                        rate = {
1357                            limit: xml.getResponseHeader("x-rate-limit-limit"),
1358                            remaining: xml.getResponseHeader("x-rate-limit-remaining"),
1359                            reset: xml.getResponseHeader("x-rate-limit-reset")
1360                        };
1361                    }
1362                    callback(reply, rate);
1363                };
1364                params.callback = callback_name;
1365                url_with_params = url + "?" + _sign(httpmethod, url, params, true);
1366                var tag = document.createElement("script");
1367                tag.type = "text/javascript";
1368                tag.src = url_with_params;
1369                var body = document.getElementsByTagName("body")[0];
1370                body.appendChild(tag);
1371                return;
1372
1373            } else if (_use_proxy) {
1374                url_with_params = url_with_params.replace(
1375                    _endpoint_base,
1376                    _endpoint_proxy
1377                ).replace(
1378                    _endpoint_base_media,
1379                    _endpoint_proxy
1380                );
1381            }
1382            xml.open(httpmethod, url_with_params, true);
1383        } else {
1384            if (_use_jsonp) {
1385                console.warn("Sending POST requests is not supported for IE7-9.");
1386                return;
1387            }
1388            if (multipart) {
1389                if (! app_only_auth) {
1390                    authorization = _sign(httpmethod, url, {});
1391                }
1392                params = _buildMultipart(method, params);
1393            } else {
1394                if (! app_only_auth) {
1395                    authorization = _sign(httpmethod, url, params);
1396                }
1397                params = _http_build_query(params);
1398            }
1399            post_fields = params;
1400            if (_use_proxy || multipart) { // force proxy for multipart base64
1401                url = url.replace(
1402                    _endpoint_base,
1403                    _endpoint_proxy
1404                ).replace(
1405                    _endpoint_base_media,
1406                    _endpoint_proxy
1407                );
1408            }
1409            xml.open(httpmethod, url, true);
1410            if (multipart) {
1411                xml.setRequestHeader("Content-Type", "multipart/form-data; boundary="
1412                    + post_fields.split("\r\n")[0].substring(2));
1413            } else {
1414                xml.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
1415            }
1416        }
1417        if (app_only_auth) {
1418            if (_oauth_consumer_key === null
1419                && _oauth_bearer_token === null
1420            ) {
1421                console.warn("To make an app-only auth API request, consumer key or bearer token must be set.");
1422            }
1423            // automatically fetch bearer token, if necessary
1424            if (_oauth_bearer_token === null) {
1425                return oauth2_token(function () {
1426                    _callApi(httpmethod, method, params, multipart, app_only_auth, false, callback);
1427                });
1428            }
1429            authorization = "Bearer " + _oauth_bearer_token;
1430        }
1431        if (authorization !== null) {
1432            xml.setRequestHeader((_use_proxy ? "X-" : "") + "Authorization", authorization);
1433        }
1434        xml.onreadystatechange = function () {
1435            if (xml.readyState >= 4) {
1436                var httpstatus = 12027;
1437                try {
1438                    httpstatus = xml.status;
1439                } catch (e) {}
1440                var response = "";
1441                try {
1442                    response = xml.responseText;
1443                } catch (e) {}
1444                var reply = _parseApiReply(response);
1445                reply.httpstatus = httpstatus;
1446                var rate = null;
1447                if (typeof xml.getResponseHeader !== "undefined"
1448                    && xml.getResponseHeader("x-rate-limit-limit") !== ""
1449                ) {
1450                    rate = {
1451                        limit: xml.getResponseHeader("x-rate-limit-limit"),
1452                        remaining: xml.getResponseHeader("x-rate-limit-remaining"),
1453                        reset: xml.getResponseHeader("x-rate-limit-reset")
1454                    };
1455                }
1456                callback(reply, rate);
1457            }
1458        };
1459        xml.send(httpmethod === "GET" ? null : post_fields);
1460        return true;
1461    };
1462
1463    /**
1464     * Parses the API reply to encode it in the set return_format
1465     *
1466     * @param string reply  The actual reply, JSON-encoded or URL-encoded
1467     *
1468     * @return array|object The parsed reply
1469     */
1470    var _parseApiReply = function (reply) {
1471        if (typeof reply !== "string" || reply === "") {
1472            return {};
1473        }
1474        if (reply === "[]") {
1475            return [];
1476        }
1477        var parsed;
1478        try {
1479            parsed = JSON.parse(reply);
1480        } catch (e) {
1481            parsed = {};
1482            if (reply.indexOf("<" + "?xml version=\"1.0\" encoding=\"UTF-8\"?" + ">") === 0) {
1483                // we received XML...
1484                // since this only happens for errors,
1485                // don't perform a full decoding
1486                parsed.request = reply.match(/<request>(.*)<\/request>/)[1];
1487                parsed.error   = reply.match(/<error>(.*)<\/error>/)[1];
1488            } else {
1489                // assume query format
1490                var elements = reply.split("&");
1491                for (var i = 0; i < elements.length; i++) {
1492                    var element = elements[i].split("=", 2);
1493                    if (element.length > 1) {
1494                        parsed[element[0]] = decodeURIComponent(element[1]);
1495                    } else {
1496                        parsed[element[0]] = null;
1497                    }
1498                }
1499            }
1500        }
1501        return parsed;
1502    };
1503
1504    return {
1505        setConsumerKey: setConsumerKey,
1506        getVersion: getVersion,
1507        setToken: setToken,
1508        setBearerToken: setBearerToken,
1509        setUseProxy: setUseProxy,
1510        setProxy: setProxy,
1511        __call: __call,
1512        oauth_authenticate: oauth_authenticate,
1513        oauth_authorize: oauth_authorize,
1514        oauth2_token: oauth2_token
1515    };
1516};
1517
1518if (typeof module === "object"
1519    && module
1520    && typeof module.exports === "object"
1521) {
1522    // Expose codebird as module.exports in loaders that implement the Node
1523    // module pattern (including browserify). Do not create the global, since
1524    // the user will be storing it themselves locally, and globals are frowned
1525    // upon in the Node module world.
1526    module.exports = Codebird;
1527} else {
1528    // Otherwise expose codebird to the global object as usual
1529    if (typeof window === "object"
1530        && window) {
1531        window.Codebird = Codebird;
1532    }
1533
1534    // Register as a named AMD module, since codebird can be concatenated with other
1535    // files that may use define, but not via a proper concatenation script that
1536    // understands anonymous AMD modules. A named AMD is safest and most robust
1537    // way to register. Lowercase codebird is used because AMD module names are
1538    // derived from file names, and codebird is normally delivered in a lowercase
1539    // file name. Do this after creating the global so that if an AMD module wants
1540    // to call noConflict to hide this version of codebird, it will work.
1541    if (typeof define === "function" && define.amd) {
1542        define("codebird", [], function () { return Codebird; });
1543    }
1544}
1545
1546})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.