diff --git a/Gruntfile.js b/Gruntfile.js index faff8d0..fb523bf 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -36,7 +36,7 @@ module.exports = function(grunt) { "toBrowser" : { "release" : { - options: { index : "index-browser.js" }, + options: { index : "index.js" }, src: ["lib/**/*.js", '!**/*-node.*'], dest: "_build/lib/<%= artifactname %>.js" } diff --git a/README.md b/README.md index 62ee639..11bc843 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,13 @@ under the License. ## Olingo OData Client for JavaScript The Olingo OData Client for JavaScript (odatajs) is a library written in JavaScript that enables browser based frontend applications to easily use the OData protocol for communication with application servers. -This library "odatajs-4.0.0.min.js" supports only the OData V4 protocol. +This library "odatajs-4.0.1.min.js" supports only the OData V4 protocol. For using the OData protocols V1-V3 please refer to the [datajs library](http://datajs.codeplex.com/) The odatajs library can be included in any html page with the script tag (for example) ``` - + ``` and its features can be used through the `odatajs` namespace (or `window.odatajs`). The odatajs library can be used together with the datajs library which uses the `window.OData` namespace. diff --git a/grunt-config/custom-tasks/rat/package.json b/grunt-config/custom-tasks/rat/package.json index 0c374c7..3d1a5bb 100644 --- a/grunt-config/custom-tasks/rat/package.json +++ b/grunt-config/custom-tasks/rat/package.json @@ -18,9 +18,6 @@ "grunt": "~0.4.0", "xml2js": "^0.4.4" }, - "peerDependencies": { - "grunt": "~0.4.0" - }, "engines": { "node": ">=0.8.0" } diff --git a/grunt-config/custom-tasks/sign.js b/grunt-config/custom-tasks/sign.js index 3db3f73..ce15b4e 100644 --- a/grunt-config/custom-tasks/sign.js +++ b/grunt-config/custom-tasks/sign.js @@ -24,7 +24,7 @@ module.exports = function(grunt) { var path = require('path'); var fs = require( 'fs' ); - var chalk = require('./rat/node_modules/chalk'); + var chalk = require('chalk'); var globalDone = this.async(); diff --git a/grunt-config/custom-tasks/toBrowser/toBrowser.js b/grunt-config/custom-tasks/toBrowser/toBrowser.js index ad2e8c8..24b4a32 100644 --- a/grunt-config/custom-tasks/toBrowser/toBrowser.js +++ b/grunt-config/custom-tasks/toBrowser/toBrowser.js @@ -49,13 +49,13 @@ module.exports = function(grunt) { //console.log('exists :'+srcPath+srcName+'-browser.js' ); tarName = srcName; - if (srcName.indexOf('-browser') > 0) { - tarName = tarName.substring(0,srcName.indexOf('-browser')); - //console.log('new srcName :'+srcName ); - } else if (grunt.file.exists(srcPath+srcName+'-browser.js')) { - //console.log('exists :yes'); - continue; //skip that file - } + // if (srcName.indexOf('-browser') > 0) { + // tarName = tarName.substring(0,srcName.indexOf('-browser')); + // //console.log('new srcName :'+srcName ); + // } else if (grunt.file.exists(srcPath+srcName+'-browser.js')) { + // //console.log('exists :yes'); + // continue; //skip that file + // } workLoad.push({ diff --git a/grunt-config/custom-tasks/toBrowser/wrapper-tpl.js b/grunt-config/custom-tasks/toBrowser/wrapper-tpl.js index 9c335db..28c5218 100644 --- a/grunt-config/custom-tasks/toBrowser/wrapper-tpl.js +++ b/grunt-config/custom-tasks/toBrowser/wrapper-tpl.js @@ -16,6 +16,7 @@ * specific language governing permissions and limitations * under the License. */ +(function(){ var init = function(exports, module, require) { '<% initFunction %>' }; @@ -29,7 +30,6 @@ var require = function(path) { if (modules[name]) { return modules[name].exports; } modules[name] = { exports : {}}; - console.log(name); if (name === 'sou') { var i = 0; } @@ -39,5 +39,5 @@ var require = function(path) { window.odatajs = {}; init.call(this,window.odatajs,window.odatajs,require); - +})(); diff --git a/grunt-config/nugetpack.nuspec b/grunt-config/nugetpack.nuspec index a0c7861..3a23d58 100644 --- a/grunt-config/nugetpack.nuspec +++ b/grunt-config/nugetpack.nuspec @@ -4,7 +4,7 @@ Olingo OData Client for JavaScript odatajs restful api open protocol odata client javascript - 4.0.0 + 4.0.1 Apache Olingo (Challen He, Kobler-Morris Sven) Apache Olingo http://www.apache.org/licenses/LICENSE-2.0 diff --git a/grunt-config/release-config.js b/grunt-config/release-config.js index 3df970e..07219f6 100644 --- a/grunt-config/release-config.js +++ b/grunt-config/release-config.js @@ -71,7 +71,7 @@ module.exports = function(grunt) { }, "release-nuget": { files: [ - { expand: true, cwd: '_build', src: ['odatajs.4.0.0.nupkg'], dest: './_dist/<%= artifactname %>', filter: 'isFile' }, + { expand: true, cwd: '_build', src: ['odatajs.4.0.1.nupkg'], dest: './_dist/<%= artifactname %>', filter: 'isFile' }, ] }, "release-doc" : { diff --git a/grunt-config/sign-config.js b/grunt-config/sign-config.js index 6c827e3..d543730 100644 --- a/grunt-config/sign-config.js +++ b/grunt-config/sign-config.js @@ -28,7 +28,7 @@ module.exports = function(grunt) { cwd : './_dist/<%= artifactname %>/', src : [ '<%= artifactname %>-lib.zip', - 'odatajs.4.0.0.nupkg', + 'odatajs.4.0.1.nupkg', '<%= artifactname %>-doc.zip', '<%= artifactname %>-sources.zip' ] @@ -39,7 +39,7 @@ module.exports = function(grunt) { cwd : './_dist/<%= artifactname %>/', src : [ '<%= artifactname %>-lib.zip', - 'odatajs.4.0.0.nupkg', + 'odatajs.4.0.1.nupkg', '<%= artifactname %>-doc.zip', '<%= artifactname %>-sources.zip' ] @@ -50,7 +50,7 @@ module.exports = function(grunt) { cwd : './_dist/<%= artifactname %>/', src : [ '<%= artifactname %>-lib.zip', - 'odatajs.4.0.0.nupkg', + 'odatajs.4.0.1.nupkg', '<%= artifactname %>-doc.zip', '<%= artifactname %>-sources.zip' ] diff --git a/index-browser.js b/index-browser.js deleted file mode 100644 index 0524487..0000000 --- a/index-browser.js +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// version information -exports.version = { major: 4, minor: 0, build: 0 }; - -// core stuff, always needed -exports.deferred = require('./lib/deferred.js'); -exports.utils = require('./lib/utils.js'); - -// only needed for xml metadata -exports.xml = require('./lib/xml.js'); - -// only need in browser case -exports.oData = require('./lib/odata.js'); -exports.store = require('./lib/store.js'); -exports.cache = require('./lib/cache.js'); - - - diff --git a/index.js b/index.js index 85aea5b..2431a7c 100644 --- a/index.js +++ b/index.js @@ -22,11 +22,10 @@ var odatajs = {}; odatajs.version = { major: 4, minor: 0, - build: 0 + build: 1 }; // core stuff, alway needed -odatajs.deferred = require('./lib/deferred.js'); odatajs.utils = require('./lib/utils.js'); // only neede for xml metadata @@ -34,13 +33,13 @@ odatajs.xml = require('./lib/xml.js'); // only need in browser case odatajs.oData = require('./lib/odata.js'); -odatajs.store = require('./lib/store.js'); -odatajs.cache = require('./lib/cache.js'); -if (typeof window !== 'undefined') { +if (odatajs.utils.inBrowser()) { //expose to browsers window object window.odatajs = odatajs; -} else { +} + +if(typeof module !== 'undefined'){ //expose in commonjs style odatajs.node = "node"; module.exports = odatajs; diff --git a/lib/cache.js b/lib/cache.js deleted file mode 100644 index dce74b6..0000000 --- a/lib/cache.js +++ /dev/null @@ -1,1445 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -'use strict'; - - /** @module cache */ - -//var odatajs = require('./odatajs/utils.js'); -var utils = require('./utils.js'); -var deferred = require('./deferred.js'); -var storeReq = require('./store.js'); -var cacheSource = require('./cache/source.js'); - - -var assigned = utils.assigned; -var delay = utils.delay; -var extend = utils.extend; -var djsassert = utils.djsassert; -var isArray = utils.isArray; -var normalizeURI = utils.normalizeURI; -var parseInt10 = utils.parseInt10; -var undefinedDefault = utils.undefinedDefault; - -var createDeferred = deferred.createDeferred; -var DjsDeferred = deferred.DjsDeferred; - - -var getJsonValueArraryLength = utils.getJsonValueArraryLength; -var sliceJsonValueArray = utils.sliceJsonValueArray; -var concatJsonValueArray = utils.concatJsonValueArray; - - - -/** Appends a page's data to the operation data. - * @param {Object} operation - Operation with (i)ndex, (c)ount and (d)ata. - * @param {Object} page - Page with (i)ndex, (c)ount and (d)ata. - */ -function appendPage(operation, page) { - - var intersection = intersectRanges(operation, page); - var start = 0; - var end = 0; - if (intersection) { - start = intersection.i - page.i; - end = start + (operation.c - getJsonValueArraryLength(operation.d)); - } - - operation.d = concatJsonValueArray(operation.d, sliceJsonValueArray(page.d, start, end)); -} - -/** Returns the {(i)ndex, (c)ount} range for the intersection of x and y. - * @param {Object} x - Range with (i)ndex and (c)ount members. - * @param {Object} y - Range with (i)ndex and (c)ount members. - * @returns {Object} The intersection (i)ndex and (c)ount; undefined if there is no intersection. - */ -function intersectRanges(x, y) { - - var xLast = x.i + x.c; - var yLast = y.i + y.c; - var resultIndex = (x.i > y.i) ? x.i : y.i; - var resultLast = (xLast < yLast) ? xLast : yLast; - var result; - if (resultLast >= resultIndex) { - result = { i: resultIndex, c: resultLast - resultIndex }; - } - - return result; -} - -/** Checks whether val is a defined number with value zero or greater. - * @param {Number} val - Value to check. - * @param {String} name - Parameter name to use in exception. - * @throws Throws an exception if the check fails - */ -function checkZeroGreater(val, name) { - - if (val === undefined || typeof val !== "number") { - throw { message: "'" + name + "' must be a number." }; - } - - if (isNaN(val) || val < 0 || !isFinite(val)) { - throw { message: "'" + name + "' must be greater than or equal to zero." }; - } -} - -/** Checks whether val is undefined or a number with value greater than zero. - * @param {Number} val - Value to check. - * @param {String} name - Parameter name to use in exception. - * @throws Throws an exception if the check fails - */ -function checkUndefinedGreaterThanZero(val, name) { - - if (val !== undefined) { - if (typeof val !== "number") { - throw { message: "'" + name + "' must be a number." }; - } - - if (isNaN(val) || val <= 0 || !isFinite(val)) { - throw { message: "'" + name + "' must be greater than zero." }; - } - } -} - -/** Checks whether val is undefined or a number - * @param {Number} val - Value to check. - * @param {String} name - Parameter name to use in exception. - * @throws Throws an exception if the check fails - */ -function checkUndefinedOrNumber(val, name) { - if (val !== undefined && (typeof val !== "number" || isNaN(val) || !isFinite(val))) { - throw { message: "'" + name + "' must be a number." }; - } -} - -/** Performs a linear search on the specified array and removes the first instance of 'item'. - * @param {Array} arr - Array to search. - * @param {*} item - Item being sought. - * @returns {Boolean} true if the item was removed otherwise false - */ -function removeFromArray(arr, item) { - - var i, len; - for (i = 0, len = arr.length; i < len; i++) { - if (arr[i] === item) { - arr.splice(i, 1); - return true; - } - } - - return false; -} - -/** Estimates the size of an object in bytes. - * Object trees are traversed recursively - * @param {Object} object - Object to determine the size of. - * @returns {Number} Estimated size of the object in bytes. - */ -function estimateSize(object) { - var size = 0; - var type = typeof object; - - if (type === "object" && object) { - for (var name in object) { - size += name.length * 2 + estimateSize(object[name]); - } - } else if (type === "string") { - size = object.length * 2; - } else { - size = 8; - } - return size; -} - -/** Snaps low and high indices into page sizes and returns a range. - * @param {Number} lowIndex - Low index to snap to a lower value. - * @param {Number} highIndex - High index to snap to a higher value. - * @param {Number} pageSize - Page size to snap to. - * @returns {Object} A range with (i)ndex and (c)ount of elements. - */ -function snapToPageBoundaries(lowIndex, highIndex, pageSize) { - lowIndex = Math.floor(lowIndex / pageSize) * pageSize; - highIndex = Math.ceil((highIndex + 1) / pageSize) * pageSize; - return { i: lowIndex, c: highIndex - lowIndex }; -} - -// The DataCache is implemented using state machines. The following constants are used to properly -// identify and label the states that these machines transition to. -var CACHE_STATE_DESTROY = "destroy"; -var CACHE_STATE_IDLE = "idle"; -var CACHE_STATE_INIT = "init"; -var CACHE_STATE_READ = "read"; -var CACHE_STATE_PREFETCH = "prefetch"; -var CACHE_STATE_WRITE = "write"; - -// DataCacheOperation state machine states. -// Transitions on operations also depend on the cache current of the cache. -var OPERATION_STATE_CANCEL = "cancel"; -var OPERATION_STATE_END = "end"; -var OPERATION_STATE_ERROR = "error"; -var OPERATION_STATE_START = "start"; -var OPERATION_STATE_WAIT = "wait"; - -// Destroy state machine states -var DESTROY_STATE_CLEAR = "clear"; - -// Read / Prefetch state machine states -var READ_STATE_DONE = "done"; -var READ_STATE_LOCAL = "local"; -var READ_STATE_SAVE = "save"; -var READ_STATE_SOURCE = "source"; - -/** Creates a new operation object. - * @class DataCacheOperation - * @param {Function} stateMachine - State machine that describes the specific behavior of the operation. - * @param {DjsDeferred} promise - Promise for requested values. - * @param {Boolean} isCancelable - Whether this operation can be canceled or not. - * @param {Number} index - Index of first item requested. - * @param {Number} count - Count of items requested. - * @param {Array} data - Array with the items requested by the operation. - * @param {Number} pending - Total number of pending prefetch records. - * @returns {DataCacheOperation} A new data cache operation instance. - */ -function DataCacheOperation(stateMachine, promise, isCancelable, index, count, data, pending) { - - var stateData; - var cacheState; - var that = this; - - that.p = promise; - that.i = index; - that.c = count; - that.d = data; - that.s = OPERATION_STATE_START; - - that.canceled = false; - that.pending = pending; - that.oncomplete = null; - - /** Transitions this operation to the cancel state and sets the canceled flag to true. - * The function is a no-op if the operation is non-cancelable. - * @method DataCacheOperation#cancel - */ - that.cancel = function cancel() { - - if (!isCancelable) { - return; - } - - var state = that.s; - if (state !== OPERATION_STATE_ERROR && state !== OPERATION_STATE_END && state !== OPERATION_STATE_CANCEL) { - that.canceled = true; - that.transition(OPERATION_STATE_CANCEL, stateData); - } - }; - - /** Transitions this operation to the end state. - * @method DataCacheOperation#complete - */ - that.complete = function () { - - djsassert(that.s !== OPERATION_STATE_END, "DataCacheOperation.complete() - operation is in the end state", that); - that.transition(OPERATION_STATE_END, stateData); - }; - - /** Transitions this operation to the error state. - * @method DataCacheOperation#error - */ - that.error = function (err) { - if (!that.canceled) { - djsassert(that.s !== OPERATION_STATE_END, "DataCacheOperation.error() - operation is in the end state", that); - djsassert(that.s !== OPERATION_STATE_ERROR, "DataCacheOperation.error() - operation is in the error state", that); - that.transition(OPERATION_STATE_ERROR, err); - } - }; - - /** Executes the operation's current state in the context of a new cache state. - * @method DataCacheOperation#run - * @param {Object} state - New cache state. - */ - that.run = function (state) { - - cacheState = state; - that.transition(that.s, stateData); - }; - - /** Transitions this operation to the wait state. - * @method DataCacheOperation#wait - */ - that.wait = function (data) { - - djsassert(that.s !== OPERATION_STATE_END, "DataCacheOperation.wait() - operation is in the end state", that); - that.transition(OPERATION_STATE_WAIT, data); - }; - - /** State machine that describes all operations common behavior. - * @method DataCacheOperation#operationStateMachine - * @param {Object} opTargetState - Operation state to transition to. - * @param {Object} cacheState - Current cache state. - * @param {Object} [data] - Additional data passed to the state. - */ - var operationStateMachine = function (opTargetState, cacheState, data) { - - switch (opTargetState) { - case OPERATION_STATE_START: - // Initial state of the operation. The operation will remain in this state until the cache has been fully initialized. - if (cacheState !== CACHE_STATE_INIT) { - stateMachine(that, opTargetState, cacheState, data); - } - break; - - case OPERATION_STATE_WAIT: - // Wait state indicating that the operation is active but waiting for an asynchronous operation to complete. - stateMachine(that, opTargetState, cacheState, data); - break; - - case OPERATION_STATE_CANCEL: - // Cancel state. - stateMachine(that, opTargetState, cacheState, data); - that.fireCanceled(); - that.transition(OPERATION_STATE_END); - break; - - case OPERATION_STATE_ERROR: - // Error state. Data is expected to be an object detailing the error condition. - stateMachine(that, opTargetState, cacheState, data); - that.canceled = true; - that.fireRejected(data); - that.transition(OPERATION_STATE_END); - break; - - case OPERATION_STATE_END: - // Final state of the operation. - if (that.oncomplete) { - that.oncomplete(that); - } - if (!that.canceled) { - that.fireResolved(); - } - stateMachine(that, opTargetState, cacheState, data); - break; - - default: - // Any other state is passed down to the state machine describing the operation's specific behavior. - - if (true) { - // Check that the state machine actually handled the sate. - var handled = stateMachine(that, opTargetState, cacheState, data); - djsassert(handled, "Bad operation state: " + opTargetState + " cacheState: " + cacheState, this); - } else { - - stateMachine(that, opTargetState, cacheState, data); - - } - - break; - } - }; - - - - /** Transitions this operation to a new state. - * @method DataCacheOperation#transition - * @param {Object} state - State to transition the operation to. - * @param {Object} [data] - - */ - that.transition = function (state, data) { - that.s = state; - stateData = data; - operationStateMachine(state, cacheState, data); - }; - - return that; -} - -/** Fires a resolved notification as necessary. - * @method DataCacheOperation#fireResolved - */ -DataCacheOperation.prototype.fireResolved = function () { - - // Fire the resolve just once. - var p = this.p; - if (p) { - this.p = null; - p.resolve(this.d); - } -}; - -/** Fires a rejected notification as necessary. - * @method DataCacheOperation#fireRejected - */ -DataCacheOperation.prototype.fireRejected = function (reason) { - - // Fire the rejection just once. - var p = this.p; - if (p) { - this.p = null; - p.reject(reason); - } -}; - -/** Fires a canceled notification as necessary. - * @method DataCacheOperation#fireCanceled - */ -DataCacheOperation.prototype.fireCanceled = function () { - - this.fireRejected({ canceled: true, message: "Operation canceled" }); -}; - - -/** Creates a data cache for a collection that is efficiently loaded on-demand. - * @class DataCache - * @param options - Options for the data cache, including name, source, pageSize, - * prefetchSize, cacheSize, storage mechanism, and initial prefetch and local-data handler. - * @returns {DataCache} A new data cache instance. - */ -function DataCache(options) { - - var state = CACHE_STATE_INIT; - var stats = { counts: 0, netReads: 0, prefetches: 0, cacheReads: 0 }; - - var clearOperations = []; - var readOperations = []; - var prefetchOperations = []; - - var actualCacheSize = 0; // Actual cache size in bytes. - var allDataLocal = false; // Whether all data is local. - var cacheSize = undefinedDefault(options.cacheSize, 1048576); // Requested cache size in bytes, default 1 MB. - var collectionCount = 0; // Number of elements in the server collection. - var highestSavedPage = 0; // Highest index of all the saved pages. - var highestSavedPageSize = 0; // Item count of the saved page with the highest index. - var overflowed = cacheSize === 0; // If the cache has overflowed (actualCacheSize > cacheSize or cacheSize == 0); - var pageSize = undefinedDefault(options.pageSize, 50); // Number of elements to store per page. - var prefetchSize = undefinedDefault(options.prefetchSize, pageSize); // Number of elements to prefetch from the source when the cache is idling. - var version = "1.0"; - var cacheFailure; - - var pendingOperations = 0; - - var source = options.source; - if (typeof source === "string") { - // Create a new cache source. - source = new cacheSource.ODataCacheSource(options); - } - source.options = options; - - // Create a cache local store. - var store = storeReq.createStore(options.name, options.mechanism); - - var that = this; - - that.onidle = options.idle; - that.stats = stats; - - /** Counts the number of items in the collection. - * @method DataCache#count - * @returns {Object} A promise with the number of items. - */ - that.count = function () { - - if (cacheFailure) { - throw cacheFailure; - } - - var deferred = createDeferred(); - var canceled = false; - - if (allDataLocal) { - delay(function () { - deferred.resolve(collectionCount); - }); - - return deferred.promise(); - } - - // TODO: Consider returning the local data count instead once allDataLocal flag is set to true. - var request = source.count(function (count) { - request = null; - stats.counts++; - deferred.resolve(count); - }, function (err) { - request = null; - deferred.reject(extend(err, { canceled: canceled })); - }); - - return extend(deferred.promise(), { - - /** Aborts the count operation (used within promise callback) - * @method DataCache#cancelCount - */ - cancel: function () { - - if (request) { - canceled = true; - request.abort(); - request = null; - } - } - }); - }; - - /** Cancels all running operations and clears all local data associated with this cache. - * New read requests made while a clear operation is in progress will not be canceled. - * Instead they will be queued for execution once the operation is completed. - * @method DataCache#clear - * @returns {Object} A promise that has no value and can't be canceled. - */ - that.clear = function () { - - if (cacheFailure) { - throw cacheFailure; - } - - if (clearOperations.length === 0) { - var deferred = createDeferred(); - var op = new DataCacheOperation(destroyStateMachine, deferred, false); - queueAndStart(op, clearOperations); - return deferred.promise(); - } - return clearOperations[0].p; - }; - - /** Filters the cache data based a predicate. - * Specifying a negative count value will yield all the items in the cache that satisfy the predicate. - * @method DataCache#filterForward - * @param {Number} index - The index of the item to start filtering forward from. - * @param {Number} count - Maximum number of items to include in the result. - * @param {Function} predicate - Callback function returning a boolean that determines whether an item should be included in the result or not. - * @returns {DjsDeferred} A promise for an array of results. - */ - that.filterForward = function (index, count, predicate) { - return filter(index, count, predicate, false); - }; - - /** Filters the cache data based a predicate. - * Specifying a negative count value will yield all the items in the cache that satisfy the predicate. - * @method DataCache#filterBack - * @param {Number} index - The index of the item to start filtering backward from. - * @param {Number} count - Maximum number of items to include in the result. - * @param {Function} predicate - Callback function returning a boolean that determines whether an item should be included in the result or not. - * @returns {DjsDeferred} A promise for an array of results. - */ - that.filterBack = function (index, count, predicate) { - return filter(index, count, predicate, true); - }; - - /** Reads a range of adjacent records. - * New read requests made while a clear operation is in progress will not be canceled. - * Instead they will be queued for execution once the operation is completed. - * @method DataCache#readRange - * @param {Number} index - Zero-based index of record range to read. - * @param {Number} count - Number of records in the range. - * @returns {DjsDeferred} A promise for an array of records; less records may be returned if the - * end of the collection is found. - */ - that.readRange = function (index, count) { - - checkZeroGreater(index, "index"); - checkZeroGreater(count, "count"); - - if (cacheFailure) { - throw cacheFailure; - } - - var deferred = createDeferred(); - - // Merging read operations would be a nice optimization here. - var op = new DataCacheOperation(readStateMachine, deferred, true, index, count, {}, 0); - queueAndStart(op, readOperations); - - return extend(deferred.promise(), { - cancel: function () { - /** Aborts the readRange operation (used within promise callback) - * @method DataCache#cancelReadRange - */ - op.cancel(); - } - }); - }; - - /** Creates an Observable object that enumerates all the cache contents. - * @method DataCache#toObservable - * @returns A new Observable object that enumerates all the cache contents. - */ - that.ToObservable = that.toObservable = function () { - if ( !utils.inBrowser()) { - throw { message: "Only in broser supported" }; - } - - if (!window.Rx || !window.Rx.Observable) { - throw { message: "Rx library not available - include rx.js" }; - } - - if (cacheFailure) { - throw cacheFailure; - } - - //return window.Rx.Observable.create(function (obs) { - return new window.Rx.Observable(function (obs) { - var disposed = false; - var index = 0; - - var errorCallback = function (error) { - if (!disposed) { - obs.onError(error); - } - }; - - var successCallback = function (data) { - if (!disposed) { - var i, len; - for (i = 0, len = data.value.length; i < len; i++) { - // The wrapper automatically checks for Dispose - // on the observer, so we don't need to check it here. - //obs.next(data.value[i]); - obs.onNext(data.value[i]); - } - - if (data.value.length < pageSize) { - //obs.completed(); - obs.onCompleted(); - } else { - index += pageSize; - that.readRange(index, pageSize).then(successCallback, errorCallback); - } - } - }; - - that.readRange(index, pageSize).then(successCallback, errorCallback); - - return { Dispose: function () { - obs.dispose(); // otherwise the check isStopped obs.onNext(data.value[i]); - disposed = true; - } }; - }); - }; - - /** Creates a function that handles a callback by setting the cache into failure mode. - * @method DataCache~cacheFailureCallback - * @param {String} message - Message text. - * @returns {Function} Function to use as error callback. - * This function will specifically handle problems with critical store resources - * during cache initialization. - */ - var cacheFailureCallback = function (message) { - - - return function (error) { - cacheFailure = { message: message, error: error }; - - // Destroy any pending clear or read operations. - // At this point there should be no prefetch operations. - // Count operations will go through but are benign because they - // won't interact with the store. - djsassert(prefetchOperations.length === 0, "prefetchOperations.length === 0"); - var i, len; - for (i = 0, len = readOperations.length; i < len; i++) { - readOperations[i].fireRejected(cacheFailure); - } - for (i = 0, len = clearOperations.length; i < len; i++) { - clearOperations[i].fireRejected(cacheFailure); - } - - // Null out the operation arrays. - readOperations = clearOperations = null; - }; - }; - - /** Updates the cache's state and signals all pending operations of the change. - * @method DataCache~changeState - * @param {Object} newState - New cache state. - * This method is a no-op if the cache's current state and the new state are the same. - */ - var changeState = function (newState) { - - if (newState !== state) { - state = newState; - var operations = clearOperations.concat(readOperations, prefetchOperations); - var i, len; - for (i = 0, len = operations.length; i < len; i++) { - operations[i].run(state); - } - } - }; - - /** Removes all the data stored in the cache. - * @method DataCache~clearStore - * @returns {DjsDeferred} A promise with no value. - */ - var clearStore = function () { - djsassert(state === CACHE_STATE_DESTROY || state === CACHE_STATE_INIT, "DataCache.clearStore() - cache is not on the destroy or initialize state, current sate = " + state); - - var deferred = new DjsDeferred(); - store.clear(function () { - - // Reset the cache settings. - actualCacheSize = 0; - allDataLocal = false; - collectionCount = 0; - highestSavedPage = 0; - highestSavedPageSize = 0; - overflowed = cacheSize === 0; - - // version is not reset, in case there is other state in eg V1.1 that is still around. - - // Reset the cache stats. - stats = { counts: 0, netReads: 0, prefetches: 0, cacheReads: 0 }; - that.stats = stats; - - store.close(); - deferred.resolve(); - }, function (err) { - deferred.reject(err); - }); - return deferred; - }; - - /** Removes an operation from the caches queues and changes the cache state to idle. - * @method DataCache~dequeueOperation - * @param {DataCacheOperation} operation - Operation to dequeue. - * This method is used as a handler for the operation's oncomplete event. - */ - var dequeueOperation = function (operation) { - - var removed = removeFromArray(clearOperations, operation); - if (!removed) { - removed = removeFromArray(readOperations, operation); - if (!removed) { - removeFromArray(prefetchOperations, operation); - } - } - - pendingOperations--; - changeState(CACHE_STATE_IDLE); - }; - - /** Requests data from the cache source. - * @method DataCache~fetchPage - * @param {Number} start - Zero-based index of items to request. - * @returns {DjsDeferred} A promise for a page object with (i)ndex, (c)ount, (d)ata. - */ - var fetchPage = function (start) { - - djsassert(state !== CACHE_STATE_DESTROY, "DataCache.fetchPage() - cache is on the destroy state"); - djsassert(state !== CACHE_STATE_IDLE, "DataCache.fetchPage() - cache is on the idle state"); - - var deferred = new DjsDeferred(); - var canceled = false; - - var request = source.read(start, pageSize, function (data) { - var length = getJsonValueArraryLength(data); - var page = { i: start, c: length, d: data }; - deferred.resolve(page); - }, function (err) { - deferred.reject(err); - }); - - return extend(deferred, { - cancel: function () { - if (request) { - request.abort(); - canceled = true; - request = null; - } - } - }); - }; - - /** Filters the cache data based a predicate. - * @method DataCache~filter - * @param {Number} index - The index of the item to start filtering from. - * @param {Number} count - Maximum number of items to include in the result. - * @param {Function} predicate - Callback function returning a boolean that determines whether an item should be included in the result or not. - * @param {Boolean} backwards - True if the filtering should move backward from the specified index, falsey otherwise. - * Specifying a negative count value will yield all the items in the cache that satisfy the predicate. - * @returns {DjsDeferred} A promise for an array of results. - */ - var filter = function (index, count, predicate, backwards) { - - index = parseInt10(index); - count = parseInt10(count); - - if (isNaN(index)) { - throw { message: "'index' must be a valid number.", index: index }; - } - if (isNaN(count)) { - throw { message: "'count' must be a valid number.", count: count }; - } - - if (cacheFailure) { - throw cacheFailure; - } - - index = Math.max(index, 0); - - var deferred = createDeferred(); - var returnData = {}; - returnData.value = []; - var canceled = false; - var pendingReadRange = null; - - var readMore = function (readIndex, readCount) { - if (!canceled) { - if (count > 0 && returnData.value.length >= count) { - deferred.resolve(returnData); - } else { - pendingReadRange = that.readRange(readIndex, readCount).then(function (data) { - if (data["@odata.context"] && !returnData["@odata.context"]) { - returnData["@odata.context"] = data["@odata.context"]; - } - - for (var i = 0, length = data.value.length; i < length && (count < 0 || returnData.value.length < count); i++) { - var dataIndex = backwards ? length - i - 1 : i; - var item = data.value[dataIndex]; - if (predicate(item)) { - var element = { - index: readIndex + dataIndex, - item: item - }; - - backwards ? returnData.value.unshift(element) : returnData.value.push(element); - } - } - - // Have we reached the end of the collection? - if ((!backwards && data.value.length < readCount) || (backwards && readIndex <= 0)) { - deferred.resolve(returnData); - } else { - var nextIndex = backwards ? Math.max(readIndex - pageSize, 0) : readIndex + readCount; - readMore(nextIndex, pageSize); - } - }, function (err) { - deferred.reject(err); - }); - } - } - }; - - // Initially, we read from the given starting index to the next/previous page boundary - var initialPage = snapToPageBoundaries(index, index, pageSize); - var initialIndex = backwards ? initialPage.i : index; - var initialCount = backwards ? index - initialPage.i + 1 : initialPage.i + initialPage.c - index; - readMore(initialIndex, initialCount); - - return extend(deferred.promise(), { - /** Aborts the filter operation (used within promise callback) - * @method DataCache#cancelFilter - */ - cancel: function () { - - if (pendingReadRange) { - pendingReadRange.cancel(); - } - canceled = true; - } - }); - }; - - /** Fires an onidle event if any functions are assigned. - * @method DataCache~fireOnIdle - */ - var fireOnIdle = function () { - - if (that.onidle && pendingOperations === 0) { - that.onidle(); - } - }; - - /** Creates and starts a new prefetch operation. - * @method DataCache~prefetch - * @param {Number} start - Zero-based index of the items to prefetch. - * This method is a no-op if any of the following conditions is true: - * 1.- prefetchSize is 0 - * 2.- All data has been read and stored locally in the cache. - * 3.- There is already an all data prefetch operation queued. - * 4.- The cache has run out of available space (overflowed). - */ - var prefetch = function (start) { - - - if (allDataLocal || prefetchSize === 0 || overflowed) { - return; - } - - djsassert(state === CACHE_STATE_READ, "DataCache.prefetch() - cache is not on the read state, current state: " + state); - - if (prefetchOperations.length === 0 || (prefetchOperations[0] && prefetchOperations[0].c !== -1)) { - // Merging prefetch operations would be a nice optimization here. - var op = new DataCacheOperation(prefetchStateMachine, null, true, start, prefetchSize, null, prefetchSize); - queueAndStart(op, prefetchOperations); - } - }; - - /** Queues an operation and runs it. - * @param {DataCacheOperation} op - Operation to queue. - * @param {Array} queue - Array that will store the operation. - */ - var queueAndStart = function (op, queue) { - - op.oncomplete = dequeueOperation; - queue.push(op); - pendingOperations++; - op.run(state); - }; - - /** Requests a page from the cache local store. - * @method DataCache~readPage - * @param {Number} key - Zero-based index of the reuqested page. - * @returns {DjsDeferred} A promise for a found flag and page object with (i)ndex, (c)ount, (d)ata, and (t)icks. - */ - var readPage = function (key) { - - djsassert(state !== CACHE_STATE_DESTROY, "DataCache.readPage() - cache is on the destroy state"); - - var canceled = false; - var deferred = extend(new DjsDeferred(), { - /** Aborts the readPage operation. (used within promise callback) - * @method DataCache#cancelReadPage - */ - cancel: function () { - canceled = true; - } - }); - - var error = storeFailureCallback(deferred, "Read page from store failure"); - - store.contains(key, function (contained) { - if (canceled) { - return; - } - if (contained) { - store.read(key, function (_, data) { - if (!canceled) { - deferred.resolve(data !== undefined, data); - } - }, error); - return; - } - deferred.resolve(false); - }, error); - return deferred; - }; - - /** Saves a page to the cache local store. - * @method DataCache~savePage - * @param {Number} key - Zero-based index of the requested page. - * @param {Object} page - Object with (i)ndex, (c)ount, (d)ata, and (t)icks. - * @returns {DjsDeferred} A promise with no value. - */ - var savePage = function (key, page) { - - djsassert(state !== CACHE_STATE_DESTROY, "DataCache.savePage() - cache is on the destroy state"); - djsassert(state !== CACHE_STATE_IDLE, "DataCache.savePage() - cache is on the idle state"); - - var canceled = false; - - var deferred = extend(new DjsDeferred(), { - /** Aborts the savePage operation. (used within promise callback) - * @method DataCache#cancelReadPage - */ - cancel: function () { - canceled = true; - } - }); - - var error = storeFailureCallback(deferred, "Save page to store failure"); - - var resolve = function () { - deferred.resolve(true); - }; - - if (page.c > 0) { - var pageBytes = estimateSize(page); - overflowed = cacheSize >= 0 && cacheSize < actualCacheSize + pageBytes; - - if (!overflowed) { - store.addOrUpdate(key, page, function () { - updateSettings(page, pageBytes); - saveSettings(resolve, error); - }, error); - } else { - resolve(); - } - } else { - updateSettings(page, 0); - saveSettings(resolve, error); - } - return deferred; - }; - - /** Saves the cache's current settings to the local store. - * @method DataCache~saveSettings - * @param {Function} success - Success callback. - * @param {Function} error - Errror callback. - */ - var saveSettings = function (success, error) { - - var settings = { - actualCacheSize: actualCacheSize, - allDataLocal: allDataLocal, - cacheSize: cacheSize, - collectionCount: collectionCount, - highestSavedPage: highestSavedPage, - highestSavedPageSize: highestSavedPageSize, - pageSize: pageSize, - sourceId: source.identifier, - version: version - }; - - store.addOrUpdate("__settings", settings, success, error); - }; - - /** Creates a function that handles a store error. - * @method DataCache~storeFailureCallback - * @param {DjsDeferred} deferred - Deferred object to resolve. - * @returns {Function} Function to use as error callback. - - * This function will specifically handle problems when interacting with the store. - */ - var storeFailureCallback = function (deferred/*, message*/) { - - - return function (/*error*/) { - // var console = windo1w.console; - // if (console && console.log) { - // console.log(message); - // console.dir(error); - // } - deferred.resolve(false); - }; - }; - - /** Updates the cache's settings based on a page object. - * @method DataCache~updateSettings - * @param {Object} page - Object with (i)ndex, (c)ount, (d)ata. - * @param {Number} pageBytes - Size of the page in bytes. - */ - var updateSettings = function (page, pageBytes) { - - var pageCount = page.c; - var pageIndex = page.i; - - // Detect the collection size. - if (pageCount === 0) { - if (highestSavedPage === pageIndex - pageSize) { - collectionCount = highestSavedPage + highestSavedPageSize; - } - } else { - highestSavedPage = Math.max(highestSavedPage, pageIndex); - if (highestSavedPage === pageIndex) { - highestSavedPageSize = pageCount; - } - actualCacheSize += pageBytes; - if (pageCount < pageSize && !collectionCount) { - collectionCount = pageIndex + pageCount; - } - } - - // Detect the end of the collection. - if (!allDataLocal && collectionCount === highestSavedPage + highestSavedPageSize) { - allDataLocal = true; - } - }; - - /** State machine describing the behavior for cancelling a read or prefetch operation. - * @method DataCache~cancelStateMachine - * @param {DataCacheOperation} operation - Operation being run. - * @param {Object} opTargetState - Operation state to transition to. - * @param {Object} cacheState - Current cache state. - * @param {Object} [data] - - * This state machine contains behavior common to read and prefetch operations. - */ - var cancelStateMachine = function (operation, opTargetState, cacheState, data) { - - - var canceled = operation.canceled && opTargetState !== OPERATION_STATE_END; - if (canceled) { - if (opTargetState === OPERATION_STATE_CANCEL) { - // Cancel state. - // Data is expected to be any pending request made to the cache. - if (data && data.cancel) { - data.cancel(); - } - } - } - return canceled; - }; - - /** State machine describing the behavior of a clear operation. - * @method DataCache~destroyStateMachine - * @param {DataCacheOperation} operation - Operation being run. - * @param {Object} opTargetState - Operation state to transition to. - * @param {Object} cacheState - Current cache state. - - * Clear operations have the highest priority and can't be interrupted by other operations; however, - * they will preempt any other operation currently executing. - */ - var destroyStateMachine = function (operation, opTargetState, cacheState) { - - - var transition = operation.transition; - - // Signal the cache that a clear operation is running. - if (cacheState !== CACHE_STATE_DESTROY) { - changeState(CACHE_STATE_DESTROY); - return true; - } - - switch (opTargetState) { - case OPERATION_STATE_START: - // Initial state of the operation. - transition(DESTROY_STATE_CLEAR); - break; - - case OPERATION_STATE_END: - // State that signals the operation is done. - fireOnIdle(); - break; - - case DESTROY_STATE_CLEAR: - // State that clears all the local data of the cache. - clearStore().then(function () { - // Terminate the operation once the local store has been cleared. - operation.complete(); - }); - // Wait until the clear request completes. - operation.wait(); - break; - - default: - return false; - } - return true; - }; - - /** State machine describing the behavior of a prefetch operation. - * @method DataCache~prefetchStateMachine - * @param {DataCacheOperation} operation - Operation being run. - * @param {Object} opTargetState - Operation state to transition to. - * @param {Object} cacheState - Current cache state. - * @param {Object} [data] - - - * Prefetch operations have the lowest priority and will be interrupted by operations of - * other kinds. A preempted prefetch operation will resume its execution only when the state - * of the cache returns to idle. - * - * If a clear operation starts executing then all the prefetch operations are canceled, - * even if they haven't started executing yet. - */ - var prefetchStateMachine = function (operation, opTargetState, cacheState, data) { - - - // Handle cancelation - if (!cancelStateMachine(operation, opTargetState, cacheState, data)) { - - var transition = operation.transition; - - // Handle preemption - if (cacheState !== CACHE_STATE_PREFETCH) { - if (cacheState === CACHE_STATE_DESTROY) { - if (opTargetState !== OPERATION_STATE_CANCEL) { - operation.cancel(); - } - } else if (cacheState === CACHE_STATE_IDLE) { - // Signal the cache that a prefetch operation is running. - changeState(CACHE_STATE_PREFETCH); - } - return true; - } - - switch (opTargetState) { - case OPERATION_STATE_START: - // Initial state of the operation. - if (prefetchOperations[0] === operation) { - transition(READ_STATE_LOCAL, operation.i); - } - break; - - case READ_STATE_DONE: - // State that determines if the operation can be resolved or has to - // continue processing. - // Data is expected to be the read page. - var pending = operation.pending; - - if (pending > 0) { - pending -= Math.min(pending, data.c); - } - - // Are we done, or has all the data been stored? - if (allDataLocal || pending === 0 || data.c < pageSize || overflowed) { - operation.complete(); - } else { - // Continue processing the operation. - operation.pending = pending; - transition(READ_STATE_LOCAL, data.i + pageSize); - } - break; - - default: - return readSaveStateMachine(operation, opTargetState, cacheState, data, true); - } - } - return true; - }; - - /** State machine describing the behavior of a read operation. - * @method DataCache~readStateMachine - * @param {DataCacheOperation} operation - Operation being run. - * @param {Object} opTargetState - Operation state to transition to. - * @param {Object} cacheState - Current cache state. - * @param {Object} [data] - - - * Read operations have a higher priority than prefetch operations, but lower than - * clear operations. They will preempt any prefetch operation currently running - * but will be interrupted by a clear operation. - * - * If a clear operation starts executing then all the currently running - * read operations are canceled. Read operations that haven't started yet will - * wait in the start state until the destory operation finishes. - */ - var readStateMachine = function (operation, opTargetState, cacheState, data) { - - - // Handle cancelation - if (!cancelStateMachine(operation, opTargetState, cacheState, data)) { - - var transition = operation.transition; - - // Handle preemption - if (cacheState !== CACHE_STATE_READ && opTargetState !== OPERATION_STATE_START) { - if (cacheState === CACHE_STATE_DESTROY) { - if (opTargetState !== OPERATION_STATE_START) { - operation.cancel(); - } - } else if (cacheState !== CACHE_STATE_WRITE) { - // Signal the cache that a read operation is running. - djsassert(state == CACHE_STATE_IDLE || state === CACHE_STATE_PREFETCH, "DataCache.readStateMachine() - cache is not on the read or idle state."); - changeState(CACHE_STATE_READ); - } - - return true; - } - - switch (opTargetState) { - case OPERATION_STATE_START: - // Initial state of the operation. - // Wait until the cache is idle or prefetching. - if (cacheState === CACHE_STATE_IDLE || cacheState === CACHE_STATE_PREFETCH) { - // Signal the cache that a read operation is running. - changeState(CACHE_STATE_READ); - if (operation.c >= 0) { - // Snap the requested range to a page boundary. - var range = snapToPageBoundaries(operation.i, operation.c, pageSize); - transition(READ_STATE_LOCAL, range.i); - } else { - transition(READ_STATE_DONE, operation); - } - } - break; - - case READ_STATE_DONE: - // State that determines if the operation can be resolved or has to - // continue processing. - // Data is expected to be the read page. - appendPage(operation, data); - var len = getJsonValueArraryLength(operation.d); - // Are we done? - if (operation.c === len || data.c < pageSize) { - // Update the stats, request for a prefetch operation. - stats.cacheReads++; - prefetch(data.i + data.c); - // Terminate the operation. - operation.complete(); - } else { - // Continue processing the operation. - transition(READ_STATE_LOCAL, data.i + pageSize); - } - break; - - default: - return readSaveStateMachine(operation, opTargetState, cacheState, data, false); - } - } - - return true; - }; - - /** State machine describing the behavior for reading and saving data into the cache. - * @method DataCache~readSaveStateMachine - * @param {DataCacheOperation} operation - Operation being run. - * @param {Object} opTargetState - Operation state to transition to. - * @param {Object} cacheState - Current cache state. - * @param {Object} [data] - - * @param {Boolean} isPrefetch - Flag indicating whether a read (false) or prefetch (true) operation is running. - * This state machine contains behavior common to read and prefetch operations. - */ - var readSaveStateMachine = function (operation, opTargetState, cacheState, data, isPrefetch) { - - var error = operation.error; - var transition = operation.transition; - var wait = operation.wait; - var request; - - switch (opTargetState) { - case OPERATION_STATE_END: - // State that signals the operation is done. - fireOnIdle(); - break; - - case READ_STATE_LOCAL: - // State that requests for a page from the local store. - // Data is expected to be the index of the page to request. - request = readPage(data).then(function (found, page) { - // Signal the cache that a read operation is running. - if (!operation.canceled) { - if (found) { - // The page is in the local store, check if the operation can be resolved. - transition(READ_STATE_DONE, page); - } else { - // The page is not in the local store, request it from the source. - transition(READ_STATE_SOURCE, data); - } - } - }); - break; - - case READ_STATE_SOURCE: - // State that requests for a page from the cache source. - // Data is expected to be the index of the page to request. - request = fetchPage(data).then(function (page) { - // Signal the cache that a read operation is running. - if (!operation.canceled) { - // Update the stats and save the page to the local store. - if (isPrefetch) { - stats.prefetches++; - } else { - stats.netReads++; - } - transition(READ_STATE_SAVE, page); - } - }, error); - break; - - case READ_STATE_SAVE: - // State that saves a page to the local store. - // Data is expected to be the page to save. - // Write access to the store is exclusive. - if (cacheState !== CACHE_STATE_WRITE) { - changeState(CACHE_STATE_WRITE); - request = savePage(data.i, data).then(function (saved) { - if (!operation.canceled) { - if (!saved && isPrefetch) { - operation.pending = 0; - } - // Check if the operation can be resolved. - transition(READ_STATE_DONE, data); - } - changeState(CACHE_STATE_IDLE); - }); - } - break; - - default: - // Unknown state that can't be handled by this state machine. - return false; - } - - if (request) { - // The operation might have been canceled between stack frames do to the async calls. - if (operation.canceled) { - request.cancel(); - } else if (operation.s === opTargetState) { - // Wait for the request to complete. - wait(request); - } - } - - return true; - }; - - // Initialize the cache. - store.read("__settings", function (_, settings) { - if (assigned(settings)) { - var settingsVersion = settings.version; - if (!settingsVersion || settingsVersion.indexOf("1.") !== 0) { - cacheFailureCallback("Unsupported cache store version " + settingsVersion)(); - return; - } - - if (pageSize !== settings.pageSize || source.identifier !== settings.sourceId) { - // The shape or the source of the data was changed so invalidate the store. - clearStore().then(function () { - // Signal the cache is fully initialized. - changeState(CACHE_STATE_IDLE); - }, cacheFailureCallback("Unable to clear store during initialization")); - } else { - // Restore the saved settings. - actualCacheSize = settings.actualCacheSize; - allDataLocal = settings.allDataLocal; - cacheSize = settings.cacheSize; - collectionCount = settings.collectionCount; - highestSavedPage = settings.highestSavedPage; - highestSavedPageSize = settings.highestSavedPageSize; - version = settingsVersion; - - // Signal the cache is fully initialized. - changeState(CACHE_STATE_IDLE); - } - } else { - // This is a brand new cache. - saveSettings(function () { - // Signal the cache is fully initialized. - changeState(CACHE_STATE_IDLE); - }, cacheFailureCallback("Unable to write settings during initialization.")); - } - }, cacheFailureCallback("Unable to read settings from store.")); - - return that; -} - -/** Creates a data cache for a collection that is efficiently loaded on-demand. - * @param options - * Options for the data cache, including name, source, pageSize, TODO check doku - * prefetchSize, cacheSize, storage mechanism, and initial prefetch and local-data handler. - * @returns {DataCache} A new data cache instance. - */ -function createDataCache (options) { - checkUndefinedGreaterThanZero(options.pageSize, "pageSize"); - checkUndefinedOrNumber(options.cacheSize, "cacheSize"); - checkUndefinedOrNumber(options.prefetchSize, "prefetchSize"); - - if (!assigned(options.name)) { - throw { message: "Undefined or null name", options: options }; - } - - if (!assigned(options.source)) { - throw { message: "Undefined source", options: options }; - } - - return new DataCache(options); -} - - -/** estimateSize (see {@link estimateSize}) */ -exports.estimateSize = estimateSize; - -/** createDataCache */ -exports.createDataCache = createDataCache; - - - diff --git a/lib/cache/source.js b/lib/cache/source.js deleted file mode 100644 index 08dea0a..0000000 --- a/lib/cache/source.js +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -'use strict'; - - /** @module cache/source */ - -var utils = require("./../utils.js"); -var odataRequest = require("./../odata.js"); - -var parseInt10 = utils.parseInt10; -var normalizeURICase = utils.normalizeURICase; - - - - -/** Appends the specified escaped query option to the specified URI. - * @param {String} uri - URI to append option to. - * @param {String} queryOption - Escaped query option to append. - */ -function appendQueryOption(uri, queryOption) { - var separator = (uri.indexOf("?") >= 0) ? "&" : "?"; - return uri + separator + queryOption; -} - -/** Appends the specified segment to the given URI. - * @param {String} uri - URI to append a segment to. - * @param {String} segment - Segment to append. - * @returns {String} The original URI with a new segment appended. - */ -function appendSegment(uri, segment) { - var index = uri.indexOf("?"); - var queryPortion = ""; - if (index >= 0) { - queryPortion = uri.substr(index); - uri = uri.substr(0, index); - } - - if (uri[uri.length - 1] !== "/") { - uri += "/"; - } - return uri + segment + queryPortion; -} - -/** Builds a request object to GET the specified URI. - * @param {String} uri - URI for request. - * @param {Object} options - Additional options. - */ -function buildODataRequest(uri, options) { - return { - method: "GET", - requestUri: uri, - user: options.user, - password: options.password, - enableJsonpCallback: options.enableJsonpCallback, - callbackParameterName: options.callbackParameterName, - formatQueryString: options.formatQueryString - }; -} - -/** Finds the index where the value of a query option starts. - * @param {String} uri - URI to search in. - * @param {String} name - Name to look for. - * @returns {Number} The index where the query option starts. - */ -function findQueryOptionStart(uri, name) { - var result = -1; - var queryIndex = uri.indexOf("?"); - if (queryIndex !== -1) { - var start = uri.indexOf("?" + name + "=", queryIndex); - if (start === -1) { - start = uri.indexOf("&" + name + "=", queryIndex); - } - if (start !== -1) { - result = start + name.length + 2; - } - } - return result; -} - -/** Gets data from an OData service. - * @param {String} uri - URI to the OData service. - * @param {Object} options - Object with additional well-known request options. - * @param {Function} success - Success callback. - * @param {Function} error - Error callback. - * @returns {Object} Object with an abort method. - */ -function queryForData (uri, options, success, error) { - return queryForDataInternal(uri, options, {}, success, error); -} - -/** Gets data from an OData service taking into consideration server side paging. - * @param {String} uri - URI to the OData service. - * @param {Object} options - Object with additional well-known request options. - * @param {Array} data - Array that stores the data provided by the OData service. - * @param {Function} success - Success callback. - * @param {Function} error - Error callback. - * @returns {Object} Object with an abort method. - */ -function queryForDataInternal(uri, options, data, success, error) { - - var request = buildODataRequest(uri, options); - var currentRequest = odataRequest.request(request, function (newData) { - var nextLink = newData["@odata.nextLink"]; - if (nextLink) { - var index = uri.indexOf(".svc/", 0); - if (index != -1) { - nextLink = uri.substring(0, index + 5) + nextLink; - } - } - - if (data.value && newData.value) { - data.value = data.value.concat(newData.value); - } - else { - for (var property in newData) { - if (property != "@odata.nextLink") { - data[property] = newData[property]; - } - } - } - - if (nextLink) { - currentRequest = queryForDataInternal(nextLink, options, data, success, error); - } - else { - success(data); - } - }, error, undefined, options.httpClient, options.metadata); - - return { - abort: function () { - currentRequest.abort(); - } - }; -} - -/** Creates a data cache source object for requesting data from an OData service. - * @class ODataCacheSource - * @param options - Options for the cache data source. - * @returns {ODataCacheSource} A new data cache source instance. - */ -function ODataCacheSource (options) { - var that = this; - var uri = options.source; - - that.identifier = normalizeURICase(encodeURI(decodeURI(uri))); - that.options = options; - - /** Gets the number of items in the collection. - * @method ODataCacheSource#count - * @param {Function} success - Success callback with the item count. - * @param {Function} error - Error callback. - * @returns {Object} Request object with an abort method. - */ - that.count = function (success, error) { - var options = that.options; - return odataRequest.request( - buildODataRequest(appendSegment(uri, "$count"), options), - function (data) { - var count = parseInt10(data.toString()); - if (isNaN(count)) { - error({ message: "Count is NaN", count: count }); - } else { - success(count); - } - }, error, undefined, options.httpClient, options.metadata - ); - }; - - /** Gets a number of consecutive items from the collection. - * @method ODataCacheSource#read - * @param {Number} index - Zero-based index of the items to retrieve. - * @param {Number} count - Number of items to retrieve. - * @param {Function} success - Success callback with the requested items. - * @param {Function} error - Error callback. - * @returns {Object} Request object with an abort method. - */ - that.read = function (index, count, success, error) { - - var queryOptions = "$skip=" + index + "&$top=" + count; - return queryForData(appendQueryOption(uri, queryOptions), that.options, success, error); - }; - - return that; -} - - - -/** ODataCacheSource (see {@link ODataCacheSource}) */ -exports.ODataCacheSource = ODataCacheSource; \ No newline at end of file diff --git a/lib/deferred.js b/lib/deferred.js deleted file mode 100644 index 520a857..0000000 --- a/lib/deferred.js +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -'use strict'; - -/** @module odatajs/deferred */ - - - -/** Creates a new function to forward a call. - * @param {Object} thisValue - Value to use as the 'this' object. - * @param {String} name - Name of function to forward to. - * @param {Object} returnValue - Return value for the forward call (helps keep identity when chaining calls). - * @returns {Function} A new function that will forward a call. - */ -function forwardCall(thisValue, name, returnValue) { - return function () { - thisValue[name].apply(thisValue, arguments); - return returnValue; - }; -} - -/** Initializes a new DjsDeferred object. - * - * @class DjsDeferred - */ - function DjsDeferred() { - this._arguments = undefined; - this._done = undefined; - this._fail = undefined; - this._resolved = false; - this._rejected = false; -} - - -DjsDeferred.prototype = { - - /** Adds success and error callbacks for this deferred object. - * See Compatibility Note A. - * @method DjsDeferred#then - * @param {function} [fulfilledHandler] - Success callback ( may be null) - * @param {function} [errorHandler] - Error callback ( may be null) - */ - then: function (fulfilledHandler, errorHandler) { - - if (fulfilledHandler) { - if (!this._done) { - this._done = [fulfilledHandler]; - } else { - this._done.push(fulfilledHandler); - } - } - - if (errorHandler) { - if (!this._fail) { - this._fail = [errorHandler]; - } else { - this._fail.push(errorHandler); - } - } - - //// See Compatibility Note A in the DjsDeferred constructor. - //// if (!this._next) { - //// this._next = createDeferred(); - //// } - //// return this._next.promise(); - - if (this._resolved) { - this.resolve.apply(this, this._arguments); - } else if (this._rejected) { - this.reject.apply(this, this._arguments); - } - - return this; - }, - - /** Invokes success callbacks for this deferred object. - * All arguments are forwarded to success callbacks. - * @method DjsDeferred#resolve - */ - resolve: function (/* args */) { - if (this._done) { - var i, len; - for (i = 0, len = this._done.length; i < len; i++) { - //// See Compability Note B - Fulfillment value. - //// var nextValue = - this._done[i].apply(null, arguments); - } - - //// See Compatibility Note A in the DjsDeferred constructor. - //// this._next.resolve(nextValue); - //// delete this._next; - - this._done = undefined; - this._resolved = false; - this._arguments = undefined; - } else { - this._resolved = true; - this._arguments = arguments; - } - }, - - /** Invokes error callbacks for this deferred object. - * All arguments are forwarded to error callbacks. - * @method DjsDeferred#reject - */ - reject: function (/* args */) { - - if (this._fail) { - var i, len; - for (i = 0, len = this._fail.length; i < len; i++) { - this._fail[i].apply(null, arguments); - } - - this._fail = undefined; - this._rejected = false; - this._arguments = undefined; - } else { - this._rejected = true; - this._arguments = arguments; - } - }, - - /** Returns a version of this object that has only the read-only methods available. - * @method DjsDeferred#promise - * @returns An object with only the promise object. - */ - - promise: function () { - var result = {}; - result.then = forwardCall(this, "then", result); - return result; - } -}; - -/** Creates a deferred object. - * @returns {DjsDeferred} A new deferred object. If jQuery is installed, then a jQueryDeferred object is returned, which provides a superset of features. -*/ -function createDeferred() { - if (window.jQuery && window.jQuery.Deferred) { - return new window.jQuery.Deferred(); - } else { - return new DjsDeferred(); - } -} - - - - -/** createDeferred (see {@link module:datajs/deferred~createDeferred}) */ -exports.createDeferred = createDeferred; - -/** DjsDeferred (see {@link DjsDeferred}) */ -exports.DjsDeferred = DjsDeferred; \ No newline at end of file diff --git a/lib/odata.js b/lib/odata.js index 6f660f6..cf7d1ab 100644 --- a/lib/odata.js +++ b/lib/odata.js @@ -21,16 +21,17 @@ /** @module odata */ // Imports +var utils = require('./utils.js'); + var odataUtils = exports.utils = require('./odata/odatautils.js'); var odataHandler = exports.handler = require('./odata/handler.js'); var odataMetadata = exports.metadata = require('./odata/metadata.js'); -var odataNet = exports.net = require('./odata/net.js'); +var webNet = require('./odata/net-browser.js'); +var odataNet = exports.net = utils.inBrowser() ? webNet : require('' + './odata/net.js'); var odataJson = exports.json = require('./odata/json.js'); exports.batch = require('./odata/batch.js'); - -var utils = require('./utils.js'); var assigned = utils.assigned; var defined = utils.defined; diff --git a/lib/odata/net-browser.js b/lib/odata/net-browser.js index 8d956ff..647d0b1 100644 --- a/lib/odata/net-browser.js +++ b/lib/odata/net-browser.js @@ -262,13 +262,21 @@ exports.defaultHttpClient = { } } + if (request.withCredentials) { + xhr.withCredentials = true; + } + // Set the timeout if available. if (request.timeoutMS) { xhr.timeout = request.timeoutMS; xhr.ontimeout = handleTimeout; } - - xhr.send(request.body); + + if(typeof request.body === 'undefined'){ + xhr.send(); + } else { + xhr.send(request.body); + } } else { if (!canUseJSONP(request)) { throw { message: "Request is not local and cannot be done through JSONP." }; diff --git a/lib/odata/net.js b/lib/odata/net.js index be45295..9bc561e 100644 --- a/lib/odata/net.js +++ b/lib/odata/net.js @@ -21,6 +21,7 @@ var http = require('http'); +var https = require('https'); var utils = require('./../utils.js'); var url = require("url"); @@ -117,8 +118,12 @@ exports.defaultHttpClient = { } } - - var xhr = http.request(options); + var httpModule = http; + if(options.protocol && options.protocol.indexOf('https:') === 0) { + httpModule = https; + } + + var xhr = httpModule.request(options); result.abort = function () { if (done) { diff --git a/lib/store.js b/lib/store.js deleted file mode 100644 index d7e321d..0000000 --- a/lib/store.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -//'use strict'; - - /** @module store */ - - - - - -exports.defaultStoreMechanism = "best"; - -/** Creates a new store object. - * @param {String} name - Store name. - * @param {String} [mechanism] - - * @returns {Object} Store object. -*/ -exports.createStore = function (name, mechanism) { - - - if (!mechanism) { - mechanism = exports.defaultStoreMechanism; - } - - if (mechanism === "best") { - mechanism = (DomStore.isSupported()) ? "dom" : "memory"; - } - - var factory = mechanisms[mechanism]; - if (factory) { - return factory.create(name); - } - - throw { message: "Failed to create store", name: name, mechanism: mechanism }; -}; - -exports.DomStore = DomStore = require('./store/dom.js'); -exports.IndexedDBStore = IndexedDBStore = require('./store/indexeddb.js'); -exports.MemoryStore = MemoryStore = require('./store/memory.js'); - -var mechanisms = { - indexeddb: IndexedDBStore, - dom: DomStore, - memory: MemoryStore -}; - -exports.mechanisms = mechanisms; - - - - diff --git a/lib/store/dom.js b/lib/store/dom.js deleted file mode 100644 index e14bb6b..0000000 --- a/lib/store/dom.js +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -'use strict'; - -/** @module store/dom */ - - - -var utils = require('./../utils.js'); - -// Imports. -var throwErrorCallback = utils.throwErrorCallback; -var delay = utils.delay; - -var localStorage = null; - -/** This method is used to override the Date.toJSON method and is called only by - * JSON.stringify. It should never be called directly. - * @summary Converts a Date object into an object representation friendly to JSON serialization. - * @returns {Object} Object that represents the Date. - */ -function domStoreDateToJSON() { - var newValue = { v: this.valueOf(), t: "[object Date]" }; - // Date objects might have extra properties on them so we save them. - for (var name in this) { - newValue[name] = this[name]; - } - return newValue; -} - -/** This method is used during JSON parsing and invoked only by the reviver function. - * It should never be called directly. - * @summary JSON reviver function for converting an object representing a Date in a JSON stream to a Date object - * @param value _ - * @param value - Object to convert. - * @returns {Date} Date object. - */ -function domStoreJSONToDate(_, value) { - if (value && value.t === "[object Date]") { - var newValue = new Date(value.v); - for (var name in value) { - if (name !== "t" && name !== "v") { - newValue[name] = value[name]; - } - } - value = newValue; - } - return value; -} - -/** Qualifies the key with the name of the store. - * @param {Object} store - Store object whose name will be used for qualifying the key. - * @param {String} key - Key string. - * @returns {String} Fully qualified key string. - */ -function qualifyDomStoreKey(store, key) { - return store.name + "#!#" + key; -} - -/** Gets the key part of a fully qualified key string. - * @param {Object} store - Store object whose name will be used for qualifying the key. - * @param {String} key - Fully qualified key string. - * @returns {String} Key part string - */ -function unqualifyDomStoreKey(store, key) { - return key.replace(store.name + "#!#", ""); -} - -/** Constructor for store objects that use DOM storage as the underlying mechanism. - * @class DomStore - * @constructor - * @param {String} name - Store name. - */ -function DomStore(name) { - this.name = name; -} - -/** Creates a store object that uses DOM Storage as its underlying mechanism. - * @method module:store/dom~DomStore.create - * @param {String} name - Store name. - * @returns {Object} Store object. - */ -DomStore.create = function (name) { - - if (DomStore.isSupported()) { - localStorage = localStorage || window.localStorage; - return new DomStore(name); - } - - throw { message: "Web Storage not supported by the browser" }; -}; - -/** Checks whether the underlying mechanism for this kind of store objects is supported by the browser. - * @method DomStore.isSupported - * @returns {Boolean} - True if the mechanism is supported by the browser; otherwise false. -*/ -DomStore.isSupported = function () { - return !!window.localStorage; -}; - -/** Adds a new value identified by a key to the store. - * @method module:store/dom~DomStore#add - * @param {String} key - Key string. - * @param value - Value that is going to be added to the store. - * @param {Function} success - Callback for a successful add operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - * This method errors out if the store already contains the specified key. - */ -DomStore.prototype.add = function (key, value, success, error) { - error = error || this.defaultError; - var store = this; - this.contains(key, function (contained) { - if (!contained) { - store.addOrUpdate(key, value, success, error); - } else { - delay(error, { message: "key already exists", key: key }); - } - }, error); -}; - -/** This method will overwrite the key's current value if it already exists in the store; otherwise it simply adds the new key and value. - * @summary Adds or updates a value identified by a key to the store. - * @method module:store/dom~DomStore#addOrUpdate - * @param {String} key - Key string. - * @param value - Value that is going to be added or updated to the store. - * @param {Function} success - Callback for a successful add or update operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - */ -DomStore.prototype.addOrUpdate = function (key, value, success, error) { - error = error || this.defaultError; - - if (key instanceof Array) { - error({ message: "Array of keys not supported" }); - } else { - var fullKey = qualifyDomStoreKey(this, key); - var oldDateToJSON = Date.prototype.toJSON; - try { - var storedValue = value; - if (storedValue !== undefined) { - // Dehydrate using json - Date.prototype.toJSON = domStoreDateToJSON; - storedValue = window.JSON.stringify(value); - } - // Save the json string. - localStorage.setItem(fullKey, storedValue); - delay(success, key, value); - } - catch (e) { - if (e.code === 22 || e.number === 0x8007000E) { - delay(error, { name: "QUOTA_EXCEEDED_ERR", error: e }); - } else { - delay(error, e); - } - } - finally { - Date.prototype.toJSON = oldDateToJSON; - } - } -}; - -/** In case of an error, this method will not restore any keys that might have been deleted at that point. - * @summary Removes all the data associated with this store object. - * @method module:store/dom~DomStore#clear - * @param {Function} success - Callback for a successful clear operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - */ -DomStore.prototype.clear = function (success, error) { - - error = error || this.defaultError; - try { - var i = 0, len = localStorage.length; - while (len > 0 && i < len) { - var fullKey = localStorage.key(i); - var key = unqualifyDomStoreKey(this, fullKey); - if (fullKey !== key) { - localStorage.removeItem(fullKey); - len = localStorage.length; - } else { - i++; - } - } - delay(success); - } - catch (e) { - delay(error, e); - } -}; - -/** This function does nothing in DomStore as it does not have a connection model - * @method module:store/dom~DomStore#close - */ -DomStore.prototype.close = function () { -}; - -/** Checks whether a key exists in the store. - * @method module:store/dom~DomStore#contains - * @param {String} key - Key string. - * @param {Function} success - Callback indicating whether the store contains the key or not. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. -*/ -DomStore.prototype.contains = function (key, success, error) { - error = error || this.defaultError; - try { - var fullKey = qualifyDomStoreKey(this, key); - var value = localStorage.getItem(fullKey); - delay(success, value !== null); - } catch (e) { - delay(error, e); - } -}; - -DomStore.prototype.defaultError = throwErrorCallback; - -/** Gets all the keys that exist in the store. - * @method module:store/dom~DomStore#getAllKeys - * @param {Function} success - Callback for a successful get operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - */ -DomStore.prototype.getAllKeys = function (success, error) { - - error = error || this.defaultError; - - var results = []; - var i, len; - - try { - for (i = 0, len = localStorage.length; i < len; i++) { - var fullKey = localStorage.key(i); - var key = unqualifyDomStoreKey(this, fullKey); - if (fullKey !== key) { - results.push(key); - } - } - delay(success, results); - } - catch (e) { - delay(error, e); - } -}; - -/** Identifies the underlying mechanism used by the store.*/ -DomStore.prototype.mechanism = "dom"; - -/** Reads the value associated to a key in the store. - * @method module:store/dom~DomStore#read - * @param {String} key - Key string. - * @param {Function} success - Callback for a successful reads operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - */ -DomStore.prototype.read = function (key, success, error) { - - error = error || this.defaultError; - - if (key instanceof Array) { - error({ message: "Array of keys not supported" }); - } else { - try { - var fullKey = qualifyDomStoreKey(this, key); - var value = localStorage.getItem(fullKey); - if (value !== null && value !== "undefined") { - // Hydrate using json - value = window.JSON.parse(value, domStoreJSONToDate); - } - else { - value = undefined; - } - delay(success, key, value); - } catch (e) { - delay(error, e); - } - } -}; - -/** Removes a key and its value from the store. - * @method module:store/dom~DomStore#remove - * @param {String} key - Key string. - * @param {Function} success - Callback for a successful remove operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - */ -DomStore.prototype.remove = function (key, success, error) { - error = error || this.defaultError; - - if (key instanceof Array) { - error({ message: "Batches not supported" }); - } else { - try { - var fullKey = qualifyDomStoreKey(this, key); - localStorage.removeItem(fullKey); - delay(success); - } catch (e) { - delay(error, e); - } - } -}; - -/** Updates the value associated to a key in the store. - * @method module:store/dom~DomStore#update - * @param {String} key - Key string. - * @param value - New value. - * @param {Function} success - Callback for a successful update operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked - * This method errors out if the specified key is not found in the store. - */ -DomStore.prototype.update = function (key, value, success, error) { - error = error || this.defaultError; - var store = this; - this.contains(key, function (contained) { - if (contained) { - store.addOrUpdate(key, value, success, error); - } else { - delay(error, { message: "key not found", key: key }); - } - }, error); -}; - -module.exports = DomStore; \ No newline at end of file diff --git a/lib/store/indexeddb.js b/lib/store/indexeddb.js deleted file mode 100644 index d7527c1..0000000 --- a/lib/store/indexeddb.js +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -'use strict'; - -/** @module store/indexeddb */ -var utils = require('./../utils.js'); - -// Imports. -var throwErrorCallback = utils.throwErrorCallback; -var delay = utils.delay; - - -var indexedDB = utils.inBrowser() ? window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.indexedDB : undefined; -var IDBKeyRange = utils.inBrowser() ? window.IDBKeyRange || window.webkitIDBKeyRange : undefined; -var IDBTransaction = utils.inBrowser() ? window.IDBTransaction || window.webkitIDBTransaction || {} : {} ; - -var IDBT_READ_ONLY = IDBTransaction.READ_ONLY || "readonly"; -var IDBT_READ_WRITE = IDBTransaction.READ_WRITE || "readwrite"; - -/** Returns either a specific error handler or the default error handler - * @param {Function} error - The specific error handler - * @param {Function} defaultError - The default error handler - * @returns {Function} The error callback - */ -function getError(error, defaultError) { - - return function (e) { - var errorFunc = error || defaultError; - if (!errorFunc) { - return; - } - - // Old api quota exceeded error support. - if (Object.prototype.toString.call(e) === "[object IDBDatabaseException]") { - if (e.code === 11 /* IndexedDb disk quota exceeded */) { - errorFunc({ name: "QuotaExceededError", error: e }); - return; - } - errorFunc(e); - return; - } - - var errName; - try { - var errObj = e.target.error || e; - errName = errObj.name; - } catch (ex) { - errName = (e.type === "blocked") ? "IndexedDBBlocked" : "UnknownError"; - } - errorFunc({ name: errName, error: e }); - }; -} - -/** Opens the store object's indexed db database. - * @param {IndexedDBStore} store - The store object - * @param {Function} success - The success callback - * @param {Function} error - The error callback - */ -function openStoreDb(store, success, error) { - - var storeName = store.name; - var dbName = "_odatajs_" + storeName; - - var request = indexedDB.open(dbName); - request.onblocked = error; - request.onerror = error; - - request.onupgradeneeded = function () { - var db = request.result; - if (!db.objectStoreNames.contains(storeName)) { - db.createObjectStore(storeName); - } - }; - - request.onsuccess = function (event) { - var db = request.result; - if (!db.objectStoreNames.contains(storeName)) { - // Should we use the old style api to define the database schema? - if ("setVersion" in db) { - var versionRequest = db.setVersion("1.0"); - versionRequest.onsuccess = function () { - var transaction = versionRequest.transaction; - transaction.oncomplete = function () { - success(db); - }; - db.createObjectStore(storeName, null, false); - }; - versionRequest.onerror = error; - versionRequest.onblocked = error; - return; - } - - // The database doesn't have the expected store. - // Fabricate an error object for the event for the schema mismatch - // and error out. - event.target.error = { name: "DBSchemaMismatch" }; - error(event); - return; - } - - db.onversionchange = function(event) { - event.target.close(); - }; - success(db); - }; -} - -/** Opens a new transaction to the store - * @param {IndexedDBStore} store - The store object - * @param {Integer} mode - The read/write mode of the transaction (constants from IDBTransaction) - * @param {Function} success - The success callback - * @param {Function} error - The error callback - */ -function openTransaction(store, mode, success, error) { - - var storeName = store.name; - var storeDb = store.db; - var errorCallback = getError(error, store.defaultError); - - if (storeDb) { - success(storeDb.transaction(storeName, mode)); - return; - } - - openStoreDb(store, function (db) { - store.db = db; - success(db.transaction(storeName, mode)); - }, errorCallback); -} - -/** Creates a new IndexedDBStore. - * @class IndexedDBStore - * @constructor - * @param {String} name - The name of the store. - * @returns {Object} The new IndexedDBStore. - */ -function IndexedDBStore(name) { - this.name = name; -} - -/** Creates a new IndexedDBStore. - * @method module:store/indexeddb~IndexedDBStore.create - * @param {String} name - The name of the store. - * @returns {Object} The new IndexedDBStore. - */ -IndexedDBStore.create = function (name) { - if (IndexedDBStore.isSupported()) { - return new IndexedDBStore(name); - } - - throw { message: "IndexedDB is not supported on this browser" }; -}; - -/** Returns whether IndexedDB is supported. - * @method module:store/indexeddb~IndexedDBStore.isSupported - * @returns {Boolean} True if IndexedDB is supported, false otherwise. - */ -IndexedDBStore.isSupported = function () { - return !!indexedDB; -}; - -/** Adds a key/value pair to the store - * @method module:store/indexeddb~IndexedDBStore#add - * @param {String} key - The key - * @param {Object} value - The value - * @param {Function} success - The success callback - * @param {Function} error - The error callback -*/ -IndexedDBStore.prototype.add = function (key, value, success, error) { - var name = this.name; - var defaultError = this.defaultError; - var keys = []; - var values = []; - - if (key instanceof Array) { - keys = key; - values = value; - } else { - keys = [key]; - values = [value]; - } - - openTransaction(this, IDBT_READ_WRITE, function (transaction) { - transaction.onabort = getError(error, defaultError, key, "add"); - transaction.oncomplete = function () { - if (key instanceof Array) { - success(keys, values); - } else { - success(key, value); - } - }; - - for (var i = 0; i < keys.length && i < values.length; i++) { - transaction.objectStore(name).add({ v: values[i] }, keys[i]); - } - }, error); -}; - -/** Adds or updates a key/value pair in the store - * @method module:store/indexeddb~IndexedDBStore#addOrUpdate - * @param {String} key - The key - * @param {Object} value - The value - * @param {Function} success - The success callback - * @param {Function} error - The error callback - */ -IndexedDBStore.prototype.addOrUpdate = function (key, value, success, error) { - var name = this.name; - var defaultError = this.defaultError; - var keys = []; - var values = []; - - if (key instanceof Array) { - keys = key; - values = value; - } else { - keys = [key]; - values = [value]; - } - - openTransaction(this, IDBT_READ_WRITE, function (transaction) { - transaction.onabort = getError(error, defaultError); - transaction.oncomplete = function () { - if (key instanceof Array) { - success(keys, values); - } else { - success(key, value); - } - }; - - for (var i = 0; i < keys.length && i < values.length; i++) { - var record = { v: values[i] }; - transaction.objectStore(name).put(record, keys[i]); - } - }, error); -}; - -/** Clears the store - * @method module:store/indexeddb~IndexedDBStore#clear - * @param {Function} success - The success callback - * @param {Function} error - The error callback - */ -IndexedDBStore.prototype.clear = function (success, error) { - var name = this.name; - var defaultError = this.defaultError; - openTransaction(this, IDBT_READ_WRITE, function (transaction) { - transaction.onerror = getError(error, defaultError); - transaction.oncomplete = function () { - success(); - }; - - transaction.objectStore(name).clear(); - }, error); -}; - -/** Closes the connection to the database - * @method module:store/indexeddb~IndexedDBStore#close -*/ -IndexedDBStore.prototype.close = function () { - - if (this.db) { - this.db.close(); - this.db = null; - } -}; - -/** Returns whether the store contains a key - * @method module:store/indexeddb~IndexedDBStore#contains - * @param {String} key - The key - * @param {Function} success - The success callback - * @param {Function} error - The error callback - */ -IndexedDBStore.prototype.contains = function (key, success, error) { - var name = this.name; - var defaultError = this.defaultError; - openTransaction(this, IDBT_READ_ONLY, function (transaction) { - var objectStore = transaction.objectStore(name); - var request = objectStore.get(key); - - transaction.oncomplete = function () { - success(!!request.result); - }; - transaction.onerror = getError(error, defaultError); - }, error); -}; - -IndexedDBStore.prototype.defaultError = throwErrorCallback; - -/** Gets all the keys from the store - * @method module:store/indexeddb~IndexedDBStore#getAllKeys - * @param {Function} success - The success callback - * @param {Function} error - The error callback - */ -IndexedDBStore.prototype.getAllKeys = function (success, error) { - var name = this.name; - var defaultError = this.defaultError; - openTransaction(this, IDBT_READ_WRITE, function (transaction) { - var results = []; - - transaction.oncomplete = function () { - success(results); - }; - - var request = transaction.objectStore(name).openCursor(); - - request.onerror = getError(error, defaultError); - request.onsuccess = function (event) { - var cursor = event.target.result; - if (cursor) { - results.push(cursor.key); - // Some tools have issues because continue is a javascript reserved word. - cursor["continue"].call(cursor); - } - }; - }, error); -}; - -/** Identifies the underlying mechanism used by the store. -*/ -IndexedDBStore.prototype.mechanism = "indexeddb"; - -/** Reads the value for the specified key - * @method module:store/indexeddb~IndexedDBStore#read - * @param {String} key - The key - * @param {Function} success - The success callback - * @param {Function} error - The error callback - * If the key does not exist, the success handler will be called with value = undefined - */ -IndexedDBStore.prototype.read = function (key, success, error) { - var name = this.name; - var defaultError = this.defaultError; - var keys = (key instanceof Array) ? key : [key]; - - openTransaction(this, IDBT_READ_ONLY, function (transaction) { - var values = []; - - transaction.onerror = getError(error, defaultError, key, "read"); - transaction.oncomplete = function () { - if (key instanceof Array) { - success(keys, values); - } else { - success(keys[0], values[0]); - } - }; - - for (var i = 0; i < keys.length; i++) { - // Some tools have issues because get is a javascript reserved word. - var objectStore = transaction.objectStore(name); - var request = objectStore.get.call(objectStore, keys[i]); - request.onsuccess = function (event) { - var record = event.target.result; - values.push(record ? record.v : undefined); - }; - } - }, error); -}; - -/** Removes the specified key from the store - * @method module:store/indexeddb~IndexedDBStore#remove - * @param {String} key - The key - * @param {Function} success - The success callback - * @param {Function} error - The error callback - */ -IndexedDBStore.prototype.remove = function (key, success, error) { - - var name = this.name; - var defaultError = this.defaultError; - var keys = (key instanceof Array) ? key : [key]; - - openTransaction(this, IDBT_READ_WRITE, function (transaction) { - transaction.onerror = getError(error, defaultError); - transaction.oncomplete = function () { - success(); - }; - - for (var i = 0; i < keys.length; i++) { - // Some tools have issues because continue is a javascript reserved word. - var objectStore = transaction.objectStore(name); - objectStore["delete"].call(objectStore, keys[i]); - } - }, error); -}; - -/** Updates a key/value pair in the store - * @method module:store/indexeddb~IndexedDBStore#update - * @param {String} key - The key - * @param {Object} value - The value - * @param {Function} success - The success callback - * @param {Function} error - The error callback - */ -IndexedDBStore.prototype.update = function (key, value, success, error) { - var name = this.name; - var defaultError = this.defaultError; - var keys = []; - var values = []; - - if (key instanceof Array) { - keys = key; - values = value; - } else { - keys = [key]; - values = [value]; - } - - openTransaction(this, IDBT_READ_WRITE, function (transaction) { - transaction.onabort = getError(error, defaultError); - transaction.oncomplete = function () { - if (key instanceof Array) { - success(keys, values); - } else { - success(key, value); - } - }; - - for (var i = 0; i < keys.length && i < values.length; i++) { - var request = transaction.objectStore(name).openCursor(IDBKeyRange.only(keys[i])); - var record = { v: values[i] }; - request.pair = { key: keys[i], value: record }; - request.onsuccess = function (event) { - var cursor = event.target.result; - if (cursor) { - cursor.update(event.target.pair.value); - } else { - transaction.abort(); - } - } - } - }, error); -}; - - -module.exports = IndexedDBStore; \ No newline at end of file diff --git a/lib/store/memory.js b/lib/store/memory.js deleted file mode 100644 index a9c69d4..0000000 --- a/lib/store/memory.js +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -'use strict'; - -/** @module store/memory */ - - -var utils = require('./../utils.js'); - -// Imports. -var throwErrorCallback = utils.throwErrorCallback; -var delay = utils.delay; - -/** Constructor for store objects that use a sorted array as the underlying mechanism. - * @class MemoryStore - * @constructor - * @param {String} name - Store name. - */ -function MemoryStore(name) { - - var holes = []; - var items = []; - var keys = {}; - - this.name = name; - - var getErrorCallback = function (error) { - return error || this.defaultError; - }; - - /** Validates that the specified key is not undefined, not null, and not an array - * @param key - Key value. - * @param {Function} error - Error callback. - * @returns {Boolean} True if the key is valid. False if the key is invalid and the error callback has been queued for execution. - */ - function validateKeyInput(key, error) { - - var messageString; - - if (key instanceof Array) { - messageString = "Array of keys not supported"; - } - - if (key === undefined || key === null) { - messageString = "Invalid key"; - } - - if (messageString) { - delay(error, { message: messageString }); - return false; - } - return true; - } - - /** This method errors out if the store already contains the specified key. - * @summary Adds a new value identified by a key to the store. - * @method module:store/memory~MemoryStore#add - * @param {String} key - Key string. - * @param value - Value that is going to be added to the store. - * @param {Function} success - Callback for a successful add operation. - * @param {Function} error - Callback for handling errors. If not specified then store.defaultError is invoked. - */ - this.add = function (key, value, success, error) { - error = getErrorCallback(error); - - if (validateKeyInput(key, error)) { - if (!keys.hasOwnProperty(key)) { - this.addOrUpdate(key, value, success, error); - } else { - error({ message: "key already exists", key: key }); - } - } - }; - - /** This method will overwrite the key's current value if it already exists in the store; otherwise it simply adds the new key and value. - * @summary Adds or updates a value identified by a key to the store. - * @method module:store/memory~MemoryStore#addOrUpdate - * @param {String} key - Key string. - * @param value - Value that is going to be added or updated to the store. - * @param {Function} success - Callback for a successful add or update operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - */ - this.addOrUpdate = function (key, value, success, error) { - - error = getErrorCallback(error); - - if (validateKeyInput(key, error)) { - var index = keys[key]; - if (index === undefined) { - if (holes.length > 0) { - index = holes.splice(0, 1); - } else { - index = items.length; - } - } - items[index] = value; - keys[key] = index; - delay(success, key, value); - } - }; - - /** Removes all the data associated with this store object. - * @method module:store/memory~MemoryStore#clear - * @param {Function} success - Callback for a successful clear operation. - */ - this.clear = function (success) { - items = []; - keys = {}; - holes = []; - delay(success); - }; - - /** Checks whether a key exists in the store. - * @method module:store/memory~MemoryStore#contains - * @param {String} key - Key string. - * @param {Function} success - Callback indicating whether the store contains the key or not. - */ - this.contains = function (key, success) { - var contained = keys.hasOwnProperty(key); - delay(success, contained); - }; - - /** Gets all the keys that exist in the store. - * @method module:store/memory~MemoryStore#getAllKeys - * @param {Function} success - Callback for a successful get operation. - */ - this.getAllKeys = function (success) { - - var results = []; - for (var name in keys) { - results.push(name); - } - delay(success, results); - }; - - /** Reads the value associated to a key in the store. - * @method module:store/memory~MemoryStore#read - * @param {String} key - Key string. - * @param {Function} success - Callback for a successful reads operation. - * @param {Function} error - Callback for handling errors. If not specified then store.defaultError is invoked. - */ - this.read = function (key, success, error) { - error = getErrorCallback(error); - - if (validateKeyInput(key, error)) { - var index = keys[key]; - delay(success, key, items[index]); - } - }; - - /** Removes a key and its value from the store. - * @method module:store/memory~MemoryStore#remove - * @param {String} key - Key string. - * @param {Function} success - Callback for a successful remove operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - */ - this.remove = function (key, success, error) { - error = getErrorCallback(error); - - if (validateKeyInput(key, error)) { - var index = keys[key]; - if (index !== undefined) { - if (index === items.length - 1) { - items.pop(); - } else { - items[index] = undefined; - holes.push(index); - } - delete keys[key]; - - // The last item was removed, no need to keep track of any holes in the array. - if (items.length === 0) { - holes = []; - } - } - - delay(success); - } - }; - - /** Updates the value associated to a key in the store. - * @method module:store/memory~MemoryStore#update - * @param {String} key - Key string. - * @param value - New value. - * @param {Function} success - Callback for a successful update operation. - * @param {Function} [error] - Callback for handling errors. If not specified then store.defaultError is invoked. - * This method errors out if the specified key is not found in the store. - */ - this.update = function (key, value, success, error) { - error = getErrorCallback(error); - if (validateKeyInput(key, error)) { - if (keys.hasOwnProperty(key)) { - this.addOrUpdate(key, value, success, error); - } else { - error({ message: "key not found", key: key }); - } - } - }; -} - -/** Creates a store object that uses memory storage as its underlying mechanism. - * @method MemoryStore.create - * @param {String} name - Store name. - * @returns {Object} Store object. - */ -MemoryStore.create = function (name) { - return new MemoryStore(name); -}; - -/** Checks whether the underlying mechanism for this kind of store objects is supported by the browser. - * @method MemoryStore.isSupported - * @returns {Boolean} True if the mechanism is supported by the browser; otherwise false. - */ -MemoryStore.isSupported = function () { - return true; -}; - -/** This function does nothing in MemoryStore as it does not have a connection model. -*/ -MemoryStore.prototype.close = function () { -}; - -MemoryStore.prototype.defaultError = throwErrorCallback; - -/** Identifies the underlying mechanism used by the store. -*/ -MemoryStore.prototype.mechanism = "memory"; - - -/** MemoryStore (see {@link MemoryStore}) */ -module.exports = MemoryStore; \ No newline at end of file diff --git a/package.json b/package.json index 044cb81..ad709a0 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,15 @@ { - "name": "odatajs", - "version": "4.0.0", + "name": "jaydata-odatajs", + "version": "4.0.1", "postfix": "", "releaseCandidate": "", "title": "Olingo OData Client for JavaScript", "description": "the Olingo OData Client for JavaScript library is a new cross-browser JavaScript library that enables data-centric web applications by leveraging modern protocols such as JSON and OData and HTML5-enabled browser features. It's designed to be small, fast and easy to use.", "homepage": "http://olingo.apache.org", - "main": "index-node.js", - "main-browser": "index.js", + "main": "index.js", "repository": { "type": "git", - "url": "http://git-wip-us.apache.org/repos/asf/olingo-odata4-js.git" + "url": "http://github.com/jaystack/olingo-odata4-js.git" }, "engines": { "node": ">= 0.10.0" @@ -27,13 +26,24 @@ { "name": "Challen He", "email": "challenh@apache.org" + }, + { + "name": "Viktor Lázár", + "email": "viktor.lazar@jaystack.com" + }, + { + "name": "Viktor Borza", + "email": "viktor.borza@jaystack.com" } ], - "scripts": { - "preinstall": "npm --prefix ./grunt-config/custom-tasks/rat install" + "dependencies": { + "xmldom": "^0.1.19" }, "devDependencies": { + "async": "^1.5.0", + "catharsis": "^0.8.7", "chai": "^2.0.0", + "chalk": "^1.1.1", "grunt": "^0.4.5", "grunt-connect-proxy": "^0.1.10", "grunt-contrib-clean": "^0.6.0", @@ -44,6 +54,8 @@ "grunt-jsdoc": "^0.5.6", "grunt-nuget": "^0.1.3", "mocha": "^2.1.0", + "taffydb": "^2.7.2", + "xml2js": "^0.4.15", "xmldom": "^0.1.19" } }