summaryrefslogtreecommitdiff
path: root/lib/dojo/data/ItemFileReadStore.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dojo/data/ItemFileReadStore.js')
-rw-r--r--lib/dojo/data/ItemFileReadStore.js946
1 files changed, 2 insertions, 944 deletions
diff --git a/lib/dojo/data/ItemFileReadStore.js b/lib/dojo/data/ItemFileReadStore.js
index 9d175ca2c..b57daa2de 100644
--- a/lib/dojo/data/ItemFileReadStore.js
+++ b/lib/dojo/data/ItemFileReadStore.js
@@ -4,947 +4,5 @@
see: http://dojotoolkit.org/license for details
*/
-
-if(!dojo._hasResource["dojo.data.ItemFileReadStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
-dojo._hasResource["dojo.data.ItemFileReadStore"] = true;
-dojo.provide("dojo.data.ItemFileReadStore");
-dojo.require("dojo.data.util.filter");
-dojo.require("dojo.data.util.simpleFetch");
-dojo.require("dojo.date.stamp");
-
-
-dojo.declare("dojo.data.ItemFileReadStore", null,{
- // summary:
- // The ItemFileReadStore implements the dojo.data.api.Read API and reads
- // data from JSON files that have contents in this format --
- // { items: [
- // { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
- // { name:'Fozzie Bear', wears:['hat', 'tie']},
- // { name:'Miss Piggy', pets:'Foo-Foo'}
- // ]}
- // Note that it can also contain an 'identifer' property that specified which attribute on the items
- // in the array of items that acts as the unique identifier for that item.
- //
- constructor: function(/* Object */ keywordParameters){
- // summary: constructor
- // keywordParameters: {url: String}
- // keywordParameters: {data: jsonObject}
- // keywordParameters: {typeMap: object)
- // The structure of the typeMap object is as follows:
- // {
- // type0: function || object,
- // type1: function || object,
- // ...
- // typeN: function || object
- // }
- // Where if it is a function, it is assumed to be an object constructor that takes the
- // value of _value as the initialization parameters. If it is an object, then it is assumed
- // to be an object of general form:
- // {
- // type: function, //constructor.
- // deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
- // }
-
- this._arrayOfAllItems = [];
- this._arrayOfTopLevelItems = [];
- this._loadFinished = false;
- this._jsonFileUrl = keywordParameters.url;
- this._ccUrl = keywordParameters.url;
- this.url = keywordParameters.url;
- this._jsonData = keywordParameters.data;
- this.data = null;
- this._datatypeMap = keywordParameters.typeMap || {};
- if(!this._datatypeMap['Date']){
- //If no default mapping for dates, then set this as default.
- //We use the dojo.date.stamp here because the ISO format is the 'dojo way'
- //of generically representing dates.
- this._datatypeMap['Date'] = {
- type: Date,
- deserialize: function(value){
- return dojo.date.stamp.fromISOString(value);
- }
- };
- }
- this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
- this._itemsByIdentity = null;
- this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
- this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
- this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
- this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
- this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
- this._queuedFetches = [];
- if(keywordParameters.urlPreventCache !== undefined){
- this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
- }
- if(keywordParameters.hierarchical !== undefined){
- this.hierarchical = keywordParameters.hierarchical?true:false;
- }
- if(keywordParameters.clearOnClose){
- this.clearOnClose = true;
- }
- if("failOk" in keywordParameters){
- this.failOk = keywordParameters.failOk?true:false;
- }
- },
-
- url: "", // use "" rather than undefined for the benefit of the parser (#3539)
-
- //Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
- //when clearOnClose and close is used.
- _ccUrl: "",
-
- data: null, // define this so that the parser can populate it
-
- typeMap: null, //Define so parser can populate.
-
- //Parameter to allow users to specify if a close call should force a reload or not.
- //By default, it retains the old behavior of not clearing if close is called. But
- //if set true, the store will be reset to default state. Note that by doing this,
- //all item handles will become invalid and a new fetch must be issued.
- clearOnClose: false,
-
- //Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
- //Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
- //Added for tracker: #6072
- urlPreventCache: false,
-
- //Parameter for specifying that it is OK for the xhrGet call to fail silently.
- failOk: false,
-
- //Parameter to indicate to process data from the url as hierarchical
- //(data items can contain other data items in js form). Default is true
- //for backwards compatibility. False means only root items are processed
- //as items, all child objects outside of type-mapped objects and those in
- //specific reference format, are left straight JS data objects.
- hierarchical: true,
-
- _assertIsItem: function(/* item */ item){
- // summary:
- // This function tests whether the item passed in is indeed an item in the store.
- // item:
- // The item to test for being contained by the store.
- if(!this.isItem(item)){
- throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");
- }
- },
-
- _assertIsAttribute: function(/* attribute-name-string */ attribute){
- // summary:
- // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
- // attribute:
- // The attribute to test for being contained by the store.
- if(typeof attribute !== "string"){
- throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");
- }
- },
-
- getValue: function( /* item */ item,
- /* attribute-name-string */ attribute,
- /* value? */ defaultValue){
- // summary:
- // See dojo.data.api.Read.getValue()
- var values = this.getValues(item, attribute);
- return (values.length > 0)?values[0]:defaultValue; // mixed
- },
-
- getValues: function(/* item */ item,
- /* attribute-name-string */ attribute){
- // summary:
- // See dojo.data.api.Read.getValues()
-
- this._assertIsItem(item);
- this._assertIsAttribute(attribute);
- // Clone it before returning. refs: #10474
- return (item[attribute] || []).slice(0); // Array
- },
-
- getAttributes: function(/* item */ item){
- // summary:
- // See dojo.data.api.Read.getAttributes()
- this._assertIsItem(item);
- var attributes = [];
- for(var key in item){
- // Save off only the real item attributes, not the special id marks for O(1) isItem.
- if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
- attributes.push(key);
- }
- }
- return attributes; // Array
- },
-
- hasAttribute: function( /* item */ item,
- /* attribute-name-string */ attribute){
- // summary:
- // See dojo.data.api.Read.hasAttribute()
- this._assertIsItem(item);
- this._assertIsAttribute(attribute);
- return (attribute in item);
- },
-
- containsValue: function(/* item */ item,
- /* attribute-name-string */ attribute,
- /* anything */ value){
- // summary:
- // See dojo.data.api.Read.containsValue()
- var regexp = undefined;
- if(typeof value === "string"){
- regexp = dojo.data.util.filter.patternToRegExp(value, false);
- }
- return this._containsValue(item, attribute, value, regexp); //boolean.
- },
-
- _containsValue: function( /* item */ item,
- /* attribute-name-string */ attribute,
- /* anything */ value,
- /* RegExp?*/ regexp){
- // summary:
- // Internal function for looking at the values contained by the item.
- // description:
- // Internal function for looking at the values contained by the item. This
- // function allows for denoting if the comparison should be case sensitive for
- // strings or not (for handling filtering cases where string case should not matter)
- //
- // item:
- // The data item to examine for attribute values.
- // attribute:
- // The attribute to inspect.
- // value:
- // The value to match.
- // regexp:
- // Optional regular expression generated off value if value was of string type to handle wildcarding.
- // If present and attribute values are string, then it can be used for comparison instead of 'value'
- return dojo.some(this.getValues(item, attribute), function(possibleValue){
- if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
- if(possibleValue.toString().match(regexp)){
- return true; // Boolean
- }
- }else if(value === possibleValue){
- return true; // Boolean
- }
- });
- },
-
- isItem: function(/* anything */ something){
- // summary:
- // See dojo.data.api.Read.isItem()
- if(something && something[this._storeRefPropName] === this){
- if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
- return true;
- }
- }
- return false; // Boolean
- },
-
- isItemLoaded: function(/* anything */ something){
- // summary:
- // See dojo.data.api.Read.isItemLoaded()
- return this.isItem(something); //boolean
- },
-
- loadItem: function(/* object */ keywordArgs){
- // summary:
- // See dojo.data.api.Read.loadItem()
- this._assertIsItem(keywordArgs.item);
- },
-
- getFeatures: function(){
- // summary:
- // See dojo.data.api.Read.getFeatures()
- return this._features; //Object
- },
-
- getLabel: function(/* item */ item){
- // summary:
- // See dojo.data.api.Read.getLabel()
- if(this._labelAttr && this.isItem(item)){
- return this.getValue(item,this._labelAttr); //String
- }
- return undefined; //undefined
- },
-
- getLabelAttributes: function(/* item */ item){
- // summary:
- // See dojo.data.api.Read.getLabelAttributes()
- if(this._labelAttr){
- return [this._labelAttr]; //array
- }
- return null; //null
- },
-
- _fetchItems: function( /* Object */ keywordArgs,
- /* Function */ findCallback,
- /* Function */ errorCallback){
- // summary:
- // See dojo.data.util.simpleFetch.fetch()
- var self = this,
- filter = function(requestArgs, arrayOfItems){
- var items = [],
- i, key;
- if(requestArgs.query){
- var value,
- ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
-
- //See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
- //same value for each item examined. Much more efficient.
- var regexpList = {};
- for(key in requestArgs.query){
- value = requestArgs.query[key];
- if(typeof value === "string"){
- regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
- }else if(value instanceof RegExp){
- regexpList[key] = value;
- }
- }
- for(i = 0; i < arrayOfItems.length; ++i){
- var match = true;
- var candidateItem = arrayOfItems[i];
- if(candidateItem === null){
- match = false;
- }else{
- for(key in requestArgs.query){
- value = requestArgs.query[key];
- if(!self._containsValue(candidateItem, key, value, regexpList[key])){
- match = false;
- }
- }
- }
- if(match){
- items.push(candidateItem);
- }
- }
- findCallback(items, requestArgs);
- }else{
- // We want a copy to pass back in case the parent wishes to sort the array.
- // We shouldn't allow resort of the internal list, so that multiple callers
- // can get lists and sort without affecting each other. We also need to
- // filter out any null values that have been left as a result of deleteItem()
- // calls in ItemFileWriteStore.
- for(i = 0; i < arrayOfItems.length; ++i){
- var item = arrayOfItems[i];
- if(item !== null){
- items.push(item);
- }
- }
- findCallback(items, requestArgs);
- }
- };
-
- if(this._loadFinished){
- filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
- }else{
- //Do a check on the JsonFileUrl and crosscheck it.
- //If it doesn't match the cross-check, it needs to be updated
- //This allows for either url or _jsonFileUrl to he changed to
- //reset the store load location. Done this way for backwards
- //compatibility. People use _jsonFileUrl (even though officially
- //private.
- if(this._jsonFileUrl !== this._ccUrl){
- dojo.deprecated("dojo.data.ItemFileReadStore: ",
- "To change the url, set the url property of the store," +
- " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
- this._ccUrl = this._jsonFileUrl;
- this.url = this._jsonFileUrl;
- }else if(this.url !== this._ccUrl){
- this._jsonFileUrl = this.url;
- this._ccUrl = this.url;
- }
-
- //See if there was any forced reset of data.
- if(this.data != null){
- this._jsonData = this.data;
- this.data = null;
- }
-
- if(this._jsonFileUrl){
- //If fetches come in before the loading has finished, but while
- //a load is in progress, we have to defer the fetching to be
- //invoked in the callback.
- if(this._loadInProgress){
- this._queuedFetches.push({args: keywordArgs, filter: filter});
- }else{
- this._loadInProgress = true;
- var getArgs = {
- url: self._jsonFileUrl,
- handleAs: "json-comment-optional",
- preventCache: this.urlPreventCache,
- failOk: this.failOk
- };
- var getHandler = dojo.xhrGet(getArgs);
- getHandler.addCallback(function(data){
- try{
- self._getItemsFromLoadedData(data);
- self._loadFinished = true;
- self._loadInProgress = false;
-
- filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions));
- self._handleQueuedFetches();
- }catch(e){
- self._loadFinished = true;
- self._loadInProgress = false;
- errorCallback(e, keywordArgs);
- }
- });
- getHandler.addErrback(function(error){
- self._loadInProgress = false;
- errorCallback(error, keywordArgs);
- });
-
- //Wire up the cancel to abort of the request
- //This call cancel on the deferred if it hasn't been called
- //yet and then will chain to the simple abort of the
- //simpleFetch keywordArgs
- var oldAbort = null;
- if(keywordArgs.abort){
- oldAbort = keywordArgs.abort;
- }
- keywordArgs.abort = function(){
- var df = getHandler;
- if(df && df.fired === -1){
- df.cancel();
- df = null;
- }
- if(oldAbort){
- oldAbort.call(keywordArgs);
- }
- };
- }
- }else if(this._jsonData){
- try{
- this._loadFinished = true;
- this._getItemsFromLoadedData(this._jsonData);
- this._jsonData = null;
- filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions));
- }catch(e){
- errorCallback(e, keywordArgs);
- }
- }else{
- errorCallback(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
- }
- }
- },
-
- _handleQueuedFetches: function(){
- // summary:
- // Internal function to execute delayed request in the store.
- //Execute any deferred fetches now.
- if(this._queuedFetches.length > 0){
- for(var i = 0; i < this._queuedFetches.length; i++){
- var fData = this._queuedFetches[i],
- delayedQuery = fData.args,
- delayedFilter = fData.filter;
- if(delayedFilter){
- delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions));
- }else{
- this.fetchItemByIdentity(delayedQuery);
- }
- }
- this._queuedFetches = [];
- }
- },
-
- _getItemsArray: function(/*object?*/queryOptions){
- // summary:
- // Internal function to determine which list of items to search over.
- // queryOptions: The query options parameter, if any.
- if(queryOptions && queryOptions.deep){
- return this._arrayOfAllItems;
- }
- return this._arrayOfTopLevelItems;
- },
-
- close: function(/*dojo.data.api.Request || keywordArgs || null */ request){
- // summary:
- // See dojo.data.api.Read.close()
- if(this.clearOnClose &&
- this._loadFinished &&
- !this._loadInProgress){
- //Reset all internalsback to default state. This will force a reload
- //on next fetch. This also checks that the data or url param was set
- //so that the store knows it can get data. Without one of those being set,
- //the next fetch will trigger an error.
-
- if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
- (this.url == "" || this.url == null)
- ) && this.data == null){
- console.debug("dojo.data.ItemFileReadStore: WARNING! Data reload " +
- " information has not been provided." +
- " Please set 'url' or 'data' to the appropriate value before" +
- " the next fetch");
- }
- this._arrayOfAllItems = [];
- this._arrayOfTopLevelItems = [];
- this._loadFinished = false;
- this._itemsByIdentity = null;
- this._loadInProgress = false;
- this._queuedFetches = [];
- }
- },
-
- _getItemsFromLoadedData: function(/* Object */ dataObject){
- // summary:
- // Function to parse the loaded data into item format and build the internal items array.
- // description:
- // Function to parse the loaded data into item format and build the internal items array.
- //
- // dataObject:
- // The JS data object containing the raw data to convery into item format.
- //
- // returns: array
- // Array of items in store item format.
-
- // First, we define a couple little utility functions...
- var addingArrays = false,
- self = this;
-
- function valueIsAnItem(/* anything */ aValue){
- // summary:
- // Given any sort of value that could be in the raw json data,
- // return true if we should interpret the value as being an
- // item itself, rather than a literal value or a reference.
- // example:
- // | false == valueIsAnItem("Kermit");
- // | false == valueIsAnItem(42);
- // | false == valueIsAnItem(new Date());
- // | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
- // | false == valueIsAnItem({_reference:'Kermit'});
- // | true == valueIsAnItem({name:'Kermit', color:'green'});
- // | true == valueIsAnItem({iggy:'pop'});
- // | true == valueIsAnItem({foo:42});
- var isItem = (
- (aValue !== null) &&
- (typeof aValue === "object") &&
- (!dojo.isArray(aValue) || addingArrays) &&
- (!dojo.isFunction(aValue)) &&
- (aValue.constructor == Object || dojo.isArray(aValue)) &&
- (typeof aValue._reference === "undefined") &&
- (typeof aValue._type === "undefined") &&
- (typeof aValue._value === "undefined") &&
- self.hierarchical
- );
- return isItem;
- }
-
- function addItemAndSubItemsToArrayOfAllItems(/* Item */ anItem){
- self._arrayOfAllItems.push(anItem);
- for(var attribute in anItem){
- var valueForAttribute = anItem[attribute];
- if(valueForAttribute){
- if(dojo.isArray(valueForAttribute)){
- var valueArray = valueForAttribute;
- for(var k = 0; k < valueArray.length; ++k){
- var singleValue = valueArray[k];
- if(valueIsAnItem(singleValue)){
- addItemAndSubItemsToArrayOfAllItems(singleValue);
- }
- }
- }else{
- if(valueIsAnItem(valueForAttribute)){
- addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
- }
- }
- }
- }
- }
-
- this._labelAttr = dataObject.label;
-
- // We need to do some transformations to convert the data structure
- // that we read from the file into a format that will be convenient
- // to work with in memory.
-
- // Step 1: Walk through the object hierarchy and build a list of all items
- var i,
- item;
- this._arrayOfAllItems = [];
- this._arrayOfTopLevelItems = dataObject.items;
-
- for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
- item = this._arrayOfTopLevelItems[i];
- if(dojo.isArray(item)){
- addingArrays = true;
- }
- addItemAndSubItemsToArrayOfAllItems(item);
- item[this._rootItemPropName]=true;
- }
-
- // Step 2: Walk through all the attribute values of all the items,
- // and replace single values with arrays. For example, we change this:
- // { name:'Miss Piggy', pets:'Foo-Foo'}
- // into this:
- // { name:['Miss Piggy'], pets:['Foo-Foo']}
- //
- // We also store the attribute names so we can validate our store
- // reference and item id special properties for the O(1) isItem
- var allAttributeNames = {},
- key;
-
- for(i = 0; i < this._arrayOfAllItems.length; ++i){
- item = this._arrayOfAllItems[i];
- for(key in item){
- if(key !== this._rootItemPropName){
- var value = item[key];
- if(value !== null){
- if(!dojo.isArray(value)){
- item[key] = [value];
- }
- }else{
- item[key] = [null];
- }
- }
- allAttributeNames[key]=key;
- }
- }
-
- // Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
- // This should go really fast, it will generally never even run the loop.
- while(allAttributeNames[this._storeRefPropName]){
- this._storeRefPropName += "_";
- }
- while(allAttributeNames[this._itemNumPropName]){
- this._itemNumPropName += "_";
- }
- while(allAttributeNames[this._reverseRefMap]){
- this._reverseRefMap += "_";
- }
-
- // Step 4: Some data files specify an optional 'identifier', which is
- // the name of an attribute that holds the identity of each item.
- // If this data file specified an identifier attribute, then build a
- // hash table of items keyed by the identity of the items.
- var arrayOfValues;
-
- var identifier = dataObject.identifier;
- if(identifier){
- this._itemsByIdentity = {};
- this._features['dojo.data.api.Identity'] = identifier;
- for(i = 0; i < this._arrayOfAllItems.length; ++i){
- item = this._arrayOfAllItems[i];
- arrayOfValues = item[identifier];
- var identity = arrayOfValues[0];
- if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
- this._itemsByIdentity[identity] = item;
- }else{
- if(this._jsonFileUrl){
- throw new Error("dojo.data.ItemFileReadStore: The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
- }else if(this._jsonData){
- throw new Error("dojo.data.ItemFileReadStore: The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
- }
- }
- }
- }else{
- this._features['dojo.data.api.Identity'] = Number;
- }
-
- // Step 5: Walk through all the items, and set each item's properties
- // for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
- for(i = 0; i < this._arrayOfAllItems.length; ++i){
- item = this._arrayOfAllItems[i];
- item[this._storeRefPropName] = this;
- item[this._itemNumPropName] = i;
- }
-
- // Step 6: We walk through all the attribute values of all the items,
- // looking for type/value literals and item-references.
- //
- // We replace item-references with pointers to items. For example, we change:
- // { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
- // into this:
- // { name:['Kermit'], friends:[miss_piggy] }
- // (where miss_piggy is the object representing the 'Miss Piggy' item).
- //
- // We replace type/value pairs with typed-literals. For example, we change:
- // { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
- // into this:
- // { name:['Kermit'], born:(new Date(1918, 6, 18)) }
- //
- // We also generate the associate map for all items for the O(1) isItem function.
- for(i = 0; i < this._arrayOfAllItems.length; ++i){
- item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
- for(key in item){
- arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
- for(var j = 0; j < arrayOfValues.length; ++j){
- value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
- if(value !== null && typeof value == "object"){
- if(("_type" in value) && ("_value" in value)){
- var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
- var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
- if(!mappingObj){
- throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
- }else if(dojo.isFunction(mappingObj)){
- arrayOfValues[j] = new mappingObj(value._value);
- }else if(dojo.isFunction(mappingObj.deserialize)){
- arrayOfValues[j] = mappingObj.deserialize(value._value);
- }else{
- throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
- }
- }
- if(value._reference){
- var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
- if(!dojo.isObject(referenceDescription)){
- // example: 'Miss Piggy'
- // from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
- arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
- }else{
- // example: {name:'Miss Piggy'}
- // from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
- for(var k = 0; k < this._arrayOfAllItems.length; ++k){
- var candidateItem = this._arrayOfAllItems[k],
- found = true;
- for(var refKey in referenceDescription){
- if(candidateItem[refKey] != referenceDescription[refKey]){
- found = false;
- }
- }
- if(found){
- arrayOfValues[j] = candidateItem;
- }
- }
- }
- if(this.referenceIntegrity){
- var refItem = arrayOfValues[j];
- if(this.isItem(refItem)){
- this._addReferenceToMap(refItem, item, key);
- }
- }
- }else if(this.isItem(value)){
- //It's a child item (not one referenced through _reference).
- //We need to treat this as a referenced item, so it can be cleaned up
- //in a write store easily.
- if(this.referenceIntegrity){
- this._addReferenceToMap(value, item, key);
- }
- }
- }
- }
- }
- }
- },
-
- _addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
- // summary:
- // Method to add an reference map entry for an item and attribute.
- // description:
- // Method to add an reference map entry for an item and attribute. //
- // refItem:
- // The item that is referenced.
- // parentItem:
- // The item that holds the new reference to refItem.
- // attribute:
- // The attribute on parentItem that contains the new reference.
-
- //Stub function, does nothing. Real processing is in ItemFileWriteStore.
- },
-
- getIdentity: function(/* item */ item){
- // summary:
- // See dojo.data.api.Identity.getIdentity()
- var identifier = this._features['dojo.data.api.Identity'];
- if(identifier === Number){
- return item[this._itemNumPropName]; // Number
- }else{
- var arrayOfValues = item[identifier];
- if(arrayOfValues){
- return arrayOfValues[0]; // Object || String
- }
- }
- return null; // null
- },
-
- fetchItemByIdentity: function(/* Object */ keywordArgs){
- // summary:
- // See dojo.data.api.Identity.fetchItemByIdentity()
-
- // Hasn't loaded yet, we have to trigger the load.
- var item,
- scope;
- if(!this._loadFinished){
- var self = this;
- //Do a check on the JsonFileUrl and crosscheck it.
- //If it doesn't match the cross-check, it needs to be updated
- //This allows for either url or _jsonFileUrl to he changed to
- //reset the store load location. Done this way for backwards
- //compatibility. People use _jsonFileUrl (even though officially
- //private.
- if(this._jsonFileUrl !== this._ccUrl){
- dojo.deprecated("dojo.data.ItemFileReadStore: ",
- "To change the url, set the url property of the store," +
- " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
- this._ccUrl = this._jsonFileUrl;
- this.url = this._jsonFileUrl;
- }else if(this.url !== this._ccUrl){
- this._jsonFileUrl = this.url;
- this._ccUrl = this.url;
- }
-
- //See if there was any forced reset of data.
- if(this.data != null && this._jsonData == null){
- this._jsonData = this.data;
- this.data = null;
- }
-
- if(this._jsonFileUrl){
-
- if(this._loadInProgress){
- this._queuedFetches.push({args: keywordArgs});
- }else{
- this._loadInProgress = true;
- var getArgs = {
- url: self._jsonFileUrl,
- handleAs: "json-comment-optional",
- preventCache: this.urlPreventCache,
- failOk: this.failOk
- };
- var getHandler = dojo.xhrGet(getArgs);
- getHandler.addCallback(function(data){
- var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
- try{
- self._getItemsFromLoadedData(data);
- self._loadFinished = true;
- self._loadInProgress = false;
- item = self._getItemByIdentity(keywordArgs.identity);
- if(keywordArgs.onItem){
- keywordArgs.onItem.call(scope, item);
- }
- self._handleQueuedFetches();
- }catch(error){
- self._loadInProgress = false;
- if(keywordArgs.onError){
- keywordArgs.onError.call(scope, error);
- }
- }
- });
- getHandler.addErrback(function(error){
- self._loadInProgress = false;
- if(keywordArgs.onError){
- var scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
- keywordArgs.onError.call(scope, error);
- }
- });
- }
-
- }else if(this._jsonData){
- // Passed in data, no need to xhr.
- self._getItemsFromLoadedData(self._jsonData);
- self._jsonData = null;
- self._loadFinished = true;
- item = self._getItemByIdentity(keywordArgs.identity);
- if(keywordArgs.onItem){
- scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
- keywordArgs.onItem.call(scope, item);
- }
- }
- }else{
- // Already loaded. We can just look it up and call back.
- item = this._getItemByIdentity(keywordArgs.identity);
- if(keywordArgs.onItem){
- scope = keywordArgs.scope?keywordArgs.scope:dojo.global;
- keywordArgs.onItem.call(scope, item);
- }
- }
- },
-
- _getItemByIdentity: function(/* Object */ identity){
- // summary:
- // Internal function to look an item up by its identity map.
- var item = null;
- if(this._itemsByIdentity &&
- Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
- item = this._itemsByIdentity[identity];
- }else if (Object.hasOwnProperty.call(this._arrayOfAllItems, identity)){
- item = this._arrayOfAllItems[identity];
- }
- if(item === undefined){
- item = null;
- }
- return item; // Object
- },
-
- getIdentityAttributes: function(/* item */ item){
- // summary:
- // See dojo.data.api.Identity.getIdentityAttributes()
-
- var identifier = this._features['dojo.data.api.Identity'];
- if(identifier === Number){
- // If (identifier === Number) it means getIdentity() just returns
- // an integer item-number for each item. The dojo.data.api.Identity
- // spec says we need to return null if the identity is not composed
- // of attributes
- return null; // null
- }else{
- return [identifier]; // Array
- }
- },
-
- _forceLoad: function(){
- // summary:
- // Internal function to force a load of the store if it hasn't occurred yet. This is required
- // for specific functions to work properly.
- var self = this;
- //Do a check on the JsonFileUrl and crosscheck it.
- //If it doesn't match the cross-check, it needs to be updated
- //This allows for either url or _jsonFileUrl to he changed to
- //reset the store load location. Done this way for backwards
- //compatibility. People use _jsonFileUrl (even though officially
- //private.
- if(this._jsonFileUrl !== this._ccUrl){
- dojo.deprecated("dojo.data.ItemFileReadStore: ",
- "To change the url, set the url property of the store," +
- " not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
- this._ccUrl = this._jsonFileUrl;
- this.url = this._jsonFileUrl;
- }else if(this.url !== this._ccUrl){
- this._jsonFileUrl = this.url;
- this._ccUrl = this.url;
- }
-
- //See if there was any forced reset of data.
- if(this.data != null){
- this._jsonData = this.data;
- this.data = null;
- }
-
- if(this._jsonFileUrl){
- var getArgs = {
- url: this._jsonFileUrl,
- handleAs: "json-comment-optional",
- preventCache: this.urlPreventCache,
- failOk: this.failOk,
- sync: true
- };
- var getHandler = dojo.xhrGet(getArgs);
- getHandler.addCallback(function(data){
- try{
- //Check to be sure there wasn't another load going on concurrently
- //So we don't clobber data that comes in on it. If there is a load going on
- //then do not save this data. It will potentially clobber current data.
- //We mainly wanted to sync/wait here.
- //TODO: Revisit the loading scheme of this store to improve multi-initial
- //request handling.
- if(self._loadInProgress !== true && !self._loadFinished){
- self._getItemsFromLoadedData(data);
- self._loadFinished = true;
- }else if(self._loadInProgress){
- //Okay, we hit an error state we can't recover from. A forced load occurred
- //while an async load was occurring. Since we cannot block at this point, the best
- //that can be managed is to throw an error.
- throw new Error("dojo.data.ItemFileReadStore: Unable to perform a synchronous load, an async load is in progress.");
- }
- }catch(e){
- console.log(e);
- throw e;
- }
- });
- getHandler.addErrback(function(error){
- throw error;
- });
- }else if(this._jsonData){
- self._getItemsFromLoadedData(self._jsonData);
- self._jsonData = null;
- self._loadFinished = true;
- }
- }
-});
-//Mix in the simple fetch implementation to this class.
-dojo.extend(dojo.data.ItemFileReadStore,dojo.data.util.simpleFetch);
-
-}
+//>>built
+define("dojo/data/ItemFileReadStore",["../_base/kernel","../_base/lang","../_base/declare","../_base/array","../_base/xhr","../Evented","../_base/window","./util/filter","./util/simpleFetch","../date/stamp"],function(_1,_2,_3,_4,_5,_6,_7,_8,_9,_a){var _b=_3("dojo.data.ItemFileReadStore",[_6],{constructor:function(_c){this._arrayOfAllItems=[];this._arrayOfTopLevelItems=[];this._loadFinished=false;this._jsonFileUrl=_c.url;this._ccUrl=_c.url;this.url=_c.url;this._jsonData=_c.data;this.data=null;this._datatypeMap=_c.typeMap||{};if(!this._datatypeMap["Date"]){this._datatypeMap["Date"]={type:Date,deserialize:function(_d){return _a.fromISOString(_d);}};}this._features={"dojo.data.api.Read":true,"dojo.data.api.Identity":true};this._itemsByIdentity=null;this._storeRefPropName="_S";this._itemNumPropName="_0";this._rootItemPropName="_RI";this._reverseRefMap="_RRM";this._loadInProgress=false;this._queuedFetches=[];if(_c.urlPreventCache!==undefined){this.urlPreventCache=_c.urlPreventCache?true:false;}if(_c.hierarchical!==undefined){this.hierarchical=_c.hierarchical?true:false;}if(_c.clearOnClose){this.clearOnClose=true;}if("failOk" in _c){this.failOk=_c.failOk?true:false;}},url:"",_ccUrl:"",data:null,typeMap:null,clearOnClose:false,urlPreventCache:false,failOk:false,hierarchical:true,_assertIsItem:function(_e){if(!this.isItem(_e)){throw new Error("dojo.data.ItemFileReadStore: Invalid item argument.");}},_assertIsAttribute:function(_f){if(typeof _f!=="string"){throw new Error("dojo.data.ItemFileReadStore: Invalid attribute argument.");}},getValue:function(_10,_11,_12){var _13=this.getValues(_10,_11);return (_13.length>0)?_13[0]:_12;},getValues:function(_14,_15){this._assertIsItem(_14);this._assertIsAttribute(_15);return (_14[_15]||[]).slice(0);},getAttributes:function(_16){this._assertIsItem(_16);var _17=[];for(var key in _16){if((key!==this._storeRefPropName)&&(key!==this._itemNumPropName)&&(key!==this._rootItemPropName)&&(key!==this._reverseRefMap)){_17.push(key);}}return _17;},hasAttribute:function(_18,_19){this._assertIsItem(_18);this._assertIsAttribute(_19);return (_19 in _18);},containsValue:function(_1a,_1b,_1c){var _1d=undefined;if(typeof _1c==="string"){_1d=_8.patternToRegExp(_1c,false);}return this._containsValue(_1a,_1b,_1c,_1d);},_containsValue:function(_1e,_1f,_20,_21){return _4.some(this.getValues(_1e,_1f),function(_22){if(_22!==null&&!_2.isObject(_22)&&_21){if(_22.toString().match(_21)){return true;}}else{if(_20===_22){return true;}}});},isItem:function(_23){if(_23&&_23[this._storeRefPropName]===this){if(this._arrayOfAllItems[_23[this._itemNumPropName]]===_23){return true;}}return false;},isItemLoaded:function(_24){return this.isItem(_24);},loadItem:function(_25){this._assertIsItem(_25.item);},getFeatures:function(){return this._features;},getLabel:function(_26){if(this._labelAttr&&this.isItem(_26)){return this.getValue(_26,this._labelAttr);}return undefined;},getLabelAttributes:function(_27){if(this._labelAttr){return [this._labelAttr];}return null;},_fetchItems:function(_28,_29,_2a){var _2b=this,_2c=function(_2d,_2e){var _2f=[],i,key;if(_2d.query){var _30,_31=_2d.queryOptions?_2d.queryOptions.ignoreCase:false;var _32={};for(key in _2d.query){_30=_2d.query[key];if(typeof _30==="string"){_32[key]=_8.patternToRegExp(_30,_31);}else{if(_30 instanceof RegExp){_32[key]=_30;}}}for(i=0;i<_2e.length;++i){var _33=true;var _34=_2e[i];if(_34===null){_33=false;}else{for(key in _2d.query){_30=_2d.query[key];if(!_2b._containsValue(_34,key,_30,_32[key])){_33=false;}}}if(_33){_2f.push(_34);}}_29(_2f,_2d);}else{for(i=0;i<_2e.length;++i){var _35=_2e[i];if(_35!==null){_2f.push(_35);}}_29(_2f,_2d);}};if(this._loadFinished){_2c(_28,this._getItemsArray(_28.queryOptions));}else{if(this._jsonFileUrl!==this._ccUrl){_1.deprecated("dojo.data.ItemFileReadStore: ","To change the url, set the url property of the store,"+" not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");this._ccUrl=this._jsonFileUrl;this.url=this._jsonFileUrl;}else{if(this.url!==this._ccUrl){this._jsonFileUrl=this.url;this._ccUrl=this.url;}}if(this.data!=null){this._jsonData=this.data;this.data=null;}if(this._jsonFileUrl){if(this._loadInProgress){this._queuedFetches.push({args:_28,filter:_2c});}else{this._loadInProgress=true;var _36={url:_2b._jsonFileUrl,handleAs:"json-comment-optional",preventCache:this.urlPreventCache,failOk:this.failOk};var _37=_5.get(_36);_37.addCallback(function(_38){try{_2b._getItemsFromLoadedData(_38);_2b._loadFinished=true;_2b._loadInProgress=false;_2c(_28,_2b._getItemsArray(_28.queryOptions));_2b._handleQueuedFetches();}catch(e){_2b._loadFinished=true;_2b._loadInProgress=false;_2a(e,_28);}});_37.addErrback(function(_39){_2b._loadInProgress=false;_2a(_39,_28);});var _3a=null;if(_28.abort){_3a=_28.abort;}_28.abort=function(){var df=_37;if(df&&df.fired===-1){df.cancel();df=null;}if(_3a){_3a.call(_28);}};}}else{if(this._jsonData){try{this._loadFinished=true;this._getItemsFromLoadedData(this._jsonData);this._jsonData=null;_2c(_28,this._getItemsArray(_28.queryOptions));}catch(e){_2a(e,_28);}}else{_2a(new Error("dojo.data.ItemFileReadStore: No JSON source data was provided as either URL or a nested Javascript object."),_28);}}}},_handleQueuedFetches:function(){if(this._queuedFetches.length>0){for(var i=0;i<this._queuedFetches.length;i++){var _3b=this._queuedFetches[i],_3c=_3b.args,_3d=_3b.filter;if(_3d){_3d(_3c,this._getItemsArray(_3c.queryOptions));}else{this.fetchItemByIdentity(_3c);}}this._queuedFetches=[];}},_getItemsArray:function(_3e){if(_3e&&_3e.deep){return this._arrayOfAllItems;}return this._arrayOfTopLevelItems;},close:function(_3f){if(this.clearOnClose&&this._loadFinished&&!this._loadInProgress){if(((this._jsonFileUrl==""||this._jsonFileUrl==null)&&(this.url==""||this.url==null))&&this.data==null){}this._arrayOfAllItems=[];this._arrayOfTopLevelItems=[];this._loadFinished=false;this._itemsByIdentity=null;this._loadInProgress=false;this._queuedFetches=[];}},_getItemsFromLoadedData:function(_40){var _41=false,_42=this;function _43(_44){return (_44!==null)&&(typeof _44==="object")&&(!_2.isArray(_44)||_41)&&(!_2.isFunction(_44))&&(_44.constructor==Object||_2.isArray(_44))&&(typeof _44._reference==="undefined")&&(typeof _44._type==="undefined")&&(typeof _44._value==="undefined")&&_42.hierarchical;};function _45(_46){_42._arrayOfAllItems.push(_46);for(var _47 in _46){var _48=_46[_47];if(_48){if(_2.isArray(_48)){var _49=_48;for(var k=0;k<_49.length;++k){var _4a=_49[k];if(_43(_4a)){_45(_4a);}}}else{if(_43(_48)){_45(_48);}}}}};this._labelAttr=_40.label;var i,_4b;this._arrayOfAllItems=[];this._arrayOfTopLevelItems=_40.items;for(i=0;i<this._arrayOfTopLevelItems.length;++i){_4b=this._arrayOfTopLevelItems[i];if(_2.isArray(_4b)){_41=true;}_45(_4b);_4b[this._rootItemPropName]=true;}var _4c={},key;for(i=0;i<this._arrayOfAllItems.length;++i){_4b=this._arrayOfAllItems[i];for(key in _4b){if(key!==this._rootItemPropName){var _4d=_4b[key];if(_4d!==null){if(!_2.isArray(_4d)){_4b[key]=[_4d];}}else{_4b[key]=[null];}}_4c[key]=key;}}while(_4c[this._storeRefPropName]){this._storeRefPropName+="_";}while(_4c[this._itemNumPropName]){this._itemNumPropName+="_";}while(_4c[this._reverseRefMap]){this._reverseRefMap+="_";}var _4e;var _4f=_40.identifier;if(_4f){this._itemsByIdentity={};this._features["dojo.data.api.Identity"]=_4f;for(i=0;i<this._arrayOfAllItems.length;++i){_4b=this._arrayOfAllItems[i];_4e=_4b[_4f];var _50=_4e[0];if(!Object.hasOwnProperty.call(this._itemsByIdentity,_50)){this._itemsByIdentity[_50]=_4b;}else{if(this._jsonFileUrl){throw new Error("dojo.data.ItemFileReadStore: The json data as specified by: ["+this._jsonFileUrl+"] is malformed. Items within the list have identifier: ["+_4f+"]. Value collided: ["+_50+"]");}else{if(this._jsonData){throw new Error("dojo.data.ItemFileReadStore: The json data provided by the creation arguments is malformed. Items within the list have identifier: ["+_4f+"]. Value collided: ["+_50+"]");}}}}}else{this._features["dojo.data.api.Identity"]=Number;}for(i=0;i<this._arrayOfAllItems.length;++i){_4b=this._arrayOfAllItems[i];_4b[this._storeRefPropName]=this;_4b[this._itemNumPropName]=i;}for(i=0;i<this._arrayOfAllItems.length;++i){_4b=this._arrayOfAllItems[i];for(key in _4b){_4e=_4b[key];for(var j=0;j<_4e.length;++j){_4d=_4e[j];if(_4d!==null&&typeof _4d=="object"){if(("_type" in _4d)&&("_value" in _4d)){var _51=_4d._type;var _52=this._datatypeMap[_51];if(!_52){throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '"+_51+"'");}else{if(_2.isFunction(_52)){_4e[j]=new _52(_4d._value);}else{if(_2.isFunction(_52.deserialize)){_4e[j]=_52.deserialize(_4d._value);}else{throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");}}}}if(_4d._reference){var _53=_4d._reference;if(!_2.isObject(_53)){_4e[j]=this._getItemByIdentity(_53);}else{for(var k=0;k<this._arrayOfAllItems.length;++k){var _54=this._arrayOfAllItems[k],_55=true;for(var _56 in _53){if(_54[_56]!=_53[_56]){_55=false;}}if(_55){_4e[j]=_54;}}}if(this.referenceIntegrity){var _57=_4e[j];if(this.isItem(_57)){this._addReferenceToMap(_57,_4b,key);}}}else{if(this.isItem(_4d)){if(this.referenceIntegrity){this._addReferenceToMap(_4d,_4b,key);}}}}}}}},_addReferenceToMap:function(_58,_59,_5a){},getIdentity:function(_5b){var _5c=this._features["dojo.data.api.Identity"];if(_5c===Number){return _5b[this._itemNumPropName];}else{var _5d=_5b[_5c];if(_5d){return _5d[0];}}return null;},fetchItemByIdentity:function(_5e){var _5f,_60;if(!this._loadFinished){var _61=this;if(this._jsonFileUrl!==this._ccUrl){_1.deprecated("dojo.data.ItemFileReadStore: ","To change the url, set the url property of the store,"+" not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");this._ccUrl=this._jsonFileUrl;this.url=this._jsonFileUrl;}else{if(this.url!==this._ccUrl){this._jsonFileUrl=this.url;this._ccUrl=this.url;}}if(this.data!=null&&this._jsonData==null){this._jsonData=this.data;this.data=null;}if(this._jsonFileUrl){if(this._loadInProgress){this._queuedFetches.push({args:_5e});}else{this._loadInProgress=true;var _62={url:_61._jsonFileUrl,handleAs:"json-comment-optional",preventCache:this.urlPreventCache,failOk:this.failOk};var _63=_5.get(_62);_63.addCallback(function(_64){var _65=_5e.scope?_5e.scope:_7.global;try{_61._getItemsFromLoadedData(_64);_61._loadFinished=true;_61._loadInProgress=false;_5f=_61._getItemByIdentity(_5e.identity);if(_5e.onItem){_5e.onItem.call(_65,_5f);}_61._handleQueuedFetches();}catch(error){_61._loadInProgress=false;if(_5e.onError){_5e.onError.call(_65,error);}}});_63.addErrback(function(_66){_61._loadInProgress=false;if(_5e.onError){var _67=_5e.scope?_5e.scope:_7.global;_5e.onError.call(_67,_66);}});}}else{if(this._jsonData){_61._getItemsFromLoadedData(_61._jsonData);_61._jsonData=null;_61._loadFinished=true;_5f=_61._getItemByIdentity(_5e.identity);if(_5e.onItem){_60=_5e.scope?_5e.scope:_7.global;_5e.onItem.call(_60,_5f);}}}}else{_5f=this._getItemByIdentity(_5e.identity);if(_5e.onItem){_60=_5e.scope?_5e.scope:_7.global;_5e.onItem.call(_60,_5f);}}},_getItemByIdentity:function(_68){var _69=null;if(this._itemsByIdentity){if(Object.hasOwnProperty.call(this._itemsByIdentity,_68)){_69=this._itemsByIdentity[_68];}}else{if(Object.hasOwnProperty.call(this._arrayOfAllItems,_68)){_69=this._arrayOfAllItems[_68];}}if(_69===undefined){_69=null;}return _69;},getIdentityAttributes:function(_6a){var _6b=this._features["dojo.data.api.Identity"];if(_6b===Number){return null;}else{return [_6b];}},_forceLoad:function(){var _6c=this;if(this._jsonFileUrl!==this._ccUrl){_1.deprecated("dojo.data.ItemFileReadStore: ","To change the url, set the url property of the store,"+" not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");this._ccUrl=this._jsonFileUrl;this.url=this._jsonFileUrl;}else{if(this.url!==this._ccUrl){this._jsonFileUrl=this.url;this._ccUrl=this.url;}}if(this.data!=null){this._jsonData=this.data;this.data=null;}if(this._jsonFileUrl){var _6d={url:this._jsonFileUrl,handleAs:"json-comment-optional",preventCache:this.urlPreventCache,failOk:this.failOk,sync:true};var _6e=_5.get(_6d);_6e.addCallback(function(_6f){try{if(_6c._loadInProgress!==true&&!_6c._loadFinished){_6c._getItemsFromLoadedData(_6f);_6c._loadFinished=true;}else{if(_6c._loadInProgress){throw new Error("dojo.data.ItemFileReadStore: Unable to perform a synchronous load, an async load is in progress.");}}}catch(e){throw e;}});_6e.addErrback(function(_70){throw _70;});}else{if(this._jsonData){_6c._getItemsFromLoadedData(_6c._jsonData);_6c._jsonData=null;_6c._loadFinished=true;}}}});_2.extend(_b,_9);return _b;}); \ No newline at end of file