diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8719c9fb..67e796cb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,17 @@
# offline-editor-js - Changelog
+## Version 3.2.0 - May 11, 2016
+
+No breaking changes.
+
+**Enhancements**
+* Added CleanFeatureService.js util for deleting all features in a demo feature service
+
+**Bug Fixes**
+* Closes #461 - proxyPath not respected in OfflineEditAdvanced. Also fixed proxyPath in OfflineEditBasic
+* Closes #462 - getNextLowestTempId does not exist in editStorePOLS.
+* Closes #463 - sample feature service no longer allows editing. Complete rewrite.
+
## Version 3.1.0 - April 21, 2016
No breaking changes.
diff --git a/README.md b/README.md
index d72809ba..a15567cb 100644
--- a/README.md
+++ b/README.md
@@ -16,9 +16,11 @@ This project is also available on npm: **[https://www.npmjs.com/package/esri-off
This repo contains the following libraries in the `/dist` directory. The use of `basic` in the name indicates intermittent offline-only, and `advanced` indicates the library can be used for both intermittent and full offline.
+Reference URLs are provided for developement only. It's recommended to use a CDN or host your own.
+
Use_Case | Name, Description and gh-pages URL
--- | ---
-Basic editing | **`offline-edit-basic-min.js`** Simple, lightweight *(14k minimized)* offline editing library that automatically caches adds, updates and deletes when the internet is temporarily interrupted.
[`http://esri.github.io/offline-editor-js/dist/offline-edit-basic-min.js`](http://esri.github.io/offline-editor-js/dist/offline-edit-basic-min.js)
+Basic editing | **`offline-edit-basic-min.js`** Simple, lightweight *(15k minimized)* offline editing library that automatically caches adds, updates and deletes when the internet is temporarily interrupted.
[`http://esri.github.io/offline-editor-js/dist/offline-edit-basic-min.js`](http://esri.github.io/offline-editor-js/dist/offline-edit-basic-min.js)
Advanced editing | **`offline-edit-advanced-min.js`** Used for intermittent and full offline editing workflows. Also includes limited support for attachments.
[`http://esri.github.io/offline-editor-js/dist/offline-edit-advanced-min.js`](http://esri.github.io/offline-editor-js/dist/offline-edit-advanced-min.js)
Basic map tiles | **`offline-tiles-basic-min.js`** Caches map tiles for simple, intermittent-only offline workflows. Use this library with ArcGIS Online Web maps as well as with tiled map services.
[`http://esri.github.io/offline-editor-js/dist/offline-tiles-basic-min.js`](http://esri.github.io/offline-editor-js/dist/offline-tiles-basic-min.js)
Advanced map tiles | **`offline-tiles-advanced-min.js`** Used for intermittent and full offline tile caching. Extends any ArcGIS Tiled Map Service. This library should be used in conjunction with an HTML5 Application Cache Manifest coding pattern.
[`http://esri.github.io/offline-editor-js/dist/offline-tiles-advanced-min.js`](http://esri.github.io/offline-editor-js/dist/offline-tiles-advanced-min.js)
@@ -83,7 +85,7 @@ Go __[here](https://github.com/Esri/offline-editor-js/wiki/FAQ)__ for answers to
##Dependencies
-* [ArcGIS API for JavaScript (v3.12+)](https://developers.arcgis.com/javascript/)
+* [ArcGIS API for JavaScript (v3.14+)](https://developers.arcgis.com/javascript/)
* [Offline.js](http://github.hubspot.com/offline/docs/welcome/) - it allows detection of the online/offline condition and provides events to hook callbacks on when this condition changes
* Node.js required for building the source
* [IndexedDBShim](https://github.com/axemclion/IndexedDBShim) - polyfill to simulate indexedDB functionality in browsers/platforms where it is not supported notably older versions desktop Safari and iOS Safari.
diff --git a/dist/offline-edit-advanced-min.js b/dist/offline-edit-advanced-min.js
index 8c23c456..3d554d7c 100644
--- a/dist/offline-edit-advanced-min.js
+++ b/dist/offline-edit-advanced-min.js
@@ -123,9 +123,9 @@ this._editStore.deletePhantomGraphic(c,function(a,b){a?g.resolve({success:!0,err
if(b.length>0&&(e.forEach(b,function(a){a.hasOwnProperty("infoTemplate")&&delete a.infoTemplate},this),i="&adds="+JSON.stringify(b)),c.length>0&&(e.forEach(c,function(a){a.hasOwnProperty("infoTemplate")&&delete a.infoTemplate},this),j="&updates="+JSON.stringify(c)),d.length>0){var l=d[0].attributes[this.DB_UID]
k="&deletes="+l}var m=h+i+j+k
a.hasOwnProperty("credential")&&a.credential&&a.credential.hasOwnProperty("token")&&a.credential.token&&(m=m+"&token="+a.credential.token)
-var n=new XMLHttpRequest
-n.open("POST",a.url+"/applyEdits",!0),n.setRequestHeader("Content-type","application/x-www-form-urlencoded"),n.onload=function(){if(200===n.status&&""!==n.responseText)try{var a=JSON.parse(this.response)
-f(a.addResults,a.updateResults,a.deleteResults)}catch(b){g("Unable to parse xhr response",n)}},n.onerror=function(a){g(a)},n.ontimeout=function(){g("xhr timeout error")},n.timeout=this._defaultXhrTimeout,n.send(m)},_parseResponsesArray:function(a){var c=new b,d=0
+var n=this.proxyPath?this.proxyPath+"?"+a.url:a.url,o=new XMLHttpRequest
+o.open("POST",n+"/applyEdits",!0),o.setRequestHeader("Content-type","application/x-www-form-urlencoded"),o.onload=function(){if(200===o.status&&""!==o.responseText)try{var a=JSON.parse(this.response)
+f(a.addResults,a.updateResults,a.deleteResults)}catch(b){g("Unable to parse xhr response",o)}},o.onerror=function(a){g(a)},o.ontimeout=function(){g("xhr timeout error")},o.timeout=this._defaultXhrTimeout,o.send(m)},_parseResponsesArray:function(a){var c=new b,d=0
for(var e in a)a.hasOwnProperty(e)&&(a[e].addResults.map(function(a){a.success||d++}),a[e].updateResults.map(function(a){a.success||d++}),a[e].deleteResults.map(function(a){a.success||d++}))
return d>0?c.resolve(!1):c.resolve(!0),c.promise}})}),"undefined"!=typeof O?O.esri.Edit={}:(O={},O.esri={Edit:{}}),O.esri.Edit.EditStore=function(){"use strict"
this._db=null,this._isDBInit=!1,this.dbName="features_store",this.objectStoreName="features",this.objectId="objectid",this.ADD="add",this.UPDATE="update",this.DELETE="delete",this.FEATURE_LAYER_JSON_ID="feature-layer-object-1001",this.FEATURE_COLLECTION_ID="feature-collection-object-1001",this.PHANTOM_GRAPHIC_PREFIX="phantom-layer",this._PHANTOM_PREFIX_TOKEN="|@|",this.isSupported=function(){return!!window.indexedDB},this.pushEdit=function(a,b,c,d){var e={id:b+"/"+c.attributes[this.objectId],operation:a,layer:b,type:c.geometry.type,graphic:c.toJson()}
diff --git a/dist/offline-edit-advanced-src.js b/dist/offline-edit-advanced-src.js
index b42ed02b..039e003b 100644
--- a/dist/offline-edit-advanced-src.js
+++ b/dist/offline-edit-advanced-src.js
@@ -1,4 +1,4 @@
-/*! esri-offline-maps - v3.1.0 - 2016-04-21
+/*! esri-offline-maps - v3.2.0 - 2016-05-12
* Copyright (c) 2016 Environmental Systems Research Institute, Inc.
* Apache License*/
// Configure offline/online detection
@@ -2056,8 +2056,11 @@ define([
}
}
+ // Respect the proxyPath if one has been set (Added at v3.2.0)
+ var url = this.proxyPath ? this.proxyPath + "?" + layer.url : layer.url;
+
var req = new XMLHttpRequest();
- req.open("POST", layer.url + "/applyEdits", true);
+ req.open("POST", url + "/applyEdits", true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.onload = function()
{
@@ -2068,7 +2071,7 @@ define([
callback(obj.addResults, obj.updateResults, obj.deleteResults);
}
catch(err) {
- console.error("EDIT REQUEST REPONSE WAS NOT SUCCESSFUL:", req);
+ console.error("EDIT REQUEST RESPONSE WAS NOT SUCCESSFUL:", req);
errback("Unable to parse xhr response", req);
}
}
@@ -2735,7 +2738,7 @@ O.esri.Edit.EditStore = function () {
else {
callback(null, "no db");
}
- },
+ };
/**
* Returns all the edits as a single Array via the callback
diff --git a/dist/offline-edit-basic-min.js b/dist/offline-edit-basic-min.js
index 764596be..819844f4 100644
--- a/dist/offline-edit-basic-min.js
+++ b/dist/offline-edit-basic-min.js
@@ -30,7 +30,7 @@ case g._editStore.UPDATE:d.operation==g._editStore.ADD&&(c.operation=g._editStor
break
case g._editStore.DELETE:var h=!0
d.operation==g._editStore.ADD&&a._deleteTemporaryFeature(c,function(a,b){a||(h=!1)}),f.resolve({success:h,graphic:c,operation:e})}else"Id not found"==d?f.resolve({success:!0,graphic:c,operation:e}):f.reject(c)}),f},a._deleteTemporaryFeature=function(b,c){g._editStore["delete"](a.url,b,function(a,b){c(a,b)})},a._getFilesFromForm=function(a){var b=[],c=e.filter(a.elements,function(a){return"file"===a.type})
-return c.forEach(function(a){b.push.apply(b,a.files)},this),b},this._editStore.getNextLowestTempId(a,function(b,c){"success"===c?a._nextTempId=b:a._nextTempId=-1}),a._getNextTempId=function(){return this._nextTempId--},c(f).then(function(a){g._autoOfflineDetect&&(Offline.on("up",function(){g.goOnline(function(a,b){})}),Offline.on("down",function(){g.goOffline()})),d(!0,null)})},goOffline:function(){this._onlineStatus=this.OFFLINE},goOnline:function(a){this._onlineStatus=this.RECONNECTING,this._replayStoredEdits(function(b,c){this._onlineStatus=this.ONLINE,a&&a(b,c)}.bind(this))},getOnlineStatus:function(){return this._onlineStatus},_initializeDB:function(a){var c=new b,d=this._editStore
+return c.forEach(function(a){b.push.apply(b,a.files)},this),b},a._getNextTempId=function(){return this._nextTempId--},c(f).then(function(b){b[0].success?(g._editStore.getNextLowestTempId(a,function(b,c){"success"===c?a._nextTempId=b:a._nextTempId=-1}),g._autoOfflineDetect&&(Offline.on("up",function(){g.goOnline(function(a,b){})}),Offline.on("down",function(){g.goOffline()})),d(!0,null)):d(!1,b[0].error)})},goOffline:function(){this._onlineStatus=this.OFFLINE},goOnline:function(a){this._onlineStatus=this.RECONNECTING,this._replayStoredEdits(function(b,c){this._onlineStatus=this.ONLINE,a&&a(b,c)}.bind(this))},getOnlineStatus:function(){return this._onlineStatus},_initializeDB:function(a){var c=new b,d=this._editStore
return d.dbName=this.DB_NAME,d.objectStoreName=this.DB_OBJECTSTORE_NAME,d.objectId=this.DB_UID,d.init(function(a,b){a?c.resolve({success:!0,error:null}):c.reject({success:!1,error:null})}),c},_replayStoredEdits:function(a){var b,d={},e=this,f=[],g=[],h=[],i=[],j=[],k=this._featureLayers,l=this._editStore
this._editStore.getAllEditsArray(function(n,o){if(n.length>0){j=n
for(var p=j.length,q=0;p>q;q++){b=k[j[q].layer],b.__onEditsComplete=b.onEditsComplete,b.onEditsComplete=function(){},f=[],g=[],h=[],i=[]
@@ -56,9 +56,9 @@ return i.attributes={},i.attributes[this.DB_UID]=h,this._editStore["delete"](a.u
if(b.length>0&&(e.forEach(b,function(a){a.hasOwnProperty("infoTemplate")&&delete a.infoTemplate},this),i="&adds="+JSON.stringify(b)),c.length>0&&(e.forEach(c,function(a){a.hasOwnProperty("infoTemplate")&&delete a.infoTemplate},this),j="&updates="+JSON.stringify(c)),d.length>0){var l=d[0].attributes[this.DB_UID]
k="&deletes="+l}var m=h+i+j+k
a.hasOwnProperty("credential")&&a.credential&&a.credential.hasOwnProperty("token")&&a.credential.token&&(m=m+"&token="+a.credential.token)
-var n=new XMLHttpRequest
-n.open("POST",a.url+"/applyEdits",!0),n.setRequestHeader("Content-type","application/x-www-form-urlencoded"),n.onload=function(){if(200===n.status&&""!==n.responseText)try{var a=JSON.parse(this.response)
-f(a.addResults,a.updateResults,a.deleteResults)}catch(b){g("Unable to parse xhr response",n)}},n.onerror=function(a){g(a)},n.ontimeout=function(){g("xhr timeout error")},n.timeout=this._defaultXhrTimeout,n.send(m)},_parseResponsesArray:function(a,b){var c=0
+var n=this.proxyPath?this.proxyPath+"?"+a.url:a.url,o=new XMLHttpRequest
+o.open("POST",n+"/applyEdits",!0),o.setRequestHeader("Content-type","application/x-www-form-urlencoded"),o.onload=function(){if(200===o.status&&""!==o.responseText)try{var a=JSON.parse(this.response)
+f(a.addResults,a.updateResults,a.deleteResults)}catch(b){g("Unable to parse xhr response",o)}},o.onerror=function(a){g(a)},o.ontimeout=function(){g("xhr timeout error")},o.timeout=this._defaultXhrTimeout,o.send(m)},_parseResponsesArray:function(a,b){var c=0
for(var d in a)a.hasOwnProperty(d)&&(a[d].addResults.forEach(function(a){a.success||c++}),a[d].updateResults.forEach(function(a){a.success||c++}),a[d].deleteResults.forEach(function(a){a.success||c++}))
b(!(c>0))}})}),"undefined"!=typeof O?O.esri.Edit={}:(O={},O.esri={Edit:{}}),O.esri.Edit.EditStorePOLS=function(){"use strict"
this._db=null,this._isDBInit=!1,this.dbName="features_store",this.objectStoreName="features",this.objectId="objectid",this.ADD="add",this.UPDATE="update",this.DELETE="delete",this.FEATURE_LAYER_JSON_ID="feature-layer-object-1001",this.FEATURE_COLLECTION_ID="feature-collection-object-1001",this.isSupported=function(){return!!window.indexedDB},this.pushEdit=function(a,b,c,d){var e={id:b+"/"+c.attributes[this.objectId],operation:a,layer:b,type:c.geometry.type,graphic:c.toJson()}
@@ -73,7 +73,13 @@ d.onsuccess=function(){var c=d.result
c&&c.id==a?b(!0,c):b(!1,"Id not found")},d.onerror=function(a){b(!1,a)}},this.getAllEditsArray=function(a){var b=[]
if(null!==this._db){var c=this.FEATURE_LAYER_JSON_ID,d=this.FEATURE_COLLECTION_ID,e=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor()
e.onsuccess=function(e){var f=e.target.result
-f&&f.value&&f.value.id?(f.value.id!==c&&f.value.id!==d&&b.push(f.value),f["continue"]()):a(b,"end")}.bind(this),e.onerror=function(b){a(null,b)}}else a(null,"no db")},this.updateExistingEdit=function(a,b,c,d){var e=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),f=e.get(c.attributes[this.objectId])
+f&&f.value&&f.value.id?(f.value.id!==c&&f.value.id!==d&&b.push(f.value),f["continue"]()):a(b,"end")}.bind(this),e.onerror=function(b){a(null,b)}}else a(null,"no db")},this.getNextLowestTempId=function(a,b){var c=[],d=this
+if(null!==this._db){var e=this.FEATURE_LAYER_JSON_ID,f=this.FEATURE_COLLECTION_ID,g=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor()
+g.onsuccess=function(g){var h=g.target.result
+if(h&&h.value&&h.value.id)h.value.id!==e&&h.value.id!==f&&h.value.layer===a.url&&"add"===h.value.operation&&c.push(h.value.graphic.attributes[d.objectId]),h["continue"]()
+else if(0===c.length)b(-1,"success")
+else{var i=c.filter(function(a){return!isNaN(a)}),j=Math.min.apply(Math,i)
+b(j-1,"success")}}.bind(this),g.onerror=function(a){b(null,a)}}else b(null,"no db")},this.updateExistingEdit=function(a,b,c,d){var e=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),f=e.get(c.attributes[this.objectId])
f.onsuccess=function(){f.result
var g={id:b+"/"+c.attributes[this.objectId],operation:a,layer:b,graphic:c.toJson()},h=e.put(g)
h.onsuccess=function(){d(!0)},h.onerror=function(a){d(!1,a)}}.bind(this)},this["delete"]=function(a,b,c){var d=this._db,e=null,f=this,g=a+"/"+b.attributes[this.objectId]
diff --git a/dist/offline-edit-basic-src.js b/dist/offline-edit-basic-src.js
index 206b5e7d..61a34a24 100644
--- a/dist/offline-edit-basic-src.js
+++ b/dist/offline-edit-basic-src.js
@@ -1,4 +1,4 @@
-/*! esri-offline-maps - v3.1.0 - 2016-04-21
+/*! esri-offline-maps - v3.2.0 - 2016-05-12
* Copyright (c) 2016 Environmental Systems Research Institute, Inc.
* Apache License*/
// Configure offline/online detection
@@ -501,21 +501,6 @@ define([
}, this);
return files;
};
-
- // we need to identify ADDs before sending them to the server
- // we assign temporary ids (using negative numbers to distinguish them from real ids)
- // query the database first to find any existing offline adds, and find the next lowest integer to start with.
- this._editStore.getNextLowestTempId(layer, function(value, status){
- if(status === "success"){
- console.log("_nextTempId:", value);
- layer._nextTempId = value;
- }
- else{
- console.log("_nextTempId, not success:", value);
- layer._nextTempId = -1;
- console.debug(layer._nextTempId);
- }
- });
layer._getNextTempId = function () {
return this._nextTempId--;
@@ -524,20 +509,39 @@ define([
// We are currently only passing in a single deferred.
all(extendPromises).then(function (r) {
- if(self._autoOfflineDetect){
- Offline.on('up', function(){ // jshint ignore:line
+ if(r[0].success){
- self.goOnline(function(success,error){ // jshint ignore:line
- console.log("GOING ONLINE");
- });
+ // we need to identify ADDs before sending them to the server
+ // we assign temporary ids (using negative numbers to distinguish them from real ids)
+ // query the database first to find any existing offline adds, and find the next lowest integer to start with.
+ self._editStore.getNextLowestTempId(layer, function(value, status){
+ if(status === "success"){
+ layer._nextTempId = value;
+ }
+ else{
+ console.log("Set _nextTempId not found: " + value + ", resetting to -1");
+ layer._nextTempId = -1;
+ }
});
- Offline.on('down', function(){ // jshint ignore:line
- self.goOffline(); // jshint ignore:line
- });
- }
+ if(self._autoOfflineDetect){
+ Offline.on('up', function(){ // jshint ignore:line
- callback(true, null);
+ self.goOnline(function(success,error){ // jshint ignore:line
+ console.log("GOING ONLINE");
+ });
+ });
+
+ Offline.on('down', function(){ // jshint ignore:line
+ self.goOffline(); // jshint ignore:line
+ });
+ }
+
+ callback(true, null);
+ }
+ else {
+ callback(false, r[0].error);
+ }
});
}, // extend
@@ -963,8 +967,11 @@ define([
}
}
+ // Respect the proxyPath if one has been set (Added at v3.2.0)
+ var url = this.proxyPath ? this.proxyPath + "?" + layer.url : layer.url;
+
var req = new XMLHttpRequest();
- req.open("POST", layer.url + "/applyEdits", true);
+ req.open("POST", url + "/applyEdits", true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.onload = function()
{
@@ -1196,6 +1203,57 @@ O.esri.Edit.EditStorePOLS = function () {
}
};
+ /*
+ * Query the database, looking for any existing Add temporary OIDs, and return the nextTempId to be used.
+ * @param feature - extended layer from offline edit advanced
+ * @param callback {int, messageString} or {null, messageString}
+ */
+ this.getNextLowestTempId = function (feature, callback) {
+ var addOIDsArray = [],
+ self = this;
+
+ if (this._db !== null) {
+
+ var fLayerJSONId = this.FEATURE_LAYER_JSON_ID;
+ var fCollectionId = this.FEATURE_COLLECTION_ID;
+
+ var transaction = this._db.transaction([this.objectStoreName])
+ .objectStore(this.objectStoreName)
+ .openCursor();
+
+ transaction.onsuccess = function (event) {
+ var cursor = event.target.result;
+ if (cursor && cursor.value && cursor.value.id) {
+ // Make sure we are not return FeatureLayer JSON data or a Phantom Graphic
+ if (cursor.value.id !== fLayerJSONId && cursor.value.id !== fCollectionId) {
+ if(cursor.value.layer === feature.url && cursor.value.operation === "add"){ // check to make sure the edit is for the feature we are looking for, and that the operation is an add.
+ addOIDsArray.push(cursor.value.graphic.attributes[self.objectId]); // add the temporary OID to the array
+ }
+ }
+ cursor.continue();
+ }
+ else {
+ if(addOIDsArray.length === 0){ // if we didn't find anything,
+ callback(-1, "success"); // we'll start with -1
+ }
+ else{
+ var filteredOIDsArray = addOIDsArray.filter(function(val){ // filter out any non numbers from the array...
+ return !isNaN(val); // .. should anything have snuck in or returned a NaN
+ });
+ var lowestTempId = Math.min.apply(Math, filteredOIDsArray); // then find the lowest number from the array
+ callback(lowestTempId-1, "success"); // and we'll start with one less than tat.
+ }
+ }
+ }.bind(this);
+ transaction.onerror = function (err) {
+ callback(null, err);
+ };
+ }
+ else {
+ callback(null, "no db");
+ }
+ };
+
/**
* Update an edit already exists in the database
* @param operation add, update or delete
diff --git a/dist/offline-tiles-advanced-src.js b/dist/offline-tiles-advanced-src.js
index f47768f7..9e1b1f9b 100644
--- a/dist/offline-tiles-advanced-src.js
+++ b/dist/offline-tiles-advanced-src.js
@@ -1,4 +1,4 @@
-/*! esri-offline-maps - v3.1.0 - 2016-04-21
+/*! esri-offline-maps - v3.2.0 - 2016-05-12
* Copyright (c) 2016 Environmental Systems Research Institute, Inc.
* Apache License*/
define([
diff --git a/dist/offline-tiles-basic-src.js b/dist/offline-tiles-basic-src.js
index dc6f9335..d9653d29 100644
--- a/dist/offline-tiles-basic-src.js
+++ b/dist/offline-tiles-basic-src.js
@@ -1,4 +1,4 @@
-/*! esri-offline-maps - v3.1.0 - 2016-04-21
+/*! esri-offline-maps - v3.2.0 - 2016-05-12
* Copyright (c) 2016 Environmental Systems Research Institute, Inc.
* Apache License*/
define([
diff --git a/dist/offline-tpk-src.js b/dist/offline-tpk-src.js
index 846d722c..0e38d419 100644
--- a/dist/offline-tpk-src.js
+++ b/dist/offline-tpk-src.js
@@ -1,4 +1,4 @@
-/*! esri-offline-maps - v3.1.0 - 2016-04-21
+/*! esri-offline-maps - v3.2.0 - 2016-05-12
* Copyright (c) 2016 Environmental Systems Research Institute, Inc.
* Apache License*/
/**
diff --git a/lib/edit/OfflineEditAdvanced.js b/lib/edit/OfflineEditAdvanced.js
index 99529c25..6ff78629 100644
--- a/lib/edit/OfflineEditAdvanced.js
+++ b/lib/edit/OfflineEditAdvanced.js
@@ -2040,8 +2040,11 @@ define([
}
}
+ // Respect the proxyPath if one has been set (Added at v3.2.0)
+ var url = this.proxyPath ? this.proxyPath + "?" + layer.url : layer.url;
+
var req = new XMLHttpRequest();
- req.open("POST", layer.url + "/applyEdits", true);
+ req.open("POST", url + "/applyEdits", true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.onload = function()
{
@@ -2052,7 +2055,7 @@ define([
callback(obj.addResults, obj.updateResults, obj.deleteResults);
}
catch(err) {
- console.error("EDIT REQUEST REPONSE WAS NOT SUCCESSFUL:", req);
+ console.error("EDIT REQUEST RESPONSE WAS NOT SUCCESSFUL:", req);
errback("Unable to parse xhr response", req);
}
}
diff --git a/lib/edit/OfflineEditBasic.js b/lib/edit/OfflineEditBasic.js
index b264dc27..1b5dd0be 100644
--- a/lib/edit/OfflineEditBasic.js
+++ b/lib/edit/OfflineEditBasic.js
@@ -485,21 +485,6 @@ define([
}, this);
return files;
};
-
- // we need to identify ADDs before sending them to the server
- // we assign temporary ids (using negative numbers to distinguish them from real ids)
- // query the database first to find any existing offline adds, and find the next lowest integer to start with.
- this._editStore.getNextLowestTempId(layer, function(value, status){
- if(status === "success"){
- console.log("_nextTempId:", value);
- layer._nextTempId = value;
- }
- else{
- console.log("_nextTempId, not success:", value);
- layer._nextTempId = -1;
- console.debug(layer._nextTempId);
- }
- });
layer._getNextTempId = function () {
return this._nextTempId--;
@@ -508,20 +493,39 @@ define([
// We are currently only passing in a single deferred.
all(extendPromises).then(function (r) {
- if(self._autoOfflineDetect){
- Offline.on('up', function(){ // jshint ignore:line
+ if(r[0].success){
- self.goOnline(function(success,error){ // jshint ignore:line
- console.log("GOING ONLINE");
- });
+ // we need to identify ADDs before sending them to the server
+ // we assign temporary ids (using negative numbers to distinguish them from real ids)
+ // query the database first to find any existing offline adds, and find the next lowest integer to start with.
+ self._editStore.getNextLowestTempId(layer, function(value, status){
+ if(status === "success"){
+ layer._nextTempId = value;
+ }
+ else{
+ console.log("Set _nextTempId not found: " + value + ", resetting to -1");
+ layer._nextTempId = -1;
+ }
});
- Offline.on('down', function(){ // jshint ignore:line
- self.goOffline(); // jshint ignore:line
- });
- }
+ if(self._autoOfflineDetect){
+ Offline.on('up', function(){ // jshint ignore:line
+
+ self.goOnline(function(success,error){ // jshint ignore:line
+ console.log("GOING ONLINE");
+ });
+ });
+
+ Offline.on('down', function(){ // jshint ignore:line
+ self.goOffline(); // jshint ignore:line
+ });
+ }
- callback(true, null);
+ callback(true, null);
+ }
+ else {
+ callback(false, r[0].error);
+ }
});
}, // extend
@@ -947,8 +951,11 @@ define([
}
}
+ // Respect the proxyPath if one has been set (Added at v3.2.0)
+ var url = this.proxyPath ? this.proxyPath + "?" + layer.url : layer.url;
+
var req = new XMLHttpRequest();
- req.open("POST", layer.url + "/applyEdits", true);
+ req.open("POST", url + "/applyEdits", true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.onload = function()
{
diff --git a/lib/edit/editStorePOLS.js b/lib/edit/editStorePOLS.js
index f0b48c99..4f044452 100644
--- a/lib/edit/editStorePOLS.js
+++ b/lib/edit/editStorePOLS.js
@@ -147,6 +147,57 @@ O.esri.Edit.EditStorePOLS = function () {
}
};
+ /*
+ * Query the database, looking for any existing Add temporary OIDs, and return the nextTempId to be used.
+ * @param feature - extended layer from offline edit advanced
+ * @param callback {int, messageString} or {null, messageString}
+ */
+ this.getNextLowestTempId = function (feature, callback) {
+ var addOIDsArray = [],
+ self = this;
+
+ if (this._db !== null) {
+
+ var fLayerJSONId = this.FEATURE_LAYER_JSON_ID;
+ var fCollectionId = this.FEATURE_COLLECTION_ID;
+
+ var transaction = this._db.transaction([this.objectStoreName])
+ .objectStore(this.objectStoreName)
+ .openCursor();
+
+ transaction.onsuccess = function (event) {
+ var cursor = event.target.result;
+ if (cursor && cursor.value && cursor.value.id) {
+ // Make sure we are not return FeatureLayer JSON data or a Phantom Graphic
+ if (cursor.value.id !== fLayerJSONId && cursor.value.id !== fCollectionId) {
+ if(cursor.value.layer === feature.url && cursor.value.operation === "add"){ // check to make sure the edit is for the feature we are looking for, and that the operation is an add.
+ addOIDsArray.push(cursor.value.graphic.attributes[self.objectId]); // add the temporary OID to the array
+ }
+ }
+ cursor.continue();
+ }
+ else {
+ if(addOIDsArray.length === 0){ // if we didn't find anything,
+ callback(-1, "success"); // we'll start with -1
+ }
+ else{
+ var filteredOIDsArray = addOIDsArray.filter(function(val){ // filter out any non numbers from the array...
+ return !isNaN(val); // .. should anything have snuck in or returned a NaN
+ });
+ var lowestTempId = Math.min.apply(Math, filteredOIDsArray); // then find the lowest number from the array
+ callback(lowestTempId-1, "success"); // and we'll start with one less than tat.
+ }
+ }
+ }.bind(this);
+ transaction.onerror = function (err) {
+ callback(null, err);
+ };
+ }
+ else {
+ callback(null, "no db");
+ }
+ };
+
/**
* Update an edit already exists in the database
* @param operation add, update or delete
diff --git a/lib/edit/editsStore.js b/lib/edit/editsStore.js
index 8a5440ae..86ffb03c 100644
--- a/lib/edit/editsStore.js
+++ b/lib/edit/editsStore.js
@@ -590,7 +590,7 @@ O.esri.Edit.EditStore = function () {
else {
callback(null, "no db");
}
- },
+ };
/**
* Returns all the edits as a single Array via the callback
diff --git a/package.json b/package.json
index aff2d1ab..4cf9c3dd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "esri-offline-maps",
- "version": "3.1.0",
+ "version": "3.2.0",
"description": "Lightweight set of libraries for working offline with map tiles and editing with ArcGIS feature services",
"author": "Andy Gup (http://blog.andygup.net)",
"license": "Apache 2.0",
diff --git a/samples/lib/CleanFeatureService.js b/samples/lib/CleanFeatureService.js
new file mode 100644
index 00000000..25921e4d
--- /dev/null
+++ b/samples/lib/CleanFeatureService.js
@@ -0,0 +1,36 @@
+"use strict";
+
+/*
+ * Utility library for deleting all features in a feature layer.
+ * Use this to reset demo feature layers.
+ * WARNING: this will delete EVERYTHING!
+ */
+
+function CleanFeatureLayer(featureLayer, callback)
+{
+ require(["esri/request"], function (esriRequest) {
+ esriRequest({
+ url: featureLayer.url + "/deleteFeatures",
+ content: { f: 'json', where: '1=1'},
+ handleAs: 'json'
+ },{usePost:true}).then( function(response)
+ {
+ callback && callback(true,response);
+ },
+ function(error)
+ {
+ callback && callback(false,error);
+ });
+ });
+}
+
+function InitCleanFeatureLayer(featureLayer){
+
+ CleanFeatureLayer(featureLayer, function(success){
+ CleanFeatureLayer( featureLayer, function(success, response)
+ {
+ console.log("FeatureLayer cleaned: " + success);
+ featureLayer.refresh();
+ });
+ });
+}
diff --git a/samples/package.json b/samples/package.json
index 59da49be..bf6a96df 100644
--- a/samples/package.json
+++ b/samples/package.json
@@ -9,7 +9,7 @@
"appHomePage": "appcache-tiles.html",
"optimizedApiURL": "../samples/jsolib",
"arcGISBaseURL": "http://js.arcgis.com/3.14",
- "version": "3.1.0",
+ "version": "3.2.0",
"private": true,
"description": "manifest generator project",
"repository": {
diff --git a/samples/simple-edit.html b/samples/simple-edit.html
index b25ada4a..6523f578 100644
--- a/samples/simple-edit.html
+++ b/samples/simple-edit.html
@@ -2,129 +2,199 @@
-
+
+
- Update Fire Perimeter
+ Simple Edit
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
- 1. Load app while online, zoom in to a fire perimeter
- 2. Cut internet and double click a feature to edit its vertices. Double click again on the feature to stop editing and apply edits.
- 3. Reestablish internet. You should be able to see the app resync edits in the developer console's network window.
-
-
-
+ var graphic = new Graphic(evt.geometry, symbol, {"name":"test2"});
+ map.graphics.add(graphic);
+
+ applyEdits(graphic);
+ }
+
+ function applyEdits(graphic) {
+ lineFeatureLayer.applyEdits([graphic], null, null,function(addResults)
+ {
+ console.log("addResults length: " + addResults.length);
+ },
+ function(error)
+ {
+ console.log("SetupFeatureService error: " + error.message);
+ });
+ }
+ });
+
\ No newline at end of file
diff --git a/test/spec/offlineEditingBasicSpec.js b/test/spec/offlineEditingBasicSpec.js
index 5d9181f8..5f76bbb4 100644
--- a/test/spec/offlineEditingBasicSpec.js
+++ b/test/spec/offlineEditingBasicSpec.js
@@ -79,6 +79,7 @@ describe("Normal online editing - Exercise the feature services", function()
async.it("add test features", function(done)
{
expect(g_featureLayers[0].graphics.length).toBe(0);
+ expect(g_featureLayers[0]._nextTempId).toBe(-1);
g1 = new g_modules.Graphic({"geometry":{"x":-105400,"y":5137000,"spatialReference":{"wkid":102100}},"attributes":{"lat":0.0,"lng":0.0,"description":"g1"}});
g2 = new g_modules.Graphic({"geometry":{"x":-105600,"y":5137000,"spatialReference":{"wkid":102100}},"attributes":{"lat":0.0,"lng":0.0,"description":"g2"}});