PageSourceSearch

https://licensecounter.jp/mt-static/data-api/v1/js/mt-data-api.js

js licensecounter.jp collected 2026-10-02 03:26:17 UTC 135,059 bytes, 4,450 lines download raw bytes

1/*
2 * Movable Type DataAPI SDK for JavaScript v1
3 * https://github.com/movabletype/mt-data-api-sdk-js
4 * Copyright (c) Six Apart Ltd.
5 * This program is distributed under the terms of the MIT license.
6 *
7 * Includes jQuery JavaScript Library in some parts.
8 * http://jquery.com/
9 * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors
10 * Released under the MIT license
11 * http://jquery.org/license
12 */
13
14;(function(window, factory) {
15    var DataAPI = factory(window);
16
17    if ( typeof module === "object" && typeof module.exports === "object" ) {
18        module.exports = DataAPI;
19    } else {
20        if ( typeof define === "function" && define.amd ) {
21            define("mt-data-api", [], function() {
22                return DataAPI;
23            });
24        }
25    }
26}(typeof window === "undefined" ? undefined : window, function(window, undefined) {
27
28"use strict";
29
30/**
31 * @namespace MT
32 */
33
34/**
35 * The MT.DataAPI is a client class for accessing to the Movable Type DataAPI.
36 * @class DataAPI
37 * @constructor
38 * @param {Object} options Options.
39 *   @param {String} options.clientId Client ID
40 *     This value allows alphanumeric, (_)underscore, (-)dash.
41 *   @param {String} options.baseUrl The absolute CGI URL of the DataAPI.
42 *     (e.g. http://example.com/mt/mt-data-api.cgi)
43 *   @param {String} [options.format] The format to serialize.
44 *   @param {String} [options.sessionStore] The session store.
45 *     In browser, the cookie is used by default.
46 *   @param {String} [options.sessionDomain] The session domain.
47 *     When using the cookie, this value is used as cookie domain.
48 *   @param {String} [options.sessionPath] The session path
49 *     When using the cookie, this value is used as cookie path.
50 *   @param {Boolean} [options.async] If true, use asynchronous
51 *      XMLHttpRequest. The default value is the true.
52 *   @param {Number} [options.timeout] The number of milliseconds a
53 *      request can take before automatically being terminated.
54 *      The default value is not set up, browser's default is used.
55 *   @param {Boolean} [options.cache] If false, add an additional
56 *      parameter "_" to request to avoid cache. The default value
57 *      is the true.
58 *   @param {Boolean} [options.withoutAuthorization] If true,
59 *      the "X-MT-Authorization" request header is not sent even if
60 *      already got accessToken. The default value is the false.
61 *   @param {Boolean} [options.loadPluginEndpoints] If true, load
62 *      endpoint data extended by plugin and generate methods to
63 *      access that endpoint automatically. The default value is
64 *      the true.
65 *      (However even if this option's value is false, you are able
66 *      to use all the methods to access to core endpoint.)
67 *   @param {Boolean} [options.suppressResponseCodes] If true, add
68 *      suppressResponseCodes parameter to each request. As a result,
69 *      the Data API always returns 200 as HTTP response code.
70 *      The default value is not set up.
71 *      The default value is the false when requested via XMLHttpRequest
72 *      or IFRAME. The default value is the true when requested via
73 *      XDomainRequest.
74 *   @param {Boolean} [options.crossOrigin] If true, requests are sent as
75 *      a cross-domain request. The default value is assigned
76 *      automatically by document's URL and baseUrl.
77 *   @param {Boolean(} [options.disableFormData] If false,use FormData
78 *      class when available that. The default value is the false.
79 */
80var DataAPI = function(options) {
81    var i, k,
82        requireds = ['clientId', 'baseUrl'];
83
84    this.o = {
85        clientId: undefined,
86        baseUrl: undefined,
87        format: undefined,
88        sessionStore: undefined,
89        sessionDomain: undefined,
90        sessionPath: undefined,
91        async: true,
92        timeout: undefined,
93        cache: true,
94        withoutAuthorization: false,
95        processOneTimeTokenOnInitialize: true,
96        loadPluginEndpoints: true,
97        suppressResponseCodes: undefined,
98        crossOrigin: undefined,
99        disableFormData: false
100    };
101    for (k in options) {
102        if (k in this.o) {
103            this.o[k] = options[k];
104        }
105        else {
106            throw 'Unkown option: ' + k;
107        }
108    }
109
110    for (i = 0; i < requireds.length; i++) {
111        if (! this.o[requireds[i]]) {
112            throw 'The "' + requireds[i] + '" is required.';
113        }
114    }
115
116    this.callbacks = {};
117    this.tokenData = null;
118    this.iframeId  = 0;
119
120    this._initOptions();
121
122    if (this.o.loadPluginEndpoints) {
123        this.loadEndpoints({
124            excludeComponents: 'core'
125        });
126    }
127
128    if (this.o.processOneTimeTokenOnInitialize) {
129        this._storeOneTimeToken();
130    }
131
132    this.trigger('initialize');
133};
134
135
136/**
137 * The API version.
138 * @property version
139 * @static
140 * @private
141 * @type Number
142 */
143DataAPI.version = 1;
144
145/**
146 * The key of access token of this api object.
147 * This value is used for the session store.
148 * @property accessTokenKey
149 * @static
150 * @private
151 * @type String
152 */
153DataAPI.accessTokenKey = 'mt_data_api_access_token';
154
155/**
156 * The name prefix for iframe that created to upload asset.
157 * @property iframePrefix
158 * @static
159 * @private
160 * @type String
161 */
162DataAPI.iframePrefix = 'mt_data_api_iframe_';
163
164/**
165 * The default format that serializes data.
166 * @property defaultFormat
167 * @static
168 * @private
169 * @type String
170 */
171DataAPI.defaultFormat = 'json';
172
173/**
174 * The default session store.
175 * @property defaultSessionStore
176 * @static
177 * @private
178 * @type String
179 */
180DataAPI.defaultSessionStore = window.document ? 'cookie-encrypted' : 'fs';
181
182/**
183 * Class level callbacks function data.
184 * @property callbacks
185 * @static
186 * @private
187 * @type Object
188 */
189DataAPI.callbacks = {};
190
191/**
192 * Available formats that serialize data.
193 * @property formats
194 * @static
195 * @private
196 * @type Object
197 */
198DataAPI.formats = {
199    json: {
200        fileExtension: 'json',
201        mimeType: 'application/json',
202        serialize: function() {
203            return JSON.stringify.apply(JSON, arguments);
204        },
205        unserialize: function() {
206            return JSON.parse.apply(JSON, arguments);
207        }
208    }
209};
210
211/**
212 * Available session stores.
213 * @property sessionStores
214 * @static
215 * @private
216 * @type Object
217 */
218DataAPI.sessionStores = {};
219;(function() {
220
221function fetchCookieValues(name) {
222    var cookie = Cookie.fetch(name);
223
224    if (! cookie) {
225        return {};
226    }
227
228    try {
229        return JSON.parse(cookie.value);
230    }
231    catch (e) {
232        return {
233            data: cookie.value
234        };
235    }
236}
237
238function fillinDefaultCookieValues(values, o) {
239    var path = values.path,
240        currentPath = extractPath(documentUrl());
241    if (! path || path.length > currentPath.length) {
242        path = currentPath;
243    }
244
245    return {
246        data: values.data,
247        domain: o.sessionDomain || values.domain || undefined,
248        path:  o.sessionPath || path
249    };
250}
251
252function documentUrl() {
253    if (! window.location) {
254        return '';
255    }
256
257    var loc;
258
259    // IE may throw an exception when accessing
260    // a field from window.location if document.domain has been set
261    try {
262        loc = window.location.href;
263    } catch( e ) {
264        // Use the href attribute of an A element
265        // since IE will modify it given document.location
266        loc = window.document.createElement( "a" );
267        loc.href = "";
268        loc = loc.href;
269    }
270
271    return loc;
272}
273
274function extractPath(url) {
275    var urlRegexp = /^[\w.+-]+:(?:\/\/[^\/?#:]*(?::\d+|)|)(.*)\/[^\/]*$/,
276        match     = urlRegexp.exec(url.toLowerCase());
277
278    return match ? match[1] : null;
279}
280
281DataAPI.sessionStores['cookie'] = {
282    save: function(name, data, remember) {
283        var expires = remember ? new Date(new Date().getTime() + 315360000000) : undefined, // after 10 years
284            values  = fillinDefaultCookieValues(fetchCookieValues(name), this.o);
285        Cookie.bake(name, JSON.stringify(values), values.domain, values.path, expires);
286    },
287    fetch: function(name) {
288        fetchCookieValues(name).data;
289    },
290    remove: function(name) {
291        var values = fillinDefaultCookieValues(fetchCookieValues(name));
292        Cookie.bake(name, '', values.domain, values.path, new Date(0));
293    }
294};
295
296})();
297
298;(function() {
299
300/** @fileOverview Javascript cryptography implementation.
301 *
302 * Crush to remove comments, shorten variable names and
303 * generally reduce transmission size.
304 *
305 * @author Emily Stark
306 * @author Mike Hamburg
307 * @author Dan Boneh
308 */
309
310"use strict";
311/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
312/*global document, window, escape, unescape */
313
314/** @namespace The Stanford Javascript Crypto Library, top-level namespace. */
315var sjcl = {
316  /** @namespace Symmetric ciphers. */
317  cipher: {},
318
319  /** @namespace Hash functions.  Right now only SHA256 is implemented. */
320  hash: {},
321  
322  /** @namespace Block cipher modes of operation. */
323  mode: {},
324
325  /** @namespace Miscellaneous.  HMAC and PBKDF2. */
326  misc: {},
327  
328  /**
329   * @namespace Bit array encoders and decoders.
330   *
331   * @description
332   * The members of this namespace are functions which translate between
333   * SJCL's bitArrays and other objects (usually strings).  Because it
334   * isn't always clear which direction is encoding and which is decoding,
335   * the method names are "fromBits" and "toBits".
336   */
337  codec: {},
338  
339  /** @namespace Exceptions. */
340  exception: {
341    /** @class Ciphertext is corrupt. */
342    corrupt: function(message) {
343      this.toString = function() { return "CORRUPT: "+this.message; };
344      this.message = message;
345    },
346    
347    /** @class Invalid parameter. */
348    invalid: function(message) {
349      this.toString = function() { return "INVALID: "+this.message; };
350      this.message = message;
351    },
352    
353    /** @class Bug or missing feature in SJCL. */
354    bug: function(message) {
355      this.toString = function() { return "BUG: "+this.message; };
356      this.message = message;
357    },
358
359    /** @class Something isn't ready. */
360    notReady: function(message) {
361      this.toString = function() { return "NOT READY: "+this.message; };
362      this.message = message;
363    }
364  }
365};
366/** @fileOverview Low-level AES implementation.
367 *
368 * This file contains a low-level implementation of AES, optimized for
369 * size and for efficiency on several browsers.  It is based on
370 * OpenSSL's aes_core.c, a public-domain implementation by Vincent
371 * Rijmen, Antoon Bosselaers and Paulo Barreto.
372 *
373 * An older version of this implementation is available in the public
374 * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh,
375 * Stanford University 2008-2010 and BSD-licensed for liability
376 * reasons.
377 *
378 * @author Emily Stark
379 * @author Mike Hamburg
380 * @author Dan Boneh
381 */
382
383/**
384 * Schedule out an AES key for both encryption and decryption.  This
385 * is a low-level class.  Use a cipher mode to do bulk encryption.
386 *
387 * @constructor
388 * @param {Array} key The key as an array of 4, 6 or 8 words.
389 *
390 * @class Advanced Encryption Standard (low-level interface)
391 */
392sjcl.cipher.aes = function (key) {
393  if (!this._tables[0][0][0]) {
394    this._precompute();
395  }
396  
397  var i, j, tmp,
398    encKey, decKey,
399    sbox = this._tables[0][4], decTable = this._tables[1],
400    keyLen = key.length, rcon = 1;
401  
402  if (keyLen !== 4 && keyLen !== 6 && keyLen !== 8) {
403    throw new sjcl.exception.invalid("invalid aes key size");
404  }
405  
406  this._key = [encKey = key.slice(0), decKey = []];
407  
408  // schedule encryption keys
409  for (i = keyLen; i < 4 * keyLen + 28; i++) {
410    tmp = encKey[i-1];
411    
412    // apply sbox
413    if (i%keyLen === 0 || (keyLen === 8 && i%keyLen === 4)) {
414      tmp = sbox[tmp>>>24]<<24 ^ sbox[tmp>>16&255]<<16 ^ sbox[tmp>>8&255]<<8 ^ sbox[tmp&255];
415      
416      // shift rows and add rcon
417      if (i%keyLen === 0) {
418        tmp = tmp<<8 ^ tmp>>>24 ^ rcon<<24;
419        rcon = rcon<<1 ^ (rcon>>7)*283;
420      }
421    }
422    
423    encKey[i] = encKey[i-keyLen] ^ tmp;
424  }
425  
426  // schedule decryption keys
427  for (j = 0; i; j++, i--) {
428    tmp = encKey[j&3 ? i : i - 4];
429    if (i<=4 || j<4) {
430      decKey[j] = tmp;
431    } else {
432      decKey[j] = decTable[0][sbox[tmp>>>24      ]] ^
433                  decTable[1][sbox[tmp>>16  & 255]] ^
434                  decTable[2][sbox[tmp>>8   & 255]] ^
435                  decTable[3][sbox[tmp      & 255]];
436    }
437  }
438};
439
440sjcl.cipher.aes.prototype = {
441  // public
442  /* Something like this might appear here eventually
443  name: "AES",
444  blockSize: 4,
445  keySizes: [4,6,8],
446  */
447  
448  /**
449   * Encrypt an array of 4 big-endian words.
450   * @param {Array} data The plaintext.
451   * @return {Array} The ciphertext.
452   */
453  encrypt:function (data) { return this._crypt(data,0); },
454  
455  /**
456   * Decrypt an array of 4 big-endian words.
457   * @param {Array} data The ciphertext.
458   * @return {Array} The plaintext.
459   */
460  decrypt:function (data) { return this._crypt(data,1); },
461  
462  /**
463   * The expanded S-box and inverse S-box tables.  These will be computed
464   * on the client so that we don't have to send them down the wire.
465   *
466   * There are two tables, _tables[0] is for encryption and
467   * _tables[1] is for decryption.
468   *
469   * The first 4 sub-tables are the expanded S-box with MixColumns.  The
470   * last (_tables[01][4]) is the S-box itself.
471   *
472   * @private
473   */
474  _tables: [[[],[],[],[],[]],[[],[],[],[],[]]],
475
476  /**
477   * Expand the S-box tables.
478   *
479   * @private
480   */
481  _precompute: function () {
482   var encTable = this._tables[0], decTable = this._tables[1],
483       sbox = encTable[4], sboxInv = decTable[4],
484       i, x, xInv, d=[], th=[], x2, x4, x8, s, tEnc, tDec;
485
486    // Compute double and third tables
487   for (i = 0; i < 256; i++) {
488     th[( d[i] = i<<1 ^ (i>>7)*283 )^i]=i;
489   }
490   
491   for (x = xInv = 0; !sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
492     // Compute sbox
493     s = xInv ^ xInv<<1 ^ xInv<<2 ^ xInv<<3 ^ xInv<<4;
494     s = s>>8 ^ s&255 ^ 99;
495     sbox[x] = s;
496     sboxInv[s] = x;
497     
498     // Compute MixColumns
499     x8 = d[x4 = d[x2 = d[x]]];
500     tDec = x8*0x1010101 ^ x4*0x10001 ^ x2*0x101 ^ x*0x1010100;
501     tEnc = d[s]*0x101 ^ s*0x1010100;
502     
503     for (i = 0; i < 4; i++) {
504       encTable[i][x] = tEnc = tEnc<<24 ^ tEnc>>>8;
505       decTable[i][s] = tDec = tDec<<24 ^ tDec>>>8;
506     }
507   }
508   
509   // Compactify.  Considerable speedup on Firefox.
510   for (i = 0; i < 5; i++) {
511     encTable[i] = encTable[i].slice(0);
512     decTable[i] = decTable[i].slice(0);
513   }
514  },
515  
516  /**
517   * Encryption and decryption core.
518   * @param {Array} input Four words to be encrypted or decrypted.
519   * @param dir The direction, 0 for encrypt and 1 for decrypt.
520   * @return {Array} The four encrypted or decrypted words.
521   * @private
522   */
523  _crypt:function (input, dir) {
524    if (input.length !== 4) {
525      throw new sjcl.exception.invalid("invalid aes block size");
526    }
527    
528    var key = this._key[dir],
529        // state variables a,b,c,d are loaded with pre-whitened data
530        a = input[0]           ^ key[0],
531        b = input[dir ? 3 : 1] ^ key[1],
532        c = input[2]           ^ key[2],
533        d = input[dir ? 1 : 3] ^ key[3],
534        a2, b2, c2,
535        
536        nInnerRounds = key.length/4 - 2,
537        i,
538        kIndex = 4,
539        out = [0,0,0,0],
540        table = this._tables[dir],
541        
542        // load up the tables
543        t0    = table[0],
544        t1    = table[1],
545        t2    = table[2],
546        t3    = table[3],
547        sbox  = table[4];
548 
549    // Inner rounds.  Cribbed from OpenSSL.
550    for (i = 0; i < nInnerRounds; i++) {
551      a2 = t0[a>>>24] ^ t1[b>>16 & 255] ^ t2[c>>8 & 255] ^ t3[d & 255] ^ key[kIndex];
552      b2 = t0[b>>>24] ^ t1[c>>16 & 255] ^ t2[d>>8 & 255] ^ t3[a & 255] ^ key[kIndex + 1];
553      c2 = t0[c>>>24] ^ t1[d>>16 & 255] ^ t2[a>>8 & 255] ^ t3[b & 255] ^ key[kIndex + 2];
554      d  = t0[d>>>24] ^ t1[a>>16 & 255] ^ t2[b>>8 & 255] ^ t3[c & 255] ^ key[kIndex + 3];
555      kIndex += 4;
556      a=a2; b=b2; c=c2;
557    }
558        
559    // Last round.
560    for (i = 0; i < 4; i++) {
561      out[dir ? 3&-i : i] =
562        sbox[a>>>24      ]<<24 ^ 
563        sbox[b>>16  & 255]<<16 ^
564        sbox[c>>8   & 255]<<8  ^
565        sbox[d      & 255]     ^
566        key[kIndex++];
567      a2=a; a=b; b=c; c=d; d=a2;
568    }
569    
570    return out;
571  }
572};
573
574/** @fileOverview Arrays of bits, encoded as arrays of Numbers.
575 *
576 * @author Emily Stark
577 * @author Mike Hamburg
578 * @author Dan Boneh
579 */
580
581/** @namespace Arrays of bits, encoded as arrays of Numbers.
582 *
583 * @description
584 * <p>
585 * These objects are the currency accepted by SJCL's crypto functions.
586 * </p>
587 *
588 * <p>
589 * Most of our crypto primitives operate on arrays of 4-byte words internally,
590 * but many of them can take arguments that are not a multiple of 4 bytes.
591 * This library encodes arrays of bits (whose size need not be a multiple of 8
592 * bits) as arrays of 32-bit words.  The bits are packed, big-endian, into an
593 * array of words, 32 bits at a time.  Since the words are double-precision
594 * floating point numbers, they fit some extra data.  We use this (in a private,
595 * possibly-changing manner) to encode the number of bits actually  present
596 * in the last word of the array.
597 * </p>
598 *
599 * <p>
600 * Because bitwise ops clear this out-of-band data, these arrays can be passed
601 * to ciphers like AES which want arrays of words.
602 * </p>
603 */
604sjcl.bitArray = {
605  /**
606   * Array slices in units of bits.
607   * @param {bitArray a} The array to slice.
608   * @param {Number} bstart The offset to the start of the slice, in bits.
609   * @param {Number} bend The offset to the end of the slice, in bits.  If this is undefined,
610   * slice until the end of the array.
611   * @return {bitArray} The requested slice.
612   */
613  bitSlice: function (a, bstart, bend) {
614    a = sjcl.bitArray._shiftRight(a.slice(bstart/32), 32 - (bstart & 31)).slice(1);
615    return (bend === undefined) ? a : sjcl.bitArray.clamp(a, bend-bstart);
616  },
617
618  /**
619   * Concatenate two bit arrays.
620   * @param {bitArray} a1 The first array.
621   * @param {bitArray} a2 The second array.
622   * @return {bitArray} The concatenation of a1 and a2.
623   */
624  concat: function (a1, a2) {
625    if (a1.length === 0 || a2.length === 0) {
626      return a1.concat(a2);
627    }
628    
629    var out, i, last = a1[a1.length-1], shift = sjcl.bitArray.getPartial(last);
630    if (shift === 32) {
631      return a1.concat(a2);
632    } else {
633      return sjcl.bitArray._shiftRight(a2, shift, last|0, a1.slice(0,a1.length-1));
634    }
635  },
636
637  /**
638   * Find the length of an array of bits.
639   * @param {bitArray} a The array.
640   * @return {Number} The length of a, in bits.
641   */
642  bitLength: function (a) {
643    var l = a.length, x;
644    if (l === 0) { return 0; }
645    x = a[l - 1];
646    return (l-1) * 32 + sjcl.bitArray.getPartial(x);
647  },
648
649  /**
650   * Truncate an array.
651   * @param {bitArray} a The array.
652   * @param {Number} len The length to truncate to, in bits.
653   * @return {bitArray} A new array, truncated to len bits.
654   */
655  clamp: function (a, len) {
656    if (a.length * 32 < len) { return a; }
657    a = a.slice(0, Math.ceil(len / 32));
658    var l = a.length;
659    len = len & 31;
660    if (l > 0 && len) {
661      a[l-1] = sjcl.bitArray.partial(len, a[l-1] & 0x80000000 >> (len-1), 1);
662    }
663    return a;
664  },
665
666  /**
667   * Make a partial word for a bit array.
668   * @param {Number} len The number of bits in the word.
669   * @param {Number} x The bits.
670   * @param {Number} [0] _end Pass 1 if x has already been shifted to the high side.
671   * @return {Number} The partial word.
672   */
673  partial: function (len, x, _end) {
674    if (len === 32) { return x; }
675    return (_end ? x|0 : x << (32-len)) + len * 0x10000000000;
676  },
677
678  /**
679   * Get the number of bits used by a partial word.
680   * @param {Number} x The partial word.
681   * @return {Number} The number of bits used by the partial word.
682   */
683  getPartial: function (x) {
684    return Math.round(x/0x10000000000) || 32;
685  },
686
687  /**
688   * Compare two arrays for equality in a predictable amount of time.
689   * @param {bitArray} a The first array.
690   * @param {bitArray} b The second array.
691   * @return {boolean} true if a == b; false otherwise.
692   */
693  equal: function (a, b) {
694    if (sjcl.bitArray.bitLength(a) !== sjcl.bitArray.bitLength(b)) {
695      return false;
696    }
697    var x = 0, i;
698    for (i=0; i<a.length; i++) {
699      x |= a[i]^b[i];
700    }
701    return (x === 0);
702  },
703
704  /** Shift an array right.
705   * @param {bitArray} a The array to shift.
706   * @param {Number} shift The number of bits to shift.
707   * @param {Number} [carry=0] A byte to carry in
708   * @param {bitArray} [out=[]] An array to prepend to the output.
709   * @private
710   */
711  _shiftRight: function (a, shift, carry, out) {
712    var i, last2=0, shift2;
713    if (out === undefined) { out = []; }
714    
715    for (; shift >= 32; shift -= 32) {
716      out.push(carry);
717      carry = 0;
718    }
719    if (shift === 0) {
720      return out.concat(a);
721    }
722    
723    for (i=0; i<a.length; i++) {
724      out.push(carry | a[i]>>>shift);
725      carry = a[i] << (32-shift);
726    }
727    last2 = a.length ? a[a.length-1] : 0;
728    shift2 = sjcl.bitArray.getPartial(last2);
729    out.push(sjcl.bitArray.partial(shift+shift2 & 31, (shift + shift2 > 32) ? carry : out.pop(),1));
730    return out;
731  },
732  
733  /** xor a block of 4 words together.
734   * @private
735   */
736  _xor4: function(x,y) {
737    return [x[0]^y[0],x[1]^y[1],x[2]^y[2],x[3]^y[3]];
738  }
739};
740/** @fileOverview Bit array codec implementations.
741 *
742 * @author Emily Stark
743 * @author Mike Hamburg
744 * @author Dan Boneh
745 */
746 
747/** @namespace UTF-8 strings */
748sjcl.codec.utf8String = {
749  /** Convert from a bitArray to a UTF-8 string. */
750  fromBits: function (arr) {
751    var out = "", bl = sjcl.bitArray.bitLength(arr), i, tmp;
752    for (i=0; i<bl/8; i++) {
753      if ((i&3) === 0) {
754        tmp = arr[i/4];
755      }
756      out += String.fromCharCode(tmp >>> 24);
757      tmp <<= 8;
758    }
759    return decodeURIComponent(escape(out));
760  },
761  
762  /** Convert from a UTF-8 string to a bitArray. */
763  toBits: function (str) {
764    str = unescape(encodeURIComponent(str));
765    var out = [], i, tmp=0;
766    for (i=0; i<str.length; i++) {
767      tmp = tmp << 8 | str.charCodeAt(i);
768      if ((i&3) === 3) {
769        out.push(tmp);
770        tmp = 0;
771      }
772    }
773    if (i&3) {
774      out.push(sjcl.bitArray.partial(8*(i&3), tmp));
775    }
776    return out;
777  }
778};
779/** @fileOverview Bit array codec implementations.
780 *
781 * @author Emily Stark
782 * @author Mike Hamburg
783 * @author Dan Boneh
784 */
785
786/** @namespace Base64 encoding/decoding */
787sjcl.codec.base64 = {
788  /** The base64 alphabet.
789   * @private
790   */
791  _chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
792  
793  /** Convert from a bitArray to a base64 string. */
794  fromBits: function (arr, _noEquals) {
795    var out = "", i, bits=0, c = sjcl.codec.base64._chars, ta=0, bl = sjcl.bitArray.bitLength(arr);
796    for (i=0; out.length * 6 < bl; ) {
797      out += c.charAt((ta ^ arr[i]>>>bits) >>> 26);
798      if (bits < 6) {
799        ta = arr[i] << (6-bits);
800        bits += 26;
801        i++;
802      } else {
803        ta <<= 6;
804        bits -= 6;
805      }
806    }
807    while ((out.length & 3) && !_noEquals) { out += "="; }
808    return out;
809  },
810  
811  /** Convert from a base64 string to a bitArray */
812  toBits: function(str) {
813    str = str.replace(/\s|=/g,'');
814    var out = [], i, bits=0, c = sjcl.codec.base64._chars, ta=0, x;
815    for (i=0; i<str.length; i++) {
816      x = c.indexOf(str.charAt(i));
817      if (x < 0) {
818        throw new sjcl.exception.invalid("this isn't base64!");
819      }
820      if (bits > 26) {
821        bits -= 26;
822        out.push(ta ^ x>>>bits);
823        ta  = x << (32-bits);
824      } else {
825        bits += 6;
826        ta ^= x << (32-bits);
827      }
828    }
829    if (bits&56) {
830      out.push(sjcl.bitArray.partial(bits&56, ta, 1));
831    }
832    return out;
833  }
834};
835/** @fileOverview Javascript SHA-256 implementation.
836 *
837 * An older version of this implementation is available in the public
838 * domain, but this one is (c) Emily Stark, Mike Hamburg, Dan Boneh,
839 * Stanford University 2008-2010 and BSD-licensed for liability
840 * reasons.
841 *
842 * Special thanks to Aldo Cortesi for pointing out several bugs in
843 * this code.
844 *
845 * @author Emily Stark
846 * @author Mike Hamburg
847 * @author Dan Boneh
848 */
849
850/**
851 * Context for a SHA-256 operation in progress.
852 * @constructor
853 * @class Secure Hash Algorithm, 256 bits.
854 */
855sjcl.hash.sha256 = function (hash) {
856  if (!this._key[0]) { this._precompute(); }
857  if (hash) {
858    this._h = hash._h.slice(0);
859    this._buffer = hash._buffer.slice(0);
860    this._length = hash._length;
861  } else {
862    this.reset();
863  }
864};
865
866/**
867 * Hash a string or an array of words.
868 * @static
869 * @param {bitArray|String} data the data to hash.
870 * @return {bitArray} The hash value, an array of 16 big-endian words.
871 */
872sjcl.hash.sha256.hash = function (data) {
873  return (new sjcl.hash.sha256()).update(data).finalize();
874};
875
876sjcl.hash.sha256.prototype = {
877  /**
878   * The hash's block size, in bits.
879   * @constant
880   */
881  blockSize: 512,
882   
883  /**
884   * Reset the hash state.
885   * @return this
886   */
887  reset:function () {
888    this._h = this._init.slice(0);
889    this._buffer = [];
890    this._length = 0;
891    return this;
892  },
893  
894  /**
895   * Input several words to the hash.
896   * @param {bitArray|String} data the data to hash.
897   * @return this
898   */
899  update: function (data) {
900    if (typeof data === "string") {
901      data = sjcl.codec.utf8String.toBits(data);
902    }
903    var i, b = this._buffer = sjcl.bitArray.concat(this._buffer, data),
904        ol = this._length,
905        nl = this._length = ol + sjcl.bitArray.bitLength(data);
906    for (i = 512+ol & -512; i <= nl; i+= 512) {
907      this._block(b.splice(0,16));
908    }
909    return this;
910  },
911  
912  /**
913   * Complete hashing and output the hash value.
914   * @return {bitArray} The hash value, an array of 16 big-endian words.
915   */
916  finalize:function () {
917    var i, b = this._buffer, h = this._h;
918
919    // Round out and push the buffer
920    b = sjcl.bitArray.concat(b, [sjcl.bitArray.partial(1,1)]);
921    
922    // Round out the buffer to a multiple of 16 words, less the 2 length words.
923    for (i = b.length + 2; i & 15; i++) {
924      b.push(0);
925    }
926    
927    // append the length
928    b.push(Math.floor(this._length / 0x100000000));
929    b.push(this._length | 0);
930
931    while (b.length) {
932      this._block(b.splice(0,16));
933    }
934
935    this.reset();
936    return h;
937  },
938
939  /**
940   * The SHA-256 initialization vector, to be precomputed.
941   * @private
942   */
943  _init:[],
944  /*
945  _init:[0x6a09e667,0xbb67ae85,0x3c6ef372,0xa54ff53a,0x510e527f,0x9b05688c,0x1f83d9ab,0x5be0cd19],
946  */
947  
948  /**
949   * The SHA-256 hash key, to be precomputed.
950   * @private
951   */
952  _key:[],
953  /*
954  _key:
955    [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
956     0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
957     0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
958     0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
959     0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
960     0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
961     0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
962     0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2],
963  */
964
965
966  /**
967   * Function to precompute _init and _key.
968   * @private
969   */
970  _precompute: function () {
971    var i = 0, prime = 2, factor;
972
973    function frac(x) { return (x-Math.floor(x)) * 0x100000000 | 0; }
974
975    outer: for (; i<64; prime++) {
976      for (factor=2; factor*factor <= prime; factor++) {
977        if (prime % factor === 0) {
978          // not a prime
979          continue outer;
980        }
981      }
982      
983      if (i<8) {
984        this._init[i] = frac(Math.pow(prime, 1/2));
985      }
986      this._key[i] = frac(Math.pow(prime, 1/3));
987      i++;
988    }
989  },
990  
991  /**
992   * Perform one cycle of SHA-256.
993   * @param {bitArray} words one block of words.
994   * @private
995   */
996  _block:function (words) {  
997    var i, tmp, a, b,
998      w = words.slice(0),
999      h = this._h,
1000      k = this._key,
1001      h0 = h[0], h1 = h[1], h2 = h[2], h3 = h[3],
1002      h4 = h[4], h5 = h[5], h6 = h[6], h7 = h[7];
1003
1004    /* Rationale for placement of |0 :
1005     * If a value can overflow is original 32 bits by a factor of more than a few
1006     * million (2^23 ish), there is a possibility that it might overflow the
1007     * 53-bit mantissa and lose precision.
1008     *
1009     * To avoid this, we clamp back to 32 bits by |'ing with 0 on any value that
1010     * propagates around the loop, and on the hash state h[].  I don't believe
1011     * that the clamps on h4 and on h0 are strictly necessary, but it's close
1012     * (for h4 anyway), and better safe than sorry.
1013     *
1014     * The clamps on h[] are necessary for the output to be correct even in the
1015     * common case and for short inputs.
1016     */
1017    for (i=0; i<64; i++) {
1018      // load up the input word for this round
1019      if (i<16) {
1020        tmp = w[i];
1021      } else {
1022        a   = w[(i+1 ) & 15];
1023        b   = w[(i+14) & 15];
1024        tmp = w[i&15] = ((a>>>7  ^ a>>>18 ^ a>>>3  ^ a<<25 ^ a<<14) + 
1025                         (b>>>17 ^ b>>>19 ^ b>>>10 ^ b<<15 ^ b<<13) +
1026                         w[i&15] + w[(i+9) & 15]) | 0;
1027      }
1028      
1029      tmp = (tmp + h7 + (h4>>>6 ^ h4>>>11 ^ h4>>>25 ^ h4<<26 ^ h4<<21 ^ h4<<7) +  (h6 ^ h4&(h5^h6)) + k[i]); // | 0;
1030      
1031      // shift register
1032      h7 = h6; h6 = h5; h5 = h4;
1033      h4 = h3 + tmp | 0;
1034      h3 = h2; h2 = h1; h1 = h0;
1035
1036      h0 = (tmp +  ((h1&h2) ^ (h3&(h1^h2))) + (h1>>>2 ^ h1>>>13 ^ h1>>>22 ^ h1<<30 ^ h1<<19 ^ h1<<10)) | 0;
1037    }
1038
1039    h[0] = h[0]+h0 | 0;
1040    h[1] = h[1]+h1 | 0;
1041    h[2] = h[2]+h2 | 0;
1042    h[3] = h[3]+h3 | 0;
1043    h[4] = h[4]+h4 | 0;
1044    h[5] = h[5]+h5 | 0;
1045    h[6] = h[6]+h6 | 0;
1046    h[7] = h[7]+h7 | 0;
1047  }
1048};
1049
1050
1051/** @fileOverview CCM mode implementation.
1052 *
1053 * Special thanks to Roy Nicholson for pointing out a bug in our
1054 * implementation.
1055 *
1056 * @author Emily Stark
1057 * @author Mike Hamburg
1058 * @author Dan Boneh
1059 */
1060
1061/** @namespace CTR mode with CBC MAC. */
1062sjcl.mode.ccm = {
1063  /** The name of the mode.
1064   * @constant
1065   */
1066  name: "ccm",
1067  
1068  /** Encrypt in CCM mode.
1069   * @static
1070   * @param {Object} prf The pseudorandom function.  It must have a block size of 16 bytes.
1071   * @param {bitArray} plaintext The plaintext data.
1072   * @param {bitArray} iv The initialization value.
1073   * @param {bitArray} [adata=[]] The authenticated data.
1074   * @param {Number} [tlen=64] the desired tag length, in bits.
1075   * @return {bitArray} The encrypted data, an array of bytes.
1076   */
1077  encrypt: function(prf, plaintext, iv, adata, tlen) {
1078    var L, i, out = plaintext.slice(0), tag, w=sjcl.bitArray, ivl = w.bitLength(iv) / 8, ol = w.bitLength(out) / 8;
1079    tlen = tlen || 64;
1080    adata = adata || [];
1081    
1082    if (ivl < 7) {
1083      throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
1084    }
1085    
1086    // compute the length of the length
1087    for (L=2; L<4 && ol >>> 8*L; L++) {}
1088    if (L < 15 - ivl) { L = 15-ivl; }
1089    iv = w.clamp(iv,8*(15-L));
1090    
1091    // compute the tag
1092    tag = sjcl.mode.ccm._computeTag(prf, plaintext, iv, adata, tlen, L);
1093    
1094    // encrypt
1095    out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L);
1096    
1097    return w.concat(out.data, out.tag);
1098  },
1099  
1100  /** Decrypt in CCM mode.
1101   * @static
1102   * @param {Object} prf The pseudorandom function.  It must have a block size of 16 bytes.
1103   * @param {bitArray} ciphertext The ciphertext data.
1104   * @param {bitArray} iv The initialization value.
1105   * @param {bitArray} [[]] adata The authenticated data.
1106   * @param {Number} [64] tlen the desired tag length, in bits.
1107   * @return {bitArray} The decrypted data.
1108   */
1109  decrypt: function(prf, ciphertext, iv, adata, tlen) {
1110    tlen = tlen || 64;
1111    adata = adata || [];
1112    var L, i, 
1113        w=sjcl.bitArray,
1114        ivl = w.bitLength(iv) / 8,
1115        ol = w.bitLength(ciphertext), 
1116        out = w.clamp(ciphertext, ol - tlen),
1117        tag = w.bitSlice(ciphertext, ol - tlen), tag2;
1118    
1119
1120    ol = (ol - tlen) / 8;
1121        
1122    if (ivl < 7) {
1123      throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");
1124    }
1125    
1126    // compute the length of the length
1127    for (L=2; L<4 && ol >>> 8*L; L++) {}
1128    if (L < 15 - ivl) { L = 15-ivl; }
1129    iv = w.clamp(iv,8*(15-L));
1130    
1131    // decrypt
1132    out = sjcl.mode.ccm._ctrMode(prf, out, iv, tag, tlen, L);
1133    
1134    // check the tag
1135    tag2 = sjcl.mode.ccm._computeTag(prf, out.data, iv, adata, tlen, L);
1136    if (!w.equal(out.tag, tag2)) {
1137      throw new sjcl.exception.corrupt("ccm: tag doesn't match");
1138    }
1139    
1140    return out.data;
1141  },
1142
1143  /* Compute the (unencrypted) authentication tag, according to the CCM specification
1144   * @param {Object} prf The pseudorandom function.
1145   * @param {bitArray} plaintext The plaintext data.
1146   * @param {bitArray} iv The initialization value.
1147   * @param {bitArray} adata The authenticated data.
1148   * @param {Number} tlen the desired tag length, in bits.
1149   * @return {bitArray} The tag, but not yet encrypted.
1150   * @private
1151   */
1152  _computeTag: function(prf, plaintext, iv, adata, tlen, L) {
1153    // compute B[0]
1154    var q, mac, field = 0, offset = 24, tmp, i, macData = [], w=sjcl.bitArray, xor = w._xor4;
1155
1156    tlen /= 8;
1157  
1158    // check tag length and message length
1159    if (tlen % 2 || tlen < 4 || tlen > 16) {
1160      throw new sjcl.exception.invalid("ccm: invalid tag length");
1161    }
1162  
1163    if (adata.length > 0xFFFFFFFF || plaintext.length > 0xFFFFFFFF) {
1164      // I don't want to deal with extracting high words from doubles.
1165      throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data");
1166    }
1167
1168    // mac the flags
1169    mac = [w.partial(8, (adata.length ? 1<<6 : 0) | (tlen-2) << 2 | L-1)];
1170
1171    // mac the iv and length
1172    mac = w.concat(mac, iv);
1173    mac[3] |= w.bitLength(plaintext)/8;
1174    mac = prf.encrypt(mac);
1175    
1176  
1177    if (adata.length) {
1178      // mac the associated data.  start with its length...
1179      tmp = w.bitLength(adata)/8;
1180      if (tmp <= 0xFEFF) {
1181        macData = [w.partial(16, tmp)];
1182      } else if (tmp <= 0xFFFFFFFF) {
1183        macData = w.concat([w.partial(16,0xFFFE)], [tmp]);
1184      } // else ...
1185    
1186      // mac the data itself
1187      macData = w.concat(macData, adata);
1188      for (i=0; i<macData.length; i += 4) {
1189        mac = prf.encrypt(xor(mac, macData.slice(i,i+4).concat([0,0,0])));
1190      }
1191    }
1192  
1193    // mac the plaintext
1194    for (i=0; i<plaintext.length; i+=4) {
1195      mac = prf.encrypt(xor(mac, plaintext.slice(i,i+4).concat([0,0,0])));
1196    }
1197
1198    return w.clamp(mac, tlen * 8);
1199  },
1200
1201  /** CCM CTR mode.
1202   * Encrypt or decrypt data and tag with the prf in CCM-style CTR mode.
1203   * May mutate its arguments.
1204   * @param {Object} prf The PRF.
1205   * @param {bitArray} data The data to be encrypted or decrypted.
1206   * @param {bitArray} iv The initialization vector.
1207   * @param {bitArray} tag The authentication tag.
1208   * @param {Number} tlen The length of th etag, in bits.
1209   * @param {Number} L The CCM L value.
1210   * @return {Object} An object with data and tag, the en/decryption of data and tag values.
1211   * @private
1212   */
1213  _ctrMode: function(prf, data, iv, tag, tlen, L) {
1214    var enc, i, w=sjcl.bitArray, xor = w._xor4, ctr, b, l = data.length, bl=w.bitLength(data);
1215
1216    // start the ctr
1217    ctr = w.concat([w.partial(8,L-1)],iv).concat([0,0,0]).slice(0,4);
1218    
1219    // en/decrypt the tag
1220    tag = w.bitSlice(xor(tag,prf.encrypt(ctr)), 0, tlen);
1221  
1222    // en/decrypt the data
1223    if (!l) { return {tag:tag, data:[]}; }
1224    
1225    for (i=0; i<l; i+=4) {
1226      ctr[3]++;
1227      enc = prf.encrypt(ctr);
1228      data[i]   ^= enc[0];
1229      data[i+1] ^= enc[1];
1230      data[i+2] ^= enc[2];
1231      data[i+3] ^= enc[3];
1232    }
1233    return { tag:tag, data:w.clamp(data,bl) };
1234  }
1235};
1236/** @fileOverview HMAC implementation.
1237 *
1238 * @author Emily Stark
1239 * @author Mike Hamburg
1240 * @author Dan Boneh
1241 */
1242
1243/** HMAC with the specified hash function.
1244 * @constructor
1245 * @param {bitArray} key the key for HMAC.
1246 * @param {Object} [hash=sjcl.hash.sha256] The hash function to use.
1247 */
1248sjcl.misc.hmac = function (key, Hash) {
1249  this._hash = Hash = Hash || sjcl.hash.sha256;
1250  var exKey = [[],[]], i,
1251      bs = Hash.prototype.blockSize / 32;
1252  this._baseHash = [new Hash(), new Hash()];
1253
1254  if (key.length > bs) {
1255    key = Hash.hash(key);
1256  }
1257  
1258  for (i=0; i<bs; i++) {
1259    exKey[0][i] = key[i]^0x36363636;
1260    exKey[1][i] = key[i]^0x5C5C5C5C;
1261  }
1262  
1263  this._baseHash[0].update(exKey[0]);
1264  this._baseHash[1].update(exKey[1]);
1265};
1266
1267/** HMAC with the specified hash function.  Also called encrypt since it's a prf.
1268 * @param {bitArray|String} data The data to mac.
1269 */
1270sjcl.misc.hmac.prototype.encrypt = sjcl.misc.hmac.prototype.mac = function (data) {
1271  var w = new (this._hash)(this._baseHash[0]).update(data).finalize();
1272  return new (this._hash)(this._baseHash[1]).update(w).finalize();
1273};
1274
1275/** @fileOverview Password-based key-derivation function, version 2.0.
1276 *
1277 * @author Emily Stark
1278 * @author Mike Hamburg
1279 * @author Dan Boneh
1280 */
1281
1282/** Password-Based Key-Derivation Function, version 2.0.
1283 *
1284 * Generate keys from passwords using PBKDF2-HMAC-SHA256.
1285 *
1286 * This is the method specified by RSA's PKCS #5 standard.
1287 *
1288 * @param {bitArray|String} password  The password.
1289 * @param {bitArray} salt The salt.  Should have lots of entropy.
1290 * @param {Number} [count=1000] The number of iterations.  Higher numbers make the function slower but more secure.
1291 * @param {Number} [length] The length of the derived key.  Defaults to the
1292                            output size of the hash function.
1293 * @param {Object} [Prff=sjcl.misc.hmac] The pseudorandom function family.
1294 * @return {bitArray} the derived key.
1295 */
1296sjcl.misc.pbkdf2 = function (password, salt, count, length, Prff) {
1297  count = count || 1000;
1298  
1299  if (length < 0 || count < 0) {
1300    throw sjcl.exception.invalid("invalid params to pbkdf2");
1301  }
1302  
1303  if (typeof password === "string") {
1304    password = sjcl.codec.utf8String.toBits(password);
1305  }
1306  
1307  Prff = Prff || sjcl.misc.hmac;
1308  
1309  var prf = new Prff(password),
1310      u, ui, i, j, k, out = [], b = sjcl.bitArray;
1311
1312  for (k = 1; 32 * out.length < (length || 1); k++) {
1313    u = ui = prf.encrypt(b.concat(salt,[k]));
1314    
1315    for (i=1; i<count; i++) {
1316      ui = prf.encrypt(ui);
1317      for (j=0; j<ui.length; j++) {
1318        u[j] ^= ui[j];
1319      }
1320    }
1321    
1322    out = out.concat(u);
1323  }
1324
1325  if (length) { out = b.clamp(out, length); }
1326
1327  return out;
1328};
1329/** @fileOverview Random number generator.
1330 *
1331 * @author Emily Stark
1332 * @author Mike Hamburg
1333 * @author Dan Boneh
1334 */
1335
1336/** @namespace Random number generator
1337 *
1338 * @description
1339 * <p>
1340 * This random number generator is a derivative of Ferguson and Schneier's
1341 * generator Fortuna.  It collects entropy from various events into several
1342 * pools, implemented by streaming SHA-256 instances.  It differs from
1343 * ordinary Fortuna in a few ways, though.
1344 * </p>
1345 *
1346 * <p>
1347 * Most importantly, it has an entropy estimator.  This is present because
1348 * there is a strong conflict here between making the generator available
1349 * as soon as possible, and making sure that it doesn't "run on empty".
1350 * In Fortuna, there is a saved state file, and the system is likely to have
1351 * time to warm up.
1352 * </p>
1353 *
1354 * <p>
1355 * Second, because users are unlikely to stay on the page for very long,
1356 * and to speed startup time, the number of pools increases logarithmically:
1357 * a new pool is created when the previous one is actually used for a reseed.
1358 * This gives the same asymptotic guarantees as Fortuna, but gives more
1359 * entropy to early reseeds.
1360 * </p>
1361 *
1362 * <p>
1363 * The entire mechanism here feels pretty klunky.  Furthermore, there are
1364 * several improvements that should be made, including support for
1365 * dedicated cryptographic functions that may be present in some browsers;
1366 * state files in local storage; cookies containing randomness; etc.  So
1367 * look for improvements in future versions.
1368 * </p>
1369 */
1370sjcl.random = {
1371  /** Generate several random words, and return them in an array
1372   * @param {Number} nwords The number of words to generate.
1373   */
1374  randomWords: function (nwords, paranoia) {
1375    var out = [], i, readiness = this.isReady(paranoia), g;
1376  
1377    if (readiness === this._NOT_READY) {
1378      throw new sjcl.exception.notReady("generator isn't seeded");
1379    } else if (readiness & this._REQUIRES_RESEED) {
1380      this._reseedFromPools(!(readiness & this._READY));
1381    }
1382  
1383    for (i=0; i<nwords; i+= 4) {
1384      if ((i+1) % this._MAX_WORDS_PER_BURST === 0) {
1385        this._gate();
1386      }
1387   
1388      g = this._gen4words();
1389      out.push(g[0],g[1],g[2],g[3]);
1390    }
1391    this._gate();
1392  
1393    return out.slice(0,nwords);
1394  },
1395  
1396  setDefaultParanoia: function (paranoia) {
1397    this._defaultParanoia = paranoia;
1398  },
1399  
1400  /**
1401   * Add entropy to the pools.
1402   * @param data The entropic value.  Should be a 32-bit integer, array of 32-bit integers, or string
1403   * @param {Number} estimatedEntropy The estimated entropy of data, in bits
1404   * @param {String} source The source of the entropy, eg "mouse"
1405   */
1406  addEntropy: function (data, estimatedEntropy, source) {
1407    source = source || "user";
1408  
1409    var id,
1410      i, tmp,
1411      t = (new Date()).valueOf(),
1412      robin = this._robins[source],
1413      oldReady = this.isReady(), err = 0;
1414      
1415    id = this._collectorIds[source];
1416    if (id === undefined) { id = this._collectorIds[source] = this._collectorIdNext ++; }
1417      
1418    if (robin === undefined) { robin = this._robins[source] = 0; }
1419    this._robins[source] = ( this._robins[source] + 1 ) % this._pools.length;
1420  
1421    switch(typeof(data)) {
1422      
1423    case "number":
1424      if (estimatedEntropy === undefined) {
1425        estimatedEntropy = 1;
1426      }
1427      this._pools[robin].update([id,this._eventId++,1,estimatedEntropy,t,1,data|0]);
1428      break;
1429      
1430    case "object":
1431      var objName = Object.prototype.toString.call(data);
1432      if (objName === "[object Uint32Array]") {
1433        tmp = [];
1434        for (i = 0; i < data.length; i++) {
1435          tmp.push(data[i]);
1436        }
1437        data = tmp;
1438      } else {
1439        if (objName !== "[object Array]") {
1440          err = 1;
1441        }
1442        for (i=0; i<data.length && !err; i++) {
1443          if (typeof(data[i]) != "number") {
1444            err = 1;
1445          }
1446        }
1447      }
1448      if (!err) {
1449        if (estimatedEntropy === undefined) {
1450          /* horrible entropy estimator */
1451          estimatedEntropy = 0;
1452          for (i=0; i<data.length; i++) {
1453            tmp= data[i];
1454            while (tmp>0) {
1455              estimatedEntropy++;
1456              tmp = tmp >>> 1;
1457            }
1458          }
1459        }
1460        this._pools[robin].update([id,this._eventId++,2,estimatedEntropy,t,data.length].concat(data));
1461      }
1462      break;
1463      
1464    case "string":
1465      if (estimatedEntropy === undefined) {
1466       /* English text has just over 1 bit per character of entropy.
1467        * But this might be HTML or something, and have far less
1468        * entropy than English...  Oh well, let's just say one bit.
1469        */
1470       estimatedEntropy = data.length;
1471      }
1472      this._pools[robin].update([id,this._eventId++,3,estimatedEntropy,t,data.length]);
1473      this._pools[robin].update(data);
1474      break;
1475      
1476    default:
1477      err=1;
1478    }
1479    if (err) {
1480      throw new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string");
1481    }
1482  
1483    /* record the new strength */
1484    this._poolEntropy[robin] += estimatedEntropy;
1485    this._poolStrength += estimatedEntropy;
1486  
1487    /* fire off events */
1488    if (oldReady === this._NOT_READY) {
1489      if (this.isReady() !== this._NOT_READY) {
1490        this._fireEvent("seeded", Math.max(this._strength, this._poolStrength));
1491      }
1492      this._fireEvent("progress", this.getProgress());
1493    }
1494  },
1495  
1496  /** Is the generator ready? */
1497  isReady: function (paranoia) {
1498    var entropyRequired = this._PARANOIA_LEVELS[ (paranoia !== undefined) ? paranoia : this._defaultParanoia ];
1499  
1500    if (this._strength && this._strength >= entropyRequired) {
1501      return (this._poolEntropy[0] > this._BITS_PER_RESEED && (new Date()).valueOf() > this._nextReseed) ?
1502        this._REQUIRES_RESEED | this._READY :
1503        this._READY;
1504    } else {
1505      return (this._poolStrength >= entropyRequired) ?
1506        this._REQUIRES_RESEED | this._NOT_READY :
1507        this._NOT_READY;
1508    }
1509  },
1510  
1511  /** Get the generator's progress toward readiness, as a fraction */
1512  getProgress: function (paranoia) {
1513    var entropyRequired = this._PARANOIA_LEVELS[ paranoia ? paranoia : this._defaultParanoia ];
1514  
1515    if (this._strength >= entropyRequired) {
1516      return 1.0;
1517    } else {
1518      return (this._poolStrength > entropyRequired) ?
1519        1.0 :
1520        this._poolStrength / entropyRequired;
1521    }
1522  },
1523  
1524  /** start the built-in entropy collectors */
1525  startCollectors: function () {
1526    if (this._collectorsStarted) { return; }
1527  
1528    if (window.addEventListener) {
1529      window.addEventListener("load", this._loadTimeCollector, false);
1530      window.addEventListener("mousemove", this._mouseCollector, false);
1531    } else if (document.attachEvent) {
1532      document.attachEvent("onload", this._loadTimeCollector);
1533      document.attachEvent("onmousemove", this._mouseCollector);
1534    }
1535    else {
1536      throw new sjcl.exception.bug("can't attach event");
1537    }
1538  
1539    this._collectorsStarted = true;
1540  },
1541  
1542  /** stop the built-in entropy collectors */
1543  stopCollectors: function () {
1544    if (!this._collectorsStarted) { return; }
1545  
1546    if (window.removeEventListener) {
1547      window.removeEventListener("load", this._loadTimeCollector, false);
1548      window.removeEventListener("mousemove", this._mouseCollector, false);
1549    } else if (window.detachEvent) {
1550      window.detachEvent("onload", this._loadTimeCollector);
1551      window.detachEvent("onmousemove", this._mouseCollector);
1552    }
1553    this._collectorsStarted = false;
1554  },
1555  
1556  /* use a cookie to store entropy.
1557  useCookie: function (all_cookies) {
1558      throw new sjcl.exception.bug("random: useCookie is unimplemented");
1559  },*/
1560  
1561  /** add an event listener for progress or seeded-ness. */
1562  addEventListener: function (name, callback) {
1563    this._callbacks[name][this._callbackI++] = callback;
1564  },
1565  
1566  /** remove an event listener for progress or seeded-ness */
1567  removeEventListener: function (name, cb) {
1568    var i, j, cbs=this._callbacks[name], jsTemp=[];
1569  
1570    /* I'm not sure if this is necessary; in C++, iterating over a
1571     * collection and modifying it at the same time is a no-no.
1572     */
1573  
1574    for (j in cbs) {
1575	if (cbs.hasOwnProperty(j) && cbs[j] === cb) {
1576        jsTemp.push(j);
1577      }
1578    }
1579  
1580    for (i=0; i<jsTemp.length; i++) {
1581      j = jsTemp[i];
1582      delete cbs[j];
1583    }
1584  },
1585  
1586  /* private */
1587  _pools                   : [new sjcl.hash.sha256()],
1588  _poolEntropy             : [0],
1589  _reseedCount             : 0,
1590  _robins                  : {},
1591  _eventId                 : 0,
1592  
1593  _collectorIds            : {},
1594  _collectorIdNext         : 0,
1595  
1596  _strength                : 0,
1597  _poolStrength            : 0,
1598  _nextReseed              : 0,
1599  _key                     : [0,0,0,0,0,0,0,0],
1600  _counter                 : [0,0,0,0],
1601  _cipher                  : undefined,
1602  _defaultParanoia         : 6,
1603  
1604  /* event listener stuff */
1605  _collectorsStarted       : false,
1606  _callbacks               : {progress: {}, seeded: {}},
1607  _callbackI               : 0,
1608  
1609  /* constants */
1610  _NOT_READY               : 0,
1611  _READY                   : 1,
1612  _REQUIRES_RESEED         : 2,
1613
1614  _MAX_WORDS_PER_BURST     : 65536,
1615  _PARANOIA_LEVELS         : [0,48,64,96,128,192,256,384,512,768,1024],
1616  _MILLISECONDS_PER_RESEED : 30000,
1617  _BITS_PER_RESEED         : 80,
1618  
1619  /** Generate 4 random words, no reseed, no gate.
1620   * @private
1621   */
1622  _gen4words: function () {
1623    for (var i=0; i<4; i++) {
1624      this._counter[i] = this._counter[i]+1 | 0;
1625      if (this._counter[i]) { break; }
1626    }
1627    return this._cipher.encrypt(this._counter);
1628  },
1629  
1630  /* Rekey the AES instance with itself after a request, or every _MAX_WORDS_PER_BURST words.
1631   * @private
1632   */
1633  _gate: function () {
1634    this._key = this._gen4words().concat(this._gen4words());
1635    this._cipher = new sjcl.cipher.aes(this._key);
1636  },
1637  
1638  /** Reseed the generator with the given words
1639   * @private
1640   */
1641  _reseed: function (seedWords) {
1642    this._key = sjcl.hash.sha256.hash(this._key.concat(seedWords));
1643    this._cipher = new sjcl.cipher.aes(this._key);
1644    for (var i=0; i<4; i++) {
1645      this._counter[i] = this._counter[i]+1 | 0;
1646      if (this._counter[i]) { break; }
1647    }
1648  },
1649  
1650  /** reseed the data from the entropy pools
1651   * @param full If set, use all the entropy pools in the reseed.
1652   */
1653  _reseedFromPools: function (full) {
1654    var reseedData = [], strength = 0, i;
1655  
1656    this._nextReseed = reseedData[0] =
1657      (new Date()).valueOf() + this._MILLISECONDS_PER_RESEED;
1658    
1659    for (i=0; i<16; i++) {
1660      /* On some browsers, this is cryptographically random.  So we might
1661       * as well toss it in the pot and stir...
1662       */
1663      reseedData.push(Math.random()*0x100000000|0);
1664    }
1665    
1666    for (i=0; i<this._pools.length; i++) {
1667     reseedData = reseedData.concat(this._pools[i].finalize());
1668     strength += this._poolEntropy[i];
1669     this._poolEntropy[i] = 0;
1670   
1671     if (!full && (this._reseedCount & (1<<i))) { break; }
1672    }
1673  
1674    /* if we used the last pool, push a new one onto the stack */
1675    if (this._reseedCount >= 1 << this._pools.length) {
1676     this._pools.push(new sjcl.hash.sha256());
1677     this._poolEntropy.push(0);
1678    }
1679  
1680    /* how strong was this reseed? */
1681    this._poolStrength -= strength;
1682    if (strength > this._strength) {
1683      this._strength = strength;
1684    }
1685  
1686    this._reseedCount ++;
1687    this._reseed(reseedData);
1688  },
1689  
1690  _mouseCollector: function (ev) {
1691    var x = ev.x || ev.clientX || ev.offsetX || 0, y = ev.y || ev.clientY || ev.offsetY || 0;
1692    sjcl.random.addEntropy([x,y], 2, "mouse");
1693  },
1694  
1695  _loadTimeCollector: function (ev) {
1696    sjcl.random.addEntropy((new Date()).valueOf(), 2, "loadtime");
1697  },
1698  
1699  _fireEvent: function (name, arg) {
1700    var j, cbs=sjcl.random._callbacks[name], cbsTemp=[];
1701    /* TODO: there is a race condition between removing collectors and firing them */ 
1702
1703    /* I'm not sure if this is necessary; in C++, iterating over a
1704     * collection and modifying it at the same time is a no-no.
1705     */
1706  
1707    for (j in cbs) {
1708     if (cbs.hasOwnProperty(j)) {
1709        cbsTemp.push(cbs[j]);
1710     }
1711    }
1712  
1713    for (j=0; j<cbsTemp.length; j++) {
1714     cbsTemp[j](arg);
1715    }
1716  }
1717};
1718
1719(function(){
1720  try {
1721    // get cryptographically strong entropy in Webkit
1722    var ab = new Uint32Array(32);
1723    crypto.getRandomValues(ab);
1724    sjcl.random.addEntropy(ab, 1024, "crypto.getRandomValues");
1725  } catch (e) {
1726    // no getRandomValues :-(
1727  }
1728})();
1729/** @fileOverview Convenince functions centered around JSON encapsulation.
1730 *
1731 * @author Emily Stark
1732 * @author Mike Hamburg
1733 * @author Dan Boneh
1734 */
1735 
1736 /** @namespace JSON encapsulation */
1737 sjcl.json = {
1738  /** Default values for encryption */
1739  defaults: { v:1, iter:1000, ks:128, ts:64, mode:"ccm", adata:"", cipher:"aes" },
1740
1741  /** Simple encryption function.
1742   * @param {String|bitArray} password The password or key.
1743   * @param {String} plaintext The data to encrypt.
1744   * @param {Object} [params] The parameters including tag, iv and salt.
1745   * @param {Object} [rp] A returned version with filled-in parameters.
1746   * @return {String} The ciphertext.
1747   * @throws {sjcl.exception.invalid} if a parameter is invalid.
1748   */
1749  encrypt: function (password, plaintext, params, rp) {
1750    params = params || {};
1751    rp = rp || {};
1752    
1753    var j = sjcl.json, p = j._add({ iv: sjcl.random.randomWords(4,0) },
1754                                  j.defaults), tmp, prp, adata;
1755    j._add(p, params);
1756    adata = p.adata;
1757    if (typeof p.salt === "string") {
1758      p.salt = sjcl.codec.base64.toBits(p.salt);
1759    }
1760    if (typeof p.iv === "string") {
1761      p.iv = sjcl.codec.base64.toBits(p.iv);
1762    }
1763    
1764    if (!sjcl.mode[p.mode] ||
1765        !sjcl.cipher[p.cipher] ||
1766        (typeof password === "string" && p.iter <= 100) ||
1767        (p.ts !== 64 && p.ts !== 96 && p.ts !== 128) ||
1768        (p.ks !== 128 && p.ks !== 192 && p.ks !== 256) ||
1769        (p.iv.length < 2 || p.iv.length > 4)) {
1770      throw new sjcl.exception.invalid("json encrypt: invalid parameters");
1771    }
1772    
1773    if (typeof password === "string") {
1774      tmp = sjcl.misc.cachedPbkdf2(password, p);
1775      password = tmp.key.slice(0,p.ks/32);
1776      p.salt = tmp.salt;
1777    }
1778    if (typeof plaintext === "string") {
1779      plaintext = sjcl.codec.utf8String.toBits(plaintext);
1780    }
1781    if (typeof adata === "string") {
1782      adata = sjcl.codec.utf8String.toBits(adata);
1783    }
1784    prp = new sjcl.cipher[p.cipher](password);
1785    
1786    /* return the json data */
1787    j._add(rp, p);
1788    rp.key = password;
1789    
1790    /* do the encryption */
1791    p.ct = sjcl.mode[p.mode].encrypt(prp, plaintext, p.iv, adata, p.ts);
1792    
1793    //return j.encode(j._subtract(p, j.defaults));
1794    return j.encode(p);
1795  },
1796  
1797  /** Simple decryption function.
1798   * @param {String|bitArray} password The password or key.
1799   * @param {String} ciphertext The ciphertext to decrypt.
1800   * @param {Object} [params] Additional non-default parameters.
1801   * @param {Object} [rp] A returned object with filled parameters.
1802   * @return {String} The plaintext.
1803   * @throws {sjcl.exception.invalid} if a parameter is invalid.
1804   * @throws {sjcl.exception.corrupt} if the ciphertext is corrupt.
1805   */
1806  decrypt: function (password, ciphertext, params, rp) {
1807    params = params || {};
1808    rp = rp || {};
1809    
1810    var j = sjcl.json, p = j._add(j._add(j._add({},j.defaults),j.decode(ciphertext)), params, true), ct, tmp, prp, adata=p.adata;
1811    if (typeof p.salt === "string") {
1812      p.salt = sjcl.codec.base64.toBits(p.salt);
1813    }
1814    if (typeof p.iv === "string") {
1815      p.iv = sjcl.codec.base64.toBits(p.iv);
1816    }
1817    
1818    if (!sjcl.mode[p.mode] ||
1819        !sjcl.cipher[p.cipher] ||
1820        (typeof password === "string" && p.iter <= 100) ||
1821        (p.ts !== 64 && p.ts !== 96 && p.ts !== 128) ||
1822        (p.ks !== 128 && p.ks !== 192 && p.ks !== 256) ||
1823        (!p.iv) ||
1824        (p.iv.length < 2 || p.iv.length > 4)) {
1825      throw new sjcl.exception.invalid("json decrypt: invalid parameters");
1826    }
1827    
1828    if (typeof password === "string") {
1829      tmp = sjcl.misc.cachedPbkdf2(password, p);
1830      password = tmp.key.slice(0,p.ks/32);
1831      p.salt  = tmp.salt;
1832    }
1833    if (typeof adata === "string") {
1834      adata = sjcl.codec.utf8String.toBits(adata);
1835    }
1836    prp = new sjcl.cipher[p.cipher](password);
1837    
1838    /* do the decryption */
1839    ct = sjcl.mode[p.mode].decrypt(prp, p.ct, p.iv, adata, p.ts);
1840    
1841    /* return the json data */
1842    j._add(rp, p);
1843    rp.key = password;
1844    
1845    return sjcl.codec.utf8String.fromBits(ct);
1846  },
1847  
1848  /** Encode a flat structure into a JSON string.
1849   * @param {Object} obj The structure to encode.
1850   * @return {String} A JSON string.
1851   * @throws {sjcl.exception.invalid} if obj has a non-alphanumeric property.
1852   * @throws {sjcl.exception.bug} if a parameter has an unsupported type.
1853   */
1854  encode: function (obj) {
1855    var i, out='{', comma='';
1856    for (i in obj) {
1857      if (obj.hasOwnProperty(i)) {
1858        if (!i.match(/^[a-z0-9]+$/i)) {
1859          throw new sjcl.exception.invalid("json encode: invalid property name");
1860        }
1861        out += comma + '"' + i + '":';
1862        comma = ',';
1863        
1864        switch (typeof obj[i]) {
1865        case 'number':
1866        case 'boolean':
1867          out += obj[i];
1868          break;
1869          
1870        case 'string':
1871          out += '"' + escape(obj[i]) + '"';
1872          break;
1873        
1874        case 'object':
1875          out += '"' + sjcl.codec.base64.fromBits(obj[i],1) + '"';
1876          break;
1877        
1878        default:
1879          throw new sjcl.exception.bug("json encode: unsupported type");
1880        }
1881      }
1882    }
1883    return out+'}';
1884  },
1885  
1886  /** Decode a simple (flat) JSON string into a structure.  The ciphertext,
1887   * adata, salt and iv will be base64-decoded.
1888   * @param {String} str The string.
1889   * @return {Object} The decoded structure.
1890   * @throws {sjcl.exception.invalid} if str isn't (simple) JSON.
1891   */
1892  decode: function (str) {
1893    str = str.replace(/\s/g,'');
1894    if (!str.match(/^\{.*\}$/)) { 
1895      throw new sjcl.exception.invalid("json decode: this isn't json!");
1896    }
1897    var a = str.replace(/^\{|\}$/g, '').split(/,/), out={}, i, m;
1898    for (i=0; i<a.length; i++) {
1899      if (!(m=a[i].match(/^(?:(["']?)([a-z][a-z0-9]*)\1):(?:(\d+)|"([a-z0-9+\/%*_.@=\-]*)")$/i))) {
1900        throw new sjcl.exception.invalid("json decode: this isn't json!");
1901      }
1902      if (m[3]) {
1903        out[m[2]] = parseInt(m[3],10);
1904      } else {
1905        out[m[2]] = m[2].match(/^(ct|salt|iv)$/) ? sjcl.codec.base64.toBits(m[4]) : unescape(m[4]);
1906      }
1907    }
1908    return out;
1909  },
1910  
1911  /** Insert all elements of src into target, modifying and returning target.
1912   * @param {Object} target The object to be modified.
1913   * @param {Object} src The object to pull data from.
1914   * @param {boolean} [requireSame=false] If true, throw an exception if any field of target differs from corresponding field of src.
1915   * @return {Object} target.
1916   * @private
1917   */
1918  _add: function (target, src, requireSame) {
1919    if (target === undefined) { target = {}; }
1920    if (src === undefined) { return target; }
1921    var i;
1922    for (i in src) {
1923      if (src.hasOwnProperty(i)) {
1924        if (requireSame && target[i] !== undefined && target[i] !== src[i]) {
1925          throw new sjcl.exception.invalid("required parameter overridden");
1926        }
1927        target[i] = src[i];
1928      }
1929    }
1930    return target;
1931  },
1932  
1933  /** Remove all elements of minus from plus.  Does not modify plus.
1934   * @private
1935  _subtract: function (plus, minus) {
1936    var out = {}, i;
1937    
1938    for (i in plus) {
1939      if (plus.hasOwnProperty(i) && plus[i] !== minus[i]) {
1940        out[i] = plus[i];
1941      }
1942    }
1943    
1944    return out;
1945  },
1946  */
1947  
1948  /** Return only the specified elements of src.
1949   * @private
1950   */
1951  _filter: function (src, filter) {
1952    var out = {}, i;
1953    for (i=0; i<filter.length; i++) {
1954      if (src[filter[i]] !== undefined) {
1955        out[filter[i]] = src[filter[i]];
1956      }
1957    }
1958    return out;
1959  }
1960};
1961
1962/** Simple encryption function; convenient shorthand for sjcl.json.encrypt.
1963 * @param {String|bitArray} password The password or key.
1964 * @param {String} plaintext The data to encrypt.
1965 * @param {Object} [params] The parameters including tag, iv and salt.
1966 * @param {Object} [rp] A returned version with filled-in parameters.
1967 * @return {String} The ciphertext.
1968 */
1969sjcl.encrypt = sjcl.json.encrypt;
1970
1971/** Simple decryption function; convenient shorthand for sjcl.json.decrypt.
1972 * @param {String|bitArray} password The password or key.
1973 * @param {String} ciphertext The ciphertext to decrypt.
1974 * @param {Object} [params] Additional non-default parameters.
1975 * @param {Object} [rp] A returned object with filled parameters.
1976 * @return {String} The plaintext.
1977 */
1978sjcl.decrypt = sjcl.json.decrypt;
1979
1980/** The cache for cachedPbkdf2.
1981 * @private
1982 */
1983sjcl.misc._pbkdf2Cache = {};
1984
1985/** Cached PBKDF2 key derivation.
1986 * @param {String} The password.  
1987 * @param {Object} The derivation params (iteration count and optional salt).
1988 * @return {Object} The derived data in key, the salt in salt.
1989 */
1990sjcl.misc.cachedPbkdf2 = function (password, obj) {
1991  var cache = sjcl.misc._pbkdf2Cache, c, cp, str, salt, iter;
1992  
1993  obj = obj || {};
1994  iter = obj.iter || 1000;
1995  
1996  /* open the cache for this password and iteration count */
1997  cp = cache[password] = cache[password] || {};
1998  c = cp[iter] = cp[iter] || { firstSalt: (obj.salt && obj.salt.length) ?
1999                     obj.salt.slice(0) : sjcl.random.randomWords(2,0) };
2000          
2001  salt = (obj.salt === undefined) ? c.firstSalt : obj.salt;
2002  
2003  c[salt] = c[salt] || sjcl.misc.pbkdf2(password, salt, obj.iter);
2004  return { key: c[salt].slice(0), salt:salt.slice(0) };
2005};
2006
2007
2008
2009
2010var localStorage = window.localStorage;
2011
2012function cookieName(name) {
2013    if (! window.location) {
2014        return name;
2015    }
2016
2017    var port = window.location.port ||
2018        (window.location.protocol === 'https:' ? 443 : 80);
2019
2020    return name + '_' + port;
2021}
2022
2023function fetchCookieValues(name) {
2024    var cookie = Cookie.fetch(cookieName(name));
2025
2026    if (! cookie) {
2027        return {};
2028    }
2029
2030    try {
2031        return JSON.parse(cookie.value);
2032    }
2033    catch (e) {
2034        return {
2035            encryptKey: cookie.value
2036        };
2037    }
2038}
2039
2040function fillinDefaultCookieValues(values, o) {
2041    function generateKey() {
2042        return sjcl.codec.base64.fromBits(sjcl.random.randomWords(8, 0));
2043    }
2044
2045    var path = values.path,
2046        currentPath = extractPath(documentUrl());
2047    if (! path || path.length > currentPath.length) {
2048        path = currentPath;
2049    }
2050
2051    return {
2052        encryptKey: values.encryptKey || generateKey(),
2053        storageKey: values.storageKey || generateKey(),
2054        domain: o.sessionDomain || values.domain || undefined,
2055        path: o.sessionPath || path
2056    };
2057}
2058
2059function documentUrl() {
2060    if (! window.location) {
2061        return '';
2062    }
2063
2064    var loc;
2065
2066    // IE may throw an exception when accessing
2067    // a field from window.location if document.domain has been set
2068    try {
2069        loc = window.location.href;
2070    } catch( e ) {
2071        // Use the href attribute of an A element
2072        // since IE will modify it given document.location
2073        loc = window.document.createElement( "a" );
2074        loc.href = "";
2075        loc = loc.href;
2076    }
2077
2078    return loc;
2079}
2080
2081function extractPath(url) {
2082    var urlRegexp = /^[\w.+-]+:(?:\/\/[^\/?#:]*(?::\d+|)|)(.*)\/[^\/]*$/,
2083        match     = urlRegexp.exec(url.toLowerCase());
2084
2085    return match ? match[1] : null;
2086}
2087
2088// DEPRECATED
2089// This method will be removed in future version.
2090function buildLocalStorageNames(name, path) {
2091    function buildName(path) {
2092        return name + ':' + path;
2093    }
2094
2095    var names = [];
2096
2097    if (! path) {
2098        return [name];
2099    }
2100
2101    while (true) {
2102        names.push(buildName(path));
2103        if (path === '/') {
2104            break;
2105        }
2106        path = path.replace(/[^\/]+\/$/, '');
2107    }
2108    return names;
2109}
2110
2111// DEPRECATED
2112// This method will be removed in future version.
2113function localStorageNames(name, o) {
2114    return buildLocalStorageNames(name, o.sessionPath || extractPath(documentUrl())+"/");
2115}
2116
2117if (! localStorage) {
2118    DataAPI.sessionStores['cookie-encrypted'] = {
2119        save:   function(){},
2120        fetch:  function(){},
2121        remove: function(){}
2122    };
2123}
2124else {
2125    DataAPI.sessionStores['cookie-encrypted'] = {
2126        save: function(name, data, remember) {
2127            var expires = remember ? new Date(new Date().getTime() + 315360000000) : undefined, // after 10 years
2128                values  = fillinDefaultCookieValues(fetchCookieValues(name), this.o);
2129
2130            Cookie.bake(cookieName(name), JSON.stringify(values), values.domain, values.path, expires);
2131            localStorage.setItem(values.storageKey, sjcl.encrypt(values.encryptKey, data));
2132        },
2133        fetch: function(name) {
2134            var values = fetchCookieValues(name),
2135                i, names, data;
2136
2137            // Backward compatibility 
2138            if (! values.storageKey) {
2139                names = localStorageNames(name, this.o);
2140                for (i = 0; i < names.length; i++) {
2141                    if (localStorage.getItem(names[i])) {
2142                        values.storageKey = names[i];
2143                        break;
2144                    }
2145                }
2146            }
2147
2148            data = localStorage.getItem(values.storageKey);
2149
2150            try {
2151                return sjcl.decrypt(values.encryptKey, data);
2152            }
2153            catch (e) {
2154            }
2155
2156            return null;
2157        },
2158        remove: function(name) {
2159            var values = fillinDefaultCookieValues(fetchCookieValues(name), this.o);
2160
2161            Cookie.bake(cookieName(name), '', values.domain, values.path, new Date(0));
2162
2163            if (values.storageKey) {
2164                localStorage.removeItem(values.storageKey);
2165            }
2166        }
2167    };
2168}
2169
2170})();
2171
2172
2173/**
2174 * Register callback to class.
2175 * @method on
2176 * @static
2177 * @param {String} key Event name
2178 * @param {Function} callback Callback function
2179 * @category core
2180 * @example
2181 *     var callback = function() {
2182 *       // Do stuff
2183 *     };
2184 *     DataAPI.on(eventName, callback);
2185 */
2186DataAPI.on = function(key, callback) {
2187    if (! this.callbacks[key]) {
2188        this.callbacks[key] = [];
2189    }
2190
2191    this.callbacks[key].push(callback);
2192};
2193
2194/**
2195 * Deregister callback from class.
2196 * @method off
2197 * @static
2198 * @param {String} key Event name
2199 * @param {Function} callback Callback function
2200 * @category core
2201 * @example
2202 *     DataAPI.off(eventName, callback);
2203 */
2204DataAPI.off = function(key, callback) {
2205    var i, callbacks;
2206
2207    if (callback) {
2208        callbacks = this.callbacks[key] || [];
2209
2210        for (i = 0; i < callbacks.length; i++) {
2211            if (callbacks[i] === callback) {
2212                callbacks.splice(i, 1);
2213                break;
2214            }
2215        }
2216    }
2217    else {
2218        delete this.callbacks[key];
2219    }
2220};
2221
2222/**
2223 * Register formats that serialize data.
2224 * @method registerFormat
2225 * @static
2226 * @param {String} key Format name
2227 * @param {Object} spec
2228 *   @param {String} spec.fileExtension Extension
2229 *   @param {String} spec.mimeType MIME type
2230 *   @param {String} spec.serialize Serializing method
2231 *   @param {String} spec.unserialize Unserializing method
2232 * @category core
2233 */
2234DataAPI.registerFormat = function(key, spec) {
2235    this.formats[key] = spec;
2236};
2237
2238/**
2239 * Register session store.
2240 * @method registerSessionStore
2241 * @static
2242 * @param {String} key Session store name
2243 * @param {Object} spec
2244 *   @param {String} spec.save Saving method
2245 *   @param {String} spec.restore Restoring method
2246 *   @param {String} spec.dispose Disposing method
2247 * @category core
2248 */
2249DataAPI.registerSessionStore = function(key, spec) {
2250    this.sessionStores[key] = spec;
2251};
2252
2253/**
2254 * Get default format of this class.
2255 * @method getDefaultFormat
2256 * @static
2257 * @return {Object} Format
2258 * @category core
2259 */
2260DataAPI.getDefaultFormat = function() {
2261    return this.formats[this.defaultFormat];
2262};
2263
2264/**
2265 * Get default session store of this class.
2266 * @method getDefaultSessionStore
2267 * @static
2268 * @return {Object} Format
2269 * @category core
2270 */
2271DataAPI.getDefaultSessionStore = function() {
2272    return this.sessionStores[this.defaultSessionStore];
2273};
2274
2275DataAPI.prototype = {
2276    constructor: DataAPI.prototype.constructor,
2277
2278    _initOptions: function() {
2279        this._initCrossDomainOption();
2280    },
2281
2282    _initCrossDomainOption: function() {
2283        var loc, locParts, baseUrl, baseParts,
2284            urlRegexp = /^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/;
2285
2286        if ( window.document && typeof this.o.crossOrigin === 'undefined') {
2287            // IE may throw an exception when accessing
2288            // a field from window.location if document.domain has been set
2289            try {
2290                loc = window.location.href;
2291            } catch( e ) {
2292                // Use the href attribute of an A element
2293                // since IE will modify it given document.location
2294                loc = window.document.createElement( "a" );
2295                loc.href = "";
2296                loc = loc.href;
2297            }
2298            locParts  = urlRegexp.exec( loc.toLowerCase() ) || [];
2299
2300            baseUrl   = this.o.baseUrl.replace(/^\/\//, locParts[1]).toLowerCase();
2301            baseParts = urlRegexp.exec( baseUrl );
2302
2303            this.o.crossOrigin = !!( baseParts &&
2304                ( baseParts[ 1 ] !== locParts[ 1 ] || baseParts[ 2 ] !== locParts[ 2 ] ||
2305                    ( baseParts[ 3 ] || ( baseParts[ 1 ] === "http:" ? "80" : "443" ) ) !==
2306                        ( locParts[ 3 ] || ( locParts[ 1 ] === "http:" ? "80" : "443" ) ) )
2307            );
2308        }
2309    },
2310
2311    /**
2312     * Get authorization URL.
2313     * @method getAuthorizationUrl
2314     * @param {String} redirectUrl The user is redirected to this URL with "#_login" if authorization succeeded.
2315     * @return {String} Authorization URL
2316     * @category core
2317     */
2318    getAuthorizationUrl: function(redirectUrl) {
2319        return this.o.baseUrl.replace(/\/*$/, '/') +
2320            'v' + this.getVersion() +
2321            '/authorization' +
2322            '?clientId=' + this.o.clientId +
2323            '&redirectUrl=' + redirectUrl;
2324    },
2325
2326    _getCurrentEpoch: function() {
2327        return Math.round(new Date().getTime() / 1000);
2328    },
2329
2330    _getNextIframeName: function() {
2331        return this.constructor.iframePrefix + (++this.iframeId);
2332    },
2333
2334    /**
2335     * Get API version.
2336     * @method getVersion
2337     * @return {String} API version
2338     * @category core
2339     */
2340    getVersion: function() {
2341        return this.constructor.version;
2342    },
2343
2344    /**
2345     * Get application key of this object.
2346     * @method getAppKey
2347     * @return {String} Application key
2348     *   This value is used for the session store.
2349     * @category core
2350     */
2351    getAppKey: function() {
2352        return this.constructor.accessTokenKey + '_' + this.o.clientId;
2353    },
2354
2355    _findFormatInternal: function(mimeType) {
2356        if (! mimeType) {
2357            return null;
2358        }
2359
2360        for (var k in this.constructor.formats) {
2361            if (this.constructor.formats[k].mimeType === mimeType) {
2362                return this.constructor.formats[k];
2363            }
2364        }
2365
2366        return null;
2367    },
2368
2369    /**
2370     * Get format by MIME Type.
2371     * @method findFormat
2372     * @param {String} mimeType MIME Type
2373     * @return {Object|null} Format. Return null if any format is not found.
2374     * @category core
2375     */
2376    findFormat: function(mimeType) {
2377        var format = this._findFormatInternal(mimeType);
2378        if (! format && mimeType.indexOf(';')) {
2379            format = this._findFormatInternal(mimeType.replace(/\s*;.*/, ''));
2380        }
2381
2382        return format;
2383    },
2384
2385    /**
2386     * Get current format of this object.
2387     * @method getCurrentFormat
2388     * @return {Object} Format
2389     * @category core
2390     */
2391    getCurrentFormat: function() {
2392        return this.constructor.formats[this.o.format] ||
2393            this.constructor.getDefaultFormat();
2394    },
2395
2396    /**
2397     * Serialize data.
2398     * @method serializeData
2399     * @param {Object} data The data to serialize
2400     * @return {String} Serialized data
2401     * @category core
2402     */
2403    serializeData: function() {
2404        return this.getCurrentFormat().serialize.apply(this, arguments);
2405    },
2406
2407    /**
2408     * Unserialize data.
2409     * @method unserializeData
2410     * @param {String} data The data to unserialize
2411     * @return {Object} Unserialized data
2412     * @category core
2413     */
2414    unserializeData: function() {
2415        return this.getCurrentFormat().unserialize.apply(this, arguments);
2416    },
2417
2418    /**
2419     * Get current session store of this object.
2420     * @method getCurrentSessionStore
2421     * @return {Object} Session store
2422     * @category core
2423     */
2424    getCurrentSessionStore: function() {
2425        return this.constructor.sessionStores[this.o.sessionStore] ||
2426            this.constructor.getDefaultSessionStore();
2427    },
2428
2429    /**
2430     * Save session data.
2431     * @method saveSessionData
2432     * @param {String} name The name of session
2433     * @param {Object} data The data to save
2434     * @category core
2435     */
2436    saveSessionData: function() {
2437        return this.getCurrentSessionStore().save.apply(this, arguments);
2438    },
2439
2440    /**
2441     * Fetch session data.
2442     * @method fetchSessionData
2443     * @param {String} name The name of session
2444     * @return {String} The data fetched
2445     * @category core
2446     */
2447    fetchSessionData: function() {
2448        return this.getCurrentSessionStore().fetch.apply(this, arguments);
2449    },
2450
2451    /**
2452     * Remove session data.
2453     * @method removeSessionData
2454     * @param {String} name The name of session
2455     * @category core
2456     */
2457    removeSessionData: function() {
2458        return this.getCurrentSessionStore().remove.apply(this, arguments);
2459    },
2460
2461    /**
2462     * Store token data via current session store.
2463     * @method storeTokenData
2464     * @param {Object} tokenData The token data
2465     *   @param {String} tokenData.accessToken access token
2466     *   @param {String} tokenData.expiresIn The number of seconds
2467     *     until access token becomes invalid
2468     *   @param {String} [tokenData.sessionId] session ID
2469     * @category core
2470     */
2471    storeTokenData: function(tokenData) {
2472        var oldData = this.getTokenData();
2473        if (! tokenData.sessionId && oldData && oldData.sessionId) {
2474            tokenData.sessionId = oldData.sessionId;
2475        }
2476
2477        tokenData.startTime = this._getCurrentEpoch();
2478        this.saveSessionData(
2479            this.getAppKey(),
2480            this.serializeData(tokenData),
2481            tokenData.sessionId && tokenData.remember
2482        );
2483        this.tokenData = tokenData;
2484    },
2485
2486    /**
2487     * Clear token data from object and session store.
2488     * @method clearTokenData
2489     * @category core
2490     */
2491    clearTokenData: function() {
2492        this.removeSessionData(this.getAppKey());
2493        this.tokenData = null;
2494    },
2495
2496    _updateTokenFromDefaultCookie: function() {
2497        var defaultKey    = this.constructor.accessTokenKey,
2498            defaultCookie = Cookie.fetch(defaultKey),
2499            defaultToken;
2500
2501        if (! defaultCookie) {
2502            return null;
2503        }
2504
2505        Cookie.bake(defaultKey, '', undefined, '/', new Date(0));
2506
2507        try {
2508            defaultToken = this.unserializeData(defaultCookie.value);
2509        }
2510        catch (e) {
2511            return null;
2512        }
2513
2514        this.storeTokenData(defaultToken);
2515        return defaultToken;
2516    },
2517
2518    _hasOneTimeToken: function() {
2519        return window.location && window.location.hash.indexOf('#_ott_') === 0;
2520    },
2521
2522    _storeOneTimeToken: function() {
2523        var token, m;
2524
2525        if (! window.location) {
2526            return undefined;
2527        }
2528
2529        m = window.location.hash.match(/^#_ott_(.*)/);
2530        if (! m) {
2531            return undefined;
2532        }
2533
2534        token = {
2535            oneTimeToken: m[1]
2536        };
2537        window.location.hash = '#_login';
2538
2539        this.storeTokenData(token);
2540        return token;
2541    },
2542
2543    /**
2544     * Get token data via current session store.
2545     * @method getTokenData
2546     * @return {Object} Token data
2547     * @category core
2548     */
2549    getTokenData: function() {
2550        var token = this.tokenData;
2551
2552        if (! token) {
2553            if (window.location) {
2554                if (window.location.hash === '#_login') {
2555                    try {
2556                        token = this._updateTokenFromDefaultCookie();
2557                    }
2558                    catch (e) {
2559                    }
2560                }
2561                else if (this._hasOneTimeToken()) {
2562                    token = this._storeOneTimeToken();
2563                }
2564            }
2565
2566            if (! token) {
2567                try {
2568                    token = this.unserializeData(this.fetchSessionData(this.getAppKey()));
2569                }
2570                catch (e) {
2571                }
2572            }
2573        }
2574
2575        if (token &&
2576            'startTime' in token &&
2577            'expiresIn' in token &&
2578            (token.startTime + token.expiresIn < this._getCurrentEpoch())) {
2579            delete token.accessToken;
2580            delete token.startTime;
2581            delete token.expiresIn;
2582        }
2583
2584        return this.tokenData = token || null;
2585    },
2586
2587    /**
2588     * Get authorization request header.
2589     * @method getAuthorizationHeader
2590     * @return {String|null} Header string. Return null if api object has no token.
2591     * @category core
2592     */
2593    getAuthorizationHeader: function(key) {
2594        var tokenData = this.getTokenData();
2595        if (tokenData) {
2596            return 'MTAuth ' + key + '=' + (tokenData[key] || '');
2597        }
2598
2599        return '';
2600    },
2601
2602    /**
2603     * Bind parameters to route spec.
2604     * @method bindEndpointParams
2605     * @param {String} route Specification of route
2606     * @param {Object} params parameters
2607     *   @param {Number|Object|Function} params.{key} Value to bind
2608     * @return {String} Endpoint to witch parameters was bound
2609     * @example
2610     *     api.bindEndpointParams('/sites/:site_id/entries/:entry_id/comments/:comment_id', {
2611     *       blog_id: 1,
2612     *       entry_id: {id: 1},
2613     *       comment_id: functioin(){ return 1; }
2614     *     });
2615     * @category core
2616     */
2617    bindEndpointParams: function(route, params) {
2618        var k, v;
2619
2620        for (k in params) {
2621            v = params[k];
2622            if (typeof v === 'object') {
2623                if (typeof v.id === 'function') {
2624                    v = v.id();
2625                }
2626                else {
2627                    v = v.id;
2628                }
2629            }
2630            if (typeof v === 'function') {
2631                v = v();
2632            }
2633            route = route.replace(new RegExp(':' + k), v);
2634        }
2635        return route;
2636    },
2637
2638    _isElement: function(e, name) {
2639        if (! e || typeof e !== 'object') {
2640            return false;
2641        }
2642        var n = e.nodeName;
2643        return n && n.toLowerCase() === name;
2644    },
2645
2646    _isFormElement: function(e) {
2647        return this._isElement(e, 'form');
2648    },
2649
2650    _isInputElement: function(e) {
2651        return this._isElement(e, 'input');
2652    },
2653
2654    _isFileInputElement: function(e) {
2655        return this._isInputElement(e) && e.type.toLowerCase() === 'file';
2656    },
2657
2658    _serializeObject: function(v) {
2659        function f(n) {
2660            return n < 10 ? '0' + n : n;
2661        }
2662
2663        function iso8601Date(v) {
2664            if (! isFinite(v.valueOf())) {
2665                return '';
2666            }
2667
2668            var off,
2669                tz = v.getTimezoneOffset();
2670            if(tz === 0) {
2671                off = 'Z';
2672            }
2673            else {
2674                off  = (tz > 0 ? '-': '+');
2675                tz   = Math.abs(tz);
2676                off += f(Math.floor(tz / 60)) + ':' + f(tz % 60);
2677            }
2678
2679            return v.getFullYear()     + '-' +
2680                f(v.getMonth() + 1) + '-' +
2681                f(v.getDate())      + 'T' +
2682                f(v.getHours())     + ':' +
2683                f(v.getMinutes())   + ':' +
2684                f(v.getSeconds())   + off;
2685        }
2686
2687        if (this._isFormElement(v)) {
2688            v = this._serializeFormElementToObject(v);
2689        }
2690
2691        var type = typeof v;
2692        if (type === 'undefined' || v === null || (type === 'number' && ! isFinite(v))) {
2693            return '';
2694        }
2695        else if (type === 'boolean') {
2696            return v ? '1' : '';
2697        }
2698        else if (v instanceof Date) {
2699            return iso8601Date(v);
2700        }
2701        else if (window.File && v instanceof window.File) {
2702            return v;
2703        }
2704        else if (this._isFileInputElement(v)) {
2705            return v.files[0];
2706        }
2707        else if (type === 'object') {
2708            return this.serializeData(v, function(key, value) {
2709                if (this[key] instanceof Date) {
2710                    return iso8601Date(this[key]);
2711                }
2712                return value;
2713            });
2714        }
2715        else {
2716            return v;
2717        }
2718    },
2719
2720    _serializeParams: function(params) {
2721        if (! params) {
2722            return params;
2723        }
2724        if (typeof params === 'string') {
2725            return params;
2726        }
2727        if (this._isFormElement(params)) {
2728            params = this._serializeFormElementToObject(params);
2729        }
2730
2731        var k,
2732            str = '';
2733        for (k in params) {
2734            if (! params.hasOwnProperty(k)) {
2735                continue;
2736            }
2737            if (str) {
2738                str += '&';
2739            }
2740
2741            str +=
2742                encodeURIComponent(k) + '=' +
2743                encodeURIComponent(this._serializeObject(params[k]));
2744        }
2745        return str;
2746    },
2747
2748    _unserializeParams: function(params) {
2749        if (typeof params !== 'string') {
2750            return params;
2751        }
2752
2753        var i, pair,
2754            data   = {},
2755            values = params.split('&');
2756
2757        for(i = 0; i < values.length; i++) {
2758            pair = values[i].split('=');
2759            data[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
2760        }
2761
2762        return data;
2763    },
2764
2765    _newXMLHttpRequestStandard: function() {
2766        try {
2767            return new window.XMLHttpRequest();
2768        } catch( e ) {}
2769    },
2770
2771    _newXMLHttpRequestActiveX: function() {
2772        try {
2773            return new window.ActiveXObject("Microsoft.XMLHTTP");
2774        } catch( e ) {}
2775    },
2776
2777    /**
2778     * Create XMLHttpRequest by higher browser compatibility way.
2779     * @method newXMLHttpRequest
2780     * @return {XMLHttpRequest} Created XMLHttpRequest
2781     * @category core
2782     */
2783    newXMLHttpRequest: function() {
2784        return this._newXMLHttpRequestStandard() ||
2785            this._newXMLHttpRequestActiveX() ||
2786            false;
2787    },
2788
2789    _findFileInput: function(params) {
2790        if (typeof params !== 'object') {
2791            return null;
2792        }
2793
2794        for (var k in params) {
2795            if (this._isFileInputElement(params[k])) {
2796                return params[k];
2797            }
2798        }
2799
2800        return null;
2801    },
2802
2803    _isEmptyObject: function(o) {
2804        if (! o) {
2805            return true;
2806        }
2807
2808        for (var k in o) {
2809            if (o.hasOwnProperty(k)) {
2810                return false;
2811            }
2812        }
2813        return true;
2814    },
2815
2816    /**
2817     * Send request to specified URL with params via XMLHttpRequest.
2818     * @method sendXMLHttpRequest
2819     * @param {XMLHttpRequest} xhr XMLHttpRequest object to send request
2820     * @param {String} method Request method
2821     * @param {String} url Request URL
2822     * @param {String|FormData} params Parameters to send with request
2823     * @return {XMLHttpRequest}
2824     * @category core
2825     */
2826    sendXMLHttpRequest: function(xhr, method, url, params, defaultHeaders) {
2827        var k, headers, uk;
2828
2829        xhr.open(method, url, this.o.async);
2830        for (k in defaultHeaders) {
2831            xhr.setRequestHeader(k, defaultHeaders[k]);
2832        }
2833        if (typeof params === 'string') {
2834            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
2835        }
2836        if (! this.o.crossOrigin) {
2837            xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
2838        }
2839
2840        function normalizeHeaderKey(all, prefix, letter) {
2841            return prefix + letter.toUpperCase();
2842        }
2843        if (params && params.getHeaders) {
2844            headers = params.getHeaders();
2845            for (k in headers) {
2846                uk = k.replace(/(^|-)([a-z])/g, normalizeHeaderKey);
2847                xhr.setRequestHeader(uk, headers[k]);
2848            }
2849        }
2850
2851        xhr.send(params);
2852
2853        return xhr;
2854    },
2855
2856    _serializeFormElementToObject: function(form) {
2857        var i, e, type,
2858            data           = {},
2859            submitterTypes = /^(?:submit|button|image|reset)$/i,
2860            submittable    = /^(?:input|select|textarea|keygen)/i,
2861            checkableTypes = /^(?:checkbox|radio)$/i;
2862
2863        for (i = 0; i < form.elements.length; i++) {
2864            e    = form.elements[i];
2865            type = e.type;
2866
2867            if (
2868                    ! e.name ||
2869                    e.disabled ||
2870                    ! submittable.test(e.nodeName) ||
2871                    submitterTypes.test(type) ||
2872                    (checkableTypes.test(type) && ! e.checked)
2873            ) {
2874                continue;
2875            }
2876
2877            if (this._isFileInputElement(e)) {
2878                data[e.name] = e;
2879            }
2880            else {
2881                data[e.name] = this._elementValue(e);
2882            }
2883        }
2884
2885        return data;
2886    },
2887
2888    _elementValue: function(e) {
2889        if (e.nodeName.toLowerCase() === 'select') {
2890            var value, option,
2891                options = e.options,
2892                index = e.selectedIndex,
2893                one = e.type === "select-one" || index < 0,
2894                values = one ? null : [],
2895                max = one ? index + 1 : options.length,
2896                i = index < 0 ?
2897                    max :
2898                    one ? index : 0;
2899
2900            // Loop through all the selected options
2901            for ( ; i < max; i++ ) {
2902                option = options[ i ];
2903
2904                // oldIE doesn't update selected after form reset (#2551)
2905                if ( ( option.selected || i === index ) &&
2906                        // Don't return options that are disabled or in a disabled optgroup
2907                        ( !option.parentNode.disabled || option.parentNode.nodeName.toLowerCase() !== "optgroup" ) ) {
2908
2909                    // Get the specific value for the option
2910                    value = option.attributes.value;
2911                    if (!value || value.specified) {
2912                        value = option.value;
2913                    }
2914                    else {
2915                        value = e.text;
2916                    }
2917
2918                    // We don't need an array for one selects
2919                    if ( one ) {
2920                        return value;
2921                    }
2922
2923                    // Multi-Selects return an array
2924                    values.push( value );
2925                }
2926            }
2927
2928            return values;
2929        }
2930        else {
2931            return e.value;
2932        }
2933    },
2934
2935    /**
2936     * Execute function with specified options.
2937     * @method withOptions
2938     * @param {option} option Option to overwrite
2939     * @param {Function}
2939 func Function to execute
2940     * @return Return value of specified func
2941     * @example
2942     *     // The DataAPI object is created with {async: true}
2943     *     api.withOptions({async: false}, function() {
2944     *       api.listEntries(1, function() {
2945     *         // This is executed synchronously
2946     *       });
2947     *     });
2948     *     api.listEntries(1, function() {
2949     *       // This is executed asynchronously
2950     *     });
2951     * @category core
2952     */
2953    withOptions: function(option, func) {
2954        var k, result,
2955            originalOption = this.o,
2956            o = {};
2957
2958        for (k in originalOption) {
2959            o[k] = originalOption[k];
2960        }
2961        for (k in option) {
2962            o[k] = option[k];
2963        }
2964
2965        this.o = o;
2966        this._initOptions();
2967
2968        result = func.apply(this);
2969
2970        this.o = originalOption;
2971        this._initOptions();
2972
2973        return result;
2974    },
2975
2976    _requestVia: function() {
2977        return (window.XDomainRequest &&
2978                this.o.crossOrigin &&
2979                /msie (8|9)\./i.test(window.navigator.appVersion)) ? 'xdr' : 'xhr';
2980    },
2981
2982    /**
2983     * Send a request to the endpoint with specified parameters.
2984     * @method request
2985     * @param {String} method Request method
2986     * @param {String} endpoint Endpoint to request
2987     * @param {String|Object} [queryParameter]
2988     * @param {String|Object|HTMLFormElement|FormData} [requestData]
2989     *   @param {String|Object|HTMLFormElement} [requestData.{the-key-requires-json-text}] Can specify json-text value by string or object or HTMLFormElement. Serialize automatically if object or HTMLFormElement is passed.
2990     *   @param {HTMLInputElement|File} [requestData.{the-key-requires-file}] Can specify file value by HTMLInputElement or File object.
2991     * @param {Function} [callback]
2992     * @return {XMLHttpRequest|null} Return XMLHttpRequest if request is sent
2993     *   via XMLHttpRequest. Return null if request is not sent
2994     *   via XMLHttpRequest (e.g. sent via iframe).
2995     * @category core
2996     */
2997    request: function(method, endpoint) {
2998        var i, k, v, base,
2999            api        = this,
3000            paramsList = [],
3001            params     = null,
3002            callback   = function(){},
3003            xhr        = null,
3004            xdr        = null,
3005            via        = this._requestVia(),
3006            tokenData  = this.getTokenData(),
3007            authHeader = this.getAuthorizationHeader('accessToken'),
3008            currentFormat     = this.getCurrentFormat(),
3009            originalMethod    = method,
3010            originalArguments = Array.prototype.slice.call(arguments),
3011            defaultParams     = {},
3012            defaultHeaders    = {};
3013
3014        function serializeParams(params) {
3015            var k, data;
3016
3017            if (! api.o.disableFormData && window.FormData) {
3018                if (params instanceof window.FormData) {
3019                    return params;
3020                }
3021                else if (api._isFormElement(params)) {
3022                    return new window.FormData(params);
3023                }
3024                else if (window.FormData && typeof params === 'object') {
3025                    data = new window.FormData();
3026                    for (k in params) {
3027                        data.append(k, api._serializeObject(params[k]));
3028                    }
3029                    return data;
3030                }
3031            }
3032
3033
3034            if (api._isFormElement(params)) {
3035                params = api._serializeFormElementToObject(params);
3036                for (k in params) {
3037                    if (params[k] instanceof Array) {
3038                        params[k] = params[k].join(',');
3039                    }
3040                }
3041            }
3042
3043            if (api._findFileInput(params)) {
3044                via = 'iframe';
3045
3046                data = {};
3047                for (k in params) {
3048                    if (api._isFileInputElement(params[k])) {
3049                        data[k] = params[k];
3050                    }
3051                    else {
3052                        data[k] = api._serializeObject(params[k]);
3053                    }
3054                }
3055                params = data;
3056            }
3057            else if (typeof params !== 'string') {
3058                params = api._serializeParams(params);
3059            }
3060
3061            return params;
3062        }
3063
3064        function runCallback(response) {
3065            var status = callback(response);
3066            if (status !== false) {
3067                if (response.error) {
3068                    api.trigger('error', response);
3069                }
3070            }
3071            return status;
3072        }
3073
3074        function needToRetry(response) {
3075            return response.error &&
3076                response.error.code === 401 &&
3077                endpoint !== '/token' &&
3078                endpoint !== '/authentication';
3079        }
3080
3081        function retryWithAuthentication() {
3082            api.request('POST', '/token', function(response) {
3083                if (response.error) {
3084                    parseArguments(originalArguments);
3085                    return runCallback(response);
3086                }
3087                else {
3088                    api.storeTokenData(response);
3089                    api.request.apply(api, originalArguments);
3090                    return false;
3091                }
3092            });
3093        }
3094
3095        function appendParamsToURL(base, params) {
3096            if (base.indexOf('?') === -1) {
3097                base += '?';
3098            }
3099            else {
3100                base += '&';
3101            }
3102            return base + api._serializeParams(params);
3103        }
3104
3105        function parseArguments(args) {
3106            for (i = 2; i < args.length; i++) {
3107                v = args[i];
3108                switch (typeof v) {
3109                case 'function':
3110                    callback = v;
3111                    break;
3112                case 'object':
3113                    if (
3114                        v &&
3115                        ! v.nodeName &&
3116                        ((window.ActiveXObject && v instanceof window.ActiveXObject) ||
3117                         (window.XMLHttpRequest && v instanceof window.XMLHttpRequest) ||
3118                         (window.XDomainRequest && v instanceof window.XDomainRequest))
3119                    ) {
3120                        if (window.XDomainRequest && v instanceof window.XDomainRequest) {
3121                            xdr = v;
3122                        }
3123                        else {
3124                            xhr = v;
3125                        }
3126                    }
3127                    else {
3128                        paramsList.push(v);
3129                    }
3130                    break;
3131                case 'string':
3132                    paramsList.push(api._unserializeParams(v));
3133                    break;
3134                }
3135            }
3136        }
3137
3138        if (! this.o.withoutAuthorization &&
3139            tokenData &&
3140            ! tokenData.accessToken &&
3141            endpoint !== '/token' &&
3142            endpoint !== '/authentication'
3143        ) {
3144            return retryWithAuthentication();
3145        }
3146
3147        if (authHeader) {
3148            defaultHeaders['X-MT-Authorization'] = authHeader;
3149        }
3150
3151        if (endpoint === '/token' || endpoint === '/authentication') {
3152            if (tokenData && tokenData.oneTimeToken) {
3153                defaultHeaders['X-MT-Authorization'] =
3154                    api.getAuthorizationHeader('oneTimeToken');
3155                delete tokenData.oneTimeToken;
3156            }
3157            else if (tokenData && tokenData.sessionId) {
3158                defaultHeaders['X-MT-Authorization'] =
3159                    api.getAuthorizationHeader('sessionId');
3160            }
3161            else if (endpoint === '/token' && originalMethod.toLowerCase() === 'post') {
3162                delete defaultHeaders['X-MT-Authorization'];
3163            }
3164            defaultParams.clientId = api.o.clientId;
3165        }
3166
3167        if (this.o.withoutAuthorization) {
3168            delete defaultHeaders['X-MT-Authorization'];
3169        }
3170
3171        if (this.o.suppressResponseCodes ||
3172            (typeof this.o.suppressResponseCodes === 'undefined' && via === 'xdr')
3173        ) {
3174            defaultParams.suppressResponseCodes = true;
3175        }
3176
3177        if (! this.o.cache) {
3178            defaultParams._ = new Date().getTime();
3179        }
3180
3181        if (currentFormat !== this.constructor.getDefaultFormat()) {
3182            defaultParams.format = currentFormat.fileExtension;
3183        }
3184
3185        if (method.match(/^(put|delete)$/i)) {
3186            defaultParams.__method = method;
3187            method = 'POST';
3188        }
3189
3190        parseArguments(arguments);
3191
3192        if (paramsList.length && (method.toLowerCase() === 'get' || paramsList.length >= 2)) {
3193            endpoint = appendParamsToURL(endpoint, paramsList.shift());
3194        }
3195
3196        if (paramsList.length) {
3197            params = paramsList.shift();
3198        }
3199
3200        if (! this._isEmptyObject(defaultParams)) {
3201            if (method.toLowerCase() === 'get') {
3202                endpoint = appendParamsToURL(endpoint, defaultParams);
3203            }
3204            else if (window.FormData && params && params instanceof window.FormData) {
3205                for (k in defaultParams) {
3206                    params.append(k, defaultParams[k]);
3207                }
3208            }
3209            else {
3210                params = params || {};
3211                for (k in defaultParams) {
3212                    params[k] = defaultParams[k];
3213                }
3214            }
3215        }
3216
3217        params = serializeParams(params);
3218
3219
3220        base = this.o.baseUrl.replace(/\/*$/, '/') + 'v' + this.getVersion();
3221        endpoint = endpoint.replace(/^\/*/, '/');
3222
3223
3224        function responseCallback(contentType, responseText, status, statusText, cleanup) {
3225            var response, mimeType, format, callbackResult;
3226
3227            try {
3228                mimeType = contentType;
3229                format   = api.findFormat(mimeType) || api.getCurrentFormat();
3230                response = format.unserialize(responseText);
3231            }
3232            catch (e) {
3233                response = {
3234                    error: {
3235                        code:    +status,
3236                        message: statusText || 'Communication Error'
3237                    }
3238                };
3239            }
3240
3241            if (needToRetry(response)) {
3242                retryWithAuthentication();
3243                if (cleanup) {
3244                    cleanup();
3245                }
3246                return false;
3247            }
3248
3249            if ((! response.error &&
3250                    endpoint === '/authentication' &&
3251                    originalMethod.toLowerCase() === 'delete') ||
3252                (response.error && response.error.code === 401 && (
3253                    (endpoint === '/authentication' &&
3254                     originalMethod.toLowerCase() === 'post') ||
3255                    (endpoint === '/token' &&
3256                     originalMethod.toLowerCase() === 'post')))) {
3257                api.clearTokenData();
3258            }
3259            else if (! response.error && (
3260                (endpoint === '/authentication' &&
3261                 originalMethod.toLowerCase() === 'post') ||
3262                (endpoint === '/token' &&
3263                 originalMethod.toLowerCase() === 'post'))) {
3264                api.storeTokenData(response);
3265            }
3266
3267            callbackResult = runCallback(response);
3268
3269            if (callbackResult !== false &&
3270                response.error && response.error.code === 401 &&
3271                endpoint !== '/authentication') {
3272                api.trigger('authorizationRequired', response);
3273            }
3274        }
3275
3276        if (via === 'xdr') {
3277            if (! this._isEmptyObject(defaultHeaders)) {
3278                throw 'Cannot set request header when sending via XDomainRequest';
3279            }
3280
3281            xdr = xdr || new window.XDomainRequest();
3282            xdr.onload = function() {
3283                responseCallback(xdr.contentType, xdr.responseText, 200);
3284            };
3285            xdr.onerror = function() {
3286                responseCallback(xdr.contentType, xdr.responseText, 404);
3287            };
3288            xdr.onprogress = function(){};
3289            xdr.ontimeout = function() {
3290                responseCallback(xdr.contentType, xdr.responseText, 0);
3291            };
3292            if (typeof this.o.timeout !== 'undefined') {
3293                xdr.timeout = this.o.timeout || Number.MAX_VALUE;
3294            }
3295            xdr.open( method, base + endpoint);
3296            xdr.send( api._serializeParams(params) || null );
3297        }
3298        else if (via === 'xhr') {
3299            xhr = xhr || this.newXMLHttpRequest();
3300            if (typeof this.o.timeout !== 'undefined') {
3301                xhr.timeout = this.o.timeout;
3302            }
3303            xhr.onreadystatechange = function() {
3304                var responseResult, url;
3305
3306                if (xhr.readyState !== 4) {
3307                    return;
3308                }
3309
3310                function cleanup() {
3311                    xhr.onreadystatechange = function(){};
3312                }
3313
3314                responseResult = responseCallback(
3315                    xhr.getResponseHeader('Content-Type'),
3316                    xhr.responseText,
3317                    xhr.status,
3318                    xhr.statusText,
3319                    cleanup
3320                );
3321
3322                if (responseResult === false) {
3323                    return;
3324                }
3325
3326                url = xhr.getResponseHeader('X-MT-Next-Phase-URL');
3327                if (url) {
3328                    xhr.abort();
3329                    api.sendXMLHttpRequest(xhr, method, base + url, params, defaultHeaders);
3330                }
3331                else {
3332                    cleanup();
3333                }
3334            };
3335            return this.sendXMLHttpRequest(xhr, method, base + endpoint, params, defaultHeaders);
3336        }
3337        else {
3338            (function() {
3339                var k, file, originalName, input,
3340                    target     = api._getNextIframeName(),
3341                    doc        = window.document,
3342                    form       = doc.createElement('form'),
3343                    iframe     = doc.createElement('iframe');
3344
3345
3346                // Set up a form element
3347                form.action        = base + endpoint;
3348                form.target        = target;
3349                form.method        = method;
3350                form.style.display = 'inline';
3351                form.encoding      = 'multipart/form-data';
3352                form.enctype       = 'multipart/form-data';
3353
3354                // Set up a iframe element
3355                iframe.name           = target;
3356                iframe.style.position = 'absolute';
3357                iframe.style.top      = '-9999px';
3358                doc.body.appendChild(iframe);
3359                iframe.contentWindow.name = target;
3360
3361
3362                params = params || {};
3363                for (k in defaultHeaders) {
3364                    params[k] = defaultHeaders[k];
3365                }
3366                params['X-MT-Requested-Via'] = 'IFRAME';
3367
3368                for (k in params) {
3369                    if (api._isFileInputElement(params[k])) {
3370                        file         = params[k];
3371                        originalName = file.name;
3372                        file.name    = k;
3373                        if (file.parentNode) {
3374                            file.parentNode.insertBefore(form, file);
3375                        }
3376                        else {
3377                            doc.body.appendChild(form);
3378                        }
3379                        form.appendChild(file);
3380                        continue;
3381                    }
3382
3383                    input       = doc.createElement('input');
3384                    input.type  = 'hidden';
3385                    input.name  = k;
3386                    input.value = params[k];
3387                    form.appendChild(input);
3388                }
3389
3390                form.submit();
3391
3392
3393                function handler() {
3394                    var body     = iframe.contentWindow.document.body,
3395                        contents = body.textContent || body.innerText,
3396                        response;
3397
3398                    function cleanup() {
3399                        setTimeout(function() {
3400                            file.name = originalName;
3401                            if (form.parentNode) {
3402                                form.parentNode.insertBefore(file, form);
3403                                form.parentNode.removeChild(form);
3404                            }
3405                            if (iframe.parentNode) {
3406                                iframe.parentNode.removeChild(iframe);
3407                            }
3408                        });
3409                    }
3410
3411                    try {
3412                        response = api.unserializeData(contents);
3413                    }
3414                    catch (e) {
3415                        response = {
3416                            error: {
3417                                code:    500,
3418                                message: 'Internal Server Error'
3419                            }
3420                        };
3421                    }
3422
3423                    if (needToRetry(response)) {
3424                        retryWithAuthentication();
3425                        cleanup();
3426                        return;
3427                    }
3428
3429                    cleanup();
3430                    runCallback(response);
3431                }
3432                if ( iframe.addEventListener ) {
3433                    iframe.addEventListener('load', handler, false);
3434                } else if ( iframe.attachEvent ) {
3435                    iframe.attachEvent('onload', handler);
3436                }
3437            })();
3438
3439            return;
3440        }
3441    },
3442
3443    /**
3444     * Register callback to instance.
3445     * @method on
3446     * @param {String} key Event name
3447     * @param {Function} callback Callback function
3448     * @category core
3449     * @example
3450     *     var callback = function() {
3451     *       // Do stuff
3452     *     };
3453     *     api.on(eventName, callback);
3454     */
3455    on: function() {
3456        this.constructor.on.apply(this, arguments);
3457    },
3458
3459    /**
3460     * Deregister callback from instance.
3461     * @method off
3462     * @param {String} key Event name
3463     * @param {Function} callback Callback function
3464     * @category core
3465     * @example
3466     *     api.off(eventName, callback);
3467     */
3468    off: function() {
3469        this.constructor.off.apply(this, arguments);
3470    },
3471
3472    /**
3473     * Trigger event.
3474     * First, run class level callbacks. Then, run instance level callbacks.
3475     * @method trigger
3476     * @param {String} key Event name
3477     * @category core
3478     */
3479    trigger: function(key) {
3480        var i,
3481            args      = Array.prototype.slice.call(arguments, 1),
3482            callbacks = (this.constructor.callbacks[key] || []) // Class level
3483                .concat(this.callbacks[key] || []); // Instance level
3484
3485        for (i = 0; i < callbacks.length; i++) {
3486            callbacks[i].apply(this, args);
3487        }
3488    },
3489
3490    _generateEndpointMethod: function(e) {
3491        var api       = this,
3492            varRegexp = new RegExp(':([a-zA-Z_-]+)', 'g'),
3493            vars      = null,
3494            name      = e.id.replace(/_(\w)/g, function(all, letter) {
3495                            return letter.toUpperCase();
3496                        });
3497
3498        function extractVars() {
3499            var m, vars = [];
3500            while ((m = varRegexp.exec(e.route)) !== null) {
3501                vars.push(m[1]);
3502            }
3503            return vars;
3504        }
3505
3506        api[name] = function() {
3507            if (! vars) {
3508                vars = extractVars();
3509            }
3510
3511            var args           = Array.prototype.slice.call(arguments),
3512                endpointParams = {},
3513                resources      = {},
3514                route, i;
3515
3516            for (i = 0; i < vars.length; i++) {
3517                endpointParams[vars[i]] = args.shift();
3518            }
3519            route = api.bindEndpointParams(e.route, endpointParams);
3520
3521            if (e.resources) {
3522                for (i = 0; i < e.resources.length; i++) {
3523                    resources[e.resources[i]] = args.shift();
3524                }
3525                args.push(resources);
3526            }
3527
3528            return api.request.apply(api, [e.verb, route].concat(args));
3529        };
3530    },
3531
3532    /**
3533     * Generate methods to access endpoint.
3534     * @method generateEndpointMethods
3535     * @param {Array.Object} endpoints Endpoints to register
3536     *   @param {Object} endpoints.{i}
3537     *     @param {String} endpoints.{i}.id Normally, the ID is snake case,
3538     *       but generated method is camel case.
3539     *     @param {String} endpoints.{i}.route The template of route
3540     *     @param {String} endpoints.{i}.verb The HTTP verb
3541     *     @param {Array.String} [endpoints.{i}.resources] The required resource data
3542     * @example
3543     *     api.generateEndpointMethods([
3544     *       {
3545     *           "id": "list_entries",
3546     *           "route": "/sites/:site_id/entries",
3547     *           "verb": "GET",
3548     *       },
3549     *       {
3550     *           "id": "create_entry",
3551     *           "route": "/sites/:site_id/entries",
3552     *           "verb": "POST",
3553     *           "resources": [
3554     *               "entry"
3555     *           ]
3556     *       }
3557     *     ]);
3558     * @category core
3559     */
3560    generateEndpointMethods: function(endpoints) {
3561        for (var i = 0; i < endpoints.length; i++) {
3562            this._generateEndpointMethod(endpoints[i]);
3563        }
3564    },
3565
3566    /**
3567     * Load endpoint from DataAPI dynamically.
3568     * @method loadEndpoints
3569     * @param {Object} [params]
3570     *   @param {String} [params.includeComponents] Comma separated component IDs to load
3571     *   @param {String} [params.excludeComponents] Comma separated component IDs to exclude
3572     * @example
3573     * Load endpoints only from specified module.
3574     *
3575     *     api.loadEndpoints({
3576     *       includeComponents: 'your-extension-module'
3577     *     });
3578     *     api.getDataViaYourExtensionModule(function(response) {
3579     *       // Do stuff
3580     *     });
3581     *
3582     * Load all endpoints except for core.
3583     * Since all the endpoints of core is already loaded.
3584     *
3585     *     api.loadEndpoints({
3586     *       excludeComponents: 'core'
3587     *     });
3588     *     api.getDataViaYourExtensionModule(function(response) {
3589     *       // Do stuff
3590     *     });
3591     * @category core
3592     */
3593    loadEndpoints: function(params) {
3594        var api = this;
3595
3596        api.withOptions({withoutAuthorization: true, async: false}, function() {
3597            api.request('GET', '/endpoints', params, function(response) {
3598                if (response.error) {
3599                    return;
3600                }
3601
3602                api.generateEndpointMethods(response.items);
3603            });
3604        });
3605    }
3606};
3607
3608/**
3609 * Triggered on initializing an instance
3610 *
3611 * @event initialize
3612 * @example
3613 *     DataAPI.on("initialize", function() {
3614 *       console.log("initializing...");
3615 *     });
3616 **/
3617
3618/**
3619 * Triggered on getting an error of a HTTP request
3620 *
3621 * @event error
3622 * @param {Object} response A response object
3623 *   @param {Number} response.code The HTTP response code
3624 *   @param {String} response.message The error message
3625 *   @param {Object} response.data The data exists only if a current error has optional data
3626 * @example
3627 *     api.on("error", function(response) {
3628 *       console.log(response.error.message);
3629 *     });
3630 **/
3631
3632/**
3633 * Triggered on receiving the HTTP response code 401 (Authorization required).
3634 *
3635 * @event authorizationRequired
3636 * @param {Object} response A response object
3637 *   @param {Number} response.code The HTTP response code
3638 *   @param {Number} response.message The error message
3639 * @example
3640 *     api.on("authorizationRequired", function(response) {
3641 *       // You will return to current URL after authorization succeeded.
3642 *       location.href = api.getAuthorizationUrl(location.href);
3643 *     });
3644 **/
3645
3646var Cookie = function( name, value, domain, path, expires, secure ) {
3647    this.name = name;
3648    this.value = value;
3649    this.domain = domain;
3650    this.path = path;
3651    this.expires = expires;
3652    this.secure = secure;
3653};
3654
3655Cookie.prototype = {
3656    /**
3657     * Get this cookie from the web browser's store of cookies.  Note that if the <code>document.cookie</code>
3658     * property has been written to repeatedly by the same client code in excess of 4K (regardless of the size
3659     * of the actual cookies), IE 6 will report an empty <code>document.cookie</code> collection of cookies.
3660     * @return <code>Cookie</code> The fetched cookie.
3661     */
3662    fetch: function() {
3663        if (! window.document) {
3664            return undefined;
3665        }
3666
3667        var prefix = escape( this.name ) + "=",
3668            cookies = ("" + window.document.cookie).split( /;\s*/ ),
3669            i;
3670
3671        for( i = 0; i < cookies.length; i++ ) {
3672            if( cookies[ i ].indexOf( prefix ) === 0 ) {
3673                this.value = unescape( cookies[ i ].substring( prefix.length ) );
3674                return this;
3675            }
3676        }
3677
3678        return undefined;
3679    },
3680
3681
3682    /**
3683     * Set and store a cookie in the the web browser's native collection of cookies.
3684     * @return <code>Cookie</code> The set and stored ("baked") cookie.
3685     */
3686    bake: function( value ) {
3687        if (! window.document) {
3688            return undefined;
3689        }
3690
3691        function exists(x) {
3692            return (x === undefined || x === null) ? false : true;
3693        }
3694
3695        if( !exists( this.name ) ) {
3696            return undefined;
3697        }
3698
3699        if( exists( value ) ) {
3700            this.value = value;
3701        }
3702        else {
3703            value = this.value;
3704        }
3705
3706        var name = escape( this.name ),
3707            attributes = ( this.domain ? "; domain=" + escape( this.domain ) : "") +
3708            (this.path ? "; path=" + escape( this.path ) : "") +
3709            (this.expires ? "; expires=" + this.expires.toGMTString() : "") +
3710            (this.secure ? "; secure=1"  : ""),
3711            batter = name + "=" + escape( value ) + attributes;
3712
3713        window.document.cookie = batter;
3714
3715        return this;
3716    },
3717
3718
3719    remove: function() {
3720        this.expires = new Date( 0 ); // "Thu, 01 Jan 1970 00:00:00 GMT"
3721        this.value = "";
3722        this.bake();
3723    }
3724};
3725
3726Cookie.fetch = function( name ) {
3727    var cookie = new this( name );
3728    return cookie.fetch();
3729};
3730
3731
3732Cookie.bake = function( name, value, domain, path, expires, secure ) {
3733    var cookie = new this( name, value, domain, path, expires, secure );
3734    return cookie.bake();
3735};
3736
3737Cookie.remove = function( name ) {
3738    var cookie = this.fetch( name );
3739    if ( cookie ) {
3740        return cookie.remove();
3741    }
3742};
3743
3744var JSON = window.JSON;
3745/*
3746    json2.js
3747    2012-10-08
3748
3749    Public Domain.
3750
3751    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
3752
3753    See http://www.JSON.org/js.html
3754
3755
3756    This code should be minified before deployment.
3757    See http://javascript.crockford.com/jsmin.html
3758
3759    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
3760    NOT CONTROL.
3761
3762
3763    This file creates a global JSON object containing two methods: stringify
3764    and parse.
3765
3766        JSON.stringify(value, replacer, space)
3767            value       any JavaScript value, usually an object or array.
3768
3769            replacer    an optional parameter that determines how object
3770                        values are stringified for objects. It can be a
3771                        function or an array of strings.
3772
3773            space       an optional parameter that specifies the indentation
3774                        of nested structures. If it is omitted, the text will
3775                        be packed without extra whitespace. If it is a number,
3776                        it will specify the number of spaces to indent at each
3777                        level. If it is a string (such as '\t' or '&nbsp;'),
3778                        it contains the characters used to indent at each level.
3779
3780            This method produces a JSON text from a JavaScript value.
3781
3782            When an object value is found, if the object contains a toJSON
3783            method, its toJSON method will be called and the result will be
3784            stringified. A toJSON method does not serialize: it returns the
3785            value represented by the name/value pair that should be serialized,
3786            or undefined if nothing should be serialized. The toJSON method
3787            will be passed the key associated with the value, and this will be
3788            bound to the value
3789
3790            For example, this would serialize Dates as ISO strings.
3791
3792                Date.prototype.toJSON = function (key) {
3793                    function f(n) {
3794                        // Format integers to have at least two digits.
3795                        return n < 10 ? '0' + n : n;
3796                    }
3797
3798                    return this.getUTCFullYear()   + '-' +
3799                         f(this.getUTCMonth() + 1) + '-' +
3800                         f(this.getUTCDate())      + 'T' +
3801                         f(this.getUTCHours())     + ':' +
3802                         f(this.getUTCMinutes())   + ':' +
3803                         f(this.getUTCSeconds())   + 'Z';
3804                };
3805
3806            You can provide an optional replacer method. It will be passed the
3807            key and value of each member, with this bound to the containing
3808            object. The value that is returned from your method will be
3809            serialized. If your method returns undefined, then the member will
3810            be excluded from the serialization.
3811
3812            If the replacer parameter is an array of strings, then it will be
3813            used to select the members to be serialized. It filters the results
3814            such that only members with keys listed in the replacer array are
3815            stringified.
3816
3817            Values that do not have JSON representations, such as undefined or
3818            functions, will not be serialized. Such values in objects will be
3819            dropped; in arrays they will be replaced with null. You can use
3820            a replacer function to replace those with JSON values.
3821            JSON.stringify(undefined) returns undefined.
3822
3823            The optional space parameter produces a stringification of the
3824            value that is filled with line breaks and indentation to make it
3825            easier to read.
3826
3827            If the space parameter is a non-empty string, then that string will
3828            be used for indentation. If the space parameter is a number, then
3829            the indentation will be that many spaces.
3830
3831            Example:
3832
3833            text = JSON.stringify(['e', {pluribus: 'unum'}]);
3834            // text is '["e",{"pluribus":"unum"}]'
3835
3836
3837            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
3838            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
3839
3840            text = JSON.stringify([new Date()], function (key, value) {
3841                return this[key] instanceof Date ?
3842                    'Date(' + this[key] + ')' : value;
3843            });
3844            // text is '["Date(---current time---)"]'
3845
3846
3847        JSON.parse(text, reviver)
3848            This method parses a JSON text to produce an object or array.
3849            It can throw a SyntaxError exception.
3850
3851            The optional reviver parameter is a function that can filter and
3852            transform the results. It receives each of the keys and values,
3853            and its return value is used instead of the original value.
3854            If it returns what it received, then the structure is not modified.
3855            If it returns undefined then the member is deleted.
3856
3857            Example:
3858
3859            // Parse the text. Values that look like ISO date strings will
3860            // be converted to Date objects.
3861
3862            myData = JSON.parse(text, function (key, value) {
3863                var a;
3864                if (typeof value === 'string') {
3865                    a =
3866/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
3867                    if (a) {
3868                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
3869                            +a[5], +a[6]));
3870                    }
3871                }
3872                return value;
3873            });
3874
3875            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
3876                var d;
3877                if (typeof value === 'string' &&
3878                        value.slice(0, 5) === 'Date(' &&
3879                        value.slice(-1) === ')') {
3880                    d = new Date(value.slice(5, -1));
3881                    if (d) {
3882                        return d;
3883                    }
3884                }
3885                return value;
3886            });
3887
3888
3889    This is a reference implementation. You are free to copy, modify, or
3890    redistribute.
3891*/
3892
3893/*jslint evil: true, regexp: true */
3894
3895/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
3896    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
3897    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
3898    lastIndex, length, parse, prototype, push, replace, slice, stringify,
3899    test, toJSON, toString, valueOf
3900*/
3901
3902
3903// Create a JSON object only if one does not already exist. We create the
3904// methods in a closure to avoid creating global variables.
3905
3906if (typeof JSON !== 'object') {
3907    JSON = {};
3908}
3909
3910(function () {
3911    'use strict';
3912
3913    function f(n) {
3914        // Format integers to have at least two digits.
3915        return n < 10 ? '0' + n : n;
3916    }
vendor: 10,031 bytes, lines 3917-4209
3917
3918    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
3919        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
3920        gap,
3921        indent,
3922        meta = {    // table of character substitutions
3923            '\b': '\\b',
3924            '\t': '\\t',
3925            '\n': '\\n',
3926            '\f': '\\f',
3927            '\r': '\\r',
3928            '"' : '\\"',
3929            '\\': '\\\\'
3930        },
3931        rep;
3932
3933
3934    function quote(string) {
3935
3936// If the string contains no control characters, no quote characters, and no
3937// backslash characters, then we can safely slap some quotes around it.
3938// Otherwise we must also replace the offending characters with safe escape
3939// sequences.
3940
3941        escapable.lastIndex = 0;
3942        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
3943            var c = meta[a];
3944            return typeof c === 'string'
3945                ? c
3946                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
3947        }) + '"' : '"' + string + '"';
3948    }
3949
3950
3951    function str(key, holder) {
3952
3953// Produce a string from holder[key].
3954
3955        var i,          // The loop counter.
3956            k,          // The member key.
3957            v,          // The member value.
3958            length,
3959            mind = gap,
3960            partial,
3961            value = holder[key];
3962
3963// If the value has a toJSON method, call it to obtain a replacement value.
3964
3965        if (value && typeof value === 'object' &&
3966                typeof value.toJSON === 'function') {
3967            value = value.toJSON(key);
3968        }
3969
3970// If we were called with a replacer function, then call the replacer to
3971// obtain a replacement value.
3972
3973        if (typeof rep === 'function') {
3974            value = rep.call(holder, key, value);
3975        }
3976
3977// What happens next depends on the value's type.
3978
3979        switch (typeof value) {
3980        case 'string':
3981            return quote(value);
3982
3983        case 'number':
3984
3985// JSON numbers must be finite. Encode non-finite numbers as null.
3986
3987            return isFinite(value) ? String(value) : 'null';
3988
3989        case 'boolean':
3990        case 'null':
3991
3992// If the value is a boolean or null, convert it to a string. Note:
3993// typeof null does not produce 'null'. The case is included here in
3994// the remote chance that this gets fixed someday.
3995
3996            return String(value);
3997
3998// If the type is 'object', we might be dealing with an object or an array or
3999// null.
4000
4001        case 'object':
4002
4003// Due to a specification blunder in ECMAScript, typeof null is 'object',
4004// so watch out for that case.
4005
4006            if (!value) {
4007                return 'null';
4008            }
4009
4010// Make an array to hold the partial results of stringifying this object value.
4011
4012            gap += indent;
4013            partial = [];
4014
4015// Is the value an array?
4016
4017            if (Object.prototype.toString.apply(value) === '[object Array]') {
4018
4019// The value is an array. Stringify every element. Use null as a placeholder
4020// for non-JSON values.
4021
4022                length = value.length;
4023                for (i = 0; i < length; i += 1) {
4024                    partial[i] = str(i, value) || 'null';
4025                }
4026
4027// Join all of the elements together, separated with commas, and wrap them in
4028// brackets.
4029
4030                v = partial.length === 0
4031                    ? '[]'
4032                    : gap
4033                    ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
4034                    : '[' + partial.join(',') + ']';
4035                gap = mind;
4036                return v;
4037            }
4038
4039// If the replacer is an array, use it to select the members to be stringified.
4040
4041            if (rep && typeof rep === 'object') {
4042                length = rep.length;
4043                for (i = 0; i < length; i += 1) {
4044                    if (typeof rep[i] === 'string') {
4045                        k = rep[i];
4046                        v = str(k, value);
4047                        if (v) {
4048                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
4049                        }
4050                    }
4051                }
4052            } else {
4053
4054// Otherwise, iterate through all of the keys in the object.
4055
4056                for (k in value) {
4057                    if (Object.prototype.hasOwnProperty.call(value, k)) {
4058                        v = str(k, value);
4059                        if (v) {
4060                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
4061                        }
4062                    }
4063                }
4064            }
4065
4066// Join all of the member texts together, separated with commas,
4067// and wrap them in braces.
4068
4069            v = partial.length === 0
4070                ? '{}'
4071                : gap
4072                ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
4073                : '{' + partial.join(',') + '}';
4074            gap = mind;
4075            return v;
4076        }
4077    }
4078
4079// If the JSON object does not yet have a stringify method, give it one.
4080
4081    if (typeof JSON.stringify !== 'function') {
4082        JSON.stringify = function (value, replacer, space) {
4083
4084// The stringify method takes a value and an optional replacer, and an optional
4085// space parameter, and returns a JSON text. The replacer can be a function
4086// that can replace values, or an array of strings that will select the keys.
4087// A default replacer method can be provided. Use of the space parameter can
4088// produce text that is more easily readable.
4089
4090            var i;
4091            gap = '';
4092            indent = '';
4093
4094// If the space parameter is a number, make an indent string containing that
4095// many spaces.
4096
4097            if (typeof space === 'number') {
4098                for (i = 0; i < space; i += 1) {
4099                    indent += ' ';
4100                }
4101
4102// If the space parameter is a string, it will be used as the indent string.
4103
4104            } else if (typeof space === 'string') {
4105                indent = space;
4106            }
4107
4108// If there is a replacer, it must be a function or an array.
4109// Otherwise, throw an error.
4110
4111            rep = replacer;
4112            if (replacer && typeof replacer !== 'function' &&
4113                    (typeof replacer !== 'object' ||
4114                    typeof replacer.length !== 'number')) {
4115                throw new Error('JSON.stringify');
4116            }
4117
4118// Make a fake root object containing our value under the key of ''.
4119// Return the result of stringifying the value.
4120
4121            return str('', {'': value});
4122        };
4123    }
4124
4125
4126// If the JSON object does not yet have a parse method, give it one.
4127
4128    if (typeof JSON.parse !== 'function') {
4129        JSON.parse = function (text, reviver) {
4130
4131// The parse method takes a text and an optional reviver function, and returns
4132// a JavaScript value if the text is a valid JSON text.
4133
4134            var j;
4135
4136            function walk(holder, key) {
4137
4138// The walk method is used to recursively walk the resulting structure so
4139// that modifications can be made.
4140
4141                var k, v, value = holder[key];
4142                if (value && typeof value === 'object') {
4143                    for (k in value) {
4144                        if (Object.prototype.hasOwnProperty.call(value, k)) {
4145                            v = walk(value, k);
4146                            if (v !== undefined) {
4147                                value[k] = v;
4148                            } else {
4149                                delete value[k];
4150                            }
4151                        }
4152                    }
4153                }
4154                return reviver.call(holder, key, value);
4155            }
4156
4157
4158// Parsing happens in four stages. In the first stage, we replace certain
4159// Unicode characters with escape sequences. JavaScript handles many characters
4160// incorrectly, either silently deleting them, or treating them as line endings.
4161
4162            text = String(text);
4163            cx.lastIndex = 0;
4164            if (cx.test(text)) {
4165                text = text.replace(cx, function (a) {
4166                    return '\\u' +
4167                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
4168                });
4169            }
4170
4171// In the second stage, we run the text against regular expressions that look
4172// for non-JSON patterns. We are especially concerned with '()' and 'new'
4173// because they can cause invocation, and '=' because it can cause mutation.
4174// But just to be safe, we want to reject all unexpected forms.
4175
4176// We split the second stage into 4 regexp operations in order to work around
4177// crippling inefficiencies in IE's and Safari's regexp engines. First we
4178// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
4179// replace all simple value tokens with ']' characters. Third, we delete all
4180// open brackets that follow a colon or comma or that begin the text. Finally,
4181// we look to see that the remaining characters are only whitespace or ']' or
4182// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
4183
4184            if (/^[\],:{}\s]*$/
4185                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
4186                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
4187                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
4188
4189// In the third stage we use the eval function to compile the text into a
4190// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
4191// in JavaScript: it can begin a block or an object literal. We wrap the text
4192// in parens to eliminate the ambiguity.
4193
4194                j = eval('(' + text + ')');
4195
4196// In the optional fourth stage, we recursively walk the new structure, passing
4197// each name/value pair to a reviver function for possible transformation.
4198
4199                return typeof reviver === 'function'
4200                    ? walk({'': j}, '')
4201                    : j;
4202            }
4203
4204// If the text is not JSON parseable, then a SyntaxError is thrown.
4205
4206            throw new SyntaxError('JSON.parse');
4207        };
4208    }
4209}
4209());
4210
4211DataAPI.on('initialize', function() {
4212    this.generateEndpointMethods(
4213        [
4214    {
4215        "id": "list_endpoints",
4216        "route": "/endpoints",
4217        "verb": "GET",
4218        "resources": null
4219    },
4220    {
4221        "id": "authenticate",
4222        "route": "/authentication",
4223        "verb": "POST",
4224        "resources": null
4225    },
4226    {
4227        "id": "get_token",
4228        "route": "/token",
4229        "verb": "POST",
4230        "resources": null
4231    },
4232    {
4233        "id": "revoke_authentication",
4234        "route": "/authentication",
4235        "verb": "DELETE",
4236        "resources": null
4237    },
4238    {
4239        "id": "revoke_token",
4240        "route": "/token",
4241        "verb": "DELETE",
4242        "resources": null
4243    },
4244    {
4245        "id": "get_user",
4246        "route": "/users/:user_id",
4247        "verb": "GET",
4248        "resources": null
4249    },
4250    {
4251        "id": "update_user",
4252        "route": "/users/:user_id",
4253        "verb": "PUT",
4254        "resources": [
4255            "user"
4256        ]
4257    },
4258    {
4259        "id": "list_blogs_for_user",
4260        "route": "/users/:user_id/sites",
4261        "verb": "GET",
4262        "resources": null
4263    },
4264    {
4265        "id": "get_blog",
4266        "route": "/sites/:blog_id",
4267        "verb": "GET",
4268        "resources": null
4269    },
4270    {
4271        "id": "list_entries",
4272        "route": "/sites/:site_id/entries",
4273        "verb": "GET",
4274        "resources": null
4275    },
4276    {
4277        "id": "create_entry",
4278        "route": "/sites/:site_id/entries",
4279        "verb": "POST",
4280        "resources": [
4281            "entry"
4282        ]
4283    },
4284    {
4285        "id": "get_entry",
4286        "route": "/sites/:site_id/entries/:entry_id",
4287        "verb": "GET",
4288        "resources": null
4289    },
4290    {
4291        "id": "update_entry",
4292        "route": "/sites/:site_id/entries/:entry_id",
4293        "verb": "PUT",
4294        "resources": [
4295            "entry"
4296        ]
4297    },
4298    {
4299        "id": "delete_entry",
4300        "route": "/sites/:site_id/entries/:entry_id",
4301        "verb": "DELETE",
4302        "resources": null
4303    },
4304    {
4305        "id": "list_categories",
4306        "route": "/sites/:site_id/categories",
4307        "verb": "GET",
4308        "resources": null
4309    },
4310    {
4311        "id": "list_comments",
4312        "route": "/sites/:site_id/comments",
4313        "verb": "GET",
4314        "resources": null
4315    },
4316    {
4317        "id": "list_comments_for_entry",
4318        "route": "/sites/:site_id/entries/:entry_id/comments",
4319        "verb": "GET",
4320        "resources": null
4321    },
4322    {
4323        "id": "create_comment",
4324        "route": "/sites/:site_id/entries/:entry_id/comments",
4325        "verb": "POST",
4326        "resources": [
4327            "comment"
4328        ]
4329    },
4330    {
4331        "id": "create_reply_comment",
4332        "route": "/sites/:site_id/entries/:entry_id/comments/:comment_id/replies",
4333        "verb": "POST",
4334        "resources": [
4335            "comment"
4336        ]
4337    },
4338    {
4339        "id": "get_comment",
4340        "route": "/sites/:site_id/comments/:comment_id",
4341        "verb": "GET",
4342        "resources": null
4343    },
4344    {
4345        "id": "update_comment",
4346        "route": "/sites/:site_id/comments/:comment_id",
4347        "verb": "PUT",
4348        "resources": [
4349            "comment"
4350        ]
4351    },
4352    {
4353        "id": "delete_comment",
4354        "route": "/sites/:site_id/comments/:comment_id",
4355        "verb": "DELETE",
4356        "resources": null
4357    },
4358    {
4359        "id": "list_trackbacks",
4360        "route": "/sites/:site_id/trackbacks",
4361        "verb": "GET",
4362        "resources": null
4363    },
4364    {
4365        "id": "list_trackbacks_for_entry",
4366        "route": "/sites/:site_id/entries/:entry_id/trackbacks",
4367        "verb": "GET",
4368        "resources": null
4369    },
4370    {
4371        "id": "get_trackback",
4372        "route": "/sites/:site_id/trackbacks/:ping_id",
4373        "verb": "GET",
4374        "resources": null
4375    },
4376    {
4377        "id": "update_trackback",
4378        "route": "/sites/:site_id/trackbacks/:ping_id",
4379        "verb": "PUT",
4380        "resources": [
4381            "trackback"
4382        ]
4383    },
4384    {
4385        "id": "delete_trackback",
4386        "route": "/sites/:site_id/trackbacks/:ping_id",
4387        "verb": "DELETE",
4388        "resources": null
4389    },
4390    {
4391        "id": "upload_asset",
4392        "route": "/sites/:site_id/assets/upload",
4393        "verb": "POST",
4394        "resources": null
4395    },
4396    {
4397        "id": "list_permissions_for_user",
4398        "route": "/users/:user_id/permissions",
4399        "verb": "GET",
4400        "resources": null
4401    },
4402    {
4403        "id": "publish_entries",
4404        "route": "/publish/entries",
4405        "verb": "GET",
4406        "resources": null
4407    },
4408    {
4409        "id": "get_stats_provider",
4410        "route": "/sites/:site_id/stats/provider",
4411        "verb": "GET",
4412        "resources": null
4413    },
4414    {
4415        "id": "list_stats_pageviews_for_path",
4416        "route": "/sites/:site_id/stats/path/pageviews",
4417        "verb": "GET",
4418        "resources": null
4419    },
4420    {
4421        "id": "list_stats_visits_for_path",
4422        "route": "/sites/:site_id/stats/path/visits",
4423        "verb": "GET",
4424        "resources": null
4425    },
4426    {
4427        "id": "list_stats_pageviews_for_date",
4428        "route": "/sites/:site_id/stats/date/pageviews",
4429        "verb": "GET",
4430        "resources": null
4431    },
4432    {
4433        "id": "list_stats_visits_for_date",
4434        "route": "/sites/:site_id/stats/date/visits",
4435        "verb": "GET",
4436        "resources": null
4437    }
4438]
4439
4440    );
4441});
4442
4443window.MT         = window.MT || {};
4444window.MT.DataAPI = window.MT.DataAPI || DataAPI;
4445window.MT.DataAPI['v' + DataAPI.version] = DataAPI;
4446
4447
4448return DataAPI;
4449
4450}));

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.