| Current File : /home/jvzmxxx/wiki1/extensions/Flow/vendor/modules/Storer.js |
/*!
* Storer.js is a fallback-reliant, HTML5 Storage-based storage system.<br/>
* <br/>
* All of its storage subsystems implement getItem, setItem, removeItem, clear, key, and length, as the HTML5 Web
* Storage specification is written, with some enhancements on them, and slight deviations on memory/cookieStorage.<br/>
* <br/>
* It piggybacks on the real HTML5 storage when available, and creates the additional functionality of being able to
* prepend a 'prefix' to all key names automatically (see initStorer params). This is useful for projects where you
* would like to use Storage without worrying about name collisions.<br/>
* <br/>
* It _always_ returns every type of storage, and falls back to others, as listed below. In the worst-case scenario,
* all the storage subsystems are instances of memoryStorage, which means no persistance is available, but that no code
* will break while performing actions on the current page.<br/>
* <br/>
* The fallbacks are as follows:<br/>
* localStorage = localStorage || userData || cookieStorage || memoryStorage<br/>
* sessionStorage = sessionStorage || window.name || memoryStorage<br/>
* cookieStorage = cookieStorage || memoryStorage<br/>
* memoryStorage = memoryStorage<br/>
* <br/>
* cookieStorage also supports an additional 'global' Boolean argument on all of its methods, allowing you to escape
* out of the 'prefix' defined, so that you may use it to fetch general cookies as well.<br/>
* <br/>
* initStorer is called, takes a callback function, which will return the storage subsystems.<br/>
* This is necessary because the Internet Explorer fallback for localStorage is userData, which needs to be able to
* insert an element into the document before proceeding. On any modern or non-IE browser, the callback function is
* triggered synchronously and immediately.<br/>
* <br/>
* Note: for IE6-7 compatibility, initStorer requires a function called domReady, or uses jQuery(document).ready if available.<br/>
* <br/>
* Here is a cat. =^.^= His name is Frisbee.
* <br/>
*
* @copyright Viafoura, Inc. <viafoura.com>
* @author Shahyar G <github.com/shahyar>, originally for <github.com/viafoura>
* @license CC-BY 3.0 <creativecommons.org/licenses/by/3.0>: Keep @copyright, @author intact.
*
* @example
* initStorer(function (Storer) {
* cookieStorage = Storer.cookieStorage;
* memoryStorage = Storer.memoryStorage;
* sessionStorage = Storer.sessionStorage;
* localStorage = Storer.localStorage;
* }, { 'prefix': '_MyStorage_' });
*/
/**
* This will return an object with each of the storage types.
* The callback will fire when all of the necessary types have been created, although it's really only necessary
* for Internet Explorer's userData storage, which requires domReady to begin.
*
* @author Shahyar G <github.com/shahyar>, originally for Viafoura, Inc. <viafoura.com>
* @param {Function} [callback]
* @param {Object} [params]
* {String} [prefix=''] automatic key prefix for sessionStorage and localStorage
* {String} [default_domain=''] default domain for cookies
* {String} [default_path=''] default path for cookies
* {Boolean} [no_cookie_fallback=false] If true, do not use cookies as fallback for localStorage
* @return {Object} {cookieStorage, localStorage, memoryStorage, sessionStorage}
* @version 0.1.3
*/
function initStorer(callback, params) {
"use strict";
var _TESTID = '__SG__',
top = window,
PREFIX = (params = Object.prototype.toString.call(callback) === "[object Object]" ? callback : (params || {})).prefix || '',
NO_COOKIE_FALLBACK = params.no_cookie_fallback || false,
_callbackNow = true,
cookieStorage, localStorage, memoryStorage, sessionStorage;
if (params === callback) {
// Allow passing params without callback
callback = null;
}
// get top within cross-domain limit if we're in an iframe
try { while (top !== top.top) { top = top.top; } } catch (e) {}
/**
* Returns result.value if result has ._end key, or returns result entirely otherwise.
* Returns null when: result is null or undefined, or end && end > current timestamp.
* @param {String|Number|Date|null|undefined} end
* @param {*} result
* @param {Function} remove_callback
* @param {String} remove_callback_key
* @returns {*}
* @private
*/
function _checkEnd(end, result, remove_callback, remove_callback_key) {
if (result === null || result === undefined || (end && parseInt(+new Date() / 1000, 10) > parseInt(end, 10))) {
// Remove this key from the data set
remove_callback(remove_callback_key);
// Return nothing
return null;
}
// Return the actual data
return result._end !== undefined ? result.value : result;
}
/**
* Parses str into JSON object, but also handles backwards compatibility with 0.0.4 when data was not automatically
* JSONified. If data._end exists, also runs _checkEnd. When not a valid JSON object, returns str back.
* @param {String|*} str
* @param {Function} [remove_callback]
* @param {String} [callback_key]
* @returns {*}
* @private
*/
function _getJSON(str, remove_callback, callback_key) {
try {
var obj = str && JSON.parse(str);
if (obj) {
// Backwards compatibility for 0.0.4, when _end did not exist
if (obj._end !== undefined) {
// Check for expiry
return _checkEnd(obj._end, obj.value, remove_callback, callback_key);
}
return obj;
}
} catch (e) {}
// Non-JSON data (0.0.4)
return str;
}
/**
* Puts data and end (standardized to seconds) in an object, and returns it for use.
* If end is valid and end > now, data = null, and remove_callback is called,
* otherwise, set_callback is called.
* @param {Object|*} data
* @param {String|Number|Date} [end]
* @param {Function} [set_callback]
* @param {Function} [remove_callback]
* @param {String} [callback_key]
* @param {Boolean} [json]
* @returns {*}
* @private
*/
function _storeEnd(data, end, set_callback, remove_callback, callback_key, json) {
var now = parseInt(+new Date() / 1000, 10);
switch (typeof end) {
case "number":
// Max-age, although we allow end=0 to mimic 0 for cookies
end = end && parseInt(now + end, 10);
break;
case "string":
// timestamp or Date string
end = end.length > 4 && "" + parseInt(end, 10) === end ? parseInt(end, 10) : parseInt(+new Date(end) / 1000, 10);
break;
case "object":
if (end.toGMTString) {
// Date object
end = parseInt(+end / 1000, 10);
}
break;
default:
end = null;
}
data = { value: end && now > end ? null : data, _end: end || null };
if (data.value === null || data.value === undefined) {
// Automatically expire this item
remove_callback && remove_callback(callback_key);
} else if (json) {
// Set the data with JSON
set_callback && set_callback(callback_key, JSON.stringify(data._end ? data : data.value));
} else {
// Set the data
set_callback && set_callback(callback_key, data._end ? data : data.value);
}
return data;
}
/**
* Clears expired data from each storage subsystem.
* @private
*/
function _clearExpired() {
var i, j, key;
// Iterate over every storage subsystem
for (i in _returnable) {
// Ignore memoryStorage, as it doesn't have anything to expire
if (_returnable.hasOwnProperty(i) && i.charAt(0) !== '_' && _returnable[i].STORE_TYPE !== 'memoryStorage') {
j = 0;
// Iterate over every key in this subsystem
while ((key = _returnable[i].key(j++))) {
// getItem automatically handles removing expired items
_returnable[i].getItem(key);
}
}
}
}
/**
* A hack for Safari's inability to extend a class with Storage.
* @param {String} name
* @param {Storage} StoreRef
* @return {Object}
*/
function _createReferencedStorage(name, StoreRef) {
var store = {
STORE_TYPE: 'ref' + name,
key: function (key) {
return StoreRef.key(key);
},
getItem: function (key) {
return StoreRef.getItem(key);
},
setItem: function (key, value, end) {
return StoreRef.setItem(key, value, end);
},
removeItem: function (key) {
return StoreRef.removeItem(key);
},
clear: function () {
return StoreRef.clear();
}
};
Object.defineProperty(store, "length", { get: function () { return StoreRef.length; } });
return store;
}
/**
* A hack for IE8's inability to extend a class with Storage. We use a DOM property getter to apply length.
* @param {String} name
* @param {Storage} StoreRef
* @return {Object}
*/
function _createDOMStorage(name, StoreRef) {
var store = document.createElement('div');
store.STORE_TYPE = 'DOM' + name;
store.key = function (key) {
try {
return StoreRef.key(key);
} catch (e) { return null; } // IE8 throws an exception on nonexistent keys
};
store.getItem = function (key) {
return StoreRef.getItem(key);
};
store.setItem = function (key, value, end) {
return StoreRef.setItem(key, value, end);
};
store.removeItem = function (key) {
return StoreRef.removeItem(key);
};
store.clear = function () {
return StoreRef.clear();
};
Object.defineProperty(store, "length", { get: function () { return StoreRef.length; } });
return store;
}
/**
* Amends getItem and setItem to support expiry times for HTML5 Storage.
* @param {Object|Storage} StoreRef
* @return {Object}
* @private
*/
function _adjustHTML5Storage(StoreRef) {
var _getItem = StoreRef.getItem,
_setItem = StoreRef.setItem,
_removeItem = StoreRef._removeItem || StoreRef.removeItem,
_removeItemCallback = function (key) {
_removeItem(key);
};
StoreRef.getItem = function (key) {
return _getJSON(_getItem(key), _removeItemCallback, key);
};
StoreRef.setItem = function (key, data, end) {
return _storeEnd(
data,
end,
function (key, value) {
_setItem(key, value);
},
_removeItemCallback,
key,
true
);
};
return StoreRef;
}
/**
*
* @param {Object|Storage} StoreRef
* @returns {*}
* @private
*/
function _assignPrefix(StoreRef) {
// Use the rest of the object natively without a prefix
// memoryStorage doesn't need prefixes
if (!PREFIX || StoreRef.STORE_TYPE === 'memoryStorage') {
return StoreRef;
}
// Rewire functions to use a prefix and avoid collisions
// @todo Rewire length for prefixes as well
StoreRef._getItem = StoreRef.getItem;
StoreRef._setItem = StoreRef.setItem;
StoreRef._removeItem = StoreRef.removeItem;
StoreRef._key = StoreRef.key;
/** Variable # of items in Storage.
* @const int length
* @memberof sessionStorage
* @memberof localStorage */
/**
* Returns an item from the current type of Storage.
* @param {String} key
* @returns {*}
* @memberof sessionStorage
* @memberof localStorage
*/
StoreRef.getItem = function (key) {
return StoreRef._getItem(PREFIX + key);
};
/**
* Sets an item in the current type of Storage.
* end is expiry: Number = seconds from now, String = date string for Date(), or Date object.
* @param {String} key
* @param {*} data
* @param {int|String|Date} [end]
* @memberof sessionStorage
* @memberof localStorage
*/
StoreRef.setItem = function (key, data, end) {
return StoreRef._setItem(PREFIX + key, data, end);
};
/**
* Removes key from the current Storage instance, if it has been set.
* @param {String} key
* @memberof sessionStorage
* @memberof localStorage
*/
StoreRef.removeItem = function (key) {
return StoreRef._removeItem(PREFIX + key);
};
StoreRef._key = StoreRef.key;
/**
* Gets the key (if any) at index, from the current Storage instance.
* @param {int} index
* @returns {String|null}
* @memberof sessionStorage
* @memberof localStorage
*/
StoreRef.key = function (index) {
if ((index = StoreRef._key(index)) !== undefined && index !== null) {
// Chop off the index
return index.indexOf(PREFIX) === 0 ? index.substr(PREFIX.length) : index;
}
return null;
};
if (StoreRef.STORE_TYPE !== 'cookieStorage') {
// cookieStorage has its own clear which supports prefixes
/**
* Removes all the current keys from this Storage instance.
* @memberof sessionStorage
* @memberof localStorage
*/
StoreRef.clear = function () {
for (var i = StoreRef.length, key; i--;) {
if ((key = StoreRef._key(i)).indexOf(PREFIX) === 0) {
StoreRef._removeItem(key);
}
}
};
} else {
// cookieStorage is the only one which implements hasItem
if (StoreRef.hasItem) {
StoreRef._hasItem = StoreRef.hasItem;
StoreRef.hasItem = function (key) {
return StoreRef._hasItem(PREFIX + key);
};
}
}
return StoreRef;
}
/**
* Returns memoryStorage on failure
* @param {String} [cookie_prefix] An additional prefix, useful for isolating fallbacks for local/sessionStorage.
* @return {cookieStorage|memoryStorage}
*/
function _createCookieStorage(cookie_prefix) {
cookie_prefix = (cookie_prefix || '');
var _cookiergx = new RegExp("(?:^|;)\\s*" + cookie_prefix + PREFIX + "[^=;]+\\s*(?:=[^;]*)?", "g"),
_nameclean = new RegExp("^;?\\s*" + cookie_prefix + PREFIX),
_cookiergxGlobal = new RegExp("(?:^|;)\\s*[^=;]+\\s*(?:=[^;]*)?", "g"),
_namecleanGlobal = new RegExp("^;?\\s*"),
_expire = (new Date(1979)).toGMTString(),
/**
* @namespace cookieStorage
* @memberof Storer
* @public
* @global
*/
_cookieStorage = {
/** @const String STORE_TYPE
* @default "cookieStorage"
* @memberof cookieStorage */
STORE_TYPE: 'cookieStorage',
/** Default domain to use in cookieStorage.setItem (set by initStorer)
* @const String DEFAULT_DOMAIN
* @memberof cookieStorage */
DEFAULT_DOMAIN: escape(params.default_domain || ''),
/** Default path to use in cookieStorage.setItem (set by initStorer)
* @const String DEFAULT_PATH
* @memberof cookieStorage */
DEFAULT_PATH: escape(params.default_path || ''),
/** Variable # of items in storage
* @const int length
* @memberof cookieStorage */
length: 0,
/**
* Returns the cookie key at idx.
* @param {int} idx
* @param {Boolean} [global=false] Omits prefix.
* @return {*}
* @memberof cookieStorage
*/
key: function (idx, global) {
var cookies = _cookieStorage.getAll(false, global);
return cookies[idx] ? cookies[idx].key : undefined;
},
/**
* Clears all cookies for this prefix.
* @param {Boolean} [global=false] true omits the prefix, and erases all cookies
* @memberof cookieStorage
*/
clear: function (global) {
var cookies = _cookieStorage.getAll(false, global),
i = cookies.length;
while (i--) {
// Don't use static _removeItemFn reference, because cookieStorage.clear is not handled by _assignPrefix
_cookieStorage.removeItem(cookies[i].key);
}
},
/**
* Returns an Array of Objects of key-value pairs, or an Object with properties-values plus length (as_object).
* @param {Boolean} [as_object=false] true returns a single object of key-value pairs
* @param {Boolean} [global=false] true gets all cookies, omitting the default prefix
* @return {Object[]|Object}
* @memberof cookieStorage
*/
getAll: function (as_object, global) {
var cleaner = global ? _namecleanGlobal : _nameclean,
matches = document.cookie.match(global ? _cookiergxGlobal : _cookiergx) || [],
i = matches.length, _cache;
if (as_object === true) { // object of properties/values
for (_cache = {length: i}; i--;) {
_cache[unescape((matches[i] = matches[i].split('='))[0].replace(cleaner, ''))] = matches[i][1];
}
} else { // array of key/value objects
for (_cache = []; i--;) {
_cache.push({ key: unescape((matches[i] = matches[i].split('='))[0].replace(cleaner, '')), value: matches[i][1] });
}
}
return _cache;
},
/**
* Get a cookie by name.
* @param {String} key
* @param {Boolean} [global=false] true omits the prefix, and searches for a match "globally"
* @return {String}
* @memberof cookieStorage
*/
getItem: function (key, global) {
if (!key || !_hasItemFn(key, global)) {
return null;
}
return ((global = document.cookie.match(new RegExp('(?:^|;) *' + escape((global ? '' : cookie_prefix) + key) + '=([^;]*)(?:;|$)'))), global && global[0] ? unescape(global[1]) : null);
},
/**
* cookieStorage.setItem(key, value, end, path, domain, is_secure);
* @param {String} key name of the cookie
* @param {String} value value of the cookie;
* @param {Number|String|Date} [end] max-age in seconds (e.g., 31536e3 for a year) or the
* expires date in GMTString format or in Date Object format; if not specified it will expire at the end of session;
* @param {String} [path] e.g., "/", "/mydir"; if not specified, defaults to the current path of the current document location;
* @param {String} [domain] e.g., "example.com", ".example.com" (includes all subdomains) or "subdomain.example.com"; if not
* specified, defaults to the host portion of the current document location;
* @param {Boolean} [is_secure=false] cookie will be transmitted only over secure protocol as https;
* @param {Boolean} [global=false] true omits prefix, defines the cookie "globally"
* @return {Boolean}
* @memberof cookieStorage
**/
setItem: function (key, value, end, path, domain, is_secure, global) {
if (!key || key === 'expires' || key === 'max-age' || key === 'path' || key === 'domain' || key === 'secure') {
return false;
}
var sExpires = "",
store_end = _storeEnd(value, end);
if (store_end._end !== null) {
sExpires = "; expires=" + (new Date(store_end._end * 1000)).toGMTString();
}
if (store_end.value !== null && value !== undefined && value !== null) {
domain = (domain = typeof domain === 'string' ? escape(domain) : _cookieStorage.DEFAULT_DOMAIN) ? '; domain=' + domain : '';
path = (path = typeof path === 'string' ? escape(path) : _cookieStorage.DEFAULT_PATH) ? '; path=' + path : '';
document.cookie = escape((global ? '' : cookie_prefix) + key) + '=' + escape(value) + sExpires + domain + path + (is_secure ? '; secure' : '');
_updateLength();
return true;
}
return _removeItemFn(key, domain, path, is_secure, global);
},
/**
* Get a cookie by name
* @param {String} key
* @param {String} [path]
* @param {String} [domain]
* @param {Boolean} [is_secure]
* @param {Boolean} [global=false] Omits prefix.
* @memberof cookieStorage
*/
removeItem: function (key, domain, path, is_secure, global) {
if (!key || !_hasItemFn(key, global)) {
return;
}
domain = (domain = typeof domain === 'string' ? escape(domain) : _cookieStorage.DEFAULT_DOMAIN) ? '; domain=' + domain : '';
path = (path = typeof path === 'string' ? escape(path) : _cookieStorage.DEFAULT_PATH) ? '; path=' + path : '';
document.cookie = escape((global ? '' : cookie_prefix) + key) + '=; expires=' + _expire + domain + path + (is_secure ? '; secure' : '');
_updateLength();
},
/**
* Returns true if a cookie with that name was found, false otherwise
* @param {String} key
* @param {Boolean} [global=false] Omits prefix.
* @param {Boolean}
* @memberof cookieStorage
*/
hasItem: function (key, global) {
return (new RegExp('(?:^|;) *' + escape((global ? '' : cookie_prefix) + key) + '=')).test(document.cookie);
}
},
// Keep backups of these functions, as they may be overriden by _assignPrefix
_removeItemFn = _cookieStorage.removeItem,
_hasItemFn = _cookieStorage.hasItem;
/**
* Updates cookieStorage.length on update
* @private
*/
function _updateLength() {
_cookieStorage.length = _cookieStorage.getAll().length;
}
_cookieStorage.setItem(_TESTID, 4);
if (_cookieStorage.getItem(_TESTID) == 4) {
_cookieStorage.removeItem(_TESTID);
return _assignPrefix(_cookieStorage);
}
return _createMemoryStorage();
}
/**
* Returns a memoryStorage object. This is a constructor to be reused as a fallback on sessionStorage & localStorage
* @return {memoryStorage}
*/
function _createMemoryStorage() {
var _data = {}, // key : data
_keys = [], // _keys key : _ikey key
_ikey = {}; // _ikey key : _keys key
/**
* @namespace memoryStorage
*/
var _memoryStorage = {
/** @const String STORE_TYPE
* @default "memoryStorage"
* @memberof memoryStorage */
STORE_TYPE: 'memoryStorage',
/** Variable # of items in storage
* @const int length
* @memberof memoryStorage */
length: 0,
/**
* Get key name by id
* @param {int} i
* @return {String|null}
* @memberof memoryStorage
*/
key: function (i) {
return _keys[i];
},
/**
* Get an item
* @param {String} key
* @return {*}
* @memberof memoryStorage
*/
getItem: function (key) {
return _checkEnd(_data[key] && _data[key]._end, _data[key], _memoryStorage.removeItem, key);
},
/**
* Set an item
* @param {String} key
* @param {String} data
* @param {String|Number|Date} [end]
* @memberof memoryStorage
*/
setItem: function (key, data, end) {
if (data !== null && data !== undefined) {
_ikey[key] === undefined && (_ikey[key] = (_memoryStorage.length = _keys.push(key)) - 1);
return (_data[key] = _storeEnd(data, end)).value;
}
return _memoryStorage.removeItem(key);
},
/**
* Removes an item
* @param {String} key
* @return {Boolean}
* @memberof memoryStorage
*/
removeItem: function (key) {
var was = _data[key] !== undefined;
if (_ikey[key] !== undefined) {
// re-reference all the keys because we've removed an item in between
for (var i = _keys.length; --i > _ikey[key];) {
_ikey[_keys[i]]--;
}
_keys.splice(_ikey[key], 1);
delete _ikey[key];
}
delete _data[key];
_memoryStorage.length = _keys.length;
return was;
},
/**
* Clears memoryStorage
* @memberof memoryStorage
*/
clear: function () {
for (var i in _data) {
if (_data.hasOwnProperty(i)) {
delete _data[i];
}
}
_memoryStorage.length = _keys.length = 0;
_ikey = {};
}
};
return _memoryStorage;
}
/**
* Returns a nameStorage object. This constructor is designed to be a fallback for sessionStorage in IE7 and under.
* It uses window.name and RC4 encryption on a per-domain basis. Inspired by LSS by Andrea Giammarchi.
* @param {DOMWindow} [win=top]
* @return {nameStorage}
*/
function _createNameStorage(win) {
if (!win) {
win = top;
}
/** RC4 Stream Cipher
* http://www.wisdom.weizmann.ac.il/~itsik/RC4/rc4.html
* -----------------------------------------------
* @description A quick stream cipher to encode & decode any string, using a random key of up to 256 bytes.
*
* @author Ported to JavaScript by Andrea Giammarchi
* @license MIT-style license
* @blog http://webreflection.blogspot.com/
* @version 1.2.1
*/
var RC4 = (function (String, fromCharCode, random) {
return {
/** RC4.decode(key:String, data:String):String
* @description given a data string encoded with the same key
* generates original data string.
* @param {String} key key precedently used to encode data
* @param {String} data data encoded using same key
* @return {String} decoded data
* @private
*/
decode: function (key, data) {
return this.encode(key, data);
},
/** RC4.encode(key:String, data:String):String
* @description encode a data string using provided key
* @param {String} key key to use for this encoding
* @param {String} data data to encode
* @return {String} encoded data. Will require same key to be decoded
* @private
*/
encode: function (key, data) {
for (var length = key.length, len = data.length, decode = [], a = [],
i = 0, j = 0, k = 0, l = 0, $;
i < 256;
i++
) {
a[i] = i;
}
for (i = 0; i < 256; i++) {
j = (j + ($ = a[i]) + key.charCodeAt(i % length)) % 256;
a[i] = a[j];
a[j] = $;
}
for (j = 0; k < len; k++) {
i = k % 256;
j = (j + ($ = a[i])) % 256;
length = a[i] = a[j];
a[j] = $;
decode[l++] = data.charCodeAt(k) ^ a[(length + $) % 256];
}
return fromCharCode.apply(String, decode);
},
/** RC4.key(length:Number):String
* @description generate a random key with arbitrary length
* @param {Number} length The length of the generated key
* @return {String} a randomly generated key
* @private
*/
key: function (length) {
for (var i = 0, key = []; i < length; i++) {
key[i] = 1 + ((random() * 255) << 0);
}
return fromCharCode.apply(String, key);
}
};
// I like to freeze stuff in interpretation time
// it makes things a bit safer when obtrusive libraries
// are around
}(String, String.fromCharCode, Math.random)),
// Opera will store on every set, because it has no onbeforeunload
is_opera = Object.prototype.toString.call(window.opera) === "[object Opera]",
// Key used for this domain
KEY;
// Try to fetch an old key
try {
KEY = decodeURI(cookieStorage.getItem('.sessionStorageKey'));
} catch (e) {}
// Generate an encryption key if we don't have a valid one.
if (!KEY || KEY.length !== STRENGTH) {
KEY = RC4.key(STRENGTH);
cookieStorage.setItem('.sessionStorageKey', encodeURI(KEY));
}
// Domain used for prefixing keys
var DOMAIN = win.document.domain,
// Encrypted domain
EDOMAIN = RC4.encode(KEY, DOMAIN),
// Start of Header
SOH = '#' + String.fromCharCode(1) + 'STOR/' + EDOMAIN,
// End of Transmission
EOT = EDOMAIN + '/STOR' + String.fromCharCode(4) + '#',
// Start of Text
STX = String.fromCharCode(2) + ';',
// End of Transmission Block
ETB = String.fromCharCode(23) + ';',
// End of Text
ETX = ';' + String.fromCharCode(3),
// Key strength in bytes (32 = 256-bit)
STRENGTH = 32,
// Lengths
SOHl = SOH.length,
EOTl = EOT.length,
STXl = STX.length,
ETBl = ETB.length,
ETXl = ETX.length,
// Data storage by key name
_dataObject = {},
// Key storage by index
_dataArray = [],
/**
* Cannot be accessed directly, and in fact appears as Storer.sessionStorage when in use.
* You can, however, know that it is in use when sessionStorage.STORE_TYPE === 'name'.
* @namespace nameStorage
*/
_nameStorage = {
/** @const String STORE_TYPE
* @default "name"
* @memberof nameStorage */
STORE_TYPE: 'name',
/** Number of items in storage */
length: 0,
/**
* Get an item key by its index
* @param {int} index
* @return {String|null} key
*/
key: function (index) {
return _dataArray[index];
},
/**
* Get an item by its key
* @param {String} key
* @return {String|null} data
*/
getItem: function (key) {
return _checkEnd(_dataObject[key] && _dataObject[key]._end, _dataObject[key], _removeItemFn, key);
},
/**
* Set an item by key
* @param {String} key
* @param {String} data
* @param {String|Number|Date} [end]
*/
setItem: function (key, data, end) {
var store_end = _storeEnd(data, end)._end;
if (store_end.value === null) {
return _removeItemFn(key);
}
if (_dataObject[key]) {
// Update an existing key's value
_dataObject[key].value = data;
_dataObject[key]._end = store_end._end;
} else {
// Store this item by its key
_dataObject[key] = {
value: data,
// For new items, increment the length property
index: (_nameStorage.length = _dataArray.push(key)) - 1,
_end: store_end._end
};
}
if (!store_end._end) {
// Save some space
delete _dataObject[key]._end;
}
is_opera && _write();
return data;
},
/**
* Remove an item by key
* @param {String} key
*/
removeItem: function (key) {
if (_dataObject[key]) {
// Validity check on _dataArray just in case, to prevent corruption
if (_dataArray[_dataObject[key].index] === key) {
// Remove the stored index
_dataArray.splice(_dataObject[key].index, 1);
// Update length property
_nameStorage.length = _dataArray.length;
// Update all other indices to point to their new locations
for (var i = _dataObject[key].index, len = _dataArray.length; i < len; i++) {
_dataObject[_dataArray[i]].index--;
}
}
// Delete the stored data
delete _dataObject[key];
is_opera && _write();
return true;
}
return false;
},
/**
* Completely empies storage
*/
clear: function () {
_dataArray.length = 0;
_dataObject = {};
is_opera && _write();
}
},
// Keep backups of these functions, as they may be overriden by _assignPrefix
_removeItemFn = _nameStorage.removeItem;
// Format: FULLCONTENT: [SOH]CONTENTPIECECONTENTPIECE...[EOT]
// Format: CONTENTPIECE: [STX]keylength[:]contentlength[ETB]key[ETB]content[ETX]
/*
win.name = SOH
+ STX + 5 + ':' + 5 + ETB + 'hello' + ETB + 'world' + ETX
+ STX + 2 + ':' + 10 + ETB + 'my' + ETB + 'abcd?fhi~k' + ETX
+ EOT;
*/
/**
* Writes _dataObject's keys and values to window.name.
*/
function _write() {
var str = win.name,
start = str.indexOf(SOH),
end = str.indexOf(EOT),
i = _dataArray.length;
// Remove any previous storage on window.name
if (start > -1 && end > start) {
win.name = str.slice(0, start) + str.slice(end + EOTl);
}
for (str = ''; i--;) {
if (_dataObject[_dataArray[i]] && _dataObject[_dataArray[i]].value) {
// STX --------KEY LENGTH--------- : ----------------CONTENT LENGTH---------------- ETB -----KEY----- ETB -------------CONTENT------------ ETX
str += STX + ('' + _dataArray[i]).length + ':' + ('' + _dataObject[_dataArray[i]].value).length + ETB + _dataArray[i] + ETB + _dataObject[_dataArray[i]].value + ETX;
}
}
// Encrypt the contents and write it to window.name
win.name += SOH + encodeURI(RC4.encode(KEY, str)) + EOT;
}
/**
* This function processes window.name, tries to find matching keys, and stores them.
*/
function _initialize() {
var str = win.name,
start = str.indexOf(SOH),
end = str.indexOf(EOT),
last_index = 0,
item_key = '',
item_klen = 0,
item_clen = 0;
if (start > -1 && end > start) {
// Remove it from the window to append it later. This helps with invalid data.
// eg. ABC;def;HIJ -> ABCHIJ
win.name = str.slice(0, start) + str.slice(end + EOTl);
// Use the rest of the string for storage parsing
str = RC4.decode(KEY, decodeURI(str.slice(start + SOHl, end)));
// Find the start of an item
while ((start = str.indexOf(STX, last_index)) !== -1) {
last_index = start + STXl; // move index to start of item, past STX
// Find out how long this item is, and its key name
if ((end = str.indexOf(ETB, last_index)) !== -1) {
// [1] content length, [0] key length
item_klen = str.slice(last_index, end).split(':');
item_clen = parseInt(item_klen[1], 10);
item_klen = parseInt(item_klen[0], 10);
last_index = end + ETBl; // move index to start of item key, past length-ETB
// Validate: Make sure ETB is immediately after the key
if ((end = str.indexOf(ETB, last_index)) === last_index + item_klen) {
// Parse out this item's key
item_key = str.substr(last_index, item_klen);
last_index = end + ETBl; // move index to start of item content, past key-ETB
// Validate: Make sure ETX is immediately after the content
if ((end = str.indexOf(ETX, last_index)) === last_index + item_clen) {
// Store this item
_nameStorage.setItem(item_key, str.substr(last_index, item_clen));
}
}
}
}
}
// _write data onbeforeunload
if (win.addEventListener) {
win.addEventListener('beforeunload', _write, true);
} else if (win.attachEvent) {
win.attachEvent('onbeforeunload', _write);
}
}
try {
_initialize();
} catch (e) {
}
return _nameStorage;
}
if (callback) {
// Create a callback wrapper to empty expired data preemptively
callback = (function (callback) {
return function () {
callback(_returnable);
setTimeout(_clearExpired, 100); // delay expiration
};
}(callback));
}
// Return this stuff
var _returnable = {
'cookieStorage': null,
'localStorage': null,
'memoryStorage': null,
'sessionStorage': null,
'_createCookieStorage': _createCookieStorage,
'_createMemoryStorage': _createMemoryStorage
};
/**
* @instanceof cookieStorage
*/
_returnable.cookieStorage = cookieStorage = _createCookieStorage();
/**
* @instanceof memoryStorage
*/
_returnable.memoryStorage = memoryStorage = _createMemoryStorage();
/**
* @namespace sessionStorage
* @mixes localStorage
*/
_returnable.sessionStorage = sessionStorage = (function () {
// Grab sessionStorage from top window
var _sessionStorage = top.sessionStorage;
// Try to use original sessionStorage
if (_sessionStorage) {
try {
// Test to make sure it works and isn't full
_sessionStorage.setItem(_TESTID, 1);
_sessionStorage.removeItem(_TESTID);
// Now clone sessionStorage so that we may extend it with our own methods
var _tmp = function () {
};
_tmp.prototype = _sessionStorage;
// jshint -W055
_tmp = new _tmp();
try {
if (_tmp.getItem) {
_tmp.setItem(_TESTID, 2);
_tmp.removeItem(_TESTID);
}
} catch (e) {
// Firefox 14+ throws a security exception when wrapping a native class
_tmp = null;
}
if (_tmp && !_tmp.getItem) {
// Internet Explorer 8 does not inherit the prototype here. We can hack around it using a DOM object
_sessionStorage = _adjustHTML5Storage(_createDOMStorage('sessionStorage', _sessionStorage));
} else if (!_tmp || Object.prototype.toString.apply(Storage.prototype) === '[object StoragePrototype]') {
// Safari throws a type error when extending with Storage
_sessionStorage = _adjustHTML5Storage(_createReferencedStorage('sessionStorage', _sessionStorage));
} else {
_sessionStorage = _adjustHTML5Storage(_tmp);
}
} catch (e) {
_sessionStorage = null;
}
}
// Build one
if (!_sessionStorage) {
try {
// instantiate nameStorage
_sessionStorage = _createNameStorage();
// Test it
_sessionStorage.setItem(_TESTID, 2);
if (_sessionStorage.getItem(_TESTID) == 2) {
_sessionStorage.removeItem(_TESTID);
} else {
_sessionStorage = null;
}
} catch (e) {
_sessionStorage = null;
}
// Last ditch effort: use memory storage
if (!_sessionStorage) {
_sessionStorage = _createMemoryStorage();
}
}
// cookieStorage already calls _assignPrefix
return _sessionStorage.STORE_TYPE === 'cookieStorage' ? _sessionStorage : _assignPrefix(_sessionStorage);
}());
/**
* @namespace localStorage
*/
_returnable.localStorage = localStorage = (function () {
var _localStorage;
if (top.localStorage || top.globalStorage) {
try {
_localStorage = top.localStorage || top.globalStorage[location.hostname];
_localStorage.setItem(_TESTID, 1);
_localStorage.removeItem(_TESTID);
// Now clone sessionStorage so that we may extend it with our own methods
var _tmp = function () {};
_tmp.prototype = _localStorage;
// jshint -W055
_tmp = new _tmp();
try {
if (_tmp.getItem) {
_tmp.setItem(_TESTID, 2);
_tmp.removeItem(_TESTID);
}
} catch (e) {
// Firefox 14+ throws a security exception when wrapping a native class
_tmp = null;
}
if (_tmp && !_tmp.getItem) {
// Internet Explorer 8 does not inherit the prototype here. We can hack around it using a DOM object
_localStorage = _adjustHTML5Storage(_createDOMStorage('localStorage', _localStorage));
} else if (!_tmp || Object.prototype.toString.apply(Storage.prototype) === '[object StoragePrototype]') {
// Safari throws a type error when extending with Storage
_localStorage = _adjustHTML5Storage(_createReferencedStorage('localStorage', _localStorage));
} else {
// Spec
_localStorage = _adjustHTML5Storage(_tmp);
}
} catch (e) {
_localStorage = null;
}
}
// Did not work, try alternatives...
// Try userData first
if (!_localStorage) {
_localStorage = (function () {
/**
* @param {String} str
* @return {String}
*/
var _esc = function (str) {
return 'PS' + str.replace(_e, '__').replace(_s, '_s');
},
_e = /_/g,
_s = / /g,
_PREFIX = _esc(PREFIX + 'uData'),
_NAME = _esc('Storer');
if (window.ActiveXObject) {
// Try userData
try {
// Data cache
var _data = {}, // key : data
_keys = [], // _keys key : _ikey key
_ikey = {}, // _ikey key : _keys key
/**
* Cannot be accessed directly, and in fact appears as Storer.localStorage when in use.
* You can, however, know that it is in use when localStorage.STORE_TYPE === 'userData'.
* @namespace userDataStorage */
userData = {
/** @const String STORE_TYPE
* @default "userData"
* @memberof userDataStorage */
STORE_TYPE: 'userData',
/** # of items */
length: 0,
/**
* Returns key of i
* @param {int} i
* @return {String}
*/
key: function (i) {
return _keys[i];
},
/**
* Gets data of key
* @param {String} key
* @return {*}
*/
getItem: function (key) {
var esckey = _esc(key);
return _checkEnd(el.getAttribute('_end_' + esckey), el.getAttribute(esckey), _removeItemFn, key);
},
/**
* Sets key to data
* @param {String} key
* @param {String} data
* @param {String|Number|Date} [end]
*/
setItem: function (key, data, end) {
if (data !== null && data !== undefined) {
var esckey = _esc(key),
store_end = _storeEnd(data, end);
if (store_end.value !== null) {
el.setAttribute(esckey, data);
if (!store_end._end) {
// Save some space
el.removeAttribute('_end_' + esckey);
} else {
el.setAttribute('_end_' + esckey, "" + store_end._end);
}
_ikey[key] === undefined && (_ikey[key] = (userData.length = _keys.push(key)) - 1);
el.save(_PREFIX + _NAME);
return (_data[key] = store_end.value);
}
}
return _removeItemFn(key);
},
/**
* Removes item at key
* @param {String} key
*/
removeItem: function (key) {
var esckey = _esc(key);
el.removeAttribute(esckey);
el.removeAttribute('_end_' + esckey);
if (_ikey[key] !== undefined) {
// re-reference all the keys because we've removed an item in between
for (var i = _keys.length; --i > _ikey[key];) {
_ikey[_keys[i]]--;
}
_keys.splice(_ikey[key], 1);
delete _ikey[key];
}
el.save(_PREFIX + _NAME);
userData.length = _keys.length;
},
/**
* Clears all data
*/
clear: function () {
for (var doc = el.xmlDocument,
attributes = doc.firstChild.attributes,
attr,
i = attributes.length;
0 <= --i;) {
attr = attributes[i];
delete _data[attr.nodeName]; // remove from cache
el.removeAttribute(attr.nodeName); // use the standard DOM properties to remove the item
userData.length--;
}
el.save(_PREFIX + _NAME);
userData.length = _keys.length = 0;
_data = {};
_ikey = {};
}
},
// Keep backups of these functions, as they may be overriden by _assignPrefix
_removeItemFn = userData.removeItem,
_hasItemFn = userData.hasItem;
// Init userData element
var el = document.createElement('input');
el.style.display = 'none';
el.addBehavior('#default#userData');
var fn = (typeof domReady === 'function' ? domReady : (typeof jQuery !== 'undefined' ? jQuery(document).ready : false));
_callbackNow = !fn;
fn && fn(function () {
try {
var bod = document.body || document.getElementsByTagName('head')[0];
bod.appendChild(el);
el.load(_PREFIX + _NAME);
// Test
userData.setItem(_TESTID, 3);
if (userData.getItem(_TESTID) == 3) {
userData.removeItem(_TESTID);
// Good. Parse.
var attr,
// the reference to the XMLDocument
doc = el.xmlDocument,
// the root element will always be the firstChild of the XMLDocument
attributes = doc.firstChild.attributes,
i = -1,
len = attributes.length;
while (++i < len) {
attr = attributes[i];
if (attr.nodeValue !== undefined && attr.nodeValue !== null) {
_ikey[attr.nodeName] = _keys.push(attr.nodeName) - 1;
_data[attr.nodeName] = attr.nodeValue; // use the standard DOM properties to retrieve the key and value
}
}
_returnable.localStorage = localStorage = userData;
callback && callback(_returnable);
} else {
userData = null;
}
} catch (e) {
userData = null;
}
if (!userData) {
_returnable.localStorage = localStorage = _localStorage = NO_COOKIE_FALLBACK ? _createMemoryStorage() : _createCookieStorage('localStorage');
callback && callback(_returnable);
}
});
return userData;
} catch (e) {}
}
}());
}
if (!_localStorage) {
// Try cookie or memory
_localStorage = NO_COOKIE_FALLBACK ? _createMemoryStorage() : _createCookieStorage('localStorage');
}
// cookieStorage already calls _assignPrefix
return _localStorage.STORE_TYPE === 'cookieStorage' ? _localStorage : _assignPrefix(_localStorage);
}());
_callbackNow && callback && callback(_returnable);
return _returnable;
}
window.mediaWiki = window.mediaWiki || {};
mediaWiki.flow = mediaWiki.flow || {};
mediaWiki.flow.vendor = mediaWiki.flow.vendor || {};
mediaWiki.flow.vendor.initStorer = initStorer;