diff --git a/lib/edit/OfflineStore.js b/lib/edit/OfflineStore.js index 1ec17fb..5282775 100755 --- a/lib/edit/OfflineStore.js +++ b/lib/edit/OfflineStore.js @@ -79,7 +79,6 @@ var OfflineStore = function(/* Map */ map) { LOCAL_STORAGE_MAX_LIMIT : 4.75 /* MB */, /* A unique token for tokenizing stringified localStorage values */ TOKEN : "|||", - EDIT_EVENT: "editEvent", EDIT_EVENT_DUPLICATE: "duplicateEditEvent", /** * An event has been attempted during an offline condition @@ -130,7 +129,8 @@ var OfflineStore = function(/* Map */ map) { } /** - * Public method for retrieving all items in the localStore. + * Public method for retrieving all items in the temporary localStore. + * This request does not return the index information. For that use getLocalStoreIndex(). * @returns {Array} Graphics */ this.getStore = function(){ @@ -214,6 +214,16 @@ var OfflineStore = function(/* Map */ map) { } } + this.getGraphicsLayerById = function(/* String */ id){ + for(var layer in this.layers) + { + if(id == this.layers[layer].layerId){ + return this.layers[layer]; + break; + } + } + } + ////////////////////////// /// /// PRIVATE methods @@ -384,9 +394,7 @@ var OfflineStore = function(/* Map */ map) { layer.add(graphic); - var obj = new this.pendingEventObject(graphic,layer.id,enumValue) - - this._dispatchEvent(obj,this._localEnum().PENDING_EDIT_EVENT); + this._dispatchEvent(true,this._localEnum().PENDING_EDIT_EVENT); } this._startOfflineListener = function(){ @@ -397,7 +405,7 @@ var OfflineStore = function(/* Map */ map) { var arr = null; try{var arr = this._getTempLocalStore()}catch(err){console.log("onlineStatusHandler: " + err.toString())}; if(arr != null){ - this._reestablishedInternetCallbackHandler(); + this._reestablishedInternet(); } } } @@ -435,22 +443,22 @@ var OfflineStore = function(/* Map */ map) { } } - /** - * Makes calls to _reestablishedInternet() and handles the callback properties - * @private - */ - this._reestablishedInternetCallbackHandler = function(){ - this._reestablishedInternet(function(/* boolean */ success, /* boolean */ done,enumString,graphicsLayerId,objectId,error){ - var obj = null; - if(success == true){ - - obj = new this._editResultObject(success,enumString,graphicsLayerId,objectId,error); - - this._deleteTempLocalStore(); //all edits should have been pushed to server before this executes! - } - this._dispatchEvent(obj,this._localEnum().EDIT_EVENT); - }.bind(this)); - } +// /** +// * Makes calls to _reestablishedInternet() and handles the callback properties +// * @private +// */ +// this._reestablishedInternetCallbackHandler = function(){ +// this._reestablishedInternet(function(/* boolean */ success, /* boolean */ done,enumString,graphicsLayerId,objectId,error){ +// var obj = null; +// if(success == true){ +// var date = JSON.stringify(this.utils.getDateMDYHMS()); +// obj = new this._editResultObject(success,enumString,graphicsLayerId,objectId,date,error); +// +// this._deleteTempLocalStore(); //all edits should have been pushed to server before this executes! +// } +// this._dispatchEvent(obj,this._localEnum().EDIT_EVENT); +// }.bind(this)); +// } /** * Creates a graphics array from localstorage and pushes all applicable edits to a @@ -459,61 +467,27 @@ var OfflineStore = function(/* Map */ map) { * if this.getStore() returns null * @private */ - this._reestablishedInternet = function(callback){ + this._reestablishedInternet = function(){ var graphicsArr = this.getStore(); if(graphicsArr != null && this.layers != null){ - var check = []; - var errCnt = 0; +// var check = []; +// var errCnt = 0; var length = graphicsArr.length; for(var i = 0; i < length; i++){ var obj1 = graphicsArr[i]; - var layer = this._getGraphicsLayerById(obj1.layer); + var layer = this.getGraphicsLayerById(obj1.layer); this._layerEditManager(obj1.graphic,layer,obj1.enumValue,this.enum(),i,function(/* Number */ num, /* boolean */ success, /* String */ id,error){ - check.push(num); - var date = new Date(); var indexObject = new this._indexObject(obj1.layer,id,obj1.enumValue,success,obj1.graphic.geometry.type,date) ; + var deleteTempItem = this._deleteItemInLocalStore(JSON.stringify(obj1)); this._setItemLocalStoreIndexObject(indexObject); - - if(success == true && check.length == graphicsArr.length){ - if(errCnt == 0){ - callback(true,true,obj1.enumValue,obj1.layer,id); - } - else{ //this could cause a bug since it doesn't set item in local store. Not verified! - console.log("_reestablishedInternet: there were errors. LocalStore still available."); - callback(false,true,obj1.enumValue,obj1.layer,id); - } - } - //Don't return anything yet - else if(success == true && check.length < graphicsArr.length){ - //Do nothing - } - else if(success == false && check.length == graphicsArr.length){ - console.log("_reestablishedInternet: error sending edit on " + id); - callback(false,true,obj1.enumValue,obj1.layer,id,error); - } - //Don't return anything yet - else if(success == false && check.length < graphicsArr.length){ - errCnt++; - console.log("_reestablishedInternet: error sending edit on " + id); - } }.bind(this)); } } else{ - callback(false,true,null,null,null); - } - } - - this._getGraphicsLayerById = function(/* String */ id){ - for(var layer in this.layers) - { - if(id == this.layers[layer].layerId){ - return this.layers[layer]; - break; - } + console.log("_reestablishedInternet: graphicsArray was null."); } } @@ -547,15 +521,15 @@ var OfflineStore = function(/* Map */ map) { * The graphic will not have a UID because it has not been processed by the feature service. * This poses a minor storage issue. So, in this version of the library we simply * store the serialized graphic information and append it to any other pending edits. - * @param geometry + * @param serializedGraphic * @returns {boolean} returns true if success, else false. Writes * error stack to console. */ - this._setTempLocalStore = function(/* Geometry */ geometry){ + this._setTempLocalStore = function(/* String */ serializedGraphic){ var success = false; try{ - localStorage.setItem(this._localEnum().STORAGE_KEY,geometry); + localStorage.setItem(this._localEnum().STORAGE_KEY,serializedGraphic); success = true; } catch(err){ @@ -568,13 +542,13 @@ var OfflineStore = function(/* Map */ map) { } /** - * Deletes an item in local store. + * Deletes an item in temporary local store. * REQUIRES: ObjectId field is present * @param objectId * @param callback * @private */ - this._deleteItemInLocalStore = function(/* String */ objectId,callback){ + this._deleteObjectIdInLocalStore = function(/* String */ objectId,callback){ var success = false; var localStore = localStorage.getItem(this._localEnum().STORAGE_KEY); @@ -588,6 +562,41 @@ var OfflineStore = function(/* Map */ map) { if(q.hasOwnProperty("objectid") && q.objectid == objectId){ splitStore.splice(parseFloat(property),1); var newArr = this._reserializeGraphicsArray(splitStore); + localStorage.removeItem(this._localEnum().STORAGE_KEY); + this._setTempLocalStore(newArr); + success = true; + break; + } + } + } + } + + callback(success); + } + + /** + * Deletes an item in temporary local store. + * REQUIRES: ObjectId field is present + * @param entry + * @param callback + * @private + */ + this._deleteItemInLocalStore = function(/* String */ entry,callback){ + var success = false; + var localStore = localStorage.getItem(this._localEnum().STORAGE_KEY); + + if(localStore != null){ + var splitStore = localStore.split(this._localEnum().TOKEN); + for(var property in splitStore){ + var test = splitStore[property]; + if(typeof test !== "undefined" && test.length > 0 && test != null && Boolean(test) != false){ +// var item = JSON.parse(splitStore[property]); +// var q = JSON.parse(item.attributes); + if(test == entry){ + splitStore.splice(parseFloat(property),1); + var newArr = this._reserializeGraphicsArray(splitStore); + localStorage.removeItem(this._localEnum().STORAGE_KEY); + this._setTempLocalStore(newArr); success = true; break; } @@ -721,6 +730,12 @@ var OfflineStore = function(/* Map */ map) { } + /** + * Returns a graphic that has been reconstituted from localStorage. + * @param item + * @returns {{graphic: esri.Graphic, layer: *, enumValue: *}} + * @private + */ this._deserializeGraphic = function(/* Graphic */ item){ var jsonItem = JSON.parse(item); @@ -728,6 +743,7 @@ var OfflineStore = function(/* Map */ map) { var attributes = JSON.parse(jsonItem.attributes); var enumValue = jsonItem.enumValue; var layer = JSON.parse(jsonItem.layer); + var date = JSON.parse(jsonItem.date); var finalGeom = null; switch(geometry.type){ @@ -750,7 +766,7 @@ var OfflineStore = function(/* Map */ map) { var graphic = new esri.Graphic(finalGeom, null, attributes, null); - return {"graphic":graphic,"layer":layer,"enumValue":enumValue}; + return {"graphic":graphic,"layer":layer,"enumValue":enumValue,"date":date}; } /** @@ -766,6 +782,7 @@ var OfflineStore = function(/* Map */ map) { json.layer = layer.layerId; json.enumValue = enumValue; json.geometry = JSON.stringify(graphic.geometry) + json.date = JSON.stringify(this.utils.getDateMDYHMS()); if(graphic.hasOwnProperty("attributes")){ if(graphic.attributes != null){ @@ -788,18 +805,6 @@ var OfflineStore = function(/* Map */ map) { /// ////////////////////////// - /** - * Model for storing info related to a pending edit event. - * @param graphic - * @param layerId - * @param enumValue - */ - this.pendingEventObject = function(/* Graphic */ graphic, /* String */ layerId, /* String */ enumValue ){ - this.graphic = graphic; - this.layerId = layerId; - this.enumValue = enumValue; - } - /** * Model for storing serialized graphics * @private @@ -809,6 +814,7 @@ var OfflineStore = function(/* Map */ map) { this.enumValue = null; this.geometry = null; this.attributes = null; + this.date = null; } /** @@ -843,11 +849,12 @@ var OfflineStore = function(/* Map */ map) { * @private */ this._editResultObject = function(/* boolean */ success, /* String */ enumString, - /* int */ graphicsLayerId, /* int */ objectId,error){ + /* int */ graphicsLayerId, /* int */ objectId,/* String */ date, error){ this.success = success; this.enumString = enumString; this.graphicsLayerId = graphicsLayerId; this.objectId = objectId; + this.date = date; this.error = error; } @@ -959,7 +966,7 @@ var OfflineStore = function(/* Map */ map) { var arr = this._getTempLocalStore(); if(arr != null && Offline.state === 'up'){ - this._reestablishedInternetCallbackHandler(); + this._reestablishedInternet(); } else if(arr != null && Offline.state !== 'up'){ this._startOfflineListener(); diff --git a/samples/edit-basic.html b/samples/edit-basic.html index b111b80..2b9b95d 100644 --- a/samples/edit-basic.html +++ b/samples/edit-basic.html @@ -258,18 +258,6 @@ require([ var message = evt.detail.message; switch(evt.detail.type){ - case "editEvent": - if(message == null){ - alert("There was a problem submitted the edit(s). See console for details.") - } - if(message.hasOwnProperty("success") && message.success == true){ - console.log("editEvent: All edits successfully pushed"); - } - else if(message.hasOwnProperty("success") && message.success == false){ - alert("Not all edits were successfully pushed to the server"); - } - headerDiv.innerHTML = getStorageInfo(); - break; case "internetStatusChangeEvent": if(message == true){ document.getElementById("offlineImg").src = "../samples/img/32-Link.png"; @@ -281,15 +269,17 @@ require([ } break; case "indexUpdateEvent": + headerDiv.innerHTML = getStorageInfo(); updateHistoryTextArea(evt); break; case "pendingEditEvent": if(message != null){ - addPendingTextArea(message); + addPendingTextArea(); } headerDiv.innerHTML = getStorageInfo(); break; case "duplicateEditEvent": + alert("Duplicate edit event"); console.log("duplicate event attempt"); break; } @@ -310,8 +300,6 @@ require([ editToolbar.on("deactivate", function(evt) { -// updatePendingTextArea(evt.graphic,"Attempting to update",null); - if(updateFlag == true){ offlineStore.applyEdits(vertices.graphic,vertices.layer,offlineStore.enum().UPDATE,function(count,success,id){ headerDiv.innerHTML = getStorageInfo(); @@ -352,8 +340,6 @@ require([ event.stop(evt); if (evt.ctrlKey === true || evt.metaKey === true) { //delete feature if ctrl key is depressed -// updatePendingTextArea(evt.graphic,"Attempting to delete",null); - try{ offlineStore.applyEdits(evt.graphic,layer,offlineStore.enum().DELETE,function(count,success,id){ headerDiv.innerHTML = getStorageInfo(); @@ -408,8 +394,6 @@ require([ var newGraphic = new Graphic(evt.geometry, null, newAttributes); //selectedTemplate.featureLayer.applyEdits([newGraphic], null, null); -// updatePendingTextArea(newGraphic,"Attempting to Add",null); - offlineStore.applyEdits(newGraphic,selectedTemplate.featureLayer,offlineStore.enum().ADD,function(count,success,id){ headerDiv.innerHTML = "Storage Used: " + offlineStore.getlocalStorageUsed() + " MBs"; }); @@ -433,52 +417,32 @@ function graphicInfoModel(/* Graphic */ graphic){ graphic.hasOwnProperty("geometry") ? this.geometryType = graphic.geometry.type : this.geometryType = null; } +function addPendingTextArea(){ -/** - * Sends pending changes to the update log - * @param graphic - * @param evt - * @param id - */ -function updatePendingTextArea(graphic,/* String */ evt,id){ - var date = new Date(); - var dateHMS = getDateHMS(); - var attributes = graphic.attributes; - var objectid = attributes.objectid; - var geometryType = graphic.geometry.type; + var logText = ""; + var graphicsArr = offlineStore.getStore(); - if (typeof objectid == "undefined" && attributes.hasOwnProperty("symbolname") == true){ - objectid = "new. Symbol name = " + attributes.symbolname; - } - else if (typeof objectid == "undefined" && attributes.hasOwnProperty("symbolname") == false){ - objectid = "new. Symbol name = unknown"; - } - else if(attributes != null || typeof attributes != "undefined" && attributes.hasOwnProperty("objectid")){ - objectid = attributes.objectid; - } - else if (id != null || typeof id != "undefined"){ - objectid = id; + if(graphicsArr != null){ + var length = graphicsArr.length; + for(var i = 0; i < length; i++){ + var symbolname = null; + var graphic = graphicsArr[i].graphic; + var layer = graphicsArr[i].layer; + var enumValue = graphicsArr[i].enumValue; + var date = graphicsArr[i].date; + var geometryType = graphic.geometry.type; + if(graphic.hasOwnProperty("attributes") && graphic.attributes.hasOwnProperty("objectid")){ + objectid = graphic.attributes.objectid; + graphic.attributes.hasOwnProperty("symbolname") == true ? symbolname = graphic.attributes.symbolname : symbolname = null; + } + else{ + + } + logText += date + " " + enumValue + " " + geometryType + " " + objectid + ", symbolname: " + symbolname + ", date:" + date + "\n\r"; + } } - _logValue += dateHMS + " " + evt + " " + geometryType + " " + objectid + "\n\r"; - document.getElementById("pendingTextArea").innerHTML = _logValue; -} - -function addPendingTextArea(message){ - var objectid = null; - var layer = message.layerId; - var enumValue = message.enumValue; - var date = offlineUtils.getDateMDYHMS(); - var graphic = message.graphic; - var geometryType = graphic.geometry.type; - if(graphic.hasOwnProperty("attributes") && graphic.attributes.hasOwnProperty("objectid")){ - objectid = graphic.attributes.objectid; - } - - _logValue += date + " " + enumValue + " " + geometryType + " " + objectid + "\n\r"; - document.getElementById("pendingTextArea").innerHTML = _logValue; - -// console.log(date + ", " + objectid + ", " + layer + ", " + enumValue); + document.getElementById("pendingTextArea").innerHTML = logText; } /** @@ -486,25 +450,17 @@ function addPendingTextArea(message){ * @param evt */ function updateHistoryTextArea(evt){ - var dateHMS = getDateHMS(); - var evtObjMsg = evt.detail.message; + var logText = ""; + var index = offlineStore.getLocalStoreIndex(); - var evtType; - - switch(evtObjMsg.type){ - case "delete": - evtType = "Delete success = " + evtObjMsg.success; - break; - case "add": - evtType = "Add success = " + evtObjMsg.success; - break; - case "update": - evtType = "Update success = " + evtObjMsg.success; - break; + if(index != null){ + for(var property in index){ + var item = index[property]; + logText += item + "\n\r"; + } } - _logValue += dateHMS + " " + evtType + " for " + evtObjMsg.geometryType + " id: " + evtObjMsg.id + "\n\r"; - document.getElementById("historyTextArea").innerHTML = _logValue; + document.getElementById("historyTextArea").innerHTML = logText; } function getDateHMS(){ diff --git a/test/spec/OfflineStoreSpec.js b/test/spec/OfflineStoreSpec.js index bef05ad..bb15461 100755 --- a/test/spec/OfflineStoreSpec.js +++ b/test/spec/OfflineStoreSpec.js @@ -5,7 +5,7 @@ describe("Initialize Offline Library", function() { }) it("get graphics layer by id", function(){ - var layer = offlineStore._getGraphicsLayerById(6); + var layer = offlineStore.getGraphicsLayerById(6); expect(layer).toEqual(jasmine.any(Object)); }) @@ -98,7 +98,7 @@ describe("Serialize/Deserialize Graphic - simple Point Graphic", function(){ it("delete one graphic from local storage", function(){ var value = null; - var attempt = offlineStore._deleteItemInLocalStore("42749",function(evt){ + var attempt = offlineStore._deleteObjectIdInLocalStore("42749",function(evt){ value = evt; }.bind(this)) expect(value).toEqual(true); @@ -484,13 +484,28 @@ describe("Validate local storage index functionality",function(){ }) describe("Reestablish internet", function(){ + + it("set item in local storage", function(){ + offlineStore._deleteLocalStoreIndex(); + var json = offlineStore._serializeGraphic(complexPolygonGraphic,landusePointLayer,offlineStore.enum().ADD); + var setItem = offlineStore._setTempLocalStore(json); + expect(setItem).toEqual(true); + }) + it("reestablish internet handler with empty store", function(){ var validate = null; - offlineStore._reestablishedInternet(function(evt){ - validate = evt; - }); - expect(validate).toEqual(false); + offlineStore._reestablishedInternet(); + + var item = offlineStore.getLocalStoreIndex(); + expect(item).toEqual(null); }) + + it("get item from local storage using getStore()", function(){ + var data = offlineStore.getStore(); + var type = Object.prototype.toString.call( data ); // === '[object Array]'; + expect(type).toEqual('[object Array]'); + }) + }) describe("Test custom event handling", function(){