mirror of
https://github.com/Esri/offline-editor-js.git
synced 2025-12-15 15:20:05 +00:00
added localStore indexing for tracking changes. Numerous other fixes, upgrades.
This commit is contained in:
parent
86482b5dc8
commit
f4925fdb83
30
index.html
30
index.html
@ -47,6 +47,8 @@
|
||||
<script>
|
||||
var map;
|
||||
var offlineStore;
|
||||
var vertices;
|
||||
var updateFlag = false;
|
||||
require([
|
||||
"esri/map",
|
||||
"esri/geometry/Point",
|
||||
@ -123,17 +125,28 @@
|
||||
console.log("layers", layers);
|
||||
|
||||
//////////
|
||||
var yeck = map.graphicsLayerIds;
|
||||
var blah = map.getLayer(yeck[1]);
|
||||
offlineStore = new OfflineStore(map);
|
||||
console.log("Storage used : " + offlineStore.getlocalStorageUsed());
|
||||
|
||||
//////////
|
||||
|
||||
var editToolbar = new Edit(map);
|
||||
|
||||
editToolbar.on("deactivate", function(evt) {
|
||||
if(updateFlag == true){
|
||||
offlineStore.applyEdits(vertices.graphic,vertices.layer,offlineStore.enum().UPDATE);
|
||||
updateFlag = false;
|
||||
}
|
||||
else{
|
||||
offlineStore.applyEdits(evt.graphic,currentLayer,offlineStore.enum().UPDATE);
|
||||
}
|
||||
});
|
||||
|
||||
map.on("click", function(evt){
|
||||
var state = editToolbar.getCurrentState();
|
||||
if(state.isModified){
|
||||
updateFlag = true;
|
||||
editToolbar.deactivate();
|
||||
}
|
||||
});
|
||||
|
||||
arrayUtils.forEach(layers, function(layer) {
|
||||
@ -142,6 +155,7 @@
|
||||
event.stop(evt);
|
||||
if (editingEnabled === false) {
|
||||
editingEnabled = true;
|
||||
vertices = new offlineStore.verticesObject(evt.graphic,layer);
|
||||
editToolbar.activate(Edit.EDIT_VERTICES , evt.graphic);
|
||||
} else {
|
||||
currentLayer = this;
|
||||
@ -225,9 +239,13 @@
|
||||
function handleRestablishedInternet(){
|
||||
offlineStore._handleRestablishedInternet(function(){
|
||||
offlineStore._stopTimer();
|
||||
offlineStore.deleteStore();
|
||||
offlineStore._deleteStore();
|
||||
});
|
||||
}
|
||||
|
||||
function getIndex(){
|
||||
console.log("index: " + offlineStore.getLocalStoreIndex());
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="claro">
|
||||
@ -238,11 +256,13 @@
|
||||
<div id="templatePickerDiv"></div>
|
||||
</div>
|
||||
<div data-dojo-type="dijit/layout/ContentPane" id="footer" data-dojo-props="region:'bottom'">
|
||||
<button style="background-color: red" onclick="offlineStore.deleteStore()">Delete localStorage</button>
|
||||
<button style="background-color: red" onclick="offlineStore._deleteStore()">Delete localStorage</button>
|
||||
<button style="background-color: red" onclick="getLocalStorage()" >Get localStorage</button>
|
||||
<button style="background-color: red" onclick="getGraphicsLayers()" >Get Graphics Layers</button>
|
||||
<button style="background-color: red" onclick="offlineStore._stopTimer()" >Stop Timer</button>
|
||||
<button style="background-color: red" onclick="offlineStore._startTimer()" >Start Timer</button>
|
||||
<button style="background-color: red" onclick="getIndex()" >Get Index</button>
|
||||
<button style="background-color: red" onclick="offlineStore._deleteLocalStoreIndex()" >Delete Index</button>
|
||||
<button style="background-color: red" onclick="handleRestablishedInternet()" >Test Restablish Internet</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -22,6 +22,7 @@ var OfflineStore = function(/* Map */ map) {
|
||||
this.isTimer = null;
|
||||
this.layers = []; //An array of all feature layers
|
||||
this.map = map;
|
||||
this.map.offlineStore = this;
|
||||
|
||||
/**
|
||||
* Public ENUMs (Constants)
|
||||
@ -48,9 +49,10 @@ var OfflineStore = function(/* Map */ map) {
|
||||
*/
|
||||
this._localEnum = (function(){
|
||||
var values = {
|
||||
VALIDATION_URL : "http://localhost/test/test.html", /* Change this to a remote server for testing! */
|
||||
VALIDATION_URL : "http://localhost/offline/test.html", /* Change this to a remote server for testing! */
|
||||
TIMER_URL : "./scripts/Timer.js", /* For use within a child process only */
|
||||
STORAGE_KEY : "___EsriOfflineStore___", /* Unique key for setting/retrieving values from localStorage */
|
||||
INDEX_KEY : "___EsriOfflineIndex___", /* Index for tracking each action (add, delete, update) in local store */
|
||||
VALIDATION_TIMEOUT : 10 * 1000, /* HTTP timeout when trying to validate internet on/off */
|
||||
LOCAL_STORAGE_MAX_LIMIT : 4.75 /* MB */, /* Most browsers offer default storage of ~5MB */
|
||||
TOKEN : "|||", /* A unique token for tokenizing stringified localStorage values */
|
||||
@ -63,6 +65,16 @@ var OfflineStore = function(/* Map */ map) {
|
||||
return values;
|
||||
});
|
||||
|
||||
/**
|
||||
* Model for handle vertices editing
|
||||
* @param graphic
|
||||
* @param layer
|
||||
*/
|
||||
this.verticesObject = function(/* Graphic */ graphic, /* FeatureLayer */ layer){
|
||||
this.graphic = graphic;
|
||||
this.layer = layer;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
///
|
||||
/// PUBLIC methods
|
||||
@ -92,11 +104,11 @@ var OfflineStore = function(/* Map */ map) {
|
||||
this._addToLocalStore(graphic,layer,enumValue);
|
||||
if(this.isTimer == null){
|
||||
this._startTimer(function(err){
|
||||
alert("unable to start background timer. Offline edits won't work. " + err.stack);
|
||||
throw ("unable to start background timer. Offline edits won't work. " + err.stack);
|
||||
});
|
||||
}
|
||||
}
|
||||
else if(internet === null || typeof internet === "undefined"){
|
||||
else if(internet == null || typeof internet == "undefined"){
|
||||
console.log("applyEdits: possible error.");
|
||||
}
|
||||
else{
|
||||
@ -130,12 +142,13 @@ var OfflineStore = function(/* Map */ map) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all items stored by this library using it's unique key.
|
||||
* Does NOT delete anything else from localStorage.
|
||||
* Provides a list of all localStorage items that have been either
|
||||
* added, deleted or updated.
|
||||
* @returns {Array}
|
||||
*/
|
||||
this.deleteStore = function(){
|
||||
console.log("deleting localStore");
|
||||
localStorage.removeItem(this._localEnum().STORAGE_KEY);
|
||||
this.getLocalStoreIndex = function(){
|
||||
var localStore = localStorage.getItem(this._localEnum().INDEX_KEY);
|
||||
return localStore != null ? localStore.split(this._localEnum().TOKEN) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,30 +192,59 @@ var OfflineStore = function(/* Map */ map) {
|
||||
case localEnum.DELETE:
|
||||
layer.applyEdits(null,null,[graphic],function(addResult,updateResult,deleteResult){
|
||||
console.log("deleteResult ObjectId: " + deleteResult[0].objectId + ", Success: " + deleteResult[0].success);
|
||||
if(mCallback != null && count != null) mCallback(count,deleteResult[0].success);
|
||||
},function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false)}
|
||||
if(mCallback != null && count != null) {
|
||||
mCallback(count,deleteResult[0].success);
|
||||
}
|
||||
else{
|
||||
this._addItemLocalStoreIndex(deleteResult[0].objectId,value,true);
|
||||
}
|
||||
|
||||
}.bind(this),
|
||||
function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false);
|
||||
this._addItemLocalStoreIndex(deleteResult[0].objectId,value,false);
|
||||
}.bind(this)
|
||||
);
|
||||
break;
|
||||
case localEnum.ADD:
|
||||
layer.applyEdits([graphic],null,null,function(addResult,updateResult,deleteResult){
|
||||
console.log("addResult ObjectId: " + addResult[0].objectId + ", Success: " + addResult[0].success);
|
||||
if(mCallback != null && count != null) mCallback(count,addResult[0].success);
|
||||
},function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false)}
|
||||
if(mCallback != null && count != null) {
|
||||
mCallback(count,deleteResult[0].success);
|
||||
}
|
||||
else{
|
||||
this._addItemLocalStoreIndex(addResult[0].objectId,value,true);
|
||||
}
|
||||
}.bind(this),
|
||||
function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false);
|
||||
this._addItemLocalStoreIndex(addResult[0].objectId,value,false);
|
||||
}.bind(this)
|
||||
);
|
||||
break;
|
||||
case localEnum.UPDATE:
|
||||
layer.applyEdits(null,[graphic],null,function(addResult,updateResult,deleteResult){
|
||||
console.log("updateResult ObjectId: " + updateResult[0].objectId + ", Success: " + updateResult[0].success);
|
||||
if(mCallback != null && count != null) mCallback(count,updateResult[0].success);
|
||||
},function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false)}
|
||||
if(mCallback != null && count != null) {
|
||||
mCallback(count,deleteResult[0].success);
|
||||
}
|
||||
else{
|
||||
this._addItemLocalStoreIndex(updateResult[0].objectId,value,true);
|
||||
}
|
||||
}.bind(this),
|
||||
function(error){
|
||||
console.log("_layer: " + error.stack); mCallback(count,false)
|
||||
this._addItemLocalStoreIndex(updateResult[0].objectId,value,false);
|
||||
}.bind(this)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._layerCallbackHandler = function(callback,count,objectid){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a serialized geometry and adds it to localStorage
|
||||
* @param geom
|
||||
@ -210,10 +252,6 @@ var OfflineStore = function(/* Map */ map) {
|
||||
*/
|
||||
this._updateExistingLocalStore = function(/* Geometry */ geom){
|
||||
|
||||
// if(geom.hasOwnProperty("geometry")){
|
||||
// geom = geom.geometry;
|
||||
// }
|
||||
|
||||
var localStore = this._getLocalStorage();
|
||||
var split = localStore.split(this._localEnum().TOKEN);
|
||||
console.log(localStore.toString());
|
||||
@ -288,7 +326,7 @@ console.log(localStore.toString());
|
||||
if(arr != null){
|
||||
this._handleRestablishedInternet(function(){
|
||||
this._stopTimer();
|
||||
this.deleteStore();
|
||||
this._deleteStore();
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
@ -331,8 +369,11 @@ console.log(localStore.toString());
|
||||
this._layerEditManager(obj1.graphic,layer,obj1.enumValue,this.enum(),i,function(/* Number */ num, /* boolean */ success){
|
||||
check.push(num);
|
||||
|
||||
var id = obj1.graphic.attributes.objectid;
|
||||
|
||||
if(success == true && check.length == graphicsArr.length){
|
||||
if(errCnt == 0){
|
||||
this._addItemLocalStoreIndex(id,obj1.enumValue,true);
|
||||
callback();
|
||||
}
|
||||
else{
|
||||
@ -340,13 +381,18 @@ console.log(localStore.toString());
|
||||
this._stopTimer();
|
||||
}
|
||||
}
|
||||
else if(success == true && check.length < graphicsArr.length){
|
||||
this._addItemLocalStoreIndex(id,obj1.enumValue,true);
|
||||
}
|
||||
else if(success == false && check.length == graphicsArr.length){
|
||||
console.log("_handleRestablishedInternet: error sending edit on " + graphicsArr[i].graphic.attributes);
|
||||
this._addItemLocalStoreIndex(id,obj1.enumValue,false);
|
||||
console.log("_handleRestablishedInternet: error sending edit on " + id);
|
||||
this._stopTimer();
|
||||
}
|
||||
else{
|
||||
else if(success == false && check.length < graphicsArr.length){
|
||||
this._addItemLocalStoreIndex(id,obj1.enumValue,false);
|
||||
errCnt++;
|
||||
console.log("_handleRestablishedInternet: error sending edit on " + graphicsArr[i].graphic.attributes);
|
||||
console.log("_handleRestablishedInternet: error sending edit on " + id);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
@ -363,6 +409,20 @@ console.log(localStore.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all items stored by this library using its unique key.
|
||||
* Does NOT delete anything else from localStorage.
|
||||
*/
|
||||
this._deleteStore = function(){
|
||||
console.log("deleting localStore");
|
||||
localStorage.removeItem(this._localEnum().STORAGE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw local storage object.
|
||||
* @returns {*}
|
||||
* @private
|
||||
*/
|
||||
this._getLocalStorage = function(){
|
||||
return localStorage.getItem(this._localEnum().STORAGE_KEY);
|
||||
}
|
||||
@ -389,6 +449,65 @@ console.log(localStore.toString());
|
||||
|
||||
}
|
||||
|
||||
this._deleteLocalStoreIndex = function(){
|
||||
console.log("deleting localStoreIndex");
|
||||
localStorage.removeItem(this._localEnum().INDEX_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if an item has been deleted.
|
||||
* @param objectId
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
this._getItemLocalStoreIndex = function(/* String */ objectId){
|
||||
var localStore = this._getLocalStorageIndex();
|
||||
var split = localStore.split(this._localEnum().TOKEN);
|
||||
for(var property in split){
|
||||
var item = JSON.parse(split[property]);
|
||||
if(typeof item !== "undefined" || item.length > 0 || item != null){
|
||||
if(item.hasOwnProperty("id") && item.id == objectId){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add item to index *if* if was successfully deleted.
|
||||
* @param objectId
|
||||
* @param type enum
|
||||
* @param success
|
||||
* @returns {boolean}
|
||||
* @private
|
||||
*/
|
||||
this._addItemLocalStoreIndex = function(/* String */ objectId, /* String */ type, /* boolean */ success){
|
||||
var index = new this._indexObject(objectId,type,success) ;
|
||||
var mIndex = JSON.stringify(index);
|
||||
|
||||
var localStore = this.getLocalStoreIndex();
|
||||
|
||||
try{
|
||||
if(localStore == null || typeof localStore == "undefined"){
|
||||
localStorage.setItem(this._localEnum().INDEX_KEY,mIndex + this._localEnum().TOKEN);
|
||||
}
|
||||
else{
|
||||
localStorage.setItem(this._localEnum().INDEX_KEY,localStore + mIndex + this._localEnum().TOKEN);
|
||||
}
|
||||
|
||||
success = true;
|
||||
}
|
||||
catch(err){
|
||||
console.log("_addItemLocalStoreIndex(): " + err.stack);
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
|
||||
}
|
||||
|
||||
this._checkInternet = function(){
|
||||
var result = null;
|
||||
|
||||
@ -458,6 +577,12 @@ console.log(localStore.toString());
|
||||
return JSON.stringify(json) + this._localEnum().TOKEN;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
///
|
||||
/// INTERNAL Models
|
||||
///
|
||||
//////////////////////////
|
||||
|
||||
/**
|
||||
* Model for storing serialized graphics
|
||||
* @private
|
||||
@ -469,6 +594,22 @@ console.log(localStore.toString());
|
||||
this.attributes = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Model for storing serialized index info.
|
||||
* @private
|
||||
*/
|
||||
this._indexObject = function(/* String */ id, /* String */ type, /* boolean */ success){
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
///
|
||||
/// INITIALISE
|
||||
///
|
||||
//////////////////////////
|
||||
|
||||
/**
|
||||
* Load scripts
|
||||
* TO-DO: Needs to be made AMD compliant!
|
||||
@ -512,9 +653,14 @@ console.log(localStore.toString());
|
||||
var layer = map.getLayer(layerIds[i]);
|
||||
|
||||
if(layer.hasOwnProperty("type") && layer.type.toLowerCase() == "feature layer"){
|
||||
if(layer.isEditable() == true){
|
||||
this.layers.push(layer);
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw ("Layer not editable: " + layer.url );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err){
|
||||
console.log("_parseFeatureLayer: " + err.stack);
|
||||
@ -540,10 +686,10 @@ console.log(localStore.toString());
|
||||
|
||||
if(this.isTimer != true && internet == false && arr != null){
|
||||
this._startTimer(function(err){
|
||||
alert("unable to start background timer. Offline edits won't work. " + err.stack);
|
||||
throw ("unable to start background timer. Offline edits won't work. " + err.stack);
|
||||
});
|
||||
}
|
||||
else if(internet === null || typeof internet === "undefined"){
|
||||
else if(internet == null || typeof internet == "undefined"){
|
||||
console.log("applyEdits: possible error.");
|
||||
}
|
||||
// else{
|
||||
@ -551,7 +697,7 @@ console.log(localStore.toString());
|
||||
// if(arr != null){
|
||||
// this._handleRestablishedInternet(function(){
|
||||
// this._stopTimer();
|
||||
// this.deleteStore();
|
||||
// this._deleteStore();
|
||||
// }.bind(this));
|
||||
// }
|
||||
// }
|
||||
@ -560,4 +706,17 @@ console.log(localStore.toString());
|
||||
}.bind(this));
|
||||
}.bind(this)()
|
||||
|
||||
/**
|
||||
* Attempt to stop timer and reduce chances of corrupting or duplicating data.
|
||||
* TO-DO some errors like those in callbacks may not be trapped by this!
|
||||
* @param msg
|
||||
* @param url
|
||||
* @param line
|
||||
* @returns {boolean}
|
||||
*/
|
||||
window.onerror = function (msg,url,line){
|
||||
console.log(msg + ", " + url + ":" + line);
|
||||
this.map.offlineStore._stopTimer();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -56,7 +56,7 @@ Poller._handleRequest = function(){
|
||||
this.callback( true );
|
||||
}
|
||||
else{
|
||||
this.callback(undefined);
|
||||
this.callback(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user