mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
- Fix issue with all local prefs being deleted on "collapse all" - Refactored history display code into galaxy.base
1811 lines
54 KiB
JavaScript
1811 lines
54 KiB
JavaScript
/*!
|
|
* jStore 2.0 - Persistent Client Side Storage
|
|
*
|
|
* Copyright (c) 2010 Eric Garside (http://eric.garside.name/)
|
|
* Dual licensed under:
|
|
* MIT: http://www.opensource.org/licenses/mit-license.php
|
|
* GPLv3: http://www.opensource.org/licenses/gpl-3.0.html
|
|
*
|
|
* ---------------------------
|
|
*
|
|
* jStore Flash Storage Component
|
|
*
|
|
* Copyright (c) 2006 Jeff Lerman (jeff@blip.tv)
|
|
* Licensed under the Creative Commons Attribution 3.0 United States License:
|
|
* http://creativecommons.org/licenses/by/3.0/us
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
/*global Class, window, jQuery, ActiveXObject, google */
|
|
|
|
/*jslint white: true, browser: true, onevar: true, undef: true, eqeqeq: true, bitwise: true, regexp: false, strict: true, newcap: true, immed: true, maxerr: 50, indent: 4 */
|
|
|
|
(function ($, window) {
|
|
|
|
//------------------------------
|
|
//
|
|
// Constants
|
|
//
|
|
//------------------------------
|
|
|
|
//------------------------------
|
|
// Exceptions
|
|
//------------------------------
|
|
|
|
/**
|
|
* An exception thrown by the StorageEngine class whenever its data accessor methods
|
|
* are called before the engine is ready to transact data.
|
|
*/
|
|
var EX_UNSTABLE = 'JSTORE_ENGINE_UNSTABLE',
|
|
|
|
/**
|
|
* An exception thrown by jStore whenever an undefined storage engine is referenced for
|
|
* some task by an invalid JRI (jStore Resource Identifier).
|
|
*/
|
|
EX_UNKNOWN = 'JSTORE_UNKNOWN_ENGINE_REQUESTED',
|
|
|
|
/**
|
|
* An exception thrown by jStore whenever a given flavor of storage is double defined.
|
|
*/
|
|
EX_COLLISION = 'JSTORE_ENGINE_NAMESPACE_COLLISION',
|
|
|
|
/**
|
|
* An exception thrown by jStore whenever a jri is double applied to a resource.
|
|
*/
|
|
EX_DUPLICATE = 'JSTORE_RESOURCE_NAMESPACE_COLLISION',
|
|
|
|
/**
|
|
* An exception thrown by jStore whenever a given flavor of storage has no defined engine.
|
|
*/
|
|
EX_UNAVAILABLE = 'JSTORE_ENGINE_UNAVAILABLE',
|
|
|
|
/**
|
|
* An exception thrown by jStore whenever an invalid flavor type is used.
|
|
*/
|
|
EX_INVALID = 'JSTORE_INVALID_FLAVOR',
|
|
|
|
//------------------------------
|
|
// Regular Expressions
|
|
//------------------------------
|
|
|
|
/**
|
|
* Regular expression to test property values for being JSON.
|
|
*/
|
|
RX_JSON = (function ()
|
|
{
|
|
try
|
|
{
|
|
return new RegExp('^("(\\\\.|[^"\\\\\\n\\r])*?"|[,:{}\\[\\]0-9.\\-+Eaeflnr-u \\n\\r\\t])+?$');
|
|
}
|
|
catch (e)
|
|
{
|
|
return (/^(true|false|null|\[.*\]|\{.*\}|".*"|\d+|\d+\.\d+)$/);
|
|
}
|
|
}()),
|
|
|
|
//------------------------------
|
|
// Storage Flavors
|
|
//------------------------------
|
|
|
|
/**
|
|
* The storage flavor identifier for HTML5 local storage.
|
|
*/
|
|
FLAVOR_LOCAL = 'jstore-html5-local',
|
|
|
|
/**
|
|
* The storage flavor identifier for HTML5 database storage.
|
|
*/
|
|
FLAVOR_SQL = 'jstore-html5-sql',
|
|
|
|
/**
|
|
* The storage flavor identifier for Adobe Flash SharedObject storage.
|
|
*/
|
|
FLAVOR_FLASH = 'jstore-flash',
|
|
|
|
/**
|
|
* The storage flavor identifier for Google Gears storage.
|
|
*/
|
|
FLAVOR_GEARS = 'jstore-google-gears',
|
|
|
|
/**
|
|
* The storage flavor identifier for Internet Explorer storage, available to IE7 and IE6.
|
|
*/
|
|
FLAVOR_MSIE = 'jstore-msie',
|
|
|
|
//------------------------------
|
|
//
|
|
// Property Declaration
|
|
//
|
|
//------------------------------
|
|
|
|
/**
|
|
* The base StorageEngine class which each "storage flavor" will extend to meet the
|
|
* requirements for its specific implementation.
|
|
*/
|
|
StorageEngine,
|
|
|
|
/**
|
|
* The jStore object. Internal to this closure, jStore is referenced by "_". It is
|
|
* exposed to jQuery below, and made publicly accessible through jQuery.jStore
|
|
*/
|
|
_ = {},
|
|
|
|
/**
|
|
* The engines available to jStore for use. These are the class definitions for flavored
|
|
* storage engines.
|
|
*
|
|
* Signature:
|
|
* {
|
|
* <storageFlavor>: <flavoredStorageEngineDefinition>,
|
|
*
|
|
* ...
|
|
* }
|
|
*/
|
|
definitions = {},
|
|
|
|
/**
|
|
* Active engines instantiated by jStore, indexed by their JRI.
|
|
*
|
|
* Signature:
|
|
* {
|
|
* <engineJRI>: <engineInstance>,
|
|
*
|
|
* ...
|
|
* }
|
|
*/
|
|
engines = {},
|
|
|
|
/**
|
|
* If we are going to be using the flash storage engine, we want to postpone the jStore ready event until the jStore
|
|
* isFlashReady flag is also true. This property is set whenever flash is determined to be the storage engine.
|
|
*/
|
|
waitForFlash = false,
|
|
|
|
/**
|
|
* Storage for listeners, indexed by content and event type.
|
|
*
|
|
* Signature:
|
|
* {
|
|
* <context>:
|
|
* {
|
|
* <eventType>: [<listener>, ...],
|
|
*
|
|
* ...
|
|
* },
|
|
*
|
|
* ...
|
|
* }
|
|
*/
|
|
events = {},
|
|
|
|
/**
|
|
* The configuration for this implementation.
|
|
*
|
|
* Signature:
|
|
* {
|
|
* project: <defaultProjectName>,
|
|
*
|
|
* flash: <pathToFlashBootloader>,
|
|
*
|
|
* json: <pathToJSONFile>,
|
|
*
|
|
* errorCallback: <listenerToNotifyOnError>
|
|
* }
|
|
*/
|
|
configurations =
|
|
{
|
|
project: undefined,
|
|
|
|
flash: 'jStore.Flash.html',
|
|
|
|
json: 'browser.json.js'
|
|
},
|
|
|
|
/**
|
|
* The active storage engine, being used to satisfy the get/set/remove functions on the jStore and jQuery
|
|
* objects.
|
|
*/
|
|
active;
|
|
|
|
//------------------------------
|
|
//
|
|
// Internal Methods
|
|
//
|
|
//------------------------------
|
|
|
|
/**
|
|
* Determine if the given flavor is valid.
|
|
*
|
|
* @param flavor The flavor to test.
|
|
*
|
|
* @return True if the flavor is valid, false otherwise.
|
|
*/
|
|
function validFlavor(flavor)
|
|
{
|
|
switch (flavor)
|
|
{
|
|
|
|
case FLAVOR_LOCAL:
|
|
case FLAVOR_SQL:
|
|
case FLAVOR_FLASH:
|
|
case FLAVOR_GEARS:
|
|
case FLAVOR_MSIE:
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performs enhanced type comparison on an object. This is more reliable method
|
|
* of type checking a variable than a simple typeof comparison. The reason is that,
|
|
* typeof will reduce to the lowest common type.
|
|
*
|
|
* "typeof []" returns Object, and not Array.
|
|
* "typeof {}" returns Object as well.
|
|
*
|
|
* typecheck( [], 'Array' ) : returns true;
|
|
* typecheck( [], 'Object' ) : returns false;
|
|
*
|
|
* @param type The variable type to check.
|
|
*
|
|
* @param compare A string representing the literal type to check.
|
|
*
|
|
* @return True if the variable "type" matches the compare literal.
|
|
*/
|
|
function typecheck(type, compare)
|
|
{
|
|
return !type ? false : type.constructor.toString().match(new RegExp(compare + '\\(\\)', 'i')) !== null;
|
|
}
|
|
|
|
/**
|
|
* If the provided listener is a valid function, it will be triggered with the provided context
|
|
* and parameters.
|
|
*
|
|
* @param listener The listener being triggered.
|
|
*
|
|
* @param context The context to provide to the listener.
|
|
*
|
|
* @param parameters The parameters to pass to the listener as arguments.
|
|
*
|
|
* @return The response of the notified listener.
|
|
*/
|
|
function notify(listener, context, parameters)
|
|
{
|
|
if (typecheck(listener, 'Function'))
|
|
{
|
|
return listener.apply(context || _, typecheck(parameters, 'Array') ? parameters : [parameters]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load the given script.
|
|
*
|
|
* @param path The path to the file to include.
|
|
*
|
|
* @param listener The listener to notify when the file finishes loading.
|
|
*/
|
|
function loadScript(path, listener)
|
|
{
|
|
$.ajax(
|
|
{
|
|
url: path,
|
|
complete: listener || $.noop(),
|
|
type: 'GET',
|
|
dataType: 'script',
|
|
cache: false
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Checks the type of the value, and returns a value safe to persist in any client-side mechanism.
|
|
*
|
|
* @param value The value which should be prepared for storage.
|
|
*
|
|
* @return A value safe for storage.
|
|
*/
|
|
function prepareForStorage(value)
|
|
{
|
|
if (value === undefined)
|
|
{
|
|
return '';
|
|
}
|
|
|
|
if (typecheck(value, 'Object') ||
|
|
typecheck(value, 'Array') ||
|
|
typecheck(value, 'Function'))
|
|
{
|
|
return JSON.stringify(value);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* Checks the type of the value, and returns a value safe for access in any client-side mechanism.
|
|
*
|
|
* @param value The value which should be prepared for use.
|
|
*
|
|
* @return A value safe for use.
|
|
*/
|
|
function prepareForRevival(value)
|
|
{
|
|
return RX_JSON.test(value) ? JSON.parse(value) : value;
|
|
}
|
|
|
|
/**
|
|
* Normalize a key before using it, to ensure it's valid.
|
|
*
|
|
* @param key The key to normalize.
|
|
*
|
|
* @return A normalized key, safe for storage.
|
|
*/
|
|
function normalizeKey(key)
|
|
{
|
|
return key.replace(/^\s+|\s+$/g, "");
|
|
}
|
|
|
|
/**
|
|
* Define a flavored storage engine.
|
|
*
|
|
* @throws EX_COLLISION, EX_INVALID
|
|
*
|
|
* @param flavor The flavor of engine being defined.
|
|
*
|
|
* @param definition An object containing the new properties and methods for the engine extension.
|
|
*
|
|
* @param availability A function to invoke which must return a boolean value indicating the
|
|
* availability of the storage flavor on this browser.
|
|
*/
|
|
function define(flavor, definition, availability)
|
|
{
|
|
if (!validFlavor(flavor))
|
|
{
|
|
throw EX_INVALID;
|
|
}
|
|
|
|
if (availability[flavor] !== undefined)
|
|
{
|
|
throw EX_COLLISION;
|
|
}
|
|
|
|
/**
|
|
* The logic here has been reworked so unavailable flavors are discarded, so we don't needlessly
|
|
* bloat the runtime size of jStore.
|
|
*/
|
|
if (notify(availability) === true)
|
|
{
|
|
_.available[flavor] = true;
|
|
|
|
definition.flavor = flavor;
|
|
|
|
definitions[flavor] = StorageEngine.extend(definition);
|
|
}
|
|
else
|
|
{
|
|
_.available[flavor] = false;
|
|
|
|
// Filter the invalid flavor out of the priority list.
|
|
_.enginePriority = $.map(_.enginePriority, function (engine)
|
|
{
|
|
if (engine === flavor)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
return engine;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Make the jStore library ready.
|
|
*/
|
|
function makeReady()
|
|
{
|
|
if (_.isReady)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ((waitForFlash && _.isFlashReady) || !waitForFlash)
|
|
{
|
|
_.isReady = true;
|
|
_.trigger('jstore-ready', [engines[active]]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a best-fit engine.
|
|
*/
|
|
function createBestFitEngine()
|
|
{
|
|
_.create(_.enginePriority[0], undefined, 'best-fit');
|
|
}
|
|
|
|
/**
|
|
* Get the flash version currently supported in this browser.
|
|
*
|
|
* @return The flash version.
|
|
*/
|
|
function flashVersion()
|
|
{
|
|
// MSIE
|
|
try
|
|
{
|
|
// avoid fp6 minor version lookup issues
|
|
// see: http://blog.deconcept.com/2006/01/11/getvariable-setvariable-crash-internet-explorer-flash-6/
|
|
var axo = new ActiveXObject('ShockwaveFlash.ShockwaveFlash.6');
|
|
|
|
try
|
|
{
|
|
axo.AllowScriptAccess = 'always';
|
|
}
|
|
catch (axo_e)
|
|
{
|
|
return '6,0,0';
|
|
}
|
|
|
|
return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
|
|
}
|
|
|
|
// Real browsers
|
|
catch (e)
|
|
{
|
|
try
|
|
{
|
|
if (navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin)
|
|
{
|
|
return (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description.replace(/\D+/g, ",").match(/^,?(.+),?$/)[1];
|
|
}
|
|
}
|
|
catch (flash_e)
|
|
{}
|
|
}
|
|
|
|
return '0,0,0';
|
|
}
|
|
|
|
/**
|
|
* Flash Detection functions copied from the jQuery Flash Plugin
|
|
*
|
|
* Copyright (c) 2006 Luke Lutman (http://jquery.lukelutman.com/plugins/flash)
|
|
*
|
|
* Dual licensed under the MIT and GPL licenses.
|
|
* http://www.opensource.org/licenses/mit-license.php
|
|
* http://www.opensource.org/licenses/gpl-license.php
|
|
*
|
|
* @param version The version to compare to.
|
|
*
|
|
* @return True if the version is greater than or equal to the required version, false otherwise.
|
|
*/
|
|
function hasFlashVersion(version)
|
|
{
|
|
var playerVersion = flashVersion().match(/\d+/g),
|
|
requiredVersion = version.match(/\d+/g),
|
|
index = 0,
|
|
player,
|
|
required;
|
|
|
|
for (; index < 3; index++)
|
|
{
|
|
player = parseInt(playerVersion[index], 10);
|
|
required = parseInt(requiredVersion[index], 10);
|
|
|
|
// Player version is less than what is required.
|
|
if (player < required)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Player version is greater than what is required.
|
|
else if (player > required)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Player and required version match exactly.
|
|
return true;
|
|
}
|
|
|
|
//------------------------------
|
|
//
|
|
// Plugin Definition
|
|
//
|
|
//------------------------------
|
|
|
|
//------------------------------
|
|
// Error Declaration
|
|
//------------------------------
|
|
|
|
//------------------------------
|
|
// Plugin Creation
|
|
//------------------------------
|
|
|
|
/**
|
|
* The jStore object. Manages a collection of StorageEngines for particular "storage flavors", or the types
|
|
* of storage solutions available to each browser.
|
|
*
|
|
* 2.0 Version Notes:
|
|
*
|
|
* - The user is now responsible for third-party script includes, with the exception of flash.
|
|
*
|
|
* - jStore has been given sole responsibility for testing engine availability.
|
|
*
|
|
* - For the sake of naming conventions, all property names now start with a lowercase, and are camel-cased.
|
|
*
|
|
* The following properties have been changed since the 1.2.x release:
|
|
*
|
|
* - EngineOrder: For the sake of naming conventions, renamed to enginePriority.
|
|
*
|
|
* The following properties and methods have been removed since the 1.2.x release:
|
|
*
|
|
* - Availability: jStore's engines would add their availability tests to this object, so jStore could test
|
|
* them. With the changes to how availability testing works, this property has been removed.
|
|
* A new property, "available" on jStore contains a set of available engines.
|
|
*
|
|
* - Engines: Formerly contained the definitions of storage engines. This property has been removed, and
|
|
* storage of these definitions has been moved internal to the closure.
|
|
*
|
|
* - Instances: Formerly contained instantiated storage engines. This property has been removed, and storage
|
|
* of instantiated engines has been moved internal to the closure.
|
|
*
|
|
* - CurrentEngine: Formerly contained the active storage engine being used for transacting data through the jStore
|
|
* and/or jQuery objects. This property has been removed, and storage of the current engine has
|
|
* been moved internal to the closure. A new method, "activeEngine" has been added to jQuery to
|
|
* get and set the active engine to use.
|
|
*
|
|
* - defaults: Formerly used to set the implementation options for jStore. This property has been removed and
|
|
* replaced with a new configuration metho on the jStore object.
|
|
*
|
|
* - delegate: The delegate class has been removed in favor of a much simpler bind/trigger accessor system, which
|
|
* is accessible contextually through storage engines, or generically through jStore.
|
|
*
|
|
* + fail: This registration class bound events on the delegate for jstore-fail events. Instead, use:
|
|
* jStore.bind('jstore-failure', listener);
|
|
*
|
|
* + flashReady: This registration class bound events on the delegate for flash-ready events. The jstore-ready method
|
|
* now accounts for waiting for flash readyness, if and only if the flash engine is being used. Simply
|
|
* call to jStore.ready().
|
|
*
|
|
* + load: Replaced with the init() method, which performs the same basic functions as the old load() method. Also,
|
|
* the init function is now domready safe, meaning it wraps itself in a domready listener, so the end user
|
|
* doesn't have to.
|
|
*
|
|
* + FindEngine: Removed entirely. The functionality provided by this method now implicitly occurs with the new define()
|
|
* system implemented for engine flavors.
|
|
*
|
|
* + setCurrentEngine: Replaced by activeEngine(). Set the current active engine by passing in the JRI.
|
|
*
|
|
* + safeStore: Replaced by a method internal to this closure, "prepareForStorage".
|
|
*
|
|
* + safeResurrect: Replaced by a method internal to this closure, "prepareForRevival".
|
|
*
|
|
* + use: Replaced by "create".
|
|
*/
|
|
$.extend(_, {
|
|
|
|
//------------------------------
|
|
// Properties
|
|
//------------------------------
|
|
|
|
/**
|
|
* The priority order in which engines should be tested for use. The lower their index in the array, the higher
|
|
* their priority for use.
|
|
*
|
|
* Be weary when reconfiguring the priority order of engines! jStore will use the first available engine it finds
|
|
* based on its priority when autoloading.
|
|
*
|
|
* This array is filtered out as engines are defined, with invalid engines being removed.
|
|
*
|
|
* Signature:
|
|
* [FLAVOR_<storageFlavor>, ...]
|
|
*/
|
|
enginePriority: [FLAVOR_LOCAL, FLAVOR_SQL, FLAVOR_FLASH, FLAVOR_MSIE],
|
|
|
|
/**
|
|
* A collection of the availability states of engines, indexed by their flavor.
|
|
*
|
|
* Signature:
|
|
* {
|
|
* <storageFlavor>: true|false,
|
|
*
|
|
* ...
|
|
* }
|
|
*/
|
|
available: {},
|
|
|
|
/**
|
|
* Flag to determine if the jStore library is ready. jStore becomes ready once the dom is ready and all necessary
|
|
* startup procedures required by jStore to function properly are completed.
|
|
*/
|
|
isReady: false,
|
|
|
|
/**
|
|
* With the flash storage engine, we have to jump through a couple of hoops before the flash engine is ready to work.
|
|
* This flag tracks whether or not the flash storage is available.
|
|
*/
|
|
isFlashReady: false,
|
|
|
|
/**
|
|
* The available engine flavors.
|
|
*/
|
|
flavors:
|
|
{
|
|
local: FLAVOR_LOCAL,
|
|
|
|
sql: FLAVOR_SQL,
|
|
|
|
flash: FLAVOR_FLASH,
|
|
|
|
gears: FLAVOR_GEARS,
|
|
|
|
msie: FLAVOR_MSIE
|
|
},
|
|
|
|
//------------------------------
|
|
// Constructor
|
|
//------------------------------
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @throws EX_INVALID
|
|
*
|
|
* @param project The name of the jStore project. Used to generate a JRI for the engine we create.
|
|
*
|
|
* @param configuration Optionally, an object containing configuration options for this implementation.
|
|
*
|
|
* @param flavor Optionally, the flavor of storage to use. If not provided, jStore will pick the
|
|
* best flavor, based on the current browser.
|
|
*
|
|
* @return jStore
|
|
*/
|
|
init: function (project, configuration, flavor)
|
|
{
|
|
// Extend our plugin configurations
|
|
$.extend(configurations, {project: project}, configuration);
|
|
|
|
$(function ()
|
|
{
|
|
// If JSON parsing isn't defined in this browser, include it.
|
|
if (window.JSON === undefined)
|
|
{
|
|
loadScript(configurations.json);
|
|
}
|
|
|
|
// If we have an explicit flavor to use, use it.
|
|
if (flavor !== undefined)
|
|
{
|
|
_.create(flavor, project, 'default');
|
|
}
|
|
|
|
// Otherwise, attempt to create a best-fit engine.
|
|
else
|
|
{
|
|
createBestFitEngine();
|
|
}
|
|
});
|
|
|
|
return _;
|
|
},
|
|
|
|
//------------------------------
|
|
// Methods
|
|
//------------------------------
|
|
|
|
/**
|
|
* Create an instance of a flavored engine.
|
|
*
|
|
* @throws EX_INVALID, EX_UNAVAILABLE, EX_DUPLICATE
|
|
*
|
|
* @param flavor The flavor to create the engine with.
|
|
*
|
|
* @param project The project identifier for this instance.
|
|
*
|
|
* @param identifier Some arbitrary identifier for this project instance of the engine.
|
|
*
|
|
* @return The created instance.
|
|
*/
|
|
create: function (flavor, project, identifier)
|
|
{
|
|
project = project || configurations.project || location.hostname.replace(/\./g, '-') || 'unknown';
|
|
|
|
if (!validFlavor(flavor))
|
|
{
|
|
throw EX_INVALID;
|
|
}
|
|
|
|
if (definitions[flavor] === undefined)
|
|
{
|
|
throw EX_UNAVAILABLE;
|
|
}
|
|
|
|
var jri = (identifier !== undefined ? identifier + '.' : '') + project + '.' + flavor,
|
|
engine;
|
|
|
|
if (engines[jri] !== undefined)
|
|
{
|
|
throw EX_DUPLICATE;
|
|
}
|
|
|
|
// Create our engine instance.
|
|
engine = engines[jri] = new definitions[flavor](project, jri);
|
|
|
|
// Set up a listener for our jstore-engine-ready event.
|
|
engine.ready(function ()
|
|
{
|
|
_.trigger('jstore-engine-ready', [engine]);
|
|
});
|
|
|
|
if (flavor === FLAVOR_FLASH && !_.isFlashReady)
|
|
{
|
|
if (active === undefined)
|
|
{
|
|
waitForFlash = true;
|
|
}
|
|
|
|
// Define a window-accessible function for flash to call via ExternalInterface
|
|
window.jstore_ready = function ()
|
|
{
|
|
_.isFlashReady = true;
|
|
_.trigger('flash-ready');
|
|
|
|
if (active === undefined)
|
|
{
|
|
makeReady();
|
|
}
|
|
|
|
// Remove the callback from the window scope, as it is no longer necessary
|
|
window.flash_ready = undefined;
|
|
};
|
|
|
|
window.jstore_error = function (message)
|
|
{
|
|
_.trigger('jstore-error', ['JSTORE_FLASH_EXCEPTION', null, message]);
|
|
};
|
|
|
|
$('<iframe style="height:1px;width:1px;position:absolute;left:0;top:0;margin-left:-100px;" id="jStoreFlashFrame" src="' +
|
|
configurations.flash + '"></iframe>').appendTo('body');
|
|
}
|
|
else if (active === undefined)
|
|
{
|
|
active = jri;
|
|
makeReady();
|
|
}
|
|
|
|
return engine;
|
|
},
|
|
|
|
/**
|
|
* Fetch an engine by it's JRI.
|
|
*
|
|
* @param jri The JRI of the engine to retrieve.
|
|
*
|
|
* @return The requested engine.
|
|
*/
|
|
engine: function (jri)
|
|
{
|
|
return engines[jri];
|
|
},
|
|
|
|
/**
|
|
* Returns the active storage engine being used. If a value is passed, sets that engine as the active engine.
|
|
*
|
|
* @throws EX_UNKNOWN
|
|
*
|
|
* @param jri Optionally, the JRI of the engine to make active, if it should be changed.
|
|
*
|
|
* @return The active storage engine.
|
|
*/
|
|
activeEngine: function (jri)
|
|
{
|
|
if (jri !== undefined)
|
|
{
|
|
if (engines[jri] === undefined)
|
|
{
|
|
throw EX_UNKNOWN;
|
|
}
|
|
else
|
|
{
|
|
active = jri;
|
|
}
|
|
}
|
|
|
|
return engines[active];
|
|
},
|
|
|
|
/**
|
|
* Bind an event listener.
|
|
*
|
|
* @param event The event to bind a listener on.
|
|
*
|
|
* @param listener The listener to notify when the event occurs.
|
|
*
|
|
* @param context The context of the binding. A string representing the engine flavor
|
|
* binding the event, or undefined to indicate it's a jStore event.
|
|
*
|
|
* @return jStore
|
|
*/
|
|
bind: function (event, listener, context)
|
|
{
|
|
context = context || 'jstore';
|
|
|
|
if (events[context] === undefined)
|
|
{
|
|
events[context] = {};
|
|
}
|
|
|
|
if (events[context][event] === undefined)
|
|
{
|
|
events[context][event] = [listener];
|
|
}
|
|
else
|
|
{
|
|
events[context][event].push(listener);
|
|
}
|
|
|
|
return _;
|
|
},
|
|
|
|
/**
|
|
* Trigger an event, notifying any bound listeners.
|
|
*
|
|
* @param event The event to trigger.
|
|
*
|
|
* @param parameters Any additional parameters to pass to the listeners being notified.
|
|
*
|
|
* @param context The context of the binding. A string representing the engine flavor
|
|
* binding the event, or undefined to indicate it's a jStore event.
|
|
*
|
|
* @return jStore
|
|
*/
|
|
trigger: function (event, parameters, context)
|
|
{
|
|
context = context || 'jstore';
|
|
|
|
if (events[context] !== undefined)
|
|
{
|
|
if (events[context][event] !== undefined)
|
|
{
|
|
$.each(events[context][event], function ()
|
|
{
|
|
notify(this, _, parameters);
|
|
});
|
|
}
|
|
}
|
|
|
|
return _;
|
|
},
|
|
|
|
/**
|
|
* Bind a listener to be notified when jStore causes a non-fatal exception.
|
|
*
|
|
* @param listener The listener to notify when a failure occurs.
|
|
*/
|
|
error: function (listener)
|
|
{
|
|
_.bind('jstore-error', listener);
|
|
},
|
|
|
|
/**
|
|
* Bind a listener to be notified when jStore is ready.
|
|
*
|
|
* @param listener The listener to notify when jStore is ready.
|
|
*
|
|
* @return jStore
|
|
*/
|
|
ready: function (listener)
|
|
{
|
|
if (_.isReady)
|
|
{
|
|
notify(listener);
|
|
}
|
|
else
|
|
{
|
|
_.bind('jstore-ready', listener);
|
|
}
|
|
|
|
return _;
|
|
},
|
|
|
|
/**
|
|
* Bind a listener to be notified when jStore and the default engine are ready.
|
|
*
|
|
* @param listener The listener to notify when jStore and it's default engine are ready.
|
|
*
|
|
* @return jStore
|
|
*/
|
|
engineReady: function (listener)
|
|
{
|
|
if (_.isReady)
|
|
{
|
|
notify(listener);
|
|
}
|
|
else
|
|
{
|
|
_.bind('jstore-engine-ready', listener);
|
|
}
|
|
|
|
return _;
|
|
},
|
|
|
|
/**
|
|
* A combined getter/setter for the active engine.
|
|
*
|
|
* @param key The key of the property to get, or set.
|
|
*
|
|
* @param value If a valid value is provided, sets the engine.
|
|
*
|
|
* @return The requested property value.
|
|
*/
|
|
store: function (key, value)
|
|
{
|
|
return value === undefined ? _.get(key) : _.set(key, value);
|
|
},
|
|
|
|
/**
|
|
* Remove a property from the active engine.
|
|
*
|
|
* @param key The key of the property to remove.
|
|
*
|
|
* @return The value of the property before removal.
|
|
*/
|
|
remove: function (key)
|
|
{
|
|
return _.activeEngine().remove(key);
|
|
},
|
|
|
|
/**
|
|
* Get a property from the active engine.
|
|
*
|
|
* @param key The key of the property to get.
|
|
*
|
|
* @return The value of the property.
|
|
*/
|
|
get: function (key)
|
|
{
|
|
return _.activeEngine().get(key);
|
|
},
|
|
|
|
/**
|
|
* Set a property on the active engine.
|
|
*
|
|
* @param key The key of the property to set.
|
|
*
|
|
* @param value The value to set the property to.
|
|
*
|
|
* @return The new value of the property.
|
|
*/
|
|
set: function (key, value)
|
|
{
|
|
return _.activeEngine().set(key, value);
|
|
}
|
|
|
|
});
|
|
|
|
//------------------------------
|
|
// Core Extension
|
|
//------------------------------
|
|
|
|
//------------------------------
|
|
//
|
|
// Class Definition
|
|
//
|
|
//------------------------------
|
|
|
|
/**
|
|
* The StorageEngine class is the unified API through which jStore accesses and manipulates
|
|
* the various storage flavors available.
|
|
*
|
|
* 2.0 Version Notes:
|
|
*
|
|
* - All third-party loading is now the responsibility of the developer.
|
|
*
|
|
* - The delegate class has been removed entirely. Engines have been given "bind" and "trigger" methods
|
|
* to interact directly with the delegate like-replacement that has been added to jStore.
|
|
*
|
|
* - Engine availability has been moved out of the engines themselves, and elevated to a jStore
|
|
* responsibility.
|
|
*
|
|
* The following methods have changed since the 1.2.x release:
|
|
*
|
|
* - get: When "get"ting a non-stored property, the get function will now return "undefined"
|
|
* instead of "null". "null" can now be used as a valid property value.
|
|
*
|
|
* - rem: Renamed to "remove". I always felt dirty about "rem" being vaguely explicit.
|
|
*
|
|
* The following properties have been removed since the 1.2.x release:
|
|
*
|
|
* - autoload: Part of the third-party loading logic.
|
|
*
|
|
* - hasIncluded: Part of the third-party loading logic.
|
|
*
|
|
* - includes: Part of the third-party loading logic.
|
|
*
|
|
* - isAvailable: Part of the availability logic elevated to jStore.
|
|
*
|
|
* @throws EX_UNSTABLE
|
|
*/
|
|
StorageEngine = Class.extend({
|
|
|
|
//------------------------------
|
|
// Properties
|
|
//------------------------------
|
|
|
|
/**
|
|
* The project which owns this storage engine.
|
|
*/
|
|
project: undefined,
|
|
|
|
/**
|
|
* The JRI (jStore Resource Identifier) acts as a uuid for this specific instance
|
|
* of the storage engine.
|
|
*/
|
|
jri: undefined,
|
|
|
|
/**
|
|
* The flavor of this engine.
|
|
*/
|
|
flavor: undefined,
|
|
|
|
/**
|
|
* The actual database object which data is transacted through.
|
|
*/
|
|
database: undefined,
|
|
|
|
/**
|
|
* A StorageEngine should always respond to fetch requests synchronously. However, some
|
|
* of the storage flavors require callback-based asynchronous access. To get around this,
|
|
* we simlpy require all engines to function off a primary data cache, to allow for
|
|
* synchronous access across all implementations.
|
|
*
|
|
* Signature:
|
|
* {
|
|
* <propertyKey>: <propertyValue>,
|
|
*
|
|
* ...
|
|
* }
|
|
*/
|
|
data: undefined,
|
|
|
|
/**
|
|
* A number of storage engines enforce a size limit as to what they will persist for a given site.
|
|
* This limit is not monitored or computed by jStore currently, and this property will merely give
|
|
* a static indication of the total size alloted to the engine, as defined by the storage flavor.
|
|
*/
|
|
limit: undefined,
|
|
|
|
/**
|
|
* Each storage flavor has a different process to go through before it's "ready" to transact data. This
|
|
* property stores the state of the engine's readyness, and uses it to notify listeners whenever jStore
|
|
* is ready to function.
|
|
*/
|
|
isReady: undefined,
|
|
|
|
//------------------------------
|
|
// Constructor
|
|
//------------------------------
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param project The project which instantiated this engine.
|
|
*
|
|
* @param jri The uuid assigned to this instance by jStore.
|
|
*/
|
|
init: function (project, jri)
|
|
{
|
|
this.project = project;
|
|
this.jri = jri;
|
|
this.data = {};
|
|
this.isReady = false;
|
|
this.updateCache();
|
|
},
|
|
|
|
//------------------------------
|
|
// Methods
|
|
//------------------------------
|
|
|
|
/**
|
|
* Update the cache.
|
|
*/
|
|
updateCache: function ()
|
|
{
|
|
this.isReady = true;
|
|
this.trigger('engine-ready', [this]);
|
|
},
|
|
|
|
/**
|
|
* Bind a listener to an event dispatched by this engine.
|
|
*
|
|
* @param event The event to bind on.
|
|
*
|
|
* @param listener The listener to notify when the event occurs.
|
|
*/
|
|
bind: function (event, listener)
|
|
{
|
|
_.bind(event, listener, this.jri);
|
|
},
|
|
|
|
/**
|
|
* Trigger an event, notifying all bound listeners.
|
|
*
|
|
* @param event The event to trigger.
|
|
*
|
|
* @param parameters An optional Array of parameters to pass to the listeners.
|
|
*/
|
|
trigger: function (event, parameters)
|
|
{
|
|
_.trigger(event, parameters, this.jri);
|
|
},
|
|
|
|
/**
|
|
* Bind a listener to the StorageEngine's ready event.
|
|
*
|
|
* @param listener The listener to notify whenever this engine is ready to transact data.
|
|
*/
|
|
ready: function (listener)
|
|
{
|
|
if (this.isReady)
|
|
{
|
|
notify(listener, this);
|
|
}
|
|
else
|
|
{
|
|
this.bind('engine-ready', listener);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Get a property from the StorageEngine.
|
|
*
|
|
* @param key The property key of the data to retrieve.
|
|
*
|
|
* @return The property value, or "undefined" if the property isn't stored.
|
|
*/
|
|
get: function (key)
|
|
{
|
|
this.__interruptAccess();
|
|
|
|
return this.data[key];
|
|
},
|
|
|
|
/**
|
|
* Sets a property in the StorageEngine.
|
|
*
|
|
* @param key The key of the property.
|
|
*
|
|
* @param value The value of the property.
|
|
*
|
|
* @return The new value of the property.
|
|
*/
|
|
set: function (key, value)
|
|
{
|
|
this.__interruptAccess();
|
|
|
|
key = normalizeKey(key);
|
|
|
|
try
|
|
{
|
|
this.__set(key, value);
|
|
}
|
|
catch (e)
|
|
{
|
|
_.trigger('jstore-error', ['JSTORE_STORAGE_FAILURE', this.jri, e]);
|
|
}
|
|
|
|
this.data[key] = value;
|
|
|
|
return value;
|
|
},
|
|
|
|
/**
|
|
* Removes a property from the StorageEngine.
|
|
*
|
|
* @param key The property key of the data to remove.
|
|
*
|
|
* @return The value of the property, before it was removed.
|
|
*/
|
|
remove: function (key)
|
|
{
|
|
this.__interruptAccess();
|
|
|
|
key = normalizeKey(key);
|
|
|
|
try
|
|
{
|
|
this.__remove(key);
|
|
}
|
|
catch (e)
|
|
{
|
|
_.trigger('jstore-error', ['JSTORE_REMOVE_FAILURE', this.jri, e]);
|
|
}
|
|
|
|
var buffer = this.data[key];
|
|
|
|
this.data[key] = undefined;
|
|
|
|
return buffer;
|
|
},
|
|
|
|
//------------------------------
|
|
// Internal Methods
|
|
//------------------------------
|
|
|
|
/**
|
|
* Ensures the engine is in a stable state for transacting data.
|
|
*
|
|
* @throws EX_UNSTABLE
|
|
*/
|
|
__interruptAccess: function ()
|
|
{
|
|
if (!this.isReady)
|
|
{
|
|
throw EX_UNSTABLE;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Sets a property in the StorageEngine. This method should be overloaded to provide actual
|
|
* storage flavor integration.
|
|
*
|
|
* @param key The key of the property.
|
|
*
|
|
* @param value The value of the property.
|
|
*
|
|
* @return The new value of the property.
|
|
*/
|
|
__set: function (key, value)
|
|
{
|
|
return;
|
|
},
|
|
|
|
/**
|
|
* Removes a property from the StorageEngine. This method should be overloaded to provide actual
|
|
* storage flavor integration.
|
|
*
|
|
* @param key The property key of the data to remove.
|
|
*
|
|
* @return The value of the property, before it was removed.
|
|
*/
|
|
__remove: function (key)
|
|
{
|
|
return;
|
|
}
|
|
|
|
});
|
|
|
|
//------------------------------
|
|
//
|
|
// jQuery Hooks
|
|
//
|
|
//------------------------------
|
|
|
|
$.extend($.fn, {
|
|
|
|
//------------------------------
|
|
// Methods
|
|
//------------------------------
|
|
|
|
/**
|
|
* A combined getter/setter for the active engine.
|
|
*
|
|
* @param key The key of the property to get, or set.
|
|
*
|
|
* @param value If a valid value is provided, sets the engine.
|
|
*
|
|
* @return jQuery
|
|
*/
|
|
store: function (key, value)
|
|
{
|
|
if (value === undefined)
|
|
{
|
|
_.get(key);
|
|
}
|
|
else
|
|
{
|
|
_.set(key, value);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Remove a property from the active engine.
|
|
*
|
|
* @param key The key of the property to remove.
|
|
*
|
|
* @return jQuery
|
|
*/
|
|
removeStore: function (key)
|
|
{
|
|
_.activeEngine().remove(key);
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Get a property from the active engine.
|
|
*
|
|
* @param key The key of the property to get.
|
|
*
|
|
* @return The value of the property.
|
|
*/
|
|
getStore: function (key)
|
|
{
|
|
return _.activeEngine().get(key);
|
|
},
|
|
|
|
/**
|
|
* Set a property on the active engine.
|
|
*
|
|
* @param key The key of the property to set.
|
|
*
|
|
* @param value The value to set the property to.
|
|
*
|
|
* @return jQuery
|
|
*/
|
|
setStore: function (key, value)
|
|
{
|
|
_.activeEngine().set(key, value);
|
|
|
|
return this;
|
|
}
|
|
|
|
});
|
|
|
|
//------------------------------
|
|
//
|
|
// Event Bindings
|
|
//
|
|
//------------------------------
|
|
|
|
//------------------------------
|
|
//
|
|
// Startup Code
|
|
//
|
|
//------------------------------
|
|
|
|
//------------------------------
|
|
// Expose jStore through jQuery
|
|
//------------------------------
|
|
|
|
window.jStore = $.jStore = _;
|
|
|
|
//------------------------------
|
|
//
|
|
// Engine Definitions
|
|
//
|
|
//------------------------------
|
|
|
|
//------------------------------
|
|
// Local
|
|
//------------------------------
|
|
|
|
define(FLAVOR_LOCAL,
|
|
{
|
|
//------------------------------
|
|
// Properties
|
|
//------------------------------
|
|
|
|
limit: parseInt(5e5, 16),
|
|
|
|
//------------------------------
|
|
// Constructor
|
|
//------------------------------
|
|
|
|
init: function (project, name)
|
|
{
|
|
this.database = window.globalStorage === undefined ? window.localStorage : window.globalStorage[location.hostname];
|
|
|
|
this._super(project, name);
|
|
},
|
|
|
|
//------------------------------
|
|
// Methods
|
|
//------------------------------
|
|
|
|
updateCache: function ()
|
|
{
|
|
var key, value;
|
|
|
|
for (key in this.database)
|
|
{
|
|
var has_key = false;
|
|
if (this.database.hasOwnProperty) {
|
|
if (this.database.hasOwnProperty(key)) {
|
|
has_key = true;
|
|
}
|
|
} else { // IE 8
|
|
if (this.database.getItem(key) !== null) {
|
|
has_key = true;
|
|
}
|
|
}
|
|
|
|
if (has_key) {
|
|
value = this.database.getItem(key);
|
|
|
|
// Gecko's getItem returns {value: 'the value'}, WebKit returns 'the value'
|
|
this.data[key] = prepareForRevival(value && value.value ? value.value : value);
|
|
}
|
|
}
|
|
|
|
this._super();
|
|
},
|
|
|
|
//------------------------------
|
|
// Internal methods
|
|
//------------------------------
|
|
|
|
__set: function (key, value)
|
|
{
|
|
this.database.setItem(key, prepareForStorage(value));
|
|
},
|
|
|
|
__remove: function (key)
|
|
{
|
|
this.database.removeItem(key);
|
|
}
|
|
},
|
|
|
|
function ()
|
|
{
|
|
return window.localStorage !== undefined || window.globalStorage !== undefined;
|
|
});
|
|
|
|
//------------------------------
|
|
// SQL
|
|
//------------------------------
|
|
|
|
define(FLAVOR_SQL,
|
|
{
|
|
//------------------------------
|
|
// Properties
|
|
//------------------------------
|
|
|
|
limit: parseInt(32e3, 16),
|
|
|
|
//------------------------------
|
|
// Constructor
|
|
//------------------------------
|
|
|
|
init: function (project, name)
|
|
{
|
|
this.database = window.openDatabase('jstore-' + project, '1.0', project, this.limit);
|
|
|
|
if (!this.database)
|
|
{
|
|
throw 'JSTORE_SQL_NO_DB';
|
|
}
|
|
|
|
this.database.transaction(function (database)
|
|
{
|
|
database.executeSql('CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)');
|
|
});
|
|
|
|
this._super(project, name);
|
|
},
|
|
|
|
//------------------------------
|
|
// Methods
|
|
//------------------------------
|
|
|
|
updateCache: function ()
|
|
{
|
|
var self = this,
|
|
_super = this._super;
|
|
|
|
this.database.transaction(function (database)
|
|
{
|
|
database.executeSql('SELECT k,v FROM jstore', [], function (database, result)
|
|
{
|
|
var rows = result.rows,
|
|
index = 0,
|
|
row;
|
|
|
|
for (; index < rows.length; ++index)
|
|
{
|
|
row = rows.item(index);
|
|
self.data[row.k] = prepareForRevival(row.v);
|
|
}
|
|
|
|
_super.apply(self);
|
|
});
|
|
});
|
|
},
|
|
|
|
//------------------------------
|
|
// Internal methods
|
|
//------------------------------
|
|
|
|
__set: function (key, value)
|
|
{
|
|
this.database.transaction(function (database)
|
|
{
|
|
database.executeSql('INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)', [key, prepareForStorage(value)]);
|
|
});
|
|
},
|
|
|
|
__remove: function (key)
|
|
{
|
|
this.database.transaction(function (database)
|
|
{
|
|
database.executeSql('DELETE FROM jstore WHERE k = ?', [key]);
|
|
});
|
|
}
|
|
},
|
|
|
|
function ()
|
|
{
|
|
return window.openDatabase !== undefined;
|
|
});
|
|
|
|
//------------------------------
|
|
// Flash
|
|
//------------------------------
|
|
|
|
define(FLAVOR_FLASH,
|
|
{
|
|
//------------------------------
|
|
// Properties
|
|
//------------------------------
|
|
|
|
limit: -1,
|
|
|
|
//------------------------------
|
|
// Constructor
|
|
//------------------------------
|
|
|
|
init: function (project, name)
|
|
{
|
|
var self = this;
|
|
|
|
_.bind('flash-ready', function ()
|
|
{
|
|
self.__flashReadyListener();
|
|
});
|
|
|
|
this._super(project, name);
|
|
},
|
|
|
|
//------------------------------
|
|
// Methods
|
|
//------------------------------
|
|
|
|
updateCache: function (enable)
|
|
{
|
|
/**
|
|
* The default call to updateCache passes no variable, so we can short circuit the
|
|
* ready state until we explictly call this after flash ready.
|
|
*/
|
|
if (enable === true)
|
|
{
|
|
var key,
|
|
dataset = this.database.jstore_get_all();
|
|
|
|
for (key in dataset)
|
|
{
|
|
if (dataset.hasOwnProperty(key))
|
|
{
|
|
this.data[key] = prepareForRevival(this.database.jstore_get(key));
|
|
}
|
|
}
|
|
|
|
this._super();
|
|
}
|
|
},
|
|
|
|
//------------------------------
|
|
// Internal methods
|
|
//------------------------------
|
|
|
|
__set: function (key, value)
|
|
{
|
|
if (!this.database.jstore_set(key, prepareForStorage(value)))
|
|
{
|
|
_.trigger('jstore-error', ['JSTORE_STORAGE_FAILURE', this.jri, 'Flash Exception']);
|
|
}
|
|
},
|
|
|
|
__remove: function (key)
|
|
{
|
|
this.database.jstore_remove(key);
|
|
},
|
|
|
|
/**
|
|
* Triggered whenever flash is ready.
|
|
*/
|
|
__flashReadyListener: function ()
|
|
{
|
|
var iFrame = $('#jStoreFlashFrame')[0],
|
|
frameDocument;
|
|
|
|
// MSIE
|
|
if (iFrame.Document !== undefined && typecheck(iFrame.Document.jStoreFlash.jstore_get, 'Function'))
|
|
{
|
|
this.database = iFrame.Document.jStoreFlash;
|
|
}
|
|
|
|
// Real Browsers
|
|
else if (iFrame.contentWindow && iFrame.contentWindow.document)
|
|
{
|
|
frameDocument = $(iFrame.contentWindow.document);
|
|
|
|
// Webkit
|
|
if (typecheck($('object', frameDocument)[0].jstore_get, 'Function'))
|
|
{
|
|
this.database = $('object', frameDocument)[0];
|
|
}
|
|
|
|
// Gecko
|
|
else if (typecheck($('embed', frameDocument)[0].jstore_get, 'Function'))
|
|
{
|
|
this.database = $('embed', frameDocument)[0];
|
|
}
|
|
}
|
|
|
|
if (this.database === undefined)
|
|
{
|
|
throw 'JSTORE_FLASH_REFERENCE_ISSUE';
|
|
}
|
|
else
|
|
{
|
|
this.updateCache(true);
|
|
}
|
|
}
|
|
},
|
|
|
|
function ()
|
|
{
|
|
return hasFlashVersion('9.0.0');
|
|
});
|
|
|
|
//------------------------------
|
|
// Gears
|
|
//------------------------------
|
|
|
|
define(FLAVOR_GEARS,
|
|
{
|
|
//------------------------------
|
|
// Properties
|
|
//------------------------------
|
|
|
|
limit: -1,
|
|
|
|
//------------------------------
|
|
// Constructor
|
|
//------------------------------
|
|
|
|
init: function (project, name)
|
|
{
|
|
this.database = google.gears.factory.create('beta.database');
|
|
this.database.open('jstore-' + project);
|
|
this.database.execute('CREATE TABLE IF NOT EXISTS jstore (k TEXT UNIQUE NOT NULL PRIMARY KEY, v TEXT NOT NULL)');
|
|
|
|
this._super(project, name);
|
|
},
|
|
|
|
//------------------------------
|
|
// Methods
|
|
//------------------------------
|
|
|
|
updateCache: function ()
|
|
{
|
|
var result = this.database.execute('SELECT k,v FROM jstore');
|
|
|
|
while (result.isValidRow())
|
|
{
|
|
this.data[result.field(0)] = prepareForRevival(result.field(1));
|
|
result.next();
|
|
}
|
|
|
|
result.close();
|
|
|
|
this._super();
|
|
},
|
|
|
|
//------------------------------
|
|
// Internal methods
|
|
//------------------------------
|
|
|
|
__set: function (key, value)
|
|
{
|
|
this.database.execute('BEGIN');
|
|
this.database.execute('INSERT OR REPLACE INTO jstore(k, v) VALUES (?, ?)', [key, prepareForStorage(value)]);
|
|
this.database.execute('COMMIT');
|
|
},
|
|
|
|
__remove: function (key)
|
|
{
|
|
this.database.execute('BEGIN');
|
|
this.database.execute('DELETE FROM jstore WHERE k = ?', [key]);
|
|
this.database.execute('COMMIT');
|
|
}
|
|
},
|
|
|
|
function ()
|
|
{
|
|
return window.google !== undefined && window.google.gears !== undefined;
|
|
});
|
|
|
|
//------------------------------
|
|
// MSIE
|
|
//------------------------------
|
|
|
|
define(FLAVOR_MSIE,
|
|
{
|
|
//------------------------------
|
|
// Properties
|
|
//------------------------------
|
|
|
|
limit: parseInt(1e4, 16),
|
|
|
|
//------------------------------
|
|
// Constructor
|
|
//------------------------------
|
|
|
|
init: function (project, name)
|
|
{
|
|
this.database = $('<div style="display:none;behavior:url(\'#default#userData\')" id="jstore-' + project + '"></div>')
|
|
.appendTo(document.body).get(0);
|
|
|
|
this._super(project, name);
|
|
},
|
|
|
|
//------------------------------
|
|
// Methods
|
|
//------------------------------
|
|
|
|
updateCache: function ()
|
|
{
|
|
this.database.load(this.project);
|
|
|
|
var node = document.getElementById('jstore-' + this.project),
|
|
xmlDoc = node.XMLDocument,
|
|
root,
|
|
index = 0;
|
|
|
|
if (xmlDoc && xmlDoc.documentElement && xmlDoc.documentElement.attributes)
|
|
{
|
|
root = xmlDoc.documentElement;
|
|
|
|
for (; index < root.attributes.length; ++index)
|
|
{
|
|
this.data[root.attributes.item(index).nodeName] = prepareForRevival(root.attributes.item(index).nodeValue);
|
|
}
|
|
}
|
|
|
|
this._super();
|
|
},
|
|
|
|
//------------------------------
|
|
// Internal methods
|
|
//------------------------------
|
|
|
|
__set: function (key, value)
|
|
{
|
|
this.database.setAttribute(key, prepareForStorage(value));
|
|
this.database.save(this.project);
|
|
},
|
|
|
|
__remove: function (key)
|
|
{
|
|
this.database.removeAttribute(key);
|
|
this.database.save(this.project);
|
|
}
|
|
},
|
|
|
|
function ()
|
|
{
|
|
return window.ActiveXObject !== undefined;
|
|
});
|
|
|
|
}(jQuery, window));
|