mirror of
https://github.com/Esri/offline-editor-js.git
synced 2025-12-15 15:20:05 +00:00
commit
075aba6f45
12
CHANGELOG.md
12
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.
|
||||
|
||||
@ -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.<br><br>[`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.<br><br>[`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. <br><br>[`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.<br><br> [`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.<br><br>[`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.
|
||||
|
||||
6
dist/offline-edit-advanced-min.js
vendored
6
dist/offline-edit-advanced-min.js
vendored
@ -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()}
|
||||
|
||||
11
dist/offline-edit-advanced-src.js
vendored
11
dist/offline-edit-advanced-src.js
vendored
@ -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
|
||||
|
||||
16
dist/offline-edit-basic-min.js
vendored
16
dist/offline-edit-basic-min.js
vendored
@ -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]
|
||||
|
||||
112
dist/offline-edit-basic-src.js
vendored
112
dist/offline-edit-basic-src.js
vendored
@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
});
|
||||
Offline.on('down', function(){ // jshint ignore:line
|
||||
self.goOffline(); // jshint ignore:line
|
||||
});
|
||||
}
|
||||
|
||||
callback(true, null);
|
||||
}
|
||||
else {
|
||||
callback(false, r[0].error);
|
||||
}
|
||||
|
||||
callback(true, null);
|
||||
});
|
||||
|
||||
}, // 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
|
||||
|
||||
2
dist/offline-tiles-advanced-src.js
vendored
2
dist/offline-tiles-advanced-src.js
vendored
@ -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([
|
||||
|
||||
2
dist/offline-tiles-basic-src.js
vendored
2
dist/offline-tiles-basic-src.js
vendored
@ -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([
|
||||
|
||||
2
dist/offline-tpk-src.js
vendored
2
dist/offline-tpk-src.js
vendored
@ -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*/
|
||||
/**
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
});
|
||||
Offline.on('down', function(){ // jshint ignore:line
|
||||
self.goOffline(); // jshint ignore:line
|
||||
});
|
||||
}
|
||||
|
||||
callback(true, null);
|
||||
}
|
||||
else {
|
||||
callback(false, r[0].error);
|
||||
}
|
||||
|
||||
callback(true, null);
|
||||
});
|
||||
|
||||
}, // 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()
|
||||
{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 <agup@esri.com> (http://blog.andygup.net)",
|
||||
"license": "Apache 2.0",
|
||||
|
||||
36
samples/lib/CleanFeatureService.js
Normal file
36
samples/lib/CleanFeatureService.js
Normal file
@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -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": {
|
||||
|
||||
@ -2,129 +2,199 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<!--The viewport meta tag is used to improve the presentation and behavior of the samples
|
||||
on iOS devices-->
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
|
||||
<title>Update Fire Perimeter</title>
|
||||
<meta name="viewport" content="width=device-width,user-scalable=no">
|
||||
|
||||
<link rel="stylesheet" href="http://js.arcgis.com/3.14/dijit/themes/claro/claro.css">
|
||||
<link rel="stylesheet" href="http://js.arcgis.com/3.14/esri/css/esri.css">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
|
||||
<title>Simple Edit</title>
|
||||
|
||||
<link rel="stylesheet" href="https://js.arcgis.com/3.16/dijit/themes/nihilo/nihilo.css">
|
||||
<link rel="stylesheet" href="https://js.arcgis.com/3.16/esri/css/esri.css">
|
||||
<style>
|
||||
html, body { height: 100%; width: 100%; margin: 0; }
|
||||
#map, #header {
|
||||
border: 1px solid #444;
|
||||
html, body, #mainWindow {
|
||||
font-family: sans-serif;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
#map {
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin: 5px;
|
||||
}
|
||||
#button-holder {
|
||||
width: 20%;
|
||||
}
|
||||
#btn-draw-line, #btn-online, #btn-offline {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
#header {
|
||||
height: 65px;
|
||||
margin: 5px 5px 0 5px;
|
||||
height: 80px;
|
||||
overflow: auto;
|
||||
padding: 0.5em;
|
||||
font-family: sans-serif;
|
||||
font-weight: 500;
|
||||
color: #0f3b5f;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.dj_ie .infowindow .window .top .right .user .content { position: relative; }
|
||||
.dj_ie .simpleInfoWindow .content { position: relative; }
|
||||
</style>
|
||||
|
||||
<script src="//github.hubspot.com/offline/offline.min.js"></script>
|
||||
<script src="//js.arcgis.com/3.14/"></script>
|
||||
<script>
|
||||
var map;
|
||||
require([
|
||||
"esri/map",
|
||||
"esri/toolbars/edit",
|
||||
"esri/layers/FeatureLayer",
|
||||
"esri/tasks/query",
|
||||
"esri/config",
|
||||
|
||||
"dojo/_base/event",
|
||||
"dojo/parser",
|
||||
|
||||
"dijit/layout/BorderContainer", "dijit/layout/ContentPane",
|
||||
|
||||
|
||||
"../dist/offline-edit-basic-src.js",
|
||||
"dojo/domReady!"
|
||||
], function(
|
||||
Map, Edit, FeatureLayer, Query, esriConfig,
|
||||
event, parser
|
||||
) {
|
||||
parser.parse();
|
||||
|
||||
// refer to "Using the Proxy Page" for more information: https://developers.arcgis.com/javascript/jshelp/ags_proxy.html
|
||||
esriConfig.defaults.io.proxyUrl = "/proxy/";
|
||||
|
||||
var offlineEdit = new O.esri.Edit.OfflineEditBasic();
|
||||
|
||||
|
||||
map = new Map("map", {
|
||||
basemap: "topo",
|
||||
center: [-117.72, 34.352],
|
||||
zoom: 11
|
||||
});
|
||||
|
||||
map.on("layers-add-result", initEditing);
|
||||
|
||||
var firePerimeterFL = new FeatureLayer("http://sampleserver6.arcgisonline.com/arcgis/rest/services/Wildfire/FeatureServer/2", {
|
||||
mode: FeatureLayer.MODE_SNAPSHOT,
|
||||
outFields: ["*"],
|
||||
id: "firePerimeterFL"
|
||||
});
|
||||
map.addLayers([firePerimeterFL]);
|
||||
|
||||
function initEditing(evt) {
|
||||
|
||||
offlineEdit.extend(firePerimeterFL,function(success,error){
|
||||
if(success){
|
||||
var editToolbar = new Edit(map);
|
||||
editToolbar.on("deactivate", function(evt) {
|
||||
if (evt.info.isModified) {
|
||||
firePerimeterFL.applyEdits(null, [evt.graphic], null);
|
||||
}
|
||||
});
|
||||
|
||||
var editingEnabled = false;
|
||||
firePerimeterFL.on("dbl-click", function(evt) {
|
||||
event.stop(evt);
|
||||
if (editingEnabled) {
|
||||
editingEnabled = false;
|
||||
editToolbar.deactivate();
|
||||
firePerimeterFL.clearSelection();
|
||||
}
|
||||
else {
|
||||
editingEnabled = true;
|
||||
editToolbar.activate(Edit.EDIT_VERTICES, evt.graphic);
|
||||
// select the feature to prevent it from being updated by map navigation
|
||||
var query = new Query();
|
||||
query.objectIds = [evt.graphic.attributes[firePerimeterFL.objectIdField]];
|
||||
firePerimeterFL.selectFeatures(query);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
alert("unable to load feature layer for offline usage");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<!-- Required for online/offline status detection -->
|
||||
<script src="http://github.hubspot.com/offline/offline.min.js"></script>
|
||||
<script src="https://js.arcgis.com/3.16/"></script>
|
||||
</head>
|
||||
<body class="claro">
|
||||
<div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline'" style="width:100%;height:100%;" gutters="false">
|
||||
<div id="header" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'" >
|
||||
1. Load app while online, zoom in to a fire perimeter<br>
|
||||
2. Cut internet and double click a feature to edit its vertices. Double click again on the feature to stop editing and apply edits.<br>
|
||||
3. Reestablish internet. You should be able to see the app resync edits in the developer console's network window.
|
||||
<body class="nihilo">
|
||||
|
||||
<div id="mainWindow" data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design:'headline'">
|
||||
<div id="header" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'top'">
|
||||
<div id="button-holder">
|
||||
<button id="btn-offline">1. Go Offline</button><br>
|
||||
<button id="btn-draw-line">2. Draw Polyline</button><br>
|
||||
<button id="btn-online">3. Go Online</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="map" data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region:'center'"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var map, toolbar, symbol, geomTask;
|
||||
|
||||
require([
|
||||
"esri/map",
|
||||
"esri/toolbars/draw",
|
||||
"esri/graphic",
|
||||
"esri/layers/FeatureLayer",
|
||||
|
||||
"esri/symbols/SimpleLineSymbol",
|
||||
|
||||
"dojo/parser",
|
||||
|
||||
"dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dijit/form/Button",
|
||||
|
||||
"../dist/offline-edit-basic-src.js",
|
||||
"dojo/domReady!"
|
||||
], function(
|
||||
Map, Draw, Graphic, FeatureLayer,
|
||||
SimpleLineSymbol,
|
||||
parser
|
||||
) {
|
||||
parser.parse();
|
||||
|
||||
var offlineEdit = new O.esri.Edit.OfflineEditBasic();
|
||||
|
||||
//
|
||||
//
|
||||
// Map and feature layer loading
|
||||
//
|
||||
//
|
||||
|
||||
map = new Map("map", {
|
||||
basemap: "streets",
|
||||
center: [-15.469, 36.428],
|
||||
zoom: 3
|
||||
});
|
||||
|
||||
var lineFeatureLayer = new FeatureLayer("https://services.arcgis.com/RkjCp6A0cLN4ubJm/arcgis/rest/services/line_edit_layer/FeatureServer/0", {
|
||||
mode: FeatureLayer.MODE_SNAPSHOT,
|
||||
outFields: ["*"],
|
||||
id: "lineFeatureLayer"
|
||||
});
|
||||
|
||||
var updateHandler = lineFeatureLayer.on("update-end", initEditing);
|
||||
|
||||
map.addLayers([lineFeatureLayer]);
|
||||
|
||||
var btnOffline = document.getElementById("btn-offline");
|
||||
var btnOnline = document.getElementById("btn-online");
|
||||
|
||||
document.getElementById("btn-draw-line").onclick = function(){
|
||||
activateDrawTool();
|
||||
};
|
||||
|
||||
btnOnline.onclick = function(){
|
||||
goOnline();
|
||||
btnOffline.style.backgroundColor = "";
|
||||
btnOffline.style.color = "black";
|
||||
btnOnline.style.backgroundColor = "green";
|
||||
};
|
||||
|
||||
btnOffline.onclick = function(){
|
||||
goOffline();
|
||||
btnOnline.style.backgroundColor = "";
|
||||
btnOffline.style.backgroundColor = "red";
|
||||
btnOffline.style.color = "white";
|
||||
};
|
||||
|
||||
//
|
||||
//
|
||||
// Editing functionality
|
||||
//
|
||||
//
|
||||
|
||||
function initEditing() {
|
||||
|
||||
updateHandler.remove();
|
||||
createDrawToolbar();
|
||||
|
||||
// Extend our feature layer for offline use
|
||||
offlineEdit.extend(lineFeatureLayer,function(success,error){
|
||||
if(success){
|
||||
console.log("TEST layer loaded and extended for offline use!");
|
||||
}
|
||||
else {
|
||||
console.warn("Unable to load FeatureLayer!");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function goOnline() {
|
||||
offlineEdit.goOnline();
|
||||
}
|
||||
|
||||
function goOffline() {
|
||||
offlineEdit.goOffline();
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// Drawing functionality
|
||||
//
|
||||
//
|
||||
|
||||
function activateDrawTool() {
|
||||
toolbar.activate(Draw.POLYLINE);
|
||||
map.hideZoomSlider();
|
||||
}
|
||||
|
||||
function createDrawToolbar() {
|
||||
toolbar = new Draw(map);
|
||||
toolbar.on("draw-end", addPolylineToMap);
|
||||
}
|
||||
|
||||
function addPolylineToMap(evt) {
|
||||
var symbol;
|
||||
toolbar.deactivate();
|
||||
map.showZoomSlider();
|
||||
switch (evt.geometry.type) {
|
||||
case "polyline":
|
||||
symbol = new SimpleLineSymbol();
|
||||
symbol.width = 5; // make it wide enough to click on
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -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"}});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user