Test case details

Preparation Code

<script > /*jshint expr:true */ /*global window:false, console:false, define:false, module:false */ /** * @license IDBWrapper - A cross-browser wrapper for IndexedDB * Copyright (c) 2011 - 2013 Jens Arps * http://jensarps.de/ * * Licensed under the MIT (X11) license */ (function (name, definition, global) { if (typeof define === 'function') { define(definition); } else if (typeof module !== 'undefined' && module.exports) { module.exports = definition(); } else { global[name] = definition(); } })('IDBStore', function () { "use strict"; var defaults = { storeName: 'Store', storePrefix: 'IDBWrapper-', dbVersion: 1, keyPath: 'id', autoIncrement: true, onStoreReady: function () { }, onError: function(error){ throw error; }, indexes: [] }; /** * * The IDBStore constructor * * @constructor * @name IDBStore * @version 1.1.0 * * @param {Object} [kwArgs] An options object used to configure the store and * set callbacks * @param {String} [kwArgs.storeName='Store'] The name of the store * @param {String} [kwArgs.storePrefix='IDBWrapper-'] A prefix that is * internally used to construct the name of the database, which will be * kwArgs.storePrefix + kwArgs.storeName * @param {Number} [kwArgs.dbVersion=1] The version of the store * @param {String} [kwArgs.keyPath='id'] The key path to use * @param {Boolean} [kwArgs.autoIncrement=true] If set to true, IDBStore will * automatically make sure a unique keyPath value is present on each object * that is stored. * @param {Function} [kwArgs.onStoreReady] A callback to be called when the * store is ready to be used. * @param {Function} [kwArgs.onError=throw] A callback to be called when an * error occurred during instantiation of the store. * @param {Array} [kwArgs.indexes=[]] An array of indexData objects * defining the indexes to use with the store. For every index to be used * one indexData object needs to be passed in the array. * An indexData object is defined as follows: * @param {Object} [kwArgs.indexes.indexData] An object defining the index to * use * @param {String} kwArgs.indexes.indexData.name The name of the index * @param {String} [kwArgs.indexes.indexData.keyPath] The key path of the index * @param {Boolean} [kwArgs.indexes.indexData.unique] Whether the index is unique * @param {Boolean} [kwArgs.indexes.indexData.multiEntry] Whether the index is multi entry * @param {Function} [onStoreReady] A callback to be called when the store * is ready to be used. * @example // create a store for customers with an additional index over the // `lastname` property. var myCustomerStore = new IDBStore({ dbVersion: 1, storeName: 'customer-index', keyPath: 'customerid', autoIncrement: true, onStoreReady: populateTable, indexes: [ { name: 'lastname', keyPath: 'lastname', unique: false, multiEntry: false } ] }); * @example // create a generic store var myCustomerStore = new IDBStore({ storeName: 'my-data-store', onStoreReady: function(){ // start working with the store. } }); */ var IDBStore = function (kwArgs, onStoreReady) { for(var key in defaults){ this[key] = typeof kwArgs[key] != 'undefined' ? kwArgs[key] : defaults[key]; } this.dbName = this.storePrefix + this.storeName; this.dbVersion = parseInt(this.dbVersion, 10); onStoreReady && (this.onStoreReady = onStoreReady); this.idb = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB; this.keyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.mozIDBKeyRange; this.consts = { 'READ_ONLY': 'readonly', 'READ_WRITE': 'readwrite', 'VERSION_CHANGE': 'versionchange', 'NEXT': 'next', 'NEXT_NO_DUPLICATE': 'nextunique', 'PREV': 'prev', 'PREV_NO_DUPLICATE': 'prevunique' }; this.openDB(); }; IDBStore.prototype = /** @lends IDBStore */ { /** * The version of IDBStore * * @type String */ version: '1.1.0', /** * A reference to the IndexedDB object * * @type Object */ db: null, /** * The full name of the IndexedDB used by IDBStore, composed of * this.storePrefix + this.storeName * * @type String */ dbName: null, /** * The version of the IndexedDB used by IDBStore * * @type Number */ dbVersion: null, /** * A reference to the objectStore used by IDBStore * * @type Object */ store: null, /** * The store name * * @type String */ storeName: null, /** * The key path * * @type String */ keyPath: null, /** * Whether IDBStore uses autoIncrement * * @type Boolean */ autoIncrement: null, /** * The indexes used by IDBStore * * @type Array */ indexes: null, /** * A hashmap of features of the used IDB implementation * * @type Object * @proprty {Boolean} autoIncrement If the implementation supports * native auto increment */ features: null, /** * The callback to be called when the store is ready to be used * * @type Function */ onStoreReady: null, /** * The callback to be called if an error occurred during instantiation * of the store * * @type Function */ onError: null, /** * The internal insertID counter * * @type Number * @private */ _insertIdCount: 0, /** * Opens an IndexedDB; called by the constructor. * * Will check if versions match and compare provided index configuration * with existing ones, and update indexes if necessary. * * Will call this.onStoreReady() if everything went well and the store * is ready to use, and this.onError() is something went wrong. * * @private * */ openDB: function () { var features = this.features = {}; features.hasAutoIncrement = !window.mozIndexedDB; // TODO: Still, really? var openRequest = this.idb.open(this.dbName, this.dbVersion); var preventSuccessCallback = false; openRequest.onerror = function (error) { var gotVersionErr = false; if ('error' in error.target) { gotVersionErr = error.target.error.name == "VersionError"; } else if ('errorCode' in error.target) { gotVersionErr = error.target.errorCode == 12; // TODO: Use const } if (gotVersionErr) { this.onError(new Error('The version number provided is lower than the existing one.')); } else { this.onError(error); } }.bind(this); openRequest.onsuccess = function (event) { if (preventSuccessCallback) { return; } if(this.db){ this.onStoreReady(); return; } this.db = event.target.result; if(typeof this.db.version == 'string'){ this.onError(new Error('The IndexedDB implementation in this browser is outdated. Please upgrade your browser.')); return; } if(!this.db.objectStoreNames.contains(this.storeName)){ // We should never ever get here. // Lets notify the user anyway. this.onError(new Error('Something is wrong with the IndexedDB implementation in this browser. Please upgrade your browser.')); return; } var emptyTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY); this.store = emptyTransaction.objectStore(this.storeName); // check indexes this.indexes.forEach(function(indexData){ var indexName = indexData.name; if(!indexName){ preventSuccessCallback = true; this.onError(new Error('Cannot create index: No index name given.')); return; } this.normalizeIndexData(indexData); if(this.hasIndex(indexName)){ // check if it complies var actualIndex = this.store.index(indexName); var complies = this.indexComplies(actualIndex, indexData); if(!complies){ preventSuccessCallback = true; this.onError(new Error('Cannot modify index "' + indexName + '" for current version. Please bump version number to ' + ( this.dbVersion + 1 ) + '.')); } } else { preventSuccessCallback = true; this.onError(new Error('Cannot create new index "' + indexName + '" for current version. Please bump version number to ' + ( this.dbVersion + 1 ) + '.')); } }, this); preventSuccessCallback || this.onStoreReady(); }.bind(this); openRequest.onupgradeneeded = function(/* IDBVersionChangeEvent */ event){ this.db = event.target.result; if(this.db.objectStoreNames.contains(this.storeName)){ this.store = event.target.transaction.objectStore(this.storeName); } else { this.store = this.db.createObjectStore(this.storeName, { keyPath: this.keyPath, autoIncrement: this.autoIncrement}); } this.indexes.forEach(function(indexData){ var indexName = indexData.name; if(!indexName){ preventSuccessCallback = true; this.onError(new Error('Cannot create index: No index name given.')); } this.normalizeIndexData(indexData); if(this.hasIndex(indexName)){ // check if it complies var actualIndex = this.store.index(indexName); var complies = this.indexComplies(actualIndex, indexData); if(!complies){ // index differs, need to delete and re-create this.store.deleteIndex(indexName); this.store.createIndex(indexName, indexData.keyPath, { unique: indexData.unique, multiEntry: indexData.multiEntry }); } } else { this.store.createIndex(indexName, indexData.keyPath, { unique: indexData.unique, multiEntry: indexData.multiEntry }); } }, this); }.bind(this); }, /** * Deletes the database used for this store if the IDB implementations * provides that functionality. */ deleteDatabase: function () { if (this.idb.deleteDatabase) { this.idb.deleteDatabase(this.dbName); } }, /********************* * data manipulation * *********************/ /** * Puts an object into the store. If an entry with the given id exists, * it will be overwritten. * * @param {Object} dataObj The object to store. * @param {Function} [onSuccess] A callback that is called if insertion * was successful. * @param {Function} [onError] A callback that is called if insertion * failed. */ put: function (dataObj, onSuccess, onError) { onError || (onError = function (error) { console.error('Could not write data.', error); }); onSuccess || (onSuccess = noop); var hasSuccess = false, result = null; if (typeof dataObj[this.keyPath] == 'undefined' && !this.features.hasAutoIncrement) { dataObj[this.keyPath] = this._getUID(); } var putTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE); putTransaction.oncomplete = function () { var callback = hasSuccess ? onSuccess : onError; callback(result); }; putTransaction.onabort = onError; putTransaction.onerror = onError; var putRequest = putTransaction.objectStore(this.storeName).put(dataObj); putRequest.onsuccess = function (event) { hasSuccess = true; result = event.target.result; }; putRequest.onerror = onError; }, /** * Retrieves an object from the store. If no entry exists with the given id, * the success handler will be called with null as first and only argument. * * @param {*} key The id of the object to fetch. * @param {Function} [onSuccess] A callback that is called if fetching * was successful. Will receive the object as only argument. * @param {Function} [onError] A callback that will be called if an error * occurred during the operation. */ get: function (key, onSuccess, onError) { onError || (onError = function (error) { console.error('Could not read data.', error); }); onSuccess || (onSuccess = noop); var hasSuccess = false, result = null; var getTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY); getTransaction.oncomplete = function () { var callback = hasSuccess ? onSuccess : onError; callback(result); }; getTransaction.onabort = onError; getTransaction.onerror = onError; var getRequest = getTransaction.objectStore(this.storeName).get(key); getRequest.onsuccess = function (event) { hasSuccess = true; result = event.target.result; }; getRequest.onerror = onError; }, /** * Removes an object from the store. * * @param {*} key The id of the object to remove. * @param {Function} [onSuccess] A callback that is called if the removal * was successful. * @param {Function} [onError] A callback that will be called if an error * occurred during the operation. */ remove: function (key, onSuccess, onError) { onError || (onError = function (error) { console.error('Could not remove data.', error); }); onSuccess || (onSuccess = noop); var hasSuccess = false, result = null; var removeTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE); removeTransaction.oncomplete = function () { var callback = hasSuccess ? onSuccess : onError; callback(result); }; removeTransaction.onabort = onError; removeTransaction.onerror = onError; var deleteRequest = removeTransaction.objectStore(this.storeName)['delete'](key); deleteRequest.onsuccess = function (event) { hasSuccess = true; result = event.target.result; }; deleteRequest.onerror = onError; }, /** * Runs a batch of put and/or remove operations on the store. * * @param {Array} dataArray An array of objects containing the operation to run * and the data object (for put operations). * @param {Function} [onSuccess] A callback that is called if all operations * were successful. * @param {Function} [onError] A callback that is called if an error * occurred during one of the operations. */ batch: function (dataArray, onSuccess, onError) { onError || (onError = function (error) { console.error('Could not apply batch.', error); }); onSuccess || (onSuccess = noop); if(Object.prototype.toString.call(dataArray) != '[object Array]'){ onError(new Error('dataArray argument must be of type Array.')); } var batchTransaction = this.db.transaction([this.storeName] , this.consts.READ_WRITE); batchTransaction.oncomplete = function () { var callback = hasSuccess ? onSuccess : onError; callback(hasSuccess); }; batchTransaction.onabort = onError; batchTransaction.onerror = onError; var count = dataArray.length; var called = false; var hasSuccess = false; var onItemSuccess = function () { count--; if (count === 0 && !called) { called = true; hasSuccess = true; } }; dataArray.forEach(function (operation) { var type = operation.type; var key = operation.key; var value = operation.value; var onItemError = function (err) { batchTransaction.abort(); if (!called) { called = true; onError(err, type, key); } }; if (type == "remove") { var deleteRequest = batchTransaction.objectStore(this.storeName)['delete'](key); deleteRequest.onsuccess = onItemSuccess; deleteRequest.onerror = onItemError; } else if (type == "put") { if (typeof value[this.keyPath] == 'undefined' && !this.features.hasAutoIncrement) { value[this.keyPath] = this._getUID(); } var putRequest = batchTransaction.objectStore(this.storeName).put(value); putRequest.onsuccess = onItemSuccess; putRequest.onerror = onItemError; } }, this); }, /** * Fetches all entries in the store. * * @param {Function} [onSuccess] A callback that is called if the operation * was successful. Will receive an array of objects. * @param {Function} [onError] A callback that will be called if an error * occurred during the operation. */ getAll: function (onSuccess, onError) { onError || (onError = function (error) { console.error('Could not read data.', error); }); onSuccess || (onSuccess = noop); var getAllTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY); var store = getAllTransaction.objectStore(this.storeName); if (store.getAll) { this._getAllNative(getAllTransaction, store, onSuccess, onError); } else { this._getAllCursor(getAllTransaction, store, onSuccess, onError); } }, /** * Implements getAll for IDB implementations that have a non-standard * getAll() method. * * @param {Object} getAllTransaction An open READ transaction. * @param {Object} store A reference to the store. * @param {Function} onSuccess A callback that will be called if the * operation was successful. * @param {Function} onError A callback that will be called if an * error occurred during the operation. * @private */ _getAllNative: function (getAllTransaction, store, onSuccess, onError) { var hasSuccess = false, result = null; getAllTransaction.oncomplete = function () { var callback = hasSuccess ? onSuccess : onError; callback(result); }; getAllTransaction.onabort = onError; getAllTransaction.onerror = onError; var getAllRequest = store.getAll(); getAllRequest.onsuccess = function (event) { hasSuccess = true; result = event.target.result; }; getAllRequest.onerror = onError; }, /** * Implements getAll for IDB implementations that do not have a getAll() * method. * * @param {Object} getAllTransaction An open READ transaction. * @param {Object} store A reference to the store. * @param {Function} onSuccess A callback that will be called if the * operation was successful. * @param {Function} onError A callback that will be called if an * error occurred during the operation. * @private */ _getAllCursor: function (getAllTransaction, store, onSuccess, onError) { var all = [], hasSuccess = false, result = null; getAllTransaction.oncomplete = function () { var callback = hasSuccess ? onSuccess : onError; callback(result); }; getAllTransaction.onabort = onError; getAllTransaction.onerror = onError; var cursorRequest = store.openCursor(); cursorRequest.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { all.push(cursor.value); cursor['continue'](); } else { hasSuccess = true; result = all; } }; cursorRequest.onError = onError; }, /** * Clears the store, i.e. deletes all entries in the store. * * @param {Function} [onSuccess] A callback that will be called if the * operation was successful. * @param {Function} [onError] A callback that will be called if an * error occurred during the operation. */ clear: function (onSuccess, onError) { onError || (onError = function (error) { console.error('Could not clear store.', error); }); onSuccess || (onSuccess = noop); var hasSuccess = false, result = null; var clearTransaction = this.db.transaction([this.storeName], this.consts.READ_WRITE); clearTransaction.oncomplete = function () { var callback = hasSuccess ? onSuccess : onError; callback(result); }; clearTransaction.onabort = onError; clearTransaction.onerror = onError; var clearRequest = clearTransaction.objectStore(this.storeName).clear(); clearRequest.onsuccess = function (event) { hasSuccess = true; result = event.target.result; }; clearRequest.onerror = onError; }, /** * Generates a numeric id unique to this instance of IDBStore. * * @return {Number} The id * @private */ _getUID: function () { // FF bails at times on non-numeric ids. So we take an even // worse approach now, using current time as id. Sigh. return this._insertIdCount++ + Date.now(); }, /************ * indexing * ************/ /** * Returns a DOMStringList of index names of the store. * * @return {DOMStringList} The list of index names */ getIndexList: function () { return this.store.indexNames; }, /** * Checks if an index with the given name exists in the store. * * @param {String} indexName The name of the index to look for * @return {Boolean} Whether the store contains an index with the given name */ hasIndex: function (indexName) { return this.store.indexNames.contains(indexName); }, /** * Normalizes an object containing index data and assures that all * properties are set. * * @param {Object} indexData The index data object to normalize * @param {String} indexData.name The name of the index * @param {String} [indexData.keyPath] The key path of the index * @param {Boolean} [indexData.unique] Whether the index is unique * @param {Boolean} [indexData.multiEntry] Whether the index is multi entry */ normalizeIndexData: function (indexData) { indexData.keyPath = indexData.keyPath || indexData.name; indexData.unique = !!indexData.unique; indexData.multiEntry = !!indexData.multiEntry; }, /** * Checks if an actual index complies with an expected index. * * @param {Object} actual The actual index found in the store * @param {Object} expected An Object describing an expected index * @return {Boolean} Whether both index definitions are identical */ indexComplies: function (actual, expected) { var complies = ['keyPath', 'unique', 'multiEntry'].every(function (key) { // IE10 returns undefined for no multiEntry if (key == 'multiEntry' && actual[key] === undefined && expected[key] === false) { return true; } return expected[key] == actual[key]; }); return complies; }, /********** * cursor * **********/ /** * Iterates over the store using the given options and calling onItem * for each entry matching the options. * * @param {Function} onItem A callback to be called for each match * @param {Object} [options] An object defining specific options * @param {Object} [options.index=null] An IDBIndex to operate on * @param {String} [options.order=ASC] The order in which to provide the * results, can be 'DESC' or 'ASC' * @param {Boolean} [options.filterDuplicates=false] Whether to exclude * duplicate matches * @param {Object} [options.keyRange=null] An IDBKeyRange to use * @param {Boolean} [options.writeAccess=false] Whether grant write access * to the store in the onItem callback * @param {Function} [options.onEnd=null] A callback to be called after * iteration has ended * @param {Function} [options.onError=console.error] A callback to be called if an error * occurred during the operation. */ iterate: function (onItem, options) { options = mixin({ index: null, order: 'ASC', filterDuplicates: false, keyRange: null, writeAccess: false, onEnd: null, onError: function (error) { console.error('Could not open cursor.', error); } }, options || {}); var directionType = options.order.toLowerCase() == 'desc' ? 'PREV' : 'NEXT'; if (options.filterDuplicates) { directionType += '_NO_DUPLICATE'; } var hasSuccess = false; var cursorTransaction = this.db.transaction([this.storeName], this.consts[options.writeAccess ? 'READ_WRITE' : 'READ_ONLY']); var cursorTarget = cursorTransaction.objectStore(this.storeName); if (options.index) { cursorTarget = cursorTarget.index(options.index); } cursorTransaction.oncomplete = function () { if (!hasSuccess) { options.onError(null); return; } if (options.onEnd) { options.onEnd(); } else { onItem(null); } }; cursorTransaction.onabort = options.onError; cursorTransaction.onerror = options.onError; var cursorRequest = cursorTarget.openCursor(options.keyRange, this.consts[directionType]); cursorRequest.onerror = options.onError; cursorRequest.onsuccess = function (event) { var cursor = event.target.result; if (cursor) { onItem(cursor.value, cursor, cursorTransaction); cursor['continue'](); } else { hasSuccess = true; } }; }, /** * Runs a query against the store and passes an array containing matched * objects to the success handler. * * @param {Function} onSuccess A callback to be called when the operation * was successful. * @param {Object} [options] An object defining specific query options * @param {Object} [options.index=null] An IDBIndex to operate on * @param {String} [options.order=ASC] The order in which to provide the * results, can be 'DESC' or 'ASC' * @param {Boolean} [options.filterDuplicates=false] Whether to exclude * duplicate matches * @param {Object} [options.keyRange=null] An IDBKeyRange to use * @param {Function} [options.onError=console.error] A callback to be called if an error * occurred during the operation. */ query: function (onSuccess, options) { var result = []; options = options || {}; options.onEnd = function () { onSuccess(result); }; this.iterate(function (item) { result.push(item); }, options); }, /** * * Runs a query against the store, but only returns the number of matches * instead of the matches itself. * * @param {Function} onSuccess A callback to be called if the opration * was successful. * @param {Object} [options] An object defining specific options * @param {Object} [options.index=null] An IDBIndex to operate on * @param {Object} [options.keyRange=null] An IDBKeyRange to use * @param {Function} [options.onError=console.error] A callback to be called if an error * occurred during the operation. */ count: function (onSuccess, options) { options = mixin({ index: null, keyRange: null }, options || {}); var onError = options.onError || function (error) { console.error('Could not open cursor.', error); }; var hasSuccess = false, result = null; var cursorTransaction = this.db.transaction([this.storeName], this.consts.READ_ONLY); cursorTransaction.oncomplete = function () { var callback = hasSuccess ? onSuccess : onError; callback(result); }; cursorTransaction.onabort = onError; cursorTransaction.onerror = onError; var cursorTarget = cursorTransaction.objectStore(this.storeName); if (options.index) { cursorTarget = cursorTarget.index(options.index); } var countRequest = cursorTarget.count(options.keyRange); countRequest.onsuccess = function (evt) { hasSuccess = true; result = evt.target.result; }; countRequest.onError = onError; }, /**************/ /* key ranges */ /**************/ /** * Creates a key range using specified options. This key range can be * handed over to the count() and iterate() methods. * * Note: You must provide at least one or both of "lower" or "upper" value. * * @param {Object} options The options for the key range to create * @param {*} [options.lower] The lower bound * @param {Boolean} [options.excludeLower] Whether to exclude the lower * bound passed in options.lower from the key range * @param {*} [options.upper] The upper bound * @param {Boolean} [options.excludeUpper] Whether to exclude the upper * bound passed in options.upper from the key range * @return {Object} The IDBKeyRange representing the specified options */ makeKeyRange: function(options){ /*jshint onecase:true */ var keyRange, hasLower = typeof options.lower != 'undefined', hasUpper = typeof options.upper != 'undefined'; switch(true){ case hasLower && hasUpper: keyRange = this.keyRange.bound(options.lower, options.upper, options.excludeLower, options.excludeUpper); break; case hasLower: keyRange = this.keyRange.lowerBound(options.lower, options.excludeLower); break; case hasUpper: keyRange = this.keyRange.upperBound(options.upper, options.excludeUpper); break; default: throw new Error('Cannot create KeyRange. Provide one or both of "lower" or "upper" value.'); } return keyRange; } }; /** helpers **/ var noop = function () { }; var empty = {}; var mixin = function (target, source) { var name, s; for (name in source) { s = source[name]; if (s !== empty[name] && s !== target[name]) { target[name] = s; } } return target; }; IDBStore.version = IDBStore.prototype.version; return IDBStore; }, this); </script> <script> var scope = {}; // store setup var idb = new IDBStore({ dbName: 'jsperftestdb', storeName: 'jsperfteststore', dbVersion: '1.0', keyPath: 'id', autoIncrement: true, onStoreReady: function() { idb.clear(); } }); var ls = window.localStorage; ls.clear(); var websql = openDatabase('benchmark', '1.0', '', 1 * 1024 * 1024); websql.transaction(function(tx) { tx.executeSql('DROP TABLE IF EXISTS benchmark;'); tx.executeSql('CREATE TABLE benchmark (id, value);'); }); // data setup var i = 0, lastnames = ['smith', 'miller', 'doe', 'frankenstein', 'furter'], firstnames = ['peter', 'john', 'frank', 'james'], dummyData = { "web-app": { "servlet": [{ "servlet-name": "cofaxCDS", "servlet-class": "org.cofax.cds.CDSServlet", "init-param": { "configGlossary:installationAt": "Philadelphia, PA", "configGlossary:adminEmail": "ksm@pobox.com", "configGlossary:poweredBy": "Cofax", "configGlossary:poweredByIcon": "/images/cofax.gif", "configGlossary:staticPath": "/content/static", "templateProcessorClass": "org.cofax.WysiwygTemplate", "templateLoaderClass": "org.cofax.FilesTemplateLoader", "templatePath": "templates", "templateOverridePath": "", "defaultListTemplate": "listTemplate.htm", "defaultFileTemplate": "articleTemplate.htm", "useJSP": false, "jspListTemplate": "listTemplate.jsp", "jspFileTemplate": "articleTemplate.jsp", "cachePackageTagsTrack": 200, "cachePackageTagsStore": 200, "cachePackageTagsRefresh": 60, "cacheTemplatesTrack": 100, "cacheTemplatesStore": 50, "cacheTemplatesRefresh": 15, "cachePagesTrack": 200, "cachePagesStore": 100, "cachePagesRefresh": 10, "cachePagesDirtyRead": 10, "searchEngineListTemplate": "forSearchEnginesList.htm", "searchEngineFileTemplate": "forSearchEngines.htm", "searchEngineRobotsDb": "WEB-INF/robots.db", "useDataStore": true, "dataStoreClass": "org.cofax.SqlDataStore", "redirectionClass": "org.cofax.SqlRedirection", "dataStoreName": "cofax", "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", "dataStoreUser": "sa", "dataStorePassword": "dataStoreTestQuery", "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", "dataStoreInitConns": 10, "dataStoreMaxConns": 100, "dataStoreConnUsageLimit": 100, "dataStoreLogLevel": "debug", "maxUrlLength": 500 } }, { "servlet-name": "cofaxEmail", "servlet-class": "org.cofax.cds.EmailServlet", "init-param": { "mailHost": "mail1", "mailHostOverride": "mail2" } }, { "servlet-name": "cofaxAdmin", "servlet-class": "org.cofax.cds.AdminServlet" }, { "servlet-name": "fileServlet", "servlet-class": "org.cofax.cds.FileServlet" }, { "servlet-name": "cofaxTools", "servlet-class": "org.cofax.cms.CofaxToolsServlet", "init-param": { "templatePath": "toolstemplates/", "log": 1, "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", "logMaxSize": "", "dataLog": 1, "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", "dataLogMaxSize": "", "removePageCache": "/content/admin/remove?cache=pages&id=", "removeTemplateCache": "/content/admin/remove?cache=templates&id=", "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", "lookInContext": 1, "adminGroupID": 4, "betaServer": true } }], "servlet-mapping": { "cofaxCDS": "/", "cofaxEmail": "/cofaxutil/aemail/*", "cofaxAdmin": "/admin/*", "fileServlet": "/static/*", "cofaxTools": "/tools/*" }, "taglib": { "taglib-uri": "cofax.tld", "taglib-location": "/WEB-INF/tlds/cofax.tld" } } }; var oneMeg = "1234567890"; for (var j = 1; j < 50000; j++) { oneMeg += "1234567890"; } var fiveMeg = oneMeg + oneMeg + oneMeg + oneMeg + oneMeg; var tenMeg = fiveMeg + fiveMeg; var fiftyMeg = tenMeg + tenMeg + tenMeg + tenMeg + tenMeg; </script>
scope.i = 0; scope.smallData = { 'lastname': lastnames[Math.floor(Math.random() * 5)], 'firstanme': firstnames[Math.floor(Math.random() * 4)], 'dummyData': {} }; scope.largeData = { 'lastname': lastnames[Math.floor(Math.random() * 5)], 'firstanme': firstnames[Math.floor(Math.random() * 4)], 'dummyData': dummyData };

Test cases

Test #1

scope.smallData.id = ++scope.i; ls.setItem(scope.smallData.id, JSON.stringify(scope.smallData)); var value = JSON.parse(ls.getItem(scope.smallData.id));

Test #2

scope.largeData.id = ++scope.i; ls.setItem(scope.largeData.id, JSON.stringify(scope.largeData)); var value = JSON.parse(ls.getItem(scope.largeData.id));

Test #3

// async test var value; scope.largeData.id = ++scope.i; scope.largeData.sixtyMeg = fiftyMeg + tenMeg; idb.put(scope.largeData, function(res) { deferred.resolve(); });

Test #4

// async test var value; scope.largeData.id = ++scope.i; scope.largeData.fiftyMeg = fiftyMeg; idb.put(scope.largeData, function(res) { deferred.resolve(); });

Test #5

// async test var value; scope.largeData.id = ++scope.i; scope.largeData.fiftyMeg = JSON.stringify(fiftyMeg); idb.put(scope.largeData, function(res) { deferred.resolve(); });