From aa3bfe0f016112bda646038adc3f2603436d52e8 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 30 Apr 2015 10:51:49 -0600 Subject: [PATCH 01/15] initial fixes to handle offline/online transitions --- doc/offlinefeaturesmanager.md | 4 ++-- samples/appcache-features.html | 31 +++++++++++++++---------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/doc/offlinefeaturesmanager.md b/doc/offlinefeaturesmanager.md index 6319d0cb..f5496cef 100644 --- a/doc/offlinefeaturesmanager.md +++ b/doc/offlinefeaturesmanager.md @@ -38,11 +38,11 @@ OfflineFeaturesManager provides the following functionality. Methods | Returns | Description --- | --- | --- -`extend(layer,callback,dataStore)`|`callback( boolean, errors )`| Overrides a feature layer, by replacing the `applyEdits()` method of the layer. You can use the FeatureLayer as always, but it's behaviour will be enhanced according to the online status of the manager and the capabilities included in this library. `Callback` is related to initialization the library.

`dataStore` is an optional Object that contains any information you need when reconsistuting the layer after an offline browser restart. Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information. +`extend( layer,` `callback, dataStore)`|`callback( boolean, errors )`| Overrides a feature layer, by replacing the `applyEdits()` method of the layer. You can use the FeatureLayer as always, but it's behaviour will be enhanced according to the online status of the manager and the capabilities included in this library. `Callback` is related to initialization the library.

`dataStore` is an optional Object that contains any information you need when reconsistuting the layer after an offline browser restart. Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information. `goOffline()` | nothing | Forces library into an offline state. Any edits applied to extended FeatureLayers during this condition will be stored locally. `goOnline(callback)` | `callback( boolean, results )` | Forces library to return to an online state. If there are pending edits, an attempt will be made to sync them with the remote feature server. Callback function will be called when resync process is done.

Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information on the `results` object. `getOnlineStatus()` | `ONLINE`, `OFFLINE` or `RECONNECTING`| Determines the current state of the manager. Please, note that this library doesn't detect actual browser offline/online condition. You need to use the `offline.min.js` library included in `vendor\offline` directory to detect connection status and connect events to goOffline() and goOnline() methods. See `military-offline.html` sample. -`getFeatureLayerJSONDataStore` | `callback( boolean, Object)` | **New @ v2.7.1** Returns the feature layer's dataStore Object. +`getFeatureLayerJSONDataStore( callback )` | `callback( boolean, Object)` | **New @ v2.7.1** Returns the feature layer's dataStore Object. `getReadableEdit()` | String | **DEPRECATED** @ v2.5. A string value representing human readable information on pending edits. Use `featureLayer.getAllEditsArray()`. diff --git a/samples/appcache-features.html b/samples/appcache-features.html index 3f2c66b5..317c3d42 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -315,7 +315,9 @@ */ function initFeatureUpdateEndListener() { - busStopFeatureLayer.on("update-end",function(evt){ + var listener = busStopFeatureLayer.on("update-end",function(evt){ + + listener.remove(); //************************************************** // @@ -404,6 +406,7 @@ function loadFeatureLayerOffline(callback) { offlineFeaturesManager.getFeatureLayerJSONDataStore(function(success,dataStore) { if(success){ + // Use the feature layer returns from getFeatureDefinition() to reconstitute the layer busStopFeatureLayer = new FeatureLayer(JSON.parse(dataStore.featureLayerCollection), { mode: FeatureLayer.MODE_SNAPSHOT, @@ -414,23 +417,19 @@ // on a mobile device. busStopFeatureLayer.setRenderer(new SimpleRenderer(defaultSymbol)); - // If/when the update-end event has been thrown then let's finish initializing - var mapListen = map.on("update-end",function(evt) { - console.log("Feature has been added back to the map while offline."); - on(btnGetTiles,"click",downloadTiles); - on(btnOnlineOffline, 'click', goOnlineOffline); + map.addLayer(busStopFeatureLayer); - setFeatureLayerClickHandler(); - setModalPopupClickListeners(); + console.log("Feature has been added back to the map while offline."); + on(btnGetTiles,"click",downloadTiles); + on(btnOnlineOffline, 'click', goOnlineOffline); - map.centerAt(dataStore.centerPt); - map.setZoom(dataStore.zoom); + setFeatureLayerClickHandler(); + setModalPopupClickListeners(); - mapListen.remove(); - callback(true); - }); + map.centerAt(dataStore.centerPt); + map.setZoom(dataStore.zoom); - map.addLayer(busStopFeatureLayer); + callback(true); } else{ alert("There was a problem retrieving feature layer options object. " + dataStore); @@ -470,7 +469,7 @@ function extendFeatureLayer(online,callback){ var featureLayerJSON = null; - if(online === true) { + if(online) { // This object contains everything we need to restore the map and feature layer after an offline restart // @@ -495,7 +494,7 @@ // If the app is online then force offlineFeaturesManager to its online state // This will force the library to check for pending edits and attempt to // resend them to the Feature Service. - if(_isOnline == true){ + if(_isOnline){ offlineFeaturesManager.goOnline(); } From 75409b0b11e4f56c56da208960661fcbde7c0454 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 30 Apr 2015 17:16:57 -0600 Subject: [PATCH 02/15] add callback when db is init --- lib/edit/offlineFeaturesManager.js | 35 ++++-------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index b77ce411..2f52ed4c 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -111,6 +111,9 @@ define([ if(!this._editStore._isDBInit) { this._initializeDB(dataStore,callback); } + else { + callback(true, null); + } // we keep track of the FeatureLayer object this._featureLayers[layer.url] = layer; @@ -1413,23 +1416,7 @@ define([ attachments.forEach(function (attachment) { console.log("sending attachment", attachment.id, "to feature", attachment.featureId); - var uploadAttachmentComplete = - this._uploadAttachment(attachment); - //.then(function (uploadResult) { - // if (uploadResult.addAttachmentResult && uploadResult.addAttachmentResult.success === true) { - // console.log("upload success", uploadResult.addAttachmentResult.success); - // return this._deleteAttachment(attachment.id, uploadResult); - // } - // else { - // console.log("upload failed", uploadResult); - // return null; - // } - //}.bind(this), - //function (err) { - // console.log("failed uploading attachment", attachment); - // return null; - //} - //); + var uploadAttachmentComplete = this._uploadAttachment(attachment); promises.push(uploadAttachmentComplete); }, this); console.log("promises", promises.length); @@ -1444,20 +1431,6 @@ define([ callback && callback(true, uploadResults,dbResults); } }); - //results.forEach(function(value){ - // if(value.attachmentResult.success){ - // // Delete an attachment from the database if it was successfully - // // submitted to the server. - // self._deleteAttachmentFromDB(value.id,null).then(function(result){ - // if(result.success){ - // callback && callback(true, results); - // } - // else{ - // callback && callback(false, results); - // } - // }); - // } - //}); }, function (err) { console.log("error!", err); From 57d286fa9ad10908559404411306640c3a24e373 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 1 May 2015 10:07:27 -0600 Subject: [PATCH 03/15] added null value in return --- lib/edit/editsStore.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/edit/editsStore.js b/lib/edit/editsStore.js index e5cc003a..67a7cd60 100644 --- a/lib/edit/editsStore.js +++ b/lib/edit/editsStore.js @@ -957,7 +957,7 @@ O.esri.Edit.EditStore = function () { this._db = event.target.result; this._isDBInit = true; console.log("database opened successfully"); - callback(true); + callback(true, null); }.bind(this); }; From 1fa55e31ed7a925f41c1907a65339537eaf18c78 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 1 May 2015 18:00:16 -0600 Subject: [PATCH 04/15] work-around attempt 1 --- lib/edit/offlineFeaturesManager.js | 103 ++++++++++++++++++++++++++--- samples/appcache-features.html | 48 +++++++++----- 2 files changed, 122 insertions(+), 29 deletions(-) diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index 2f52ed4c..f1e3cad8 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -11,12 +11,13 @@ define([ "esri/config", "esri/layers/GraphicsLayer", "esri/graphic", + "esri/request", "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/urlUtils"], function (Evented, Deferred, all, declare, array, domAttr, domStyle, query, - esriConfig, GraphicsLayer, Graphic, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, urlUtils) { + esriConfig, GraphicsLayer, Graphic, esriRequest, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, urlUtils) { "use strict"; return declare("O.esri.Edit.OfflineFeaturesManager", [Evented], { @@ -101,23 +102,46 @@ define([ var self = this; // NOTE: At v2.6.1 we've discovered that not all feature layers support objectIdField. - // However, we want to try to be consistent here with how the library is managing Ids. + // However, to try to be consistent here with how the library is managing Ids. // So, we force the layer.objectIdField to DB_UID. This should be consistent with // how esri.Graphics assign a unique ID to a graphic. If it is not, then this // library will break and we'll have to re-architect how we manage UIDs. layer.objectIdField = this.DB_UID; + var url = null; + + if(layer.url) { + url = layer.url; + // we keep track of the FeatureLayer object + this._featureLayers[layer.url] = layer; + } + // Initialize the database as well as set offline data. if(!this._editStore._isDBInit) { - this._initializeDB(dataStore,callback); + this._initializeDB(dataStore, url, function(success, error) { + if(success && !url) { + + // This functionality is specifically for offline restarts + // and attempts to retrieve a feature layer url. + // It's a hack because layer.toJson() doesn't convert layer.url. + this._editStore.getFeatureLayerJSON(function(success,message){ + if(success) { + this._featureLayers[message.__featureLayerURL] = layer; + } + else { + console.error("getFeatureLayerJSON() failed."); + } + + }.bind(this)); + } + // NOTE: If this is an error then _featureLayers[] isn't going to be set correct + callback(success,error); + }.bind(this)); } else { callback(true, null); } - // we keep track of the FeatureLayer object - this._featureLayers[layer.url] = layer; - // replace the applyEdits() method layer._applyEdits = layer.applyEdits; @@ -1054,7 +1078,7 @@ define([ console.log("offlineFeaturesManager going online"); this._onlineStatus = this.RECONNECTING; this._replayStoredEdits(function (success, responses) { - var result = {features: {success: success, responses: responses}}; + var result = {success: success, responses: responses}; this._onlineStatus = this.ONLINE; if (this.attachmentsStore != null) { console.log("sending attachments"); @@ -1105,7 +1129,7 @@ define([ */ getFeatureLayerJSONDataStore: function(callback){ if(!this._editStore._isDBInit){ - this._initializeDB(null,function(success) { + this._initializeDB(null, null, function(success) { if(success){ this._editStore.getFeatureLayerJSON(function(success,message){ callback(success,message); @@ -1123,12 +1147,14 @@ define([ /* internal methods */ /** - * Intialize the database and push featureLayer JSON to DB if required + * Intialize the database and push featureLayer JSON to DB if required. + * NOTE: also stores feature layer url in hidden dataStore property dataStore.__featureLayerURL. * @param dataStore Object + * @param url Feature Layer's url. This is used by this library for internal feature identification. * @param callback * @private */ - _initializeDB: function(dataStore,callback){ + _initializeDB: function(dataStore,url,callback){ var editStore = this._editStore; @@ -1153,6 +1179,14 @@ define([ //////////////////////////////////////////////////// if (typeof dataStore === "object" && result === true && (dataStore !== undefined) && (dataStore !== null)) { + + // Add a hidden property to hold the feature layer's url + // When converting a feature layer to json (layer.toJson()) we lose this information. + // This library needs to know the feature layer url. + if(url) { + dataStore.__featureLayerURL = url; + } + editStore.pushFeatureLayerJSON(dataStore, function (success, err) { if (success) { callback(true, null); @@ -1479,7 +1513,7 @@ define([ console.log("NOTICE: you may need to run OfflineFeaturesManager.initAttachments(). Check the Attachments doc for more info. Layer id: " + layer.id + " accepts attachments"); } else if(layer.hasAttachments === false){ - console.error("WARNING: Layer " + layer.id + "doesn't seem to accept attachments. Recheck the layer permissions."); + console.error("WARNING: Layer " + layer.id + " doesn't seem to accept attachments. Recheck the layer permissions."); callback(false,"WARNING: Attachments not supported in layer: " + layer.id); } @@ -1718,6 +1752,12 @@ define([ _internalApplyEdits: function (layer, id, tempObjectIds, adds, updates, deletes) { var dfd = new Deferred(); +this._makeEditRequest(layer.url,adds,updates,deletes).then(function(result){ + console.log(result); +},function(error){ + console.log("error"); +}); + layer._applyEdits(adds, updates, deletes, function (addResults, updateResults, deleteResults) { layer._phantomLayer.clear(); @@ -1760,6 +1800,47 @@ define([ return dfd.promise; }, + _makeEditRequest: function(url,adds, updates, deletes) { + + var dfd = new Deferred(); + + var data = new FormData(); + data.append("f", "json"); + if(adds.length > 0) { + data.append("adds", JSON.stringify(adds)); + } + if(updates.length > 0) { + data.append("updates", JSON.stringify(updates)); + } + if(deletes.length > 0) { + data.append("deletes", JSON.stringify(deletes)); + } + + var req = new XMLHttpRequest(); + req.open("POST", url + "/applyEdits", true); + req.onload = function() + { + if( req.status === 200 && req.responseText !== "") + { + var obj = JSON.parse(this.response); + dfd.resolve(obj); + //callback(obj.addResults, obj.updateResults, obj.deleteResults); + //callback(this.response); + //Object.keys(this.response).forEach(function(key) { + // console.log(key, this.response[key]); + //}); + } + }; + req.onerror = function(e) + { + console.log("_getTileInfoPrivate failed: " + e); + dfd.error(e); + }; + req.send(data); + + return dfd.promise; + }, + /** * Deprecated @ v2.5. Internal-use only * @returns {string} diff --git a/samples/appcache-features.html b/samples/appcache-features.html index 317c3d42..53046825 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -221,7 +221,7 @@ * This is a utility check to 100% validate if the application is online or * offline prior to launching any map functionality. */ - verifyOnline(function(result) { + validateOnline(function(result) { if(result) { _isOnline = true; setUIOnline(); @@ -271,9 +271,6 @@ */ function startMap() { - //Make sure map shows up after a browser refresh - Offline.check(); - tileLayer = new O.esri.Tiles.OfflineTileEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ console.log("Tile Layer initialized for offline. App state is: " + Offline.state); },_isOnline); @@ -408,10 +405,13 @@ if(success){ // Use the feature layer returns from getFeatureDefinition() to reconstitute the layer - busStopFeatureLayer = new FeatureLayer(JSON.parse(dataStore.featureLayerCollection), { - mode: FeatureLayer.MODE_SNAPSHOT, - outFields: ["GlobalID","BSID","ROUTES","STOPNAME"] - }); + // We don't have to set any other properties on the layer because we are using it + // in SNAPSHOT mode which downloads all features within the given extent. + busStopFeatureLayer = new FeatureLayer(JSON.parse(dataStore.featureLayerCollection)); + + if(busStopFeatureLayer.url == null){ + busStopFeatureLayer.url = dataStore.featureLayerUrl; + } // Set the graphic symbols to red diamond to make it easy to click on them // on a mobile device. @@ -460,9 +460,9 @@ // Refer to "Using the Proxy Page" for more information: https://developers.arcgis.com/en/javascript/jshelp/ags_proxy.html offlineFeaturesManager.proxyPath = null; - offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_ENQUEUED, editsEnqueued); + offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_ENQUEUED, updateStatus); offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT, updateStatus); - offlineFeaturesManager.on(offlineFeaturesManager.events.ALL_EDITS_SENT, allEditsSent); + offlineFeaturesManager.on(offlineFeaturesManager.events.ALL_EDITS_SENT, updateStatus); offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT_ERROR, editsError); } @@ -495,7 +495,14 @@ // This will force the library to check for pending edits and attempt to // resend them to the Feature Service. if(_isOnline){ - offlineFeaturesManager.goOnline(); + offlineFeaturesManager.goOnline(function(result){ + if(!result.success){ + alert("There was a problem when attempting to go back online."); + } + }); + } + else { + offlineFeaturesManager.goOffline(); } callback(true); @@ -504,7 +511,7 @@ callback(false); alert("Unable to initialize the database. " + error); } - },/* This is the optional offline configuration property */featureLayerJSON); + }.bind(this),/* This is the optional offline configuration property */featureLayerJSON); } /** @@ -571,10 +578,12 @@ } function getFeatureLayerJSON() { + return { "featureLayerCollection": JSON.stringify(busStopFeatureLayer.toJson()), "zoomLevel": map.getZoom(), - "centerPt" : (map.extent.getCenter()).toJson() + "centerPt" : (map.extent.getCenter()).toJson(), + "featureLayerUrl": busStopFeatureLayer.url } } @@ -752,15 +761,17 @@ * Use this in conjunction with the offline checker library: offline.min.js * @param callback */ - function verifyOnline(callback) { + function validateOnline(callback) { var req = new XMLHttpRequest(); req.open("GET", "http://esri.github.io/offline-editor-js/samples/images/blue-pin.png?" + (Math.floor(Math.random() * 1000000000)), true); req.onload = function() { if( req.status === 200 && req.responseText !== "") { + req = null; callback(true); } else { console.log("verifyOffline failed"); + req = null; callback(false); } }; @@ -795,10 +806,11 @@ alert("There was a problem. Not all edits were synced with the server. " + JSON.stringify(evt)); } - function updateStatus() { - busStopFeatureLayer.pendingEditsCount(function(count) { - pendingEdits.innerHTML = count; - }); + function updateStatus(evt) { + console.log("TEST"); +// busStopFeatureLayer.pendingEditsCount(function(count) { +// pendingEdits.innerHTML = count; +// }); } }); From e58ce36c4ae7243247503a7ae8f533e9411e3e9a Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 4 May 2015 15:45:22 -0600 Subject: [PATCH 05/15] feature manager spec tweak --- test/spec/offlineFeaturesManagerSpec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/spec/offlineFeaturesManagerSpec.js b/test/spec/offlineFeaturesManagerSpec.js index 4ed7c48e..d9f64108 100644 --- a/test/spec/offlineFeaturesManagerSpec.js +++ b/test/spec/offlineFeaturesManagerSpec.js @@ -1111,14 +1111,14 @@ describe("Offline Editing", function() console.log("Library is now back online"); expect(g_offlineFeaturesManager.getOnlineStatus()).toBe(g_offlineFeaturesManager.ONLINE); expect(listener).toHaveBeenCalled(); - expect(results.features.success).toBeTruthy(); + expect(results.success).toBeTruthy(); //console.log("RESPONSES " + JSON.stringify(responses) + ", " + JSON.stringify(results)) - expect(Object.keys(results.features.responses).length).toBe(5); - for (var key in results.features.responses) { + expect(Object.keys(results.responses).length).toBe(5); + for (var key in results.responses) { - var response = results.features.responses[key]; + var response = results.responses[key]; console.log("RESPONSE " + JSON.stringify(response)) From f29b664f3aebae58a9378307b2e1aeea0dd217ca Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 4 May 2015 15:46:17 -0600 Subject: [PATCH 06/15] attachments spec tweaks --- test/spec/offlineAttachmentsSpec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/spec/offlineAttachmentsSpec.js b/test/spec/offlineAttachmentsSpec.js index 56591a94..144ac146 100644 --- a/test/spec/offlineAttachmentsSpec.js +++ b/test/spec/offlineAttachmentsSpec.js @@ -631,9 +631,9 @@ describe("Attachments", function() console.log("went online"); expect(g_offlineFeaturesManager.getOnlineStatus()).toBe(g_offlineFeaturesManager.ONLINE); expect(listener).toHaveBeenCalled(); - expect(result.features.success).toBeTruthy(); + expect(result.success).toBeTruthy(); expect(result.attachments.success).toBeTruthy(); - expect(Object.keys(result.features.responses).length).toBe(1); + expect(Object.keys(result.responses).length).toBe(1); expect(Object.keys(result.attachments.responses).length).toBe(4); var attachmentResults = result.attachments.responses; @@ -644,8 +644,8 @@ describe("Attachments", function() //expect(attachmentResults[1].addAttachmentResult).not.toBeUndefined(); //expect(attachmentResults[1].addAttachmentResult.success).toBeTruthy(); - expect(result.features.responses[0]).not.toBeUndefined(); - var featureResults = result.features.responses[0]; + expect(result.responses[0]).not.toBeUndefined(); + var featureResults = result.responses[0]; expect(featureResults.addResults.length).toBe(1); expect(featureResults.updateResults.length).toBe(0); expect(featureResults.deleteResults.length).toBe(0); From 4cd458e2ef041e37d9499af9d22974c17c26d593 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 4 May 2015 15:46:43 -0600 Subject: [PATCH 07/15] doc updates --- doc/howtouseeditlibrary.md | 36 ++++++++++++++++++++++++++++++++--- doc/offlinefeaturesmanager.md | 5 +++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/doc/howtouseeditlibrary.md b/doc/howtouseeditlibrary.md index 34f51b3a..ee748956 100644 --- a/doc/howtouseeditlibrary.md +++ b/doc/howtouseeditlibrary.md @@ -75,7 +75,9 @@ NOTE: You can also monitor standard ArcGIS API for JavaScript layer events using ``` -**Step 4** After the `layers-add-result` event fires extend the feature layer using the `extend()` method. Optionally, if you are building a fully offline app then you will also need to set the `dataStore` property in the constructor. +**Step 4** After the `layers-add-result` event fires extend the feature layer using the `extend()` method. Optionally, if you are building a fully offline app then you will also need to set the `dataStore` property in the constructor. + +Note: the `layer.extend()` callback only indicates that the edits database has been successfully initialized. ```js @@ -95,6 +97,34 @@ NOTE: You can also monitor standard ArcGIS API for JavaScript layer events using ``` +When working with fully offline browser restarts you may want to verify that a layer has been successfully extended: + +```js + + offlineFeaturesManager.on(offlineFeaturesManager.events.EXTEND_COMPLETE, function(evt) { + if(evt) { + // If the app is online then force offlineFeaturesManager to its online state + // This will force the library to check for pending edits and attempt to + // resend them to the Feature Service. + if(_isOnline){ // Check if app is online or offline + offlineFeaturesManager.goOnline(function(result){ + if(!result.success){ + alert("There was a problem when attempting to go back online."); + } + else { + // Do somthing good! + } + }); + } + else { + offlineFeaturesManager.goOffline(); + } + } + }); + +``` + + The `dataStore` property is an object that is used to store any data related to your app that will assist in restoring it and any feature layers after a full offline browser restart. The `dataStore` object has one reserved key and that is `id`. If you overwrite the `id` key the application will fail to update the `dataStore` object correctly. Here is an example of one possible `dataStore` object: ```js @@ -160,9 +190,9 @@ Force the library to return to an online condition. If there are pending edits, ```js function goOnline() { - offlineFeaturesManager.goOnline(function(success,results) + offlineFeaturesManager.goOnline(function(result) { - if(success){ + if(result.success){ //Modify user inteface depending on success/failure } }); diff --git a/doc/offlinefeaturesmanager.md b/doc/offlinefeaturesmanager.md index f5496cef..2791a93e 100644 --- a/doc/offlinefeaturesmanager.md +++ b/doc/offlinefeaturesmanager.md @@ -38,9 +38,9 @@ OfflineFeaturesManager provides the following functionality. Methods | Returns | Description --- | --- | --- -`extend( layer,` `callback, dataStore)`|`callback( boolean, errors )`| Overrides a feature layer, by replacing the `applyEdits()` method of the layer. You can use the FeatureLayer as always, but it's behaviour will be enhanced according to the online status of the manager and the capabilities included in this library. `Callback` is related to initialization the library.

`dataStore` is an optional Object that contains any information you need when reconsistuting the layer after an offline browser restart. Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information. +`extend( layer,` `callback, dataStore)`|`callback( boolean, errors )`| Overrides a feature layer, by replacing the `applyEdits()` method of the layer. You can use the FeatureLayer as always, but it's behaviour will be enhanced according to the online status of the manager and the capabilities included in this library.

`Callback` is related to initialization the edits database. If you want to detect when the library has fully extended a layer then listen for `event.EXTEND_COMPLETE`.

`dataStore` is an optional Object that contains any information you need when reconsistuting the layer after an offline browser restart. Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information. `goOffline()` | nothing | Forces library into an offline state. Any edits applied to extended FeatureLayers during this condition will be stored locally. -`goOnline(callback)` | `callback( boolean, results )` | Forces library to return to an online state. If there are pending edits, an attempt will be made to sync them with the remote feature server. Callback function will be called when resync process is done.

Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information on the `results` object. +`goOnline(callback)` | No attachments: `callback( {success: boolean, responses: Object } )`

With attachments: `callback( {success: boolean, responses: uploadedResponses, dbResponses: dbResponses })` | Forces library to return to an online state. If there are pending edits, an attempt will be made to sync them with the remote feature server. Callback function will be called when resync process is done.

Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information on the `results` object. `getOnlineStatus()` | `ONLINE`, `OFFLINE` or `RECONNECTING`| Determines the current state of the manager. Please, note that this library doesn't detect actual browser offline/online condition. You need to use the `offline.min.js` library included in `vendor\offline` directory to detect connection status and connect events to goOffline() and goOnline() methods. See `military-offline.html` sample. `getFeatureLayerJSONDataStore( callback )` | `callback( boolean, Object)` | **New @ v2.7.1** Returns the feature layer's dataStore Object. `getReadableEdit()` | String | **DEPRECATED** @ v2.5. A string value representing human readable information on pending edits. Use `featureLayer.getAllEditsArray()`. @@ -68,6 +68,7 @@ Event | Value | Returns | Description `events.ALL_EDITS_SENT` | "all-edits-sent" | {[addResults] ,[updateResults], [deleteResults]} | After going online and there are no pending edits remaining in the queue. Be sure to also check for `EDITS_SENT_ERROR`. `events.ATTACHMENT_ENQUEUED` | "attachment-enqueued" | nothing | An attachment is in the queue to be sent to the server. `events.ATTACHMENTS_SENT` | "attachments-sent" | nothing | When any attachment is actually sent to the server. +`events.EXTEND_COMPLETE` | "extend-complete" | true | Indicates the layer has been successfully extended. ###FeatureLayer From 2a5a12404ed6998d6b3de34a7b1a01a91668c4eb Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 4 May 2015 16:15:21 -0600 Subject: [PATCH 08/15] revamped full offline life-cycle of sample --- samples/appcache-features.html | 190 +++++++++++++++------------------ 1 file changed, 86 insertions(+), 104 deletions(-) diff --git a/samples/appcache-features.html b/samples/appcache-features.html index 53046825..2954c7a5 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -144,7 +144,7 @@
- +
Pending Edits 0 @@ -185,6 +185,7 @@ var map = null; var _isOnline = true; var defaultSymbol; + var _listener; // Variables for editing handling var currentFeature, busStopFeatureLayer = null, offlineFeaturesManager = null; @@ -285,6 +286,10 @@ sliderStyle: "small" }); + map.on("load",function(evt) { + _currentExtent = evt.map.extent; + }); + // Add our offline enabled tile layer to the map map.addLayer(tileLayer); @@ -298,8 +303,7 @@ //on a mobile device. busStopFeatureLayer.setRenderer(new SimpleRenderer(defaultSymbol)); - initFeatureUpdateEndListener(); - initMapLoadListener(); + _listener = busStopFeatureLayer.on("update-end",initFeatureUpdateEndListener); map.addLayer(busStopFeatureLayer); } @@ -312,64 +316,56 @@ */ function initFeatureUpdateEndListener() { - var listener = busStopFeatureLayer.on("update-end",function(evt){ + _listener.remove(); - listener.remove(); + //************************************************** + // + // This is where we detect an offline condition + // within the lifecycle of the "mapping" application. + // If we are offline then run our offline + // specific code for reconstituting our map. + // + //************************************************** - //************************************************** - // - // This is where we detect an offline condition - // within the lifecycle of the "mapping" application. - // If we are offline then run our offline - // specific code for reconstituting our map. - // - //************************************************** + // + // Extend the feature layer with offline capabilities. + // - // - // Extend the feature layer with offline capabilities. - // + initOfflineFeaturesMgr(); - initOfflineFeaturesMgr(); - - // If app is online then we ONLY need to extend the feature layer. - if(_isOnline){ - extendFeatureLayer(_isOnline, function(success) { - if(success){ - - // Set click listeners - on(btnGetTiles,"click",downloadTiles); - on(btnOnlineOffline, 'click', goOnlineOffline); - - initPanZoomListeners(); - setFeatureLayerClickHandler(); - setModalPopupClickListeners(); - } - else{ - alert("There was a problem initializing the map for offline."); - } - }); - } - // If the app is offline then we need to retrieve the dataStore from OfflineFeaturesManager - // and then extend the feature layer using that information. - else { - loadFeatureLayerOffline(function(success) { - if(success) { - extendFeatureLayer(_isOnline, function(success) { - console.log("Feature Layer extended successfully OFFLINE!"); - }); - } - else { - alert("There was a problem initializing the map for offline."); - } - }); - } - }); - } + // If app is online then we ONLY need to extend the feature layer. + if(_isOnline){ + extendFeatureLayer(_isOnline, function(success) { + if(success){ + + // Set click listeners + on(btnGetTiles,"click",downloadTiles); + on(btnOnlineOffline, 'click', goOnlineOffline); + + initPanZoomListeners(); + setFeatureLayerClickHandler(); + setModalPopupClickListeners(); + } + else{ + alert("There was a problem initializing the map for offline."); + } + }); + } + // If the app is offline then we need to retrieve the dataStore from OfflineFeaturesManager + // and then extend the feature layer using that information. + else { + loadFeatureLayerOffline(function(success) { + if(success) { + extendFeatureLayer(_isOnline, function(success) { + console.log("Feature Layer extended successfully OFFLINE!"); + }); + } + else { + alert("There was a problem initializing the map for offline."); + } + }); + } - function initMapLoadListener() { - map.on("load",function(evt) { - _currentExtent = evt.map.extent; - }); } /** @@ -464,6 +460,26 @@ offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT, updateStatus); offlineFeaturesManager.on(offlineFeaturesManager.events.ALL_EDITS_SENT, updateStatus); offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT_ERROR, editsError); + offlineFeaturesManager.on(offlineFeaturesManager.events.EXTEND_COMPLETE, function(evt) { + if(evt) { + // If the app is online then force offlineFeaturesManager to its online state + // This will force the library to check for pending edits and attempt to + // resend them to the Feature Service. + if(_isOnline){ + offlineFeaturesManager.goOnline(function(result){ + if(!result.success){ + alert("There was a problem when attempting to go back online."); + } + else { + updateStatus(); + } + }); + } + else { + offlineFeaturesManager.goOffline(); + } + } + }); } function extendFeatureLayer(online,callback){ @@ -487,24 +503,9 @@ console.log("offlineFeaturesManager initialized."); // This sets listeners to detect if the app goes online or offline. - Offline.check(); Offline.on('up', goOnline); Offline.on('down', goOffline); - // If the app is online then force offlineFeaturesManager to its online state - // This will force the library to check for pending edits and attempt to - // resend them to the Feature Service. - if(_isOnline){ - offlineFeaturesManager.goOnline(function(result){ - if(!result.success){ - alert("There was a problem when attempting to go back online."); - } - }); - } - else { - offlineFeaturesManager.goOffline(); - } - callback(true); } else { @@ -652,7 +653,7 @@ } else { globalState.downloadState = 'downloaded'; - btnOnlineOffline.disabled = false; +// btnOnlineOffline.disabled = false; alert("Tile download complete"); } @@ -677,6 +678,10 @@ loading.style.visibility = "visible"; + if(busStopFeatureLayer && busStopFeatureLayer.offlineExtended) { + + } + offlineFeaturesManager.goOnline(function(success,error) { if(error === undefined) { setUIOnline(); @@ -743,19 +748,6 @@ }) } - /** - * Forces a check of the Offline.js state - */ - function forceInternalOfflineCheck() { - Offline.check(); - if(Offline.state == "up") { - goOnline(); - } - if(Offline.state == "down") { - goOffline(); - } - } - /** * Attempts a manual http request to verify if app is online or offline. * Use this in conjunction with the offline checker library: offline.min.js @@ -788,29 +780,19 @@ * ***********************************************å */ - function editsEnqueued() { - - busStopFeatureLayer.pendingEditsCount(function(count) { - pendingEdits.innerHTML = count; - }); - } - - function allEditsSent(){ - - busStopFeatureLayer.pendingEditsCount(function(count) { - pendingEdits.innerHTML = count; - }); - } - function editsError(evt) { alert("There was a problem. Not all edits were synced with the server. " + JSON.stringify(evt)); } - function updateStatus(evt) { - console.log("TEST"); -// busStopFeatureLayer.pendingEditsCount(function(count) { -// pendingEdits.innerHTML = count; -// }); + function updateStatus() { + + // Depending on application life cycle busStopFeatureLayer may or may not be extended + // So we have to protect pendingEditsCount() from throwing an "is not a function" error. + if(busStopFeatureLayer.hasOwnProperty("pendingEditsCount")) { + busStopFeatureLayer.pendingEditsCount(function(count) { + pendingEdits.innerHTML = count; + }); + } } }); From 2e5eb28a3633f28028aa1d5cdf0fabe0746a3a3a Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 4 May 2015 16:20:19 -0600 Subject: [PATCH 09/15] Closes #336 --- lib/edit/offlineFeaturesManager.js | 134 +++++++++++++++++++++++------ 1 file changed, 107 insertions(+), 27 deletions(-) diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index f1e3cad8..f98939ea 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -23,6 +23,7 @@ define([ { _onlineStatus: "online", _featureLayers: {}, + _featureCollectionUsageFlag: false, // if a feature collection was used to create the feature layer. _editStore: new O.esri.Edit.EditStore(), ONLINE: "online", // all edits will directly go to the server @@ -51,7 +52,8 @@ define([ EDITS_SENT_ERROR: "edits-sent-error", // ...there was a problem with one or more edits! ALL_EDITS_SENT: "all-edits-sent", // ...after going online and there are no pending edits in the queue ATTACHMENT_ENQUEUED: "attachment-enqueued", - ATTACHMENTS_SENT: "attachments-sent" + ATTACHMENTS_SENT: "attachments-sent", + EXTEND_COMPLETE: "extend-complete" // ...when the libary has completed its initialization }, /** @@ -100,33 +102,52 @@ define([ */ extend: function (layer, callback, dataStore) { var self = this; + layer.offlineExtended = true; // to identify layer has been extended // NOTE: At v2.6.1 we've discovered that not all feature layers support objectIdField. - // However, to try to be consistent here with how the library is managing Ids. - // So, we force the layer.objectIdField to DB_UID. This should be consistent with + // However, to try to be consistent here with how the library is managing Ids + // we force the layer.objectIdField to DB_UID. This should be consistent with // how esri.Graphics assign a unique ID to a graphic. If it is not, then this - // library will break and we'll have to re-architect how we manage UIDs. + // library will break and we'll have to re-architect how it manages UIDs. layer.objectIdField = this.DB_UID; var url = null; + // There have been reproducible use cases showing when a browser is restarted offline that + // for some reason the layer.url may be undefined. + // This is an attempt to minimize the possibility of that situation causing errors. if(layer.url) { url = layer.url; // we keep track of the FeatureLayer object this._featureLayers[layer.url] = layer; } + // This is a fairly brittle solution to detecting if a feature layer collection + // was used to create the feature layer. + // Is there a better way?? + if(layer._mode.featureLayer.hasOwnProperty("_collection")){ + // This means a feature collection was used to create the feature layer and it will + // require different handling when running applyEdit() + this._featureCollectionUsageFlag = true; + } + // Initialize the database as well as set offline data. + // This approach leaves open the possibility that if the db is initialized + // and !layer.url then the library could encounter errors. At this time, it appears + // that the likelihood of this happening is fairly small. if(!this._editStore._isDBInit) { this._initializeDB(dataStore, url, function(success, error) { - if(success && !url) { + // If we weren't able to find a url then we need to retrieve it from the feature layer + // dataStore. + if(success && !url) { // This functionality is specifically for offline restarts // and attempts to retrieve a feature layer url. // It's a hack because layer.toJson() doesn't convert layer.url. this._editStore.getFeatureLayerJSON(function(success,message){ if(success) { this._featureLayers[message.__featureLayerURL] = layer; + layer.url = message.__featureLayerURL; } else { console.error("getFeatureLayerJSON() failed."); @@ -159,7 +180,7 @@ define([ 3. remove an attachment that is already in the server... (DONE) 4. remove an attachment that is not in the server yet (DONE) 5. update an existing attachment to an existing feature (DONE) - 6. update a new attachment (NOT YET) + 6. update a new attachment (DONE) concerns: - manage the relationship between offline features and attachments: what if the user wants to add @@ -167,9 +188,6 @@ define([ the feature is sent to the server and receives a final objectid we replace the temporary negative id by its final objectid (DONE) - what if the user deletes an offline feature that had offline attachments? we need to discard the attachment (DONE) - - pending tasks: - - check for hasAttachments attribute in the FeatureLayer (NOT YET) */ // @@ -1059,6 +1077,10 @@ define([ _initPhantomLayer(); + // Send event indicating that all the functions above were successfully + // added to the feature layer. + this.emit(this.events.EXTEND_COMPLETE, true); + }, // extend /** @@ -1147,7 +1169,7 @@ define([ /* internal methods */ /** - * Intialize the database and push featureLayer JSON to DB if required. + * Initialize the database and push featureLayer JSON to DB if required. * NOTE: also stores feature layer url in hidden dataStore property dataStore.__featureLayerURL. * @param dataStore Object * @param url Feature Layer's url. This is used by this library for internal feature identification. @@ -1512,10 +1534,6 @@ define([ if (attachmentsStore == null && layer.hasAttachments) { console.log("NOTICE: you may need to run OfflineFeaturesManager.initAttachments(). Check the Attachments doc for more info. Layer id: " + layer.id + " accepts attachments"); } - else if(layer.hasAttachments === false){ - console.error("WARNING: Layer " + layer.id + " doesn't seem to accept attachments. Recheck the layer permissions."); - callback(false,"WARNING: Attachments not supported in layer: " + layer.id); - } // Assign the attachmentsStore to the layer as a private var so we can access it from // the promises applyEdits() method. @@ -1554,7 +1572,13 @@ define([ break; } - promises[n] = that._internalApplyEdits(layer, tempArray[n].id, tempObjectIds, adds, updates, deletes); + if(that._featureCollectionUsageFlag){ + // Note: when the feature layer is created with a feature collection we have to handle applyEdits() differently + promises[n] = that._internalApplyEditsFeatureCollection(layer, tempArray[n].id, tempObjectIds, adds, updates, deletes); + } + else { + promises[n] = that._internalApplyEdits(layer, tempArray[n].id, tempObjectIds, adds, updates, deletes); + } } // wait for all requests to finish @@ -1752,12 +1776,6 @@ define([ _internalApplyEdits: function (layer, id, tempObjectIds, adds, updates, deletes) { var dfd = new Deferred(); -this._makeEditRequest(layer.url,adds,updates,deletes).then(function(result){ - console.log(result); -},function(error){ - console.log("error"); -}); - layer._applyEdits(adds, updates, deletes, function (addResults, updateResults, deleteResults) { layer._phantomLayer.clear(); @@ -1800,10 +1818,71 @@ this._makeEditRequest(layer.url,adds,updates,deletes).then(function(result){ return dfd.promise; }, - _makeEditRequest: function(url,adds, updates, deletes) { - + _internalApplyEditsFeatureCollection: function (layer, id, tempObjectIds, adds, updates, deletes) { var dfd = new Deferred(); + this._makeEditRequest(layer.url, adds, updates, deletes, + function (addResults, updateResults, deleteResults) { + layer._phantomLayer.clear(); + + var newObjectIds = addResults.map(function (r) { + return r.objectId; + }); + + // We use a different pattern if the attachmentsStore is valid and the layer has attachments + if (layer._attachmentsStore != null && layer.hasAttachments && tempObjectIds.length > 0) { + layer._replaceFeatureIds(tempObjectIds, newObjectIds, function (success) { + dfd.resolve({ + id: id, + layer: layer.url, + tempId: tempObjectIds, // let's us internally match an ADD to it's new ObjectId + addResults: addResults, + updateResults: updateResults, + deleteResults: deleteResults + }); // wrap three arguments in a single object + }); + } + else { + dfd.resolve({ + id: id, + layer: layer.url, + tempId: tempObjectIds, // let's us internally match an ADD to it's new ObjectId + addResults: addResults, + updateResults: updateResults, + deleteResults: deleteResults + }); // wrap three arguments in a single object + } + }, + function (error) { + layer.onEditsComplete = layer.__onEditsComplete; + delete layer.__onEditsComplete; + + dfd.reject(error); + } + ); + return dfd.promise; + }, + + /** + * Used when a feature layer is created with a feature collection. + * + * In the current version of the ArcGIS JSAPI 3.12+ the applyEdit() method doesn't send requests + * to the server when a feature layer is created with a feature collection. + * + * The use case for using this is: clean start app > go offline and make edits > offline restart browser > + * go online. + * + * @param url + * @param adds + * @param updates + * @param deletes + * @returns {*|r} + * @private + */ + _makeEditRequest: function(url,adds, updates, deletes, callback, errback) { + + //var dfd = new Deferred(); + var data = new FormData(); data.append("f", "json"); if(adds.length > 0) { @@ -1823,8 +1902,8 @@ this._makeEditRequest(layer.url,adds,updates,deletes).then(function(result){ if( req.status === 200 && req.responseText !== "") { var obj = JSON.parse(this.response); - dfd.resolve(obj); - //callback(obj.addResults, obj.updateResults, obj.deleteResults); + //dfd.resolve(obj); + callback(obj.addResults, obj.updateResults, obj.deleteResults); //callback(this.response); //Object.keys(this.response).forEach(function(key) { // console.log(key, this.response[key]); @@ -1834,11 +1913,12 @@ this._makeEditRequest(layer.url,adds,updates,deletes).then(function(result){ req.onerror = function(e) { console.log("_getTileInfoPrivate failed: " + e); - dfd.error(e); + errback(e); + //dfd.error(e); }; req.send(data); - return dfd.promise; + //return dfd.promise; }, /** From dbf46d7f8fe5cb2ff33eaa7d20812fc66b9a376d Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 4 May 2015 16:34:52 -0600 Subject: [PATCH 10/15] updateStatus after successive offline restarts --- samples/appcache-features.html | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/appcache-features.html b/samples/appcache-features.html index 2954c7a5..736d6e77 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -477,6 +477,7 @@ } else { offlineFeaturesManager.goOffline(); + updateStatus(); } } }); From 0c9e88f0fcc408c4edf35d6a4a77eedd31f2b279 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Tue, 5 May 2015 10:00:56 -0600 Subject: [PATCH 11/15] reverted back to using callback --- samples/appcache-features.html | 41 ++++++++++++++++------------------ 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/samples/appcache-features.html b/samples/appcache-features.html index 736d6e77..fc88a4ac 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -460,27 +460,6 @@ offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT, updateStatus); offlineFeaturesManager.on(offlineFeaturesManager.events.ALL_EDITS_SENT, updateStatus); offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT_ERROR, editsError); - offlineFeaturesManager.on(offlineFeaturesManager.events.EXTEND_COMPLETE, function(evt) { - if(evt) { - // If the app is online then force offlineFeaturesManager to its online state - // This will force the library to check for pending edits and attempt to - // resend them to the Feature Service. - if(_isOnline){ - offlineFeaturesManager.goOnline(function(result){ - if(!result.success){ - alert("There was a problem when attempting to go back online."); - } - else { - updateStatus(); - } - }); - } - else { - offlineFeaturesManager.goOffline(); - updateStatus(); - } - } - }); } function extendFeatureLayer(online,callback){ @@ -507,6 +486,24 @@ Offline.on('up', goOnline); Offline.on('down', goOffline); + // If the app is online then force offlineFeaturesManager to its online state + // This will force the library to check for pending edits and attempt to + // resend them to the Feature Service. + if(_isOnline){ + offlineFeaturesManager.goOnline(function(result){ + if(!result.success){ + alert("There was a problem when attempting to go back online."); + } + else { + updateStatus(); + } + }); + } + else { + offlineFeaturesManager.goOffline(); + updateStatus(); + } + callback(true); } else { @@ -789,7 +786,7 @@ // Depending on application life cycle busStopFeatureLayer may or may not be extended // So we have to protect pendingEditsCount() from throwing an "is not a function" error. - if(busStopFeatureLayer.hasOwnProperty("pendingEditsCount")) { + if("pendingEditsCount" in busStopFeatureLayer) { busStopFeatureLayer.pendingEditsCount(function(count) { pendingEdits.innerHTML = count; }); From cdb841b05d4797613615fdd7d4c8920c47e664dc Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Tue, 5 May 2015 10:01:28 -0600 Subject: [PATCH 12/15] implemented init promises --- doc/howtouseeditlibrary.md | 8 +- doc/offlinefeaturesmanager.md | 3 +- lib/edit/offlineFeaturesManager.js | 117 ++++++++++++++++++++--------- 3 files changed, 87 insertions(+), 41 deletions(-) diff --git a/doc/howtouseeditlibrary.md b/doc/howtouseeditlibrary.md index ee748956..62b748b7 100644 --- a/doc/howtouseeditlibrary.md +++ b/doc/howtouseeditlibrary.md @@ -88,7 +88,7 @@ Note: the `layer.extend()` callback only indicates that the edits database has b // options.graphics = JSON.stringify(layer1.toJson()); // options.zoom = map.getZoom(); - offlineFeaturesManager.extend(layer1,function(success,error){ + offlineFeaturesManager.extend(layer1,function(success, error){ if(success){ console.log("layer1 has been extended for offline use."); } @@ -97,12 +97,12 @@ Note: the `layer.extend()` callback only indicates that the edits database has b ``` -When working with fully offline browser restarts you may want to verify that a layer has been successfully extended: +When working with fully offline browser restarts you should wait until the layer has been successfully extended before forcing the library to go back online. The workflow for this coding pattern is you start out online > offline > browser restart > then back online. ```js - offlineFeaturesManager.on(offlineFeaturesManager.events.EXTEND_COMPLETE, function(evt) { - if(evt) { + offlineFeaturesManager.extend(layer1, function(success, error) { + if(success) { // If the app is online then force offlineFeaturesManager to its online state // This will force the library to check for pending edits and attempt to // resend them to the Feature Service. diff --git a/doc/offlinefeaturesmanager.md b/doc/offlinefeaturesmanager.md index 2791a93e..d2151ed2 100644 --- a/doc/offlinefeaturesmanager.md +++ b/doc/offlinefeaturesmanager.md @@ -38,7 +38,7 @@ OfflineFeaturesManager provides the following functionality. Methods | Returns | Description --- | --- | --- -`extend( layer,` `callback, dataStore)`|`callback( boolean, errors )`| Overrides a feature layer, by replacing the `applyEdits()` method of the layer. You can use the FeatureLayer as always, but it's behaviour will be enhanced according to the online status of the manager and the capabilities included in this library.

`Callback` is related to initialization the edits database. If you want to detect when the library has fully extended a layer then listen for `event.EXTEND_COMPLETE`.

`dataStore` is an optional Object that contains any information you need when reconsistuting the layer after an offline browser restart. Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information. +`extend( layer,` `callback, dataStore)`|`callback( boolean, errors )`| Overrides a feature layer, by replacing the `applyEdits()` method of the layer. You can use the FeatureLayer as always, but it's behaviour will be enhanced according to the online status of the manager and the capabilities included in this library.

`Callback` indicates the layer has been extended.

`dataStore` is an optional Object that contains any information you need when reconsistuting the layer after an offline browser restart. Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information. `goOffline()` | nothing | Forces library into an offline state. Any edits applied to extended FeatureLayers during this condition will be stored locally. `goOnline(callback)` | No attachments: `callback( {success: boolean, responses: Object } )`

With attachments: `callback( {success: boolean, responses: uploadedResponses, dbResponses: dbResponses })` | Forces library to return to an online state. If there are pending edits, an attempt will be made to sync them with the remote feature server. Callback function will be called when resync process is done.

Refer to the [How to use the edit library doc](howtouseeditlibrary.md) for addition information on the `results` object. `getOnlineStatus()` | `ONLINE`, `OFFLINE` or `RECONNECTING`| Determines the current state of the manager. Please, note that this library doesn't detect actual browser offline/online condition. You need to use the `offline.min.js` library included in `vendor\offline` directory to detect connection status and connect events to goOffline() and goOnline() methods. See `military-offline.html` sample. @@ -68,7 +68,6 @@ Event | Value | Returns | Description `events.ALL_EDITS_SENT` | "all-edits-sent" | {[addResults] ,[updateResults], [deleteResults]} | After going online and there are no pending edits remaining in the queue. Be sure to also check for `EDITS_SENT_ERROR`. `events.ATTACHMENT_ENQUEUED` | "attachment-enqueued" | nothing | An attachment is in the queue to be sent to the server. `events.ATTACHMENTS_SENT` | "attachments-sent" | nothing | When any attachment is actually sent to the server. -`events.EXTEND_COMPLETE` | "extend-complete" | true | Indicates the layer has been successfully extended. ###FeatureLayer diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index f98939ea..7777e5aa 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -101,6 +101,9 @@ define([ * @returns deferred */ extend: function (layer, callback, dataStore) { + + var extendPromises = []; + var self = this; layer.offlineExtended = true; // to identify layer has been extended @@ -136,32 +139,41 @@ define([ // and !layer.url then the library could encounter errors. At this time, it appears // that the likelihood of this happening is fairly small. if(!this._editStore._isDBInit) { - this._initializeDB(dataStore, url, function(success, error) { - - // If we weren't able to find a url then we need to retrieve it from the feature layer - // dataStore. - if(success && !url) { - // This functionality is specifically for offline restarts - // and attempts to retrieve a feature layer url. - // It's a hack because layer.toJson() doesn't convert layer.url. - this._editStore.getFeatureLayerJSON(function(success,message){ - if(success) { - this._featureLayers[message.__featureLayerURL] = layer; - layer.url = message.__featureLayerURL; - } - else { - console.error("getFeatureLayerJSON() failed."); - } - }.bind(this)); - } - // NOTE: If this is an error then _featureLayers[] isn't going to be set correct - callback(success,error); - }.bind(this)); - } - else { - callback(true, null); + extendPromises.push(this._initializeDB(dataStore, url)); + + //this._initializeDB(dataStore, url, function(success, error) { + // var deferred = new Deferred(); + // + // // If we weren't able to find a url then we need to retrieve it from the feature layer + // // dataStore. + // if(success && !url) { + // // This functionality is specifically for offline restarts + // // and attempts to retrieve a feature layer url. + // // It's a hack because layer.toJson() doesn't convert layer.url. + // this._editStore.getFeatureLayerJSON(function(success,message){ + // if(success) { + // this._featureLayers[message.__featureLayerURL] = layer; + // layer.url = message.__featureLayerURL; + // deferred.resolve({success:success, message: message}); + // } + // else { + // console.error("getFeatureLayerJSON() failed."); + // deferred.reject({success:false, message: message}); + // } + // + // }.bind(this)); + // } + // // NOTE: If this is an error then _featureLayers[] isn't going to be set correct + // //callback(success,error); + // + // return deferred; + // + //}.bind(this)); } + //else { + // callback(true, null); + //} // replace the applyEdits() method layer._applyEdits = layer.applyEdits; @@ -1077,9 +1089,37 @@ define([ _initPhantomLayer(); - // Send event indicating that all the functions above were successfully - // added to the feature layer. - this.emit(this.events.EXTEND_COMPLETE, true); + all(extendPromises).then(function (r) { + if(r.length === 0){ + callback(true, null); + } + else if(r[0].success && !url){ + + // This functionality is specifically for offline restarts + // and attempts to retrieve a feature layer url. + // It's a hack because layer.toJson() doesn't convert layer.url. + this._editStore.getFeatureLayerJSON(function(success,message){ + if(success) { + this._featureLayers[message.__featureLayerURL] = layer; + layer.url = message.__featureLayerURL; + callback(true, null); + } + else { + console.error("getFeatureLayerJSON() failed."); + callback(false, message); + } + + }.bind(this)); + + //// Send event indicating that all the functions above were successfully + //// added to the feature layer. + //this.emit(this.events.EXTEND_COMPLETE, true); + //callback(r[0]); + } + else if(r[0].success){ + callback(true, null); + } + }.bind(this)); }, // extend @@ -1151,13 +1191,16 @@ define([ */ getFeatureLayerJSONDataStore: function(callback){ if(!this._editStore._isDBInit){ - this._initializeDB(null, null, function(success) { - if(success){ + + this._initializeDB(null,null).then(function(result){ + if(result.success){ this._editStore.getFeatureLayerJSON(function(success,message){ callback(success,message); }); } - }.bind(this)); + }.bind(this), function(err){ + callback(false, err); + }); } else { this._editStore.getFeatureLayerJSON(function(success,message){ @@ -1176,7 +1219,9 @@ define([ * @param callback * @private */ - _initializeDB: function(dataStore,url,callback){ + //_initializeDB: function(dataStore,url,callback){ + _initializeDB: function(dataStore,url){ + var deferred = new Deferred(); var editStore = this._editStore; @@ -1211,20 +1256,22 @@ define([ editStore.pushFeatureLayerJSON(dataStore, function (success, err) { if (success) { - callback(true, null); + deferred.resolve({success:true, error: null}); } else { - callback(false, err); + deferred.reject({success:false, error: err}); } }); } else if(result){ - callback(true, null); + deferred.resolve({success:true, error: null}); } else{ - callback(false, error); + deferred.reject({success:false, error: null}); } }); + + return deferred; }, /** From e45ca72476a33f6cc222153e3bd0243468ed6a6d Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Tue, 5 May 2015 10:44:09 -0600 Subject: [PATCH 13/15] change button color --- samples/appcache-features.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/appcache-features.html b/samples/appcache-features.html index fc88a4ac..4d0755c4 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -143,8 +143,8 @@
- - + +
Pending Edits 0 From 60af3979433c47a5279db87f1ae2b72d2620f0d1 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Tue, 5 May 2015 11:57:46 -0600 Subject: [PATCH 14/15] clean up --- lib/edit/offlineFeaturesManager.js | 75 ++++++++++-------------------- samples/appcache-features.html | 1 - 2 files changed, 25 insertions(+), 51 deletions(-) diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index 7777e5aa..b34e2c64 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -39,7 +39,7 @@ define([ ATTACHMENTS_DB_NAME: "attachments_store", //Sets attachments database name ATTACHMENTS_DB_OBJECTSTORE_NAME: "attachments", - // NOTE: attachments don't have the same issues as Graphics as related to UIDs. + // NOTE: attachments don't have the same issues as Graphics as related to UIDs (e.g. the need for DB_UID). // You can manually create a graphic, but it would be very rare for someone to // manually create an attachment. So, we don't provide a public property for // the attachments database UID. @@ -102,7 +102,7 @@ define([ */ extend: function (layer, callback, dataStore) { - var extendPromises = []; + var extendPromises = []; // deferred promises related to initializing this method var self = this; layer.offlineExtended = true; // to identify layer has been extended @@ -125,7 +125,7 @@ define([ this._featureLayers[layer.url] = layer; } - // This is a fairly brittle solution to detecting if a feature layer collection + // This is a potentially brittle solution to detecting if a feature layer collection // was used to create the feature layer. // Is there a better way?? if(layer._mode.featureLayer.hasOwnProperty("_collection")){ @@ -135,45 +135,9 @@ define([ } // Initialize the database as well as set offline data. - // This approach leaves open the possibility that if the db is initialized - // and !layer.url then the library could encounter errors. At this time, it appears - // that the likelihood of this happening is fairly small. if(!this._editStore._isDBInit) { - extendPromises.push(this._initializeDB(dataStore, url)); - - //this._initializeDB(dataStore, url, function(success, error) { - // var deferred = new Deferred(); - // - // // If we weren't able to find a url then we need to retrieve it from the feature layer - // // dataStore. - // if(success && !url) { - // // This functionality is specifically for offline restarts - // // and attempts to retrieve a feature layer url. - // // It's a hack because layer.toJson() doesn't convert layer.url. - // this._editStore.getFeatureLayerJSON(function(success,message){ - // if(success) { - // this._featureLayers[message.__featureLayerURL] = layer; - // layer.url = message.__featureLayerURL; - // deferred.resolve({success:success, message: message}); - // } - // else { - // console.error("getFeatureLayerJSON() failed."); - // deferred.reject({success:false, message: message}); - // } - // - // }.bind(this)); - // } - // // NOTE: If this is an error then _featureLayers[] isn't going to be set correct - // //callback(success,error); - // - // return deferred; - // - //}.bind(this)); } - //else { - // callback(true, null); - //} // replace the applyEdits() method layer._applyEdits = layer.applyEdits; @@ -338,7 +302,7 @@ define([ } if (!self.attachmentsStore) { - console.log("in order to support attachments you need to call initAttachments() method of offlineFeaturesManager"); + console.error("in order to support attachments you need to call initAttachments() method of offlineFeaturesManager"); return; } @@ -522,8 +486,7 @@ define([ all(promises).then(function (r) { // Make sure all edits were successful. If not throw an error. var success = true; - var length = r.length; - for (var v = 0; v < length; v++) { + for (var v = 0; v < r.length; v++) { if (r[v] === false) { success = false; } @@ -777,7 +740,7 @@ define([ /* internal methods */ /** - * Pushes an DELETE request to the database after it's been validated + * Pushes a DELETE request to the database after it's been validated * @param layer * @param deleteEdit * @param operation @@ -1089,6 +1052,7 @@ define([ _initPhantomLayer(); + // We are currently only passing in a single deferred. all(extendPromises).then(function (r) { if(r.length === 0){ callback(true, null); @@ -1108,13 +1072,7 @@ define([ console.error("getFeatureLayerJSON() failed."); callback(false, message); } - }.bind(this)); - - //// Send event indicating that all the functions above were successfully - //// added to the feature layer. - //this.emit(this.events.EXTEND_COMPLETE, true); - //callback(r[0]); } else if(r[0].success){ callback(true, null); @@ -1810,7 +1768,7 @@ define([ }, /** - * Executes the _applyEdits() method + * Executes the _applyEdits() method when a feature layer is created using a REST endpoint * @param layer * @param id the unique id that identifies the Graphic in the database * @param tempObjectIds @@ -1865,6 +1823,23 @@ define([ return dfd.promise; }, + /** + * Executes the _applyEdits() method when a feature layer is created using a feature collection. + * This works around specific behaviors in esri.layers.FeatureLayer when using the pattern + * new FeatureLayer(featureCollectionObject). + * + * Details on the specific behaviors can be found here: + * https://developers.arcgis.com/javascript/jsapi/featurelayer-amd.html#featurelayer2 + * + * @param layer + * @param id + * @param tempObjectIds + * @param adds + * @param updates + * @param deletes + * @returns {*|r} + * @private + */ _internalApplyEditsFeatureCollection: function (layer, id, tempObjectIds, adds, updates, deletes) { var dfd = new Deferred(); diff --git a/samples/appcache-features.html b/samples/appcache-features.html index 4d0755c4..14c79c57 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -651,7 +651,6 @@ } else { globalState.downloadState = 'downloaded'; -// btnOnlineOffline.disabled = false; alert("Tile download complete"); } From b9c3345bd9cc5a31603d7baccbac40ac4e6e51c4 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Tue, 5 May 2015 11:59:31 -0600 Subject: [PATCH 15/15] v2.8 --- CHANGELOG.md | 14 ++ dist/offline-edit-min.js | 6 +- dist/offline-edit-src.js | 290 ++++++++++++++++++++++------- dist/offline-tiles-advanced-min.js | 2 +- dist/offline-tiles-advanced-src.js | 2 +- dist/offline-tiles-basic-min.js | 2 +- dist/offline-tiles-basic-src.js | 2 +- dist/offline-tpk-min.js | 2 +- dist/offline-tpk-src.js | 2 +- package.json | 2 +- samples/package.json | 2 +- 11 files changed, 248 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e45474cf..8552b4e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # offline-editor-js - Changelog +## Version 2.8 - May 4, 2015 + +This release focused on updating full offline editing capabilities. Recommended update. No breaking changes. + +**Enhancements** +* Added functionality to `offlineFeaturesManager.js` to detect and handle when a feature layer is created using a feature collection. +* Addresses browser changes in Chrome 42.x and Firefox 37.x with respect to how they handle HTML/JS apps when going offline and +then transitioning back online. +* Updated `appcache-features.html` sample. + +**Bug Fix** +* Closes #336 - problem with appcache-features sample. The application now correctly syncs when going back online after +a full offline restart. It also contains improvements in how the app life-cycle is handled. + ## Version 2.7.1 - April 29, 2015 This release has enhancements and has no breaking changes. diff --git a/dist/offline-edit-min.js b/dist/offline-edit-min.js index efb31359..6935f059 100644 --- a/dist/offline-edit-min.js +++ b/dist/offline-edit-min.js @@ -1,6 +1,6 @@ -/*! offline-editor-js - v2.7.1 - 2015-04-29 +/*! offline-editor-js - v2.8 - 2015-05-05 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ -define(["dojo/Evented","dojo/_base/Deferred","dojo/promise/all","dojo/_base/declare","dojo/_base/array","dojo/dom-attr","dojo/dom-style","dojo/query","esri/config","esri/layers/GraphicsLayer","esri/graphic","esri/symbols/SimpleMarkerSymbol","esri/symbols/SimpleLineSymbol","esri/symbols/SimpleFillSymbol","esri/urlUtils"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o){"use strict";return d("O.esri.Edit.OfflineFeaturesManager",[a],{_onlineStatus:"online",_featureLayers:{},_editStore:new O.esri.Edit.EditStore,ONLINE:"online",OFFLINE:"offline",RECONNECTING:"reconnecting",attachmentsStore:null,proxyPath:null,DB_NAME:"features_store",DB_OBJECTSTORE_NAME:"features",DB_UID:"objectid",ATTACHMENTS_DB_NAME:"attachments_store",ATTACHMENTS_DB_OBJECTSTORE_NAME:"attachments",events:{EDITS_SENT:"edits-sent",EDITS_ENQUEUED:"edits-enqueued",EDITS_ENQUEUED_ERROR:"edits-enqueued-error",EDITS_SENT_ERROR:"edits-sent-error",ALL_EDITS_SENT:"all-edits-sent",ATTACHMENT_ENQUEUED:"attachment-enqueued",ATTACHMENTS_SENT:"attachments-sent"},initAttachments:function(a){if(a=a||function(a){},!this._checkFileAPIs())return a(!1,"File APIs not supported");try{if(this.attachmentsStore=new O.esri.Edit.AttachmentsStore,this.attachmentsStore.dbName=this.ATTACHMENTS_DB_NAME,this.attachmentsStore.objectStoreName=this.ATTACHMENTS_DB_OBJECTSTORE_NAME,!this.attachmentsStore.isSupported())return a(!1,"indexedDB not supported");this.attachmentsStore.init(a)}catch(b){}},extend:function(a,d,i){function l(){try{a._phantomLayer=new j({opacity:.8}),a._map.addLayer(a._phantomLayer)}catch(b){}}var m=this;a.objectIdField=this.DB_UID,this._editStore._isDBInit||this._initializeDB(i,d),this._featureLayers[a.url]=a,a._applyEdits=a.applyEdits,a._addAttachment=a.addAttachment,a._queryAttachmentInfos=a.queryAttachmentInfos,a._deleteAttachments=a.deleteAttachments,a._updateAttachment=a.updateAttachment,a.queryAttachmentInfos=function(a,c,d){if(m.getOnlineStatus()===m.ONLINE){var e=this._queryAttachmentInfos(a,function(){m.emit(m.events.ATTACHMENTS_INFO,arguments),c&&c.apply(this,arguments)},d);return e}if(m.attachmentsStore){var f=new b;return m.attachmentsStore.getAttachmentsByFeatureId(this.url,a,function(a){c&&c(a),f.resolve(a)}),f}},a.addAttachment=function(a,c,d,e){if(m.getOnlineStatus()===m.ONLINE)return this._addAttachment(a,c,function(){m.emit(m.events.ATTACHMENTS_SENT,arguments),d&&d.apply(this,arguments)},function(a){e&&e.apply(this,arguments)});if(m.attachmentsStore){var f=this._getFilesFromForm(c),g=f[0],i=new b,j=this._getNextTempId();return m.attachmentsStore.store(this.url,j,a,g,m.attachmentsStore.TYPE.ADD,function(b,c){var f={attachmentId:j,objectId:a,success:b};if(b){m.emit(m.events.ATTACHMENT_ENQUEUED,f),d&&d(f),i.resolve(f);var g=this._url.path+"/"+a+"/attachments/"+j,k=h("[href="+g+"]");k.attr("href",c.url)}else f.error="can't store attachment",e&&e(f),i.reject(f)}.bind(this)),i}},a.updateAttachment=function(a,c,d,e,f){if(m.getOnlineStatus()===m.ONLINE)return this._updateAttachment(a,c,d,function(){e&&e.apply(this,arguments)},function(a){f&&f.apply(this,arguments)});if(m.attachmentsStore){var g=this._getFilesFromForm(d),i=g[0],j=new b;return m.attachmentsStore.store(this.url,c,a,i,m.attachmentsStore.TYPE.UPDATE,function(b,d){var g={attachmentId:c,objectId:a,success:b};if(b){m.emit(m.events.ATTACHMENT_ENQUEUED,g),e&&e(g),j.resolve(g);var i=this._url.path+"/"+a+"/attachments/"+c,k=h("[href="+i+"]");k.attr("href",d.url)}else g.error="layer.updateAttachment::attachmentStore can't store attachment",f&&f(g),j.reject(g)}.bind(this)),j}},a.deleteAttachments=function(a,d,e,f){if(m.getOnlineStatus()===m.ONLINE){var g=this._deleteAttachments(a,d,function(){e&&e.apply(this,arguments)},function(a){f&&f.apply(this,arguments)});return g}if(m.attachmentsStore){var h=[];d.forEach(function(c){c=parseInt(c,10);var d=new b;if(0>c)m.attachmentsStore["delete"](c,function(b){var e={objectId:a,attachmentId:c,success:b};d.resolve(e)});else{var e=new Blob([],{type:"image/png"});m.attachmentsStore.store(this.url,c,a,e,m.attachmentsStore.TYPE.DELETE,function(b,e){var f={attachmentId:c,objectId:a,success:b};b?d.resolve(f):d.reject(f)}.bind(this))}h.push(d)},this);var i=c(h);return i.then(function(a){e&&e(a)}),i}},a.applyEdits=function(a,d,e,f,g){var h=[];if(m.getOnlineStatus()===m.ONLINE){var i=this._applyEdits(a,d,e,function(){m.emit(m.events.EDITS_SENT,arguments),f&&f.apply(this,arguments)},g);return i}var j=new b,k={addResults:[],updateResults:[],deleteResults:[]},l={};return a=a||[],a.forEach(function(a){var c=new b,d=this._getNextTempId();a.attributes[this.objectIdField]=d;var e=this;this._validateFeature(a,this.url,m._editStore.ADD).then(function(b){b.success?e._pushValidatedAddFeatureToDB(e,a,b.operation,k,d,c):c.resolve(!0)},function(a){c.reject(a)}),h.push(c)},this),d=d||[],d.forEach(function(a){var c=new b,d=a.attributes[this.objectIdField];l[d]=a;var e=this;this._validateFeature(a,this.url,m._editStore.UPDATE).then(function(b){b.success?e._pushValidatedUpdateFeatureToDB(e,a,b.operation,k,d,c):c.resolve(!0)},function(a){c.reject(a)}),h.push(c)},this),e=e||[],e.forEach(function(a){var c=new b,d=a.attributes[this.objectIdField],e=this;this._validateFeature(a,this.url,m._editStore.DELETE).then(function(b){b.success?e._pushValidatedDeleteFeatureToDB(e,a,b.operation,k,d,c):c.resolve(!0)},function(a){c.reject(a)}),h.push(c)},this),c(h).then(function(b){for(var c=!0,d=b.length,e=0;d>e;e++)b[e]===!1&&(c=!1);this._editHandler(k,a,l,f,g,j),c===!0?m.emit(m.events.EDITS_ENQUEUED,k):m.emit(m.events.EDITS_ENQUEUED_ERROR,k)}.bind(this)),j},a.convertGraphicLayerToJSON=function(a,b,c){var d={};d.objectIdFieldName=b.target.hasOwnProperty("objectIdField")?b.target.objectIdField:this.objectIdField,d.globalIdFieldName=b.target.globalIdField,d.geometryType=b.target.geometryType,d.spatialReference=b.target.spatialReference,d.fields=b.target.fields;for(var e=a.length,f=[],g=0;e>g;g++){var h=a[g].toJson();if(f.push(h),g==e-1){var i=JSON.stringify(f),j=JSON.stringify(d);c(i,j);break}}},a.getFeatureLayerJSON=function(a,b){require(["esri/request"],function(c){var d=c({url:a,content:{f:"json"},handleAs:"json",callbackParamName:"callback"});d.then(function(a){b(!0,a)},function(a){b(!1,a.message)})})},a.setFeatureLayerJSONDataStore=function(a,b){m._editStore.pushFeatureLayerJSON(a,function(a,c){b(a,c)})},a.getFeatureLayerJSONDataStore=function(a){m._editStore.getFeatureLayerJSON(function(b,c){a(b,c)})},a.setPhantomLayerGraphics=function(a){var b=a.length;if(b>0)for(var c=0;b>c;c++){var d=new k(a[c]);this._phantomLayer.add(d)}},a.getPhantomLayerGraphics=function(b){for(var c=a._phantomLayer.graphics,d=a._phantomLayer.graphics.length,e=[],f=0;d>f;f++){var g=c[f].toJson();if(e.push(g),f==d-1){var h=JSON.stringify(e);b(h);break}}},a.getPhantomGraphicsArray=function(a){m._editStore.getPhantomGraphicsArray(function(b,c){"end"==c?a(!0,b):a(!1,c)})},a.getAttachmentsUsage=function(a){m.attachmentsStore.getUsage(function(b,c){a(b,c)})},a.resetAttachmentsDatabase=function(a){m.attachmentsStore.resetAttachmentsQueue(function(b,c){a(b,c)})},a.getUsage=function(a){m._editStore.getUsage(function(b,c){a(b,c)})},a.resetDatabase=function(a){m._editStore.resetEditsQueue(function(b,c){a(b,c)})},a.pendingEditsCount=function(a){m._editStore.pendingEditsCount(function(b){a(b)})},a.getFeatureDefinition=function(a,b,c,d){var e={layerDefinition:a,featureSet:{features:b,geometryType:c}};d(e)},a.getAllEditsArray=function(a){m._editStore.getAllEditsArray(function(b,c){"end"==c?a(!0,b):a(!1,c)})},a._pushValidatedDeleteFeatureToDB=function(a,b,c,d,e,h){m._editStore.pushEdit(c,a.url,b,function(c,i){if(c){d.deleteResults.push({success:!0,error:null,objectId:e});var j={};j[m.DB_UID]=e;var l=new k(b.geometry,m._getPhantomSymbol(b.geometry,m._editStore.DELETE),j);a._phantomLayer.add(l),m._editStore.pushPhantomGraphic(l,function(a){}),f.set(l.getNode(),"stroke-dasharray","4,4"),g.set(l.getNode(),"pointer-events","none"),m.attachmentsStore&&m.attachmentsStore.deleteAttachmentsByFeatureId(a.url,e,function(a){})}else d.deleteResults.push({success:!1,error:i,objectId:e});h.resolve(c)})},a._pushValidatedUpdateFeatureToDB=function(a,b,c,d,e,h){m._editStore.pushEdit(c,a.url,b,function(c,i){if(c){d.updateResults.push({success:!0,error:null,objectId:e});var j={};j[m.DB_UID]=e;var l=new k(b.geometry,m._getPhantomSymbol(b.geometry,m._editStore.UPDATE),j);a._phantomLayer.add(l),m._editStore.pushPhantomGraphic(l,function(a){}),f.set(l.getNode(),"stroke-dasharray","5,2"),g.set(l.getNode(),"pointer-events","none")}else d.updateResults.push({success:!1,error:i,objectId:e});h.resolve(c)})},a._pushValidatedAddFeatureToDB=function(a,b,c,d,e,h){m._editStore.pushEdit(c,a.url,b,function(c,i){if(c){d.addResults.push({success:!0,error:null,objectId:e});var j={};j[m.DB_UID]=e;var l=new k(b.geometry,m._getPhantomSymbol(b.geometry,m._editStore.ADD),j);a._phantomLayer.add(l),m._editStore.pushPhantomGraphic(l,function(a){}),f.set(l.getNode(),"stroke-dasharray","10,4"),g.set(l.getNode(),"pointer-events","none")}else d.addResults.push({success:!1,error:i,objectId:e});h.resolve(c)})},a._validateFeature=function(c,d,e){var f=new b,g=d+"/"+c.attributes.objectid;return m._editStore.getEdit(g,function(b,d){if(b)switch(e){case m._editStore.ADD:f.resolve({success:!0,graphic:c,operation:e});break;case m._editStore.UPDATE:d.operation==m._editStore.ADD&&(c.operation=m._editStore.ADD,e=m._editStore.ADD),f.resolve({success:!0,graphic:c,operation:e});break;case m._editStore.DELETE:var g=!0;d.operation==m._editStore.ADD&&a._deleteTemporaryFeature(c,function(a){a||(g=!1)}),f.resolve({success:g,graphic:c,operation:e})}else"Id not found"==d?f.resolve({success:!0,graphic:c,operation:e}):f.reject(c)}),f},a._deleteTemporaryFeature=function(d,e){function f(){var c=new b;return m._editStore["delete"](a.url,d,function(a,b){c.resolve(a?!0:!1)}),c.promise}function g(){var a=new b;return m._editStore.deletePhantomGraphic(h,function(b){a.resolve(b?!0:!1)}),a.promise}var h=m._editStore.PHANTOM_GRAPHIC_PREFIX+m._editStore._PHANTOM_PREFIX_TOKEN+d.attributes[m.DB_UID];c([f(),g()]).then(function(a){e(a)})},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},a._replaceFeatureIds=function(a,b,c){a.length||c(0);var d,e=a.length,f=e,g=0;for(d=0;e>d;d++)m.attachmentsStore.replaceFeatureId(this.url,a[d],b[d],function(a){--f,g+=a?1:0,0===f&&c(g)}.bind(this))},a._nextTempId=-1,a._getNextTempId=function(){return this._nextTempId--},l()},goOffline:function(){this._onlineStatus=this.OFFLINE},goOnline:function(a){this._onlineStatus=this.RECONNECTING,this._replayStoredEdits(function(b,c){var d={features:{success:b,responses:c}};this._onlineStatus=this.ONLINE,null!=this.attachmentsStore?this._sendStoredAttachments(function(b,c,e){this._onlineStatus=this.ONLINE,d.attachments={success:b,responses:c,dbResponses:e},a&&a(d)}.bind(this)):(this._onlineStatus=this.ONLINE,a&&a(d))}.bind(this))},getOnlineStatus:function(){return this._onlineStatus},serializeFeatureGraphicsArray:function(a,b){for(var c=a.length,d=[],e=0;c>e;e++){var f=a[e].toJson();if(d.push(f),e==c-1){var g=JSON.stringify(d);b(g);break}}},getFeatureLayerJSONDataStore:function(a){this._editStore._isDBInit?this._editStore.getFeatureLayerJSON(function(b,c){a(b,c)}):this._initializeDB(null,function(b){b&&this._editStore.getFeatureLayerJSON(function(b,c){a(b,c)})}.bind(this))},_initializeDB:function(a,b){var c=this._editStore;c.dbName=this.DB_NAME,c.objectStoreName=this.DB_OBJECTSTORE_NAME,c.objectId=this.DB_UID,c.init(function(d,e){"object"==typeof a&&d===!0&&void 0!==a&&null!==a?c.pushFeatureLayerJSON(a,function(a,c){a?b(!0,null):b(!1,c)}):d?b(!0,null):b(!1,e)})},_checkFileAPIs:function(){return window.File&&window.FileReader&&window.FileList&&window.Blob?(XMLHttpRequest.prototype.sendAsBinary||(XMLHttpRequest.prototype.sendAsBinary=function(a){function b(a){return 255&a.charCodeAt(0)}var c=Array.prototype.map.call(a,b),d=new Uint8Array(c);this.send(d.buffer)}),!0):!1},_extendAjaxReq:function(a){a.sendAsBinary=XMLHttpRequest.prototype.sendAsBinary},_phantomSymbols:[],_getPhantomSymbol:function(a,b){if(0===this._phantomSymbols.length){var c=[0,255,0,255],d=1.5;this._phantomSymbols.point=[],this._phantomSymbols.point[this._editStore.ADD]=new l({type:"esriSMS",style:"esriSMSCross",xoffset:10,yoffset:10,color:[255,255,255,0],size:15,outline:{color:c,width:d,type:"esriSLS",style:"esriSLSSolid"}}),this._phantomSymbols.point[this._editStore.UPDATE]=new l({type:"esriSMS",style:"esriSMSCircle",xoffset:0,yoffset:0,color:[255,255,255,0],size:15,outline:{color:c,width:d,type:"esriSLS",style:"esriSLSSolid"}}),this._phantomSymbols.point[this._editStore.DELETE]=new l({type:"esriSMS",style:"esriSMSX",xoffset:0,yoffset:0,color:[255,255,255,0],size:15,outline:{color:c,width:d,type:"esriSLS",style:"esriSLSSolid"}}),this._phantomSymbols.multipoint=null,this._phantomSymbols.polyline=[],this._phantomSymbols.polyline[this._editStore.ADD]=new m({type:"esriSLS",style:"esriSLSSolid",color:c,width:d}),this._phantomSymbols.polyline[this._editStore.UPDATE]=new m({type:"esriSLS",style:"esriSLSSolid",color:c,width:d}),this._phantomSymbols.polyline[this._editStore.DELETE]=new m({type:"esriSLS",style:"esriSLSSolid",color:c,width:d}),this._phantomSymbols.polygon=[],this._phantomSymbols.polygon[this._editStore.ADD]=new n({type:"esriSFS",style:"esriSFSSolid",color:[255,255,255,0],outline:{type:"esriSLS",style:"esriSLSSolid",color:c,width:d}}),this._phantomSymbols.polygon[this._editStore.UPDATE]=new n({type:"esriSFS",style:"esriSFSSolid",color:[255,255,255,0],outline:{type:"esriSLS",style:"esriSLSDash",color:c,width:d}}),this._phantomSymbols.polygon[this._editStore.DELETE]=new n({type:"esriSFS",style:"esriSFSSolid",color:[255,255,255,0],outline:{type:"esriSLS",style:"esriSLSDot",color:c,width:d}})}return this._phantomSymbols[a.type][b]},_fieldSegment:function(a,b){return'Content-Disposition: form-data; name="'+a+'"\r\n\r\n'+b+"\r\n"},_fileSegment:function(a,b,c,d){return'Content-Disposition: form-data; name="'+a+'"; filename="'+b+'"\r\nContent-Type: '+c+"\r\n\r\n"+d+"\r\n"},_uploadAttachment:function(a){var c=new b,d=this._featureLayers[a.featureLayerUrl],e=new FormData;switch(e.append("attachment",a.file),a.type){case this.attachmentsStore.TYPE.ADD:d.addAttachment(a.objectId,e,function(b){c.resolve({attachmentResult:b,id:a.id})},function(a){c.reject(a)});break;case this.attachmentsStore.TYPE.UPDATE:e.append("attachmentId",a.id),d._sendAttachment("update",a.objectId,e,function(b){c.resolve({attachmentResult:b,id:a.id})},function(a){c.reject(a)});break;case this.attachmentsStore.TYPE.DELETE:d.deleteAttachments(a.objectId,[a.id],function(b){c.resolve({attachmentResult:b,id:a.id})},function(a){c.reject(a)})}return c.promise},_deleteAttachmentFromDB:function(a,c){var d=new b;return this.attachmentsStore["delete"](a,function(a){d.resolve({success:a,result:c})}),d},_cleanAttachmentsDB:function(a,b){var d=this,e=[],f=0;a.forEach(function(a){"object"==typeof a.attachmentResult&&a.attachmentResult.success?e.push(d._deleteAttachmentFromDB(a.id,null)):a.attachmentResult instanceof Array?a.attachmentResult.forEach(function(b){b.success?e.push(d._deleteAttachmentFromDB(a.id,null)):f++}):f++});var g=c(e);g.then(function(c){b(f>0?{errors:!0,attachmentsDBResults:c,uploadResults:a}:{errors:!1,attachmentsDBResults:c,uploadResults:a})})},_sendStoredAttachments:function(a){this.attachmentsStore.getAllAttachments(function(b){var d=this,e=[];b.forEach(function(a){var b=this._uploadAttachment(a);e.push(b)},this);var f=c(e);f.then(function(b){d._cleanAttachmentsDB(b,function(c){c.errors?a&&a(!1,b,c):a&&a(!0,b,c)})},function(b){a&&a(!1,b)})}.bind(this))},_replayStoredEdits:function(a){var b,d={},e=this,f=[],g=[],h=[],i=[],j=[],l=this._featureLayers,m=this.attachmentsStore,n=this._editStore;this._editStore.getAllEditsArray(function(o,p){if(o.length>0){j=o;for(var q=j.length,r=0;q>r;r++){b=l[j[r].layer],null==m&&b.hasAttachments||b.hasAttachments===!1&&a(!1,"WARNING: Attachments not supported in layer: "+b.id),b._attachmentsStore=m,b.__onEditsComplete=b.onEditsComplete,b.onEditsComplete=function(){},f=[],g=[],h=[],i=[];var s=new k(j[r].graphic);switch(j[r].operation){case n.ADD:for(var t=0;t0&&(g.updateResults[0].success?(h.layer=g.layer,h.id=g.updateResults[0].objectId,d.push(h)):e.push(g)),g.deleteResults.length>0&&(g.deleteResults[0].success?(h.layer=g.layer,h.id=g.deleteResults[0].objectId,d.push(h)):e.push(g)),g.addResults.length>0&&(g.addResults[0].success?(h.layer=g.layer,h.id=g.tempId,d.push(h)):e.push(g))}for(var i={},j=d.length,k=0;j>k;k++)i[k]=this._updateDatabase(d[k]);var l=c(i);l.then(function(a){e.length>0?b(!1,a):b(!0,a)},function(a){b(!1,a)})}else b(!0,{})},_updateDatabase:function(a){var c=new b,d={};return d.attributes={},d.attributes[this.DB_UID]=a.id,this._editStore["delete"](a.layer,d,function(a,b){a?c.resolve({success:!0,error:null}):c.reject({success:!1,error:b})}.bind(this)),c.promise},getFeatureLayerJSON:function(a,b){require(["esri/request"],function(c){var d=c({url:a,content:{f:"json"},handleAs:"json",callbackParamName:"callback"});d.then(function(a){b(!0,a)},function(a){b(!1,a.message)})})},_internalApplyEdits:function(a,c,d,e,f,g){var h=new b;return a._applyEdits(e,f,g,function(b,e,f){a._phantomLayer.clear();var g=b.map(function(a){return a.objectId});null!=a._attachmentsStore&&a.hasAttachments&&d.length>0?a._replaceFeatureIds(d,g,function(g){h.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:e,deleteResults:f})}):h.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:e,deleteResults:f})},function(b){a.onEditsComplete=a.__onEditsComplete,delete a.__onEditsComplete,h.reject(b)}),h.promise},_optimizeEditsQueue:function(){return"DEPRECATED at v2.5!"},getReadableEdit:function(a){return"DEPRECATED at v2.5!"}})}),"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";var a="featureId";this.ADD="add",this.UPDATE="update",this.DELETE="delete",this.FEATURE_LAYER_JSON_ID="feature-layer-object-1001",this.PHANTOM_GRAPHIC_PREFIX="phantom-layer",this._PHANTOM_PREFIX_TOKEN="|@|",this.isSupported=function(){return window.indexedDB?!0:!1},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()};if("undefined"==typeof c.attributes[this.objectId])d(!1,"editsStore.pushEdit() - failed to insert undefined objectId into database. Did you set offlineFeaturesManager.DB_UID? "+JSON.stringify(c.attributes));else{var f=this._db.transaction([this.objectStoreName],"readwrite");f.oncomplete=function(a){d(!0)},f.onerror=function(a){d(!1,a.target.error.message)};var g=f.objectStore(this.objectStoreName);g.put(e)}},this.pushFeatureLayerJSON=function(a,b){"object"!=typeof a&&b(!1,"dataObject type is not an object.");var c=this._db;a.id=this.FEATURE_LAYER_JSON_ID,this.getFeatureLayerJSON(function(d,e){var f;if(d&&"undefined"!=typeof e){f=c.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName);for(var g in a)a.hasOwnProperty(g)&&(e[g]=a[g]);var h=f.put(e);h.onsuccess=function(){b(!0,null)},h.onerror=function(a){b(!1,a)}}else{var i=c.transaction([this.objectStoreName],"readwrite");i.oncomplete=function(a){b(!0,null)},i.onerror=function(a){b(!1,a.target.error.message)},f=i.objectStore(this.objectStoreName);try{f.put(a)}catch(j){b(!1,JSON.stringify(j))}}}.bind(this))},this.getFeatureLayerJSON=function(a){var b=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),c=b.get(this.FEATURE_LAYER_JSON_ID);c.onsuccess=function(){var b=c.result;"undefined"!=typeof b?a(!0,b):a(!1,"nothing found")},c.onerror=function(b){a(!1,b)}},this.deleteFeatureLayerJSON=function(a){var b=this._db,c=null,d=this,e=this.FEATURE_LAYER_JSON_ID;require(["dojo/Deferred"],function(f){c=new f,c.then(function(b){d.editExists(e).then(function(b){b.success===!1?a(!0,{message:"id does not exist"}):a(!1,{message:null})},function(b){a(!0,{message:"id does not exist"})})},function(b){a(!1,{message:"id does not exist"})}),d.editExists(e).then(function(a){if(a&&a.success){var f=b.transaction([d.objectStoreName],"readwrite").objectStore(d.objectStoreName),g=f["delete"](e);g.onsuccess=function(){c.resolve(!0)},g.onerror=function(a){c.reject({success:!1,error:a})}}else c.reject({success:!1,message:"id does not exist"})},function(a){c.reject({success:!1,message:a})}.bind(this))})},this.pushPhantomGraphic=function(a,b){var c=this._db,d=this.PHANTOM_GRAPHIC_PREFIX+this._PHANTOM_PREFIX_TOKEN+a.attributes[this.objectId],e={id:d,graphic:a.toJson()},f=c.transaction([this.objectStoreName],"readwrite");f.oncomplete=function(a){b(!0,null)},f.onerror=function(a){b(!1,a.target.error.message)};var g=f.objectStore(this.objectStoreName);g.put(e)},this.getPhantomGraphicsArray=function(a){var b=[];if(null!==this._db){var c=this.PHANTOM_GRAPHIC_PREFIX,d=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();d.onsuccess=function(d){var e=d.target.result;e&&e.value&&e.value.id?(-1!=e.value.id.indexOf(c)&&b.push(e.value),e["continue"]()):a(b,"end")}.bind(this),d.onerror=function(b){a(null,b)}}else a(null,"no db")},this._getPhantomGraphicsArraySimple=function(a){var b=[];if(null!==this._db){var c=this.PHANTOM_GRAPHIC_PREFIX,d=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();d.onsuccess=function(d){var e=d.target.result;e&&e.value&&e.value.id?(-1!=e.value.id.indexOf(c)&&b.push(e.value.id),e["continue"]()):a(b,"end")}.bind(this),d.onerror=function(b){a(null,b)}}else a(null,"no db")},this.deletePhantomGraphic=function(a,b){var c=this._db,d=null,e=this;require(["dojo/Deferred"],function(f){d=new f,e.editExists(a).then(function(f){if(f.success){d.then(function(c){e.editExists(a).then(function(a){b(a.success===!1?!0:!1)},function(a){b(!0)})},function(a){b(!1,a)});var g=c.transaction([e.objectStoreName],"readwrite").objectStore(e.objectStoreName),h=g["delete"](a);h.onsuccess=function(){d.resolve(!0)},h.onerror=function(a){d.reject({success:!1,error:a})}}},function(a){b(!1)})})},this.resetLimitedPhantomGraphicsQueue=function(a,b){if(Object.keys(a).length>0){var c=this._db,d=0,e=c.transaction([this.objectStoreName],"readwrite"),f=e.objectStore(this.objectStoreName);f.onerror=function(){d++},e.oncomplete=function(){b(0===d?!0:!1)};for(var g in a)if(a.hasOwnProperty(g)){var h=a[g],i=this.PHANTOM_GRAPHIC_PREFIX+this._PHANTOM_PREFIX_TOKEN+h.id;h.updateResults.length>0&&h.updateResults[0].success&&f["delete"](i),h.deleteResults.length>0&&h.deleteResults[0].success&&f["delete"](i),h.addResults.length>0&&h.addResults[0].success&&f["delete"](i)}}else b(!0)},this.resetPhantomGraphicsQueue=function(a){var b=this._db;this._getPhantomGraphicsArraySimple(function(c){if(c!=[]){var d=0,e=b.transaction([this.objectStoreName],"readwrite"),f=e.objectStore(this.objectStoreName);f.onerror=function(){d++},e.oncomplete=function(){a(0===d?!0:!1)};for(var g=c.length,h=0;g>h;h++)f["delete"](c[h])}else a(!0)}.bind(this))},this.getEdit=function(a,b){var c=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName);require(["dojo/Deferred"],function(d){if("undefined"==typeof a)return void b(!1,"id is undefined.");var e=c.get(a);e.onsuccess=function(){var c=e.result;c&&c.id==a?b(!0,c):b(!1,"Id not found")},e.onerror=function(a){b(!1,a)}})},this.getAllEdits=function(a){if(null!==this._db){var b=this.FEATURE_LAYER_JSON_ID,c=this.PHANTOM_GRAPHIC_PREFIX,d=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();d.onsuccess=function(d){var e=d.target.result;e&&e.hasOwnProperty("value")&&e.value.hasOwnProperty("id")?(e.value.id!==b&&-1==e.value.id.indexOf(c)&&a(e.value,null),e["continue"]()):a(null,"end")}.bind(this),d.onerror=function(b){a(null,b)}}else a(null,"no db")},this.getAllEditsArray=function(a){var b=[];if(null!==this._db){var c=this.FEATURE_LAYER_JSON_ID,d=this.PHANTOM_GRAPHIC_PREFIX,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&&-1==f.value.id.indexOf(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.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];require(["dojo/Deferred"],function(a){e=new a,f.editExists(g).then(function(a){if(a.success){e.then(function(a){f.editExists(g).then(function(a){c(a.success===!1?!0:!1)},function(a){c(!0)})},function(a){c(!1,a)});var b=d.transaction([f.objectStoreName],"readwrite").objectStore(f.objectStoreName),h=b["delete"](g);h.onsuccess=function(){e.resolve(!0)},h.onerror=function(a){e.reject({success:!1,error:a})}}},function(a){c(!1)})})},this.resetEditsQueue=function(a){var b=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName).clear();b.onsuccess=function(b){setTimeout(function(){a(!0)},0)},b.onerror=function(b){a(!1,b)}},this.pendingEditsCount=function(a){var b=0,c=this.FEATURE_LAYER_JSON_ID,d=this.PHANTOM_GRAPHIC_PREFIX,e=this._db.transaction([this.objectStoreName],"readwrite"),f=e.objectStore(this.objectStoreName);f.openCursor().onsuccess=function(e){var f=e.target.result;f&&f.value&&f.value.id&&-1==f.value.id.indexOf(d)?(f.value.id!==c&&b++,f["continue"]()):a(b)}},this.editExists=function(a){var b=this._db,c=null,d=this;return require(["dojo/Deferred"],function(e){c=new e;var f=b.transaction([d.objectStoreName],"readwrite").objectStore(d.objectStoreName),g=f.get(a);g.onsuccess=function(){var b=g.result;b&&b.id==a?c.resolve({success:!0,error:null}):c.reject({success:!1,error:"Layer id is not a match."})},g.onerror=function(a){c.reject({success:!1,error:a})}}),c},this.getUsage=function(a){var b=this.FEATURE_LAYER_JSON_ID,c=this.PHANTOM_GRAPHIC_PREFIX,d={sizeBytes:0,editCount:0},e=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();e.onsuccess=function(e){var f=e.target.result;if(f&&f.value&&f.value.id){var g=f.value,h=JSON.stringify(g);d.sizeBytes+=h.length,-1==f.value.id.indexOf(c)&&f.value.id!==b&&(d.editCount+=1),f["continue"]()}else a(d,null)},e.onerror=function(b){a(null,b)}},this._serialize=function(a){var b=a.toJson(),c={attributes:b.attributes,geometry:b.geometry,infoTemplate:b.infoTemplate,symbol:b.symbol};return JSON.stringify(c)},this._deserialize=function(a){var b;return require(["esri/graphic"],function(c){b=new c(JSON.parse(a))}),b},this.init=function(b){var c=indexedDB.open(this.dbName,11);b=b||function(a){}.bind(this),c.onerror=function(a){b(!1,a.target.errorCode)}.bind(this),c.onupgradeneeded=function(b){var c=b.target.result;c.objectStoreNames.contains(this.objectStoreName)&&c.deleteObjectStore(this.objectStoreName);var d=c.createObjectStore(this.objectStoreName,{keyPath:"id"});d.createIndex(a,a,{unique:!1})}.bind(this),c.onsuccess=function(a){this._db=a.target.result,this._isDBInit=!0,b(!0)}.bind(this)},this.hasPendingEdits=function(){return"DEPRECATED at v2.5!"},this._isEditDuplicated=function(a,b){return"DEPRECATED at v2.5!"},this._storeEditsQueue=function(a){return"DEPRECATED at v2.5!"},this._unpackArrayOfEdits=function(a){return"DEPRECATED at v2.5!"},this.getLocalStorageSizeBytes=function(){return"DEPRECATED at v2.5!"},this.peekFirstEdit=function(){return"DEPRECATED at v2.5!"},this.popFirstEdit=function(){return"DEPRECATED at v2.5!"}},O.esri.Edit.AttachmentsStore=function(){"use strict";this._db=null,this.dbName="attachments_store",this.objectStoreName="attachments",this.TYPE={ADD:"add",UPDATE:"update",DELETE:"delete"},this.isSupported=function(){return window.indexedDB?!0:!1},this.store=function(a,b,c,d,e,f){try{e==this.TYPE.ADD||e==this.TYPE.UPDATE||e==this.TYPE.DELETE?this._readFile(d,function(g,h){if(g){var i={id:b,objectId:c,type:e,featureId:a+"/"+c,contentType:d.type,name:d.name,size:d.size,featureLayerUrl:a,content:h,file:d},j=this._db.transaction([this.objectStoreName],"readwrite");j.oncomplete=function(a){f(!0,i)},j.onerror=function(a){f(!1,a.target.error.message)};var k=j.objectStore(this.objectStoreName),l=k.put(i);l.onsuccess=function(a){}}else f(!1,h)}.bind(this)):f(!1,"attachmentsStore.store() Invalid type in the constructor!")}catch(g){f(!1,g.stack)}},this.retrieve=function(a,b){var c=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName),d=c.get(a);d.onsuccess=function(a){var c=a.target.result;c?b(!0,c):b(!1,"not found")},d.onerror=function(a){b(!1,a)}},this.getAttachmentsByFeatureId=function(a,b,c){var d=a+"/"+b,e=[],f=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName),g=f.index("featureId"),h=IDBKeyRange.only(d);g.openCursor(h).onsuccess=function(a){var b=a.target.result;b?(e.push(b.value),b["continue"]()):c(e)}},this.getAttachmentsByFeatureLayer=function(a,b){var c=[],d=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName),e=d.index("featureLayerUrl"),f=IDBKeyRange.only(a);e.openCursor(f).onsuccess=function(a){var d=a.target.result;d?(c.push(d.value),d["continue"]()):b(c)}},this.getAllAttachments=function(a){var b=[],c=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName);c.openCursor().onsuccess=function(c){var d=c.target.result;d?(b.push(d.value),d["continue"]()):a(b)}},this.deleteAttachmentsByFeatureId=function(a,b,c){var d=a+"/"+b,e=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),f=e.index("featureId"),g=IDBKeyRange.only(d),h=0;f.openCursor(g).onsuccess=function(a){var b=a.target.result;b?(e["delete"](b.primaryKey),h++,b["continue"]()):setTimeout(function(){c(h)},0)}.bind(this)},this["delete"]=function(a,b){this.retrieve(a,function(c,d){if(!c)return void b(!1,"attachment "+a+" not found");var e=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName)["delete"](a);e.onsuccess=function(a){setTimeout(function(){b(!0)},0)},e.onerror=function(a){b(!1,a)}}.bind(this))},this.deleteAll=function(a){this.getAllAttachments(function(b){var c=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName).clear();c.onsuccess=function(b){setTimeout(function(){a(!0)},0)},c.onerror=function(b){a(!1,b)}}.bind(this))},this.replaceFeatureId=function(a,b,c,d){var e=a+"/"+b,f=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),g=f.index("featureId"),h=IDBKeyRange.only(e),i=0; +define(["dojo/Evented","dojo/_base/Deferred","dojo/promise/all","dojo/_base/declare","dojo/_base/array","dojo/dom-attr","dojo/dom-style","dojo/query","esri/config","esri/layers/GraphicsLayer","esri/graphic","esri/request","esri/symbols/SimpleMarkerSymbol","esri/symbols/SimpleLineSymbol","esri/symbols/SimpleFillSymbol","esri/urlUtils"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p){"use strict";return d("O.esri.Edit.OfflineFeaturesManager",[a],{_onlineStatus:"online",_featureLayers:{},_featureCollectionUsageFlag:!1,_editStore:new O.esri.Edit.EditStore,ONLINE:"online",OFFLINE:"offline",RECONNECTING:"reconnecting",attachmentsStore:null,proxyPath:null,DB_NAME:"features_store",DB_OBJECTSTORE_NAME:"features",DB_UID:"objectid",ATTACHMENTS_DB_NAME:"attachments_store",ATTACHMENTS_DB_OBJECTSTORE_NAME:"attachments",events:{EDITS_SENT:"edits-sent",EDITS_ENQUEUED:"edits-enqueued",EDITS_ENQUEUED_ERROR:"edits-enqueued-error",EDITS_SENT_ERROR:"edits-sent-error",ALL_EDITS_SENT:"all-edits-sent",ATTACHMENT_ENQUEUED:"attachment-enqueued",ATTACHMENTS_SENT:"attachments-sent",EXTEND_COMPLETE:"extend-complete"},initAttachments:function(a){if(a=a||function(a){},!this._checkFileAPIs())return a(!1,"File APIs not supported");try{if(this.attachmentsStore=new O.esri.Edit.AttachmentsStore,this.attachmentsStore.dbName=this.ATTACHMENTS_DB_NAME,this.attachmentsStore.objectStoreName=this.ATTACHMENTS_DB_OBJECTSTORE_NAME,!this.attachmentsStore.isSupported())return a(!1,"indexedDB not supported");this.attachmentsStore.init(a)}catch(b){}},extend:function(a,d,i){function l(){try{a._phantomLayer=new j({opacity:.8}),a._map.addLayer(a._phantomLayer)}catch(b){}}var m=[],n=this;a.offlineExtended=!0,a.objectIdField=this.DB_UID;var o=null;a.url&&(o=a.url,this._featureLayers[a.url]=a),a._mode.featureLayer.hasOwnProperty("_collection")&&(this._featureCollectionUsageFlag=!0),this._editStore._isDBInit||m.push(this._initializeDB(i,o)),a._applyEdits=a.applyEdits,a._addAttachment=a.addAttachment,a._queryAttachmentInfos=a.queryAttachmentInfos,a._deleteAttachments=a.deleteAttachments,a._updateAttachment=a.updateAttachment,a.queryAttachmentInfos=function(a,c,d){if(n.getOnlineStatus()===n.ONLINE){var e=this._queryAttachmentInfos(a,function(){n.emit(n.events.ATTACHMENTS_INFO,arguments),c&&c.apply(this,arguments)},d);return e}if(n.attachmentsStore){var f=new b;return n.attachmentsStore.getAttachmentsByFeatureId(this.url,a,function(a){c&&c(a),f.resolve(a)}),f}},a.addAttachment=function(a,c,d,e){if(n.getOnlineStatus()===n.ONLINE)return this._addAttachment(a,c,function(){n.emit(n.events.ATTACHMENTS_SENT,arguments),d&&d.apply(this,arguments)},function(a){e&&e.apply(this,arguments)});if(n.attachmentsStore){var f=this._getFilesFromForm(c),g=f[0],i=new b,j=this._getNextTempId();return n.attachmentsStore.store(this.url,j,a,g,n.attachmentsStore.TYPE.ADD,function(b,c){var f={attachmentId:j,objectId:a,success:b};if(b){n.emit(n.events.ATTACHMENT_ENQUEUED,f),d&&d(f),i.resolve(f);var g=this._url.path+"/"+a+"/attachments/"+j,k=h("[href="+g+"]");k.attr("href",c.url)}else f.error="can't store attachment",e&&e(f),i.reject(f)}.bind(this)),i}},a.updateAttachment=function(a,c,d,e,f){if(n.getOnlineStatus()===n.ONLINE)return this._updateAttachment(a,c,d,function(){e&&e.apply(this,arguments)},function(a){f&&f.apply(this,arguments)});if(n.attachmentsStore){var g=this._getFilesFromForm(d),i=g[0],j=new b;return n.attachmentsStore.store(this.url,c,a,i,n.attachmentsStore.TYPE.UPDATE,function(b,d){var g={attachmentId:c,objectId:a,success:b};if(b){n.emit(n.events.ATTACHMENT_ENQUEUED,g),e&&e(g),j.resolve(g);var i=this._url.path+"/"+a+"/attachments/"+c,k=h("[href="+i+"]");k.attr("href",d.url)}else g.error="layer.updateAttachment::attachmentStore can't store attachment",f&&f(g),j.reject(g)}.bind(this)),j}},a.deleteAttachments=function(a,d,e,f){if(n.getOnlineStatus()===n.ONLINE){var g=this._deleteAttachments(a,d,function(){e&&e.apply(this,arguments)},function(a){f&&f.apply(this,arguments)});return g}if(n.attachmentsStore){var h=[];d.forEach(function(c){c=parseInt(c,10);var d=new b;if(0>c)n.attachmentsStore["delete"](c,function(b){var e={objectId:a,attachmentId:c,success:b};d.resolve(e)});else{var e=new Blob([],{type:"image/png"});n.attachmentsStore.store(this.url,c,a,e,n.attachmentsStore.TYPE.DELETE,function(b,e){var f={attachmentId:c,objectId:a,success:b};b?d.resolve(f):d.reject(f)}.bind(this))}h.push(d)},this);var i=c(h);return i.then(function(a){e&&e(a)}),i}},a.applyEdits=function(a,d,e,f,g){var h=[];if(n.getOnlineStatus()===n.ONLINE){var i=this._applyEdits(a,d,e,function(){n.emit(n.events.EDITS_SENT,arguments),f&&f.apply(this,arguments)},g);return i}var j=new b,k={addResults:[],updateResults:[],deleteResults:[]},l={};return a=a||[],a.forEach(function(a){var c=new b,d=this._getNextTempId();a.attributes[this.objectIdField]=d;var e=this;this._validateFeature(a,this.url,n._editStore.ADD).then(function(b){b.success?e._pushValidatedAddFeatureToDB(e,a,b.operation,k,d,c):c.resolve(!0)},function(a){c.reject(a)}),h.push(c)},this),d=d||[],d.forEach(function(a){var c=new b,d=a.attributes[this.objectIdField];l[d]=a;var e=this;this._validateFeature(a,this.url,n._editStore.UPDATE).then(function(b){b.success?e._pushValidatedUpdateFeatureToDB(e,a,b.operation,k,d,c):c.resolve(!0)},function(a){c.reject(a)}),h.push(c)},this),e=e||[],e.forEach(function(a){var c=new b,d=a.attributes[this.objectIdField],e=this;this._validateFeature(a,this.url,n._editStore.DELETE).then(function(b){b.success?e._pushValidatedDeleteFeatureToDB(e,a,b.operation,k,d,c):c.resolve(!0)},function(a){c.reject(a)}),h.push(c)},this),c(h).then(function(b){for(var c=!0,d=0;dg;g++){var h=a[g].toJson();if(f.push(h),g==e-1){var i=JSON.stringify(f),j=JSON.stringify(d);c(i,j);break}}},a.getFeatureLayerJSON=function(a,b){require(["esri/request"],function(c){var d=c({url:a,content:{f:"json"},handleAs:"json",callbackParamName:"callback"});d.then(function(a){b(!0,a)},function(a){b(!1,a.message)})})},a.setFeatureLayerJSONDataStore=function(a,b){n._editStore.pushFeatureLayerJSON(a,function(a,c){b(a,c)})},a.getFeatureLayerJSONDataStore=function(a){n._editStore.getFeatureLayerJSON(function(b,c){a(b,c)})},a.setPhantomLayerGraphics=function(a){var b=a.length;if(b>0)for(var c=0;b>c;c++){var d=new k(a[c]);this._phantomLayer.add(d)}},a.getPhantomLayerGraphics=function(b){for(var c=a._phantomLayer.graphics,d=a._phantomLayer.graphics.length,e=[],f=0;d>f;f++){var g=c[f].toJson();if(e.push(g),f==d-1){var h=JSON.stringify(e);b(h);break}}},a.getPhantomGraphicsArray=function(a){n._editStore.getPhantomGraphicsArray(function(b,c){"end"==c?a(!0,b):a(!1,c)})},a.getAttachmentsUsage=function(a){n.attachmentsStore.getUsage(function(b,c){a(b,c)})},a.resetAttachmentsDatabase=function(a){n.attachmentsStore.resetAttachmentsQueue(function(b,c){a(b,c)})},a.getUsage=function(a){n._editStore.getUsage(function(b,c){a(b,c)})},a.resetDatabase=function(a){n._editStore.resetEditsQueue(function(b,c){a(b,c)})},a.pendingEditsCount=function(a){n._editStore.pendingEditsCount(function(b){a(b)})},a.getFeatureDefinition=function(a,b,c,d){var e={layerDefinition:a,featureSet:{features:b,geometryType:c}};d(e)},a.getAllEditsArray=function(a){n._editStore.getAllEditsArray(function(b,c){"end"==c?a(!0,b):a(!1,c)})},a._pushValidatedDeleteFeatureToDB=function(a,b,c,d,e,h){n._editStore.pushEdit(c,a.url,b,function(c,i){if(c){d.deleteResults.push({success:!0,error:null,objectId:e});var j={};j[n.DB_UID]=e;var l=new k(b.geometry,n._getPhantomSymbol(b.geometry,n._editStore.DELETE),j);a._phantomLayer.add(l),n._editStore.pushPhantomGraphic(l,function(a){}),f.set(l.getNode(),"stroke-dasharray","4,4"),g.set(l.getNode(),"pointer-events","none"),n.attachmentsStore&&n.attachmentsStore.deleteAttachmentsByFeatureId(a.url,e,function(a){})}else d.deleteResults.push({success:!1,error:i,objectId:e});h.resolve(c)})},a._pushValidatedUpdateFeatureToDB=function(a,b,c,d,e,h){n._editStore.pushEdit(c,a.url,b,function(c,i){if(c){d.updateResults.push({success:!0,error:null,objectId:e});var j={};j[n.DB_UID]=e;var l=new k(b.geometry,n._getPhantomSymbol(b.geometry,n._editStore.UPDATE),j);a._phantomLayer.add(l),n._editStore.pushPhantomGraphic(l,function(a){}),f.set(l.getNode(),"stroke-dasharray","5,2"),g.set(l.getNode(),"pointer-events","none")}else d.updateResults.push({success:!1,error:i,objectId:e});h.resolve(c)})},a._pushValidatedAddFeatureToDB=function(a,b,c,d,e,h){n._editStore.pushEdit(c,a.url,b,function(c,i){if(c){d.addResults.push({success:!0,error:null,objectId:e});var j={};j[n.DB_UID]=e;var l=new k(b.geometry,n._getPhantomSymbol(b.geometry,n._editStore.ADD),j);a._phantomLayer.add(l),n._editStore.pushPhantomGraphic(l,function(a){}),f.set(l.getNode(),"stroke-dasharray","10,4"),g.set(l.getNode(),"pointer-events","none")}else d.addResults.push({success:!1,error:i,objectId:e});h.resolve(c)})},a._validateFeature=function(c,d,e){var f=new b,g=d+"/"+c.attributes.objectid;return n._editStore.getEdit(g,function(b,d){if(b)switch(e){case n._editStore.ADD:f.resolve({success:!0,graphic:c,operation:e});break;case n._editStore.UPDATE:d.operation==n._editStore.ADD&&(c.operation=n._editStore.ADD,e=n._editStore.ADD),f.resolve({success:!0,graphic:c,operation:e});break;case n._editStore.DELETE:var g=!0;d.operation==n._editStore.ADD&&a._deleteTemporaryFeature(c,function(a){a||(g=!1)}),f.resolve({success:g,graphic:c,operation:e})}else"Id not found"==d?f.resolve({success:!0,graphic:c,operation:e}):f.reject(c)}),f},a._deleteTemporaryFeature=function(d,e){function f(){var c=new b;return n._editStore["delete"](a.url,d,function(a,b){c.resolve(a?!0:!1)}),c.promise}function g(){var a=new b;return n._editStore.deletePhantomGraphic(h,function(b){a.resolve(b?!0:!1)}),a.promise}var h=n._editStore.PHANTOM_GRAPHIC_PREFIX+n._editStore._PHANTOM_PREFIX_TOKEN+d.attributes[n.DB_UID];c([f(),g()]).then(function(a){e(a)})},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},a._replaceFeatureIds=function(a,b,c){a.length||c(0);var d,e=a.length,f=e,g=0;for(d=0;e>d;d++)n.attachmentsStore.replaceFeatureId(this.url,a[d],b[d],function(a){--f,g+=a?1:0,0===f&&c(g)}.bind(this))},a._nextTempId=-1,a._getNextTempId=function(){return this._nextTempId--},l(),c(m).then(function(b){0===b.length?d(!0,null):b[0].success&&!o?this._editStore.getFeatureLayerJSON(function(b,c){b?(this._featureLayers[c.__featureLayerURL]=a,a.url=c.__featureLayerURL,d(!0,null)):d(!1,c)}.bind(this)):b[0].success&&d(!0,null)}.bind(this))},goOffline:function(){this._onlineStatus=this.OFFLINE},goOnline:function(a){this._onlineStatus=this.RECONNECTING,this._replayStoredEdits(function(b,c){var d={success:b,responses:c};this._onlineStatus=this.ONLINE,null!=this.attachmentsStore?this._sendStoredAttachments(function(b,c,e){this._onlineStatus=this.ONLINE,d.attachments={success:b,responses:c,dbResponses:e},a&&a(d)}.bind(this)):(this._onlineStatus=this.ONLINE,a&&a(d))}.bind(this))},getOnlineStatus:function(){return this._onlineStatus},serializeFeatureGraphicsArray:function(a,b){for(var c=a.length,d=[],e=0;c>e;e++){var f=a[e].toJson();if(d.push(f),e==c-1){var g=JSON.stringify(d);b(g);break}}},getFeatureLayerJSONDataStore:function(a){this._editStore._isDBInit?this._editStore.getFeatureLayerJSON(function(b,c){a(b,c)}):this._initializeDB(null,null).then(function(b){b.success&&this._editStore.getFeatureLayerJSON(function(b,c){a(b,c)})}.bind(this),function(b){a(!1,b)})},_initializeDB:function(a,c){var d=new b,e=this._editStore;return e.dbName=this.DB_NAME,e.objectStoreName=this.DB_OBJECTSTORE_NAME,e.objectId=this.DB_UID,e.init(function(b,f){"object"==typeof a&&b===!0&&void 0!==a&&null!==a?(c&&(a.__featureLayerURL=c),e.pushFeatureLayerJSON(a,function(a,b){a?d.resolve({success:!0,error:null}):d.reject({success:!1,error:b})})):b?d.resolve({success:!0,error:null}):d.reject({success:!1,error:null})}),d},_checkFileAPIs:function(){return window.File&&window.FileReader&&window.FileList&&window.Blob?(XMLHttpRequest.prototype.sendAsBinary||(XMLHttpRequest.prototype.sendAsBinary=function(a){function b(a){return 255&a.charCodeAt(0)}var c=Array.prototype.map.call(a,b),d=new Uint8Array(c);this.send(d.buffer)}),!0):!1},_extendAjaxReq:function(a){a.sendAsBinary=XMLHttpRequest.prototype.sendAsBinary},_phantomSymbols:[],_getPhantomSymbol:function(a,b){if(0===this._phantomSymbols.length){var c=[0,255,0,255],d=1.5;this._phantomSymbols.point=[],this._phantomSymbols.point[this._editStore.ADD]=new m({type:"esriSMS",style:"esriSMSCross",xoffset:10,yoffset:10,color:[255,255,255,0],size:15,outline:{color:c,width:d,type:"esriSLS",style:"esriSLSSolid"}}),this._phantomSymbols.point[this._editStore.UPDATE]=new m({type:"esriSMS",style:"esriSMSCircle",xoffset:0,yoffset:0,color:[255,255,255,0],size:15,outline:{color:c,width:d,type:"esriSLS",style:"esriSLSSolid"}}),this._phantomSymbols.point[this._editStore.DELETE]=new m({type:"esriSMS",style:"esriSMSX",xoffset:0,yoffset:0,color:[255,255,255,0],size:15,outline:{color:c,width:d,type:"esriSLS",style:"esriSLSSolid"}}),this._phantomSymbols.multipoint=null,this._phantomSymbols.polyline=[],this._phantomSymbols.polyline[this._editStore.ADD]=new n({type:"esriSLS",style:"esriSLSSolid",color:c,width:d}),this._phantomSymbols.polyline[this._editStore.UPDATE]=new n({type:"esriSLS",style:"esriSLSSolid",color:c,width:d}),this._phantomSymbols.polyline[this._editStore.DELETE]=new n({type:"esriSLS",style:"esriSLSSolid",color:c,width:d}),this._phantomSymbols.polygon=[],this._phantomSymbols.polygon[this._editStore.ADD]=new o({type:"esriSFS",style:"esriSFSSolid",color:[255,255,255,0],outline:{type:"esriSLS",style:"esriSLSSolid",color:c,width:d}}),this._phantomSymbols.polygon[this._editStore.UPDATE]=new o({type:"esriSFS",style:"esriSFSSolid",color:[255,255,255,0],outline:{type:"esriSLS",style:"esriSLSDash",color:c,width:d}}),this._phantomSymbols.polygon[this._editStore.DELETE]=new o({type:"esriSFS",style:"esriSFSSolid",color:[255,255,255,0],outline:{type:"esriSLS",style:"esriSLSDot",color:c,width:d}})}return this._phantomSymbols[a.type][b]},_fieldSegment:function(a,b){return'Content-Disposition: form-data; name="'+a+'"\r\n\r\n'+b+"\r\n"},_fileSegment:function(a,b,c,d){return'Content-Disposition: form-data; name="'+a+'"; filename="'+b+'"\r\nContent-Type: '+c+"\r\n\r\n"+d+"\r\n"},_uploadAttachment:function(a){var c=new b,d=this._featureLayers[a.featureLayerUrl],e=new FormData;switch(e.append("attachment",a.file),a.type){case this.attachmentsStore.TYPE.ADD:d.addAttachment(a.objectId,e,function(b){c.resolve({attachmentResult:b,id:a.id})},function(a){c.reject(a)});break;case this.attachmentsStore.TYPE.UPDATE:e.append("attachmentId",a.id),d._sendAttachment("update",a.objectId,e,function(b){c.resolve({attachmentResult:b,id:a.id})},function(a){c.reject(a)});break;case this.attachmentsStore.TYPE.DELETE:d.deleteAttachments(a.objectId,[a.id],function(b){c.resolve({attachmentResult:b,id:a.id})},function(a){c.reject(a)})}return c.promise},_deleteAttachmentFromDB:function(a,c){var d=new b;return this.attachmentsStore["delete"](a,function(a){d.resolve({success:a,result:c})}),d},_cleanAttachmentsDB:function(a,b){var d=this,e=[],f=0;a.forEach(function(a){"object"==typeof a.attachmentResult&&a.attachmentResult.success?e.push(d._deleteAttachmentFromDB(a.id,null)):a.attachmentResult instanceof Array?a.attachmentResult.forEach(function(b){b.success?e.push(d._deleteAttachmentFromDB(a.id,null)):f++}):f++});var g=c(e);g.then(function(c){b(f>0?{errors:!0,attachmentsDBResults:c,uploadResults:a}:{errors:!1,attachmentsDBResults:c,uploadResults:a})})},_sendStoredAttachments:function(a){this.attachmentsStore.getAllAttachments(function(b){var d=this,e=[];b.forEach(function(a){var b=this._uploadAttachment(a);e.push(b)},this);var f=c(e);f.then(function(b){d._cleanAttachmentsDB(b,function(c){c.errors?a&&a(!1,b,c):a&&a(!0,b,c)})},function(b){a&&a(!1,b)})}.bind(this))},_replayStoredEdits:function(a){var b,d={},e=this,f=[],g=[],h=[],i=[],j=[],l=this._featureLayers,m=this.attachmentsStore,n=this._editStore;this._editStore.getAllEditsArray(function(o,p){if(o.length>0){j=o;for(var q=j.length,r=0;q>r;r++){b=l[j[r].layer],null==m&&b.hasAttachments,b._attachmentsStore=m,b.__onEditsComplete=b.onEditsComplete,b.onEditsComplete=function(){},f=[],g=[],h=[],i=[];var s=new k(j[r].graphic);switch(j[r].operation){case n.ADD:for(var t=0;t0&&(g.updateResults[0].success?(h.layer=g.layer,h.id=g.updateResults[0].objectId,d.push(h)):e.push(g)),g.deleteResults.length>0&&(g.deleteResults[0].success?(h.layer=g.layer,h.id=g.deleteResults[0].objectId,d.push(h)):e.push(g)),g.addResults.length>0&&(g.addResults[0].success?(h.layer=g.layer,h.id=g.tempId,d.push(h)):e.push(g))}for(var i={},j=d.length,k=0;j>k;k++)i[k]=this._updateDatabase(d[k]);var l=c(i);l.then(function(a){e.length>0?b(!1,a):b(!0,a)},function(a){b(!1,a)})}else b(!0,{})},_updateDatabase:function(a){var c=new b,d={};return d.attributes={},d.attributes[this.DB_UID]=a.id,this._editStore["delete"](a.layer,d,function(a,b){a?c.resolve({success:!0,error:null}):c.reject({success:!1,error:b})}.bind(this)),c.promise},getFeatureLayerJSON:function(a,b){require(["esri/request"],function(c){var d=c({url:a,content:{f:"json"},handleAs:"json",callbackParamName:"callback"});d.then(function(a){b(!0,a)},function(a){b(!1,a.message)})})},_internalApplyEdits:function(a,c,d,e,f,g){var h=new b;return a._applyEdits(e,f,g,function(b,e,f){a._phantomLayer.clear();var g=b.map(function(a){return a.objectId});null!=a._attachmentsStore&&a.hasAttachments&&d.length>0?a._replaceFeatureIds(d,g,function(g){h.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:e,deleteResults:f})}):h.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:e,deleteResults:f})},function(b){a.onEditsComplete=a.__onEditsComplete,delete a.__onEditsComplete,h.reject(b)}),h.promise},_internalApplyEditsFeatureCollection:function(a,c,d,e,f,g){var h=new b;return this._makeEditRequest(a.url,e,f,g,function(b,e,f){a._phantomLayer.clear();var g=b.map(function(a){return a.objectId});null!=a._attachmentsStore&&a.hasAttachments&&d.length>0?a._replaceFeatureIds(d,g,function(g){h.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:e,deleteResults:f})}):h.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:e,deleteResults:f})},function(b){a.onEditsComplete=a.__onEditsComplete,delete a.__onEditsComplete,h.reject(b)}),h.promise},_makeEditRequest:function(a,b,c,d,e,f){var g=new FormData;g.append("f","json"),b.length>0&&g.append("adds",JSON.stringify(b)),c.length>0&&g.append("updates",JSON.stringify(c)),d.length>0&&g.append("deletes",JSON.stringify(d));var h=new XMLHttpRequest;h.open("POST",a+"/applyEdits",!0),h.onload=function(){if(200===h.status&&""!==h.responseText){var a=JSON.parse(this.response);e(a.addResults,a.updateResults,a.deleteResults)}},h.onerror=function(a){f(a)},h.send(g)},_optimizeEditsQueue:function(){return"DEPRECATED at v2.5!"},getReadableEdit:function(a){return"DEPRECATED at v2.5!"}})}),"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";var a="featureId";this.ADD="add",this.UPDATE="update",this.DELETE="delete",this.FEATURE_LAYER_JSON_ID="feature-layer-object-1001",this.PHANTOM_GRAPHIC_PREFIX="phantom-layer",this._PHANTOM_PREFIX_TOKEN="|@|",this.isSupported=function(){return window.indexedDB?!0:!1},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()};if("undefined"==typeof c.attributes[this.objectId])d(!1,"editsStore.pushEdit() - failed to insert undefined objectId into database. Did you set offlineFeaturesManager.DB_UID? "+JSON.stringify(c.attributes));else{var f=this._db.transaction([this.objectStoreName],"readwrite");f.oncomplete=function(a){d(!0)},f.onerror=function(a){d(!1,a.target.error.message)};var g=f.objectStore(this.objectStoreName);g.put(e)}},this.pushFeatureLayerJSON=function(a,b){"object"!=typeof a&&b(!1,"dataObject type is not an object.");var c=this._db;a.id=this.FEATURE_LAYER_JSON_ID,this.getFeatureLayerJSON(function(d,e){var f;if(d&&"undefined"!=typeof e){f=c.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName);for(var g in a)a.hasOwnProperty(g)&&(e[g]=a[g]);var h=f.put(e);h.onsuccess=function(){b(!0,null)},h.onerror=function(a){b(!1,a)}}else{var i=c.transaction([this.objectStoreName],"readwrite");i.oncomplete=function(a){b(!0,null)},i.onerror=function(a){b(!1,a.target.error.message)},f=i.objectStore(this.objectStoreName);try{f.put(a)}catch(j){b(!1,JSON.stringify(j))}}}.bind(this))},this.getFeatureLayerJSON=function(a){var b=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),c=b.get(this.FEATURE_LAYER_JSON_ID);c.onsuccess=function(){var b=c.result;"undefined"!=typeof b?a(!0,b):a(!1,"nothing found")},c.onerror=function(b){a(!1,b)}},this.deleteFeatureLayerJSON=function(a){var b=this._db,c=null,d=this,e=this.FEATURE_LAYER_JSON_ID;require(["dojo/Deferred"],function(f){c=new f,c.then(function(b){d.editExists(e).then(function(b){b.success===!1?a(!0,{message:"id does not exist"}):a(!1,{message:null})},function(b){a(!0,{message:"id does not exist"})})},function(b){a(!1,{message:"id does not exist"})}),d.editExists(e).then(function(a){if(a&&a.success){var f=b.transaction([d.objectStoreName],"readwrite").objectStore(d.objectStoreName),g=f["delete"](e);g.onsuccess=function(){c.resolve(!0)},g.onerror=function(a){c.reject({success:!1,error:a})}}else c.reject({success:!1,message:"id does not exist"})},function(a){c.reject({success:!1,message:a})}.bind(this))})},this.pushPhantomGraphic=function(a,b){var c=this._db,d=this.PHANTOM_GRAPHIC_PREFIX+this._PHANTOM_PREFIX_TOKEN+a.attributes[this.objectId],e={id:d,graphic:a.toJson()},f=c.transaction([this.objectStoreName],"readwrite");f.oncomplete=function(a){b(!0,null)},f.onerror=function(a){b(!1,a.target.error.message)};var g=f.objectStore(this.objectStoreName);g.put(e)},this.getPhantomGraphicsArray=function(a){var b=[];if(null!==this._db){var c=this.PHANTOM_GRAPHIC_PREFIX,d=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();d.onsuccess=function(d){var e=d.target.result;e&&e.value&&e.value.id?(-1!=e.value.id.indexOf(c)&&b.push(e.value),e["continue"]()):a(b,"end")}.bind(this),d.onerror=function(b){a(null,b)}}else a(null,"no db")},this._getPhantomGraphicsArraySimple=function(a){var b=[];if(null!==this._db){var c=this.PHANTOM_GRAPHIC_PREFIX,d=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();d.onsuccess=function(d){var e=d.target.result;e&&e.value&&e.value.id?(-1!=e.value.id.indexOf(c)&&b.push(e.value.id),e["continue"]()):a(b,"end")}.bind(this),d.onerror=function(b){a(null,b)}}else a(null,"no db")},this.deletePhantomGraphic=function(a,b){var c=this._db,d=null,e=this;require(["dojo/Deferred"],function(f){d=new f,e.editExists(a).then(function(f){if(f.success){d.then(function(c){e.editExists(a).then(function(a){b(a.success===!1?!0:!1)},function(a){b(!0)})},function(a){b(!1,a)});var g=c.transaction([e.objectStoreName],"readwrite").objectStore(e.objectStoreName),h=g["delete"](a);h.onsuccess=function(){d.resolve(!0)},h.onerror=function(a){d.reject({success:!1,error:a})}}},function(a){b(!1)})})},this.resetLimitedPhantomGraphicsQueue=function(a,b){if(Object.keys(a).length>0){var c=this._db,d=0,e=c.transaction([this.objectStoreName],"readwrite"),f=e.objectStore(this.objectStoreName);f.onerror=function(){d++},e.oncomplete=function(){b(0===d?!0:!1)};for(var g in a)if(a.hasOwnProperty(g)){var h=a[g],i=this.PHANTOM_GRAPHIC_PREFIX+this._PHANTOM_PREFIX_TOKEN+h.id;h.updateResults.length>0&&h.updateResults[0].success&&f["delete"](i),h.deleteResults.length>0&&h.deleteResults[0].success&&f["delete"](i),h.addResults.length>0&&h.addResults[0].success&&f["delete"](i)}}else b(!0)},this.resetPhantomGraphicsQueue=function(a){var b=this._db;this._getPhantomGraphicsArraySimple(function(c){if(c!=[]){var d=0,e=b.transaction([this.objectStoreName],"readwrite"),f=e.objectStore(this.objectStoreName);f.onerror=function(){d++},e.oncomplete=function(){a(0===d?!0:!1)};for(var g=c.length,h=0;g>h;h++)f["delete"](c[h])}else a(!0)}.bind(this))},this.getEdit=function(a,b){var c=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName);require(["dojo/Deferred"],function(d){if("undefined"==typeof a)return void b(!1,"id is undefined.");var e=c.get(a);e.onsuccess=function(){var c=e.result;c&&c.id==a?b(!0,c):b(!1,"Id not found")},e.onerror=function(a){b(!1,a)}})},this.getAllEdits=function(a){if(null!==this._db){var b=this.FEATURE_LAYER_JSON_ID,c=this.PHANTOM_GRAPHIC_PREFIX,d=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();d.onsuccess=function(d){var e=d.target.result;e&&e.hasOwnProperty("value")&&e.value.hasOwnProperty("id")?(e.value.id!==b&&-1==e.value.id.indexOf(c)&&a(e.value,null),e["continue"]()):a(null,"end")}.bind(this),d.onerror=function(b){a(null,b)}}else a(null,"no db")},this.getAllEditsArray=function(a){var b=[];if(null!==this._db){var c=this.FEATURE_LAYER_JSON_ID,d=this.PHANTOM_GRAPHIC_PREFIX,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&&-1==f.value.id.indexOf(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.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];require(["dojo/Deferred"],function(a){e=new a,f.editExists(g).then(function(a){if(a.success){e.then(function(a){f.editExists(g).then(function(a){c(a.success===!1?!0:!1)},function(a){c(!0)})},function(a){c(!1,a)});var b=d.transaction([f.objectStoreName],"readwrite").objectStore(f.objectStoreName),h=b["delete"](g);h.onsuccess=function(){e.resolve(!0)},h.onerror=function(a){e.reject({success:!1,error:a})}}},function(a){c(!1)})})},this.resetEditsQueue=function(a){var b=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName).clear();b.onsuccess=function(b){setTimeout(function(){a(!0)},0)},b.onerror=function(b){a(!1,b)}},this.pendingEditsCount=function(a){var b=0,c=this.FEATURE_LAYER_JSON_ID,d=this.PHANTOM_GRAPHIC_PREFIX,e=this._db.transaction([this.objectStoreName],"readwrite"),f=e.objectStore(this.objectStoreName);f.openCursor().onsuccess=function(e){var f=e.target.result;f&&f.value&&f.value.id&&-1==f.value.id.indexOf(d)?(f.value.id!==c&&b++,f["continue"]()):a(b)}},this.editExists=function(a){var b=this._db,c=null,d=this;return require(["dojo/Deferred"],function(e){c=new e;var f=b.transaction([d.objectStoreName],"readwrite").objectStore(d.objectStoreName),g=f.get(a);g.onsuccess=function(){var b=g.result;b&&b.id==a?c.resolve({success:!0,error:null}):c.reject({success:!1,error:"Layer id is not a match."})},g.onerror=function(a){c.reject({success:!1,error:a})}}),c},this.getUsage=function(a){var b=this.FEATURE_LAYER_JSON_ID,c=this.PHANTOM_GRAPHIC_PREFIX,d={sizeBytes:0,editCount:0},e=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();e.onsuccess=function(e){var f=e.target.result;if(f&&f.value&&f.value.id){var g=f.value,h=JSON.stringify(g);d.sizeBytes+=h.length,-1==f.value.id.indexOf(c)&&f.value.id!==b&&(d.editCount+=1),f["continue"]()}else a(d,null)},e.onerror=function(b){a(null,b)}},this._serialize=function(a){var b=a.toJson(),c={attributes:b.attributes,geometry:b.geometry,infoTemplate:b.infoTemplate,symbol:b.symbol};return JSON.stringify(c)},this._deserialize=function(a){var b;return require(["esri/graphic"],function(c){b=new c(JSON.parse(a))}),b},this.init=function(b){var c=indexedDB.open(this.dbName,11);b=b||function(a){}.bind(this),c.onerror=function(a){b(!1,a.target.errorCode)}.bind(this),c.onupgradeneeded=function(b){var c=b.target.result;c.objectStoreNames.contains(this.objectStoreName)&&c.deleteObjectStore(this.objectStoreName);var d=c.createObjectStore(this.objectStoreName,{keyPath:"id"});d.createIndex(a,a,{unique:!1})}.bind(this),c.onsuccess=function(a){this._db=a.target.result,this._isDBInit=!0,b(!0,null)}.bind(this)},this.hasPendingEdits=function(){return"DEPRECATED at v2.5!"},this._isEditDuplicated=function(a,b){return"DEPRECATED at v2.5!"},this._storeEditsQueue=function(a){return"DEPRECATED at v2.5!"},this._unpackArrayOfEdits=function(a){return"DEPRECATED at v2.5!"},this.getLocalStorageSizeBytes=function(){return"DEPRECATED at v2.5!"},this.peekFirstEdit=function(){return"DEPRECATED at v2.5!"},this.popFirstEdit=function(){return"DEPRECATED at v2.5!"}},O.esri.Edit.AttachmentsStore=function(){"use strict";this._db=null,this.dbName="attachments_store",this.objectStoreName="attachments",this.TYPE={ADD:"add",UPDATE:"update",DELETE:"delete"},this.isSupported=function(){return window.indexedDB?!0:!1},this.store=function(a,b,c,d,e,f){try{e==this.TYPE.ADD||e==this.TYPE.UPDATE||e==this.TYPE.DELETE?this._readFile(d,function(g,h){if(g){var i={id:b,objectId:c,type:e,featureId:a+"/"+c,contentType:d.type,name:d.name,size:d.size,featureLayerUrl:a,content:h,file:d},j=this._db.transaction([this.objectStoreName],"readwrite");j.oncomplete=function(a){f(!0,i)},j.onerror=function(a){f(!1,a.target.error.message)};var k=j.objectStore(this.objectStoreName),l=k.put(i);l.onsuccess=function(a){}}else f(!1,h)}.bind(this)):f(!1,"attachmentsStore.store() Invalid type in the constructor!")}catch(g){f(!1,g.stack)}},this.retrieve=function(a,b){var c=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName),d=c.get(a);d.onsuccess=function(a){var c=a.target.result;c?b(!0,c):b(!1,"not found")},d.onerror=function(a){b(!1,a)}},this.getAttachmentsByFeatureId=function(a,b,c){var d=a+"/"+b,e=[],f=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName),g=f.index("featureId"),h=IDBKeyRange.only(d); -g.openCursor(h).onsuccess=function(b){var e=b.target.result;if(e){var g=a+"/"+c,h=e.value;h.featureId=g,h.objectId=c,f.put(h),i++,e["continue"]()}else setTimeout(function(){d(i)},1)}},this.getUsage=function(a){var b={sizeBytes:0,attachmentCount:0},c=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();c.onsuccess=function(c){var d=c.target.result;if(d){var e=d.value,f=JSON.stringify(e);b.sizeBytes+=f.length,b.attachmentCount+=1,d["continue"]()}else a(b,null)}.bind(this),c.onerror=function(b){a(null,b)}},this.resetAttachmentsQueue=function(a){var b=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName).clear();b.onsuccess=function(b){setTimeout(function(){a(!0)},0)},b.onerror=function(b){a(!1,b)}},this._readFile=function(a,b){var c=new FileReader;c.onload=function(a){b(!0,a.target.result)},c.onerror=function(a){b(!1,a.target.result)},c.readAsBinaryString(a)},this.init=function(a){var b=indexedDB.open(this.dbName,12);a=a||function(a){}.bind(this),b.onerror=function(b){a(!1,b.target.errorCode)}.bind(this),b.onupgradeneeded=function(a){var b=a.target.result;b.objectStoreNames.contains(this.objectStoreName)&&b.deleteObjectStore(this.objectStoreName);var c=b.createObjectStore(this.objectStoreName,{keyPath:"id"});c.createIndex("featureId","featureId",{unique:!1}),c.createIndex("featureLayerUrl","featureLayerUrl",{unique:!1})}.bind(this),b.onsuccess=function(b){this._db=b.target.result,a(!0)}.bind(this)}}; \ No newline at end of file +g.openCursor(h).onsuccess=function(a){var b=a.target.result;b?(e.push(b.value),b["continue"]()):c(e)}},this.getAttachmentsByFeatureLayer=function(a,b){var c=[],d=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName),e=d.index("featureLayerUrl"),f=IDBKeyRange.only(a);e.openCursor(f).onsuccess=function(a){var d=a.target.result;d?(c.push(d.value),d["continue"]()):b(c)}},this.getAllAttachments=function(a){var b=[],c=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName);c.openCursor().onsuccess=function(c){var d=c.target.result;d?(b.push(d.value),d["continue"]()):a(b)}},this.deleteAttachmentsByFeatureId=function(a,b,c){var d=a+"/"+b,e=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),f=e.index("featureId"),g=IDBKeyRange.only(d),h=0;f.openCursor(g).onsuccess=function(a){var b=a.target.result;b?(e["delete"](b.primaryKey),h++,b["continue"]()):setTimeout(function(){c(h)},0)}.bind(this)},this["delete"]=function(a,b){this.retrieve(a,function(c,d){if(!c)return void b(!1,"attachment "+a+" not found");var e=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName)["delete"](a);e.onsuccess=function(a){setTimeout(function(){b(!0)},0)},e.onerror=function(a){b(!1,a)}}.bind(this))},this.deleteAll=function(a){this.getAllAttachments(function(b){var c=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName).clear();c.onsuccess=function(b){setTimeout(function(){a(!0)},0)},c.onerror=function(b){a(!1,b)}}.bind(this))},this.replaceFeatureId=function(a,b,c,d){var e=a+"/"+b,f=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),g=f.index("featureId"),h=IDBKeyRange.only(e),i=0;g.openCursor(h).onsuccess=function(b){var e=b.target.result;if(e){var g=a+"/"+c,h=e.value;h.featureId=g,h.objectId=c,f.put(h),i++,e["continue"]()}else setTimeout(function(){d(i)},1)}},this.getUsage=function(a){var b={sizeBytes:0,attachmentCount:0},c=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor();c.onsuccess=function(c){var d=c.target.result;if(d){var e=d.value,f=JSON.stringify(e);b.sizeBytes+=f.length,b.attachmentCount+=1,d["continue"]()}else a(b,null)}.bind(this),c.onerror=function(b){a(null,b)}},this.resetAttachmentsQueue=function(a){var b=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName).clear();b.onsuccess=function(b){setTimeout(function(){a(!0)},0)},b.onerror=function(b){a(!1,b)}},this._readFile=function(a,b){var c=new FileReader;c.onload=function(a){b(!0,a.target.result)},c.onerror=function(a){b(!1,a.target.result)},c.readAsBinaryString(a)},this.init=function(a){var b=indexedDB.open(this.dbName,12);a=a||function(a){}.bind(this),b.onerror=function(b){a(!1,b.target.errorCode)}.bind(this),b.onupgradeneeded=function(a){var b=a.target.result;b.objectStoreNames.contains(this.objectStoreName)&&b.deleteObjectStore(this.objectStoreName);var c=b.createObjectStore(this.objectStoreName,{keyPath:"id"});c.createIndex("featureId","featureId",{unique:!1}),c.createIndex("featureLayerUrl","featureLayerUrl",{unique:!1})}.bind(this),b.onsuccess=function(b){this._db=b.target.result,a(!0)}.bind(this)}}; \ No newline at end of file diff --git a/dist/offline-edit-src.js b/dist/offline-edit-src.js index a2b6295e..055628b7 100644 --- a/dist/offline-edit-src.js +++ b/dist/offline-edit-src.js @@ -1,4 +1,4 @@ -/*! offline-editor-js - v2.7.1 - 2015-04-29 +/*! offline-editor-js - v2.8 - 2015-05-05 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ /*jshint -W030 */ @@ -14,17 +14,19 @@ define([ "esri/config", "esri/layers/GraphicsLayer", "esri/graphic", + "esri/request", "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/urlUtils"], function (Evented, Deferred, all, declare, array, domAttr, domStyle, query, - esriConfig, GraphicsLayer, Graphic, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, urlUtils) { + esriConfig, GraphicsLayer, Graphic, esriRequest, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, urlUtils) { "use strict"; return declare("O.esri.Edit.OfflineFeaturesManager", [Evented], { _onlineStatus: "online", _featureLayers: {}, + _featureCollectionUsageFlag: false, // if a feature collection was used to create the feature layer. _editStore: new O.esri.Edit.EditStore(), ONLINE: "online", // all edits will directly go to the server @@ -40,7 +42,7 @@ define([ ATTACHMENTS_DB_NAME: "attachments_store", //Sets attachments database name ATTACHMENTS_DB_OBJECTSTORE_NAME: "attachments", - // NOTE: attachments don't have the same issues as Graphics as related to UIDs. + // NOTE: attachments don't have the same issues as Graphics as related to UIDs (e.g. the need for DB_UID). // You can manually create a graphic, but it would be very rare for someone to // manually create an attachment. So, we don't provide a public property for // the attachments database UID. @@ -53,7 +55,8 @@ define([ EDITS_SENT_ERROR: "edits-sent-error", // ...there was a problem with one or more edits! ALL_EDITS_SENT: "all-edits-sent", // ...after going online and there are no pending edits in the queue ATTACHMENT_ENQUEUED: "attachment-enqueued", - ATTACHMENTS_SENT: "attachments-sent" + ATTACHMENTS_SENT: "attachments-sent", + EXTEND_COMPLETE: "extend-complete" // ...when the libary has completed its initialization }, /** @@ -101,23 +104,44 @@ define([ * @returns deferred */ extend: function (layer, callback, dataStore) { + + var extendPromises = []; // deferred promises related to initializing this method + var self = this; + layer.offlineExtended = true; // to identify layer has been extended // NOTE: At v2.6.1 we've discovered that not all feature layers support objectIdField. - // However, we want to try to be consistent here with how the library is managing Ids. - // So, we force the layer.objectIdField to DB_UID. This should be consistent with + // However, to try to be consistent here with how the library is managing Ids + // we force the layer.objectIdField to DB_UID. This should be consistent with // how esri.Graphics assign a unique ID to a graphic. If it is not, then this - // library will break and we'll have to re-architect how we manage UIDs. + // library will break and we'll have to re-architect how it manages UIDs. layer.objectIdField = this.DB_UID; + var url = null; + + // There have been reproducible use cases showing when a browser is restarted offline that + // for some reason the layer.url may be undefined. + // This is an attempt to minimize the possibility of that situation causing errors. + if(layer.url) { + url = layer.url; + // we keep track of the FeatureLayer object + this._featureLayers[layer.url] = layer; + } + + // This is a potentially brittle solution to detecting if a feature layer collection + // was used to create the feature layer. + // Is there a better way?? + if(layer._mode.featureLayer.hasOwnProperty("_collection")){ + // This means a feature collection was used to create the feature layer and it will + // require different handling when running applyEdit() + this._featureCollectionUsageFlag = true; + } + // Initialize the database as well as set offline data. if(!this._editStore._isDBInit) { - this._initializeDB(dataStore,callback); + extendPromises.push(this._initializeDB(dataStore, url)); } - // we keep track of the FeatureLayer object - this._featureLayers[layer.url] = layer; - // replace the applyEdits() method layer._applyEdits = layer.applyEdits; @@ -135,7 +159,7 @@ define([ 3. remove an attachment that is already in the server... (DONE) 4. remove an attachment that is not in the server yet (DONE) 5. update an existing attachment to an existing feature (DONE) - 6. update a new attachment (NOT YET) + 6. update a new attachment (DONE) concerns: - manage the relationship between offline features and attachments: what if the user wants to add @@ -143,9 +167,6 @@ define([ the feature is sent to the server and receives a final objectid we replace the temporary negative id by its final objectid (DONE) - what if the user deletes an offline feature that had offline attachments? we need to discard the attachment (DONE) - - pending tasks: - - check for hasAttachments attribute in the FeatureLayer (NOT YET) */ // @@ -284,7 +305,7 @@ define([ } if (!self.attachmentsStore) { - console.log("in order to support attachments you need to call initAttachments() method of offlineFeaturesManager"); + console.error("in order to support attachments you need to call initAttachments() method of offlineFeaturesManager"); return; } @@ -468,8 +489,7 @@ define([ all(promises).then(function (r) { // Make sure all edits were successful. If not throw an error. var success = true; - var length = r.length; - for (var v = 0; v < length; v++) { + for (var v = 0; v < r.length; v++) { if (r[v] === false) { success = false; } @@ -723,7 +743,7 @@ define([ /* internal methods */ /** - * Pushes an DELETE request to the database after it's been validated + * Pushes a DELETE request to the database after it's been validated * @param layer * @param deleteEdit * @param operation @@ -1035,6 +1055,33 @@ define([ _initPhantomLayer(); + // We are currently only passing in a single deferred. + all(extendPromises).then(function (r) { + if(r.length === 0){ + callback(true, null); + } + else if(r[0].success && !url){ + + // This functionality is specifically for offline restarts + // and attempts to retrieve a feature layer url. + // It's a hack because layer.toJson() doesn't convert layer.url. + this._editStore.getFeatureLayerJSON(function(success,message){ + if(success) { + this._featureLayers[message.__featureLayerURL] = layer; + layer.url = message.__featureLayerURL; + callback(true, null); + } + else { + console.error("getFeatureLayerJSON() failed."); + callback(false, message); + } + }.bind(this)); + } + else if(r[0].success){ + callback(true, null); + } + }.bind(this)); + }, // extend /** @@ -1054,7 +1101,7 @@ define([ console.log("offlineFeaturesManager going online"); this._onlineStatus = this.RECONNECTING; this._replayStoredEdits(function (success, responses) { - var result = {features: {success: success, responses: responses}}; + var result = {success: success, responses: responses}; this._onlineStatus = this.ONLINE; if (this.attachmentsStore != null) { console.log("sending attachments"); @@ -1105,13 +1152,16 @@ define([ */ getFeatureLayerJSONDataStore: function(callback){ if(!this._editStore._isDBInit){ - this._initializeDB(null,function(success) { - if(success){ + + this._initializeDB(null,null).then(function(result){ + if(result.success){ this._editStore.getFeatureLayerJSON(function(success,message){ callback(success,message); }); } - }.bind(this)); + }.bind(this), function(err){ + callback(false, err); + }); } else { this._editStore.getFeatureLayerJSON(function(success,message){ @@ -1123,12 +1173,16 @@ define([ /* internal methods */ /** - * Intialize the database and push featureLayer JSON to DB if required + * Initialize the database and push featureLayer JSON to DB if required. + * NOTE: also stores feature layer url in hidden dataStore property dataStore.__featureLayerURL. * @param dataStore Object + * @param url Feature Layer's url. This is used by this library for internal feature identification. * @param callback * @private */ - _initializeDB: function(dataStore,callback){ + //_initializeDB: function(dataStore,url,callback){ + _initializeDB: function(dataStore,url){ + var deferred = new Deferred(); var editStore = this._editStore; @@ -1153,22 +1207,32 @@ define([ //////////////////////////////////////////////////// if (typeof dataStore === "object" && result === true && (dataStore !== undefined) && (dataStore !== null)) { + + // Add a hidden property to hold the feature layer's url + // When converting a feature layer to json (layer.toJson()) we lose this information. + // This library needs to know the feature layer url. + if(url) { + dataStore.__featureLayerURL = url; + } + editStore.pushFeatureLayerJSON(dataStore, function (success, err) { if (success) { - callback(true, null); + deferred.resolve({success:true, error: null}); } else { - callback(false, err); + deferred.reject({success:false, error: err}); } }); } else if(result){ - callback(true, null); + deferred.resolve({success:true, error: null}); } else{ - callback(false, error); + deferred.reject({success:false, error: null}); } }); + + return deferred; }, /** @@ -1416,23 +1480,7 @@ define([ attachments.forEach(function (attachment) { console.log("sending attachment", attachment.id, "to feature", attachment.featureId); - var uploadAttachmentComplete = - this._uploadAttachment(attachment); - //.then(function (uploadResult) { - // if (uploadResult.addAttachmentResult && uploadResult.addAttachmentResult.success === true) { - // console.log("upload success", uploadResult.addAttachmentResult.success); - // return this._deleteAttachment(attachment.id, uploadResult); - // } - // else { - // console.log("upload failed", uploadResult); - // return null; - // } - //}.bind(this), - //function (err) { - // console.log("failed uploading attachment", attachment); - // return null; - //} - //); + var uploadAttachmentComplete = this._uploadAttachment(attachment); promises.push(uploadAttachmentComplete); }, this); console.log("promises", promises.length); @@ -1447,20 +1495,6 @@ define([ callback && callback(true, uploadResults,dbResults); } }); - //results.forEach(function(value){ - // if(value.attachmentResult.success){ - // // Delete an attachment from the database if it was successfully - // // submitted to the server. - // self._deleteAttachmentFromDB(value.id,null).then(function(result){ - // if(result.success){ - // callback && callback(true, results); - // } - // else{ - // callback && callback(false, results); - // } - // }); - // } - //}); }, function (err) { console.log("error!", err); @@ -1508,10 +1542,6 @@ define([ if (attachmentsStore == null && layer.hasAttachments) { console.log("NOTICE: you may need to run OfflineFeaturesManager.initAttachments(). Check the Attachments doc for more info. Layer id: " + layer.id + " accepts attachments"); } - else if(layer.hasAttachments === false){ - console.error("WARNING: Layer " + layer.id + "doesn't seem to accept attachments. Recheck the layer permissions."); - callback(false,"WARNING: Attachments not supported in layer: " + layer.id); - } // Assign the attachmentsStore to the layer as a private var so we can access it from // the promises applyEdits() method. @@ -1550,7 +1580,13 @@ define([ break; } - promises[n] = that._internalApplyEdits(layer, tempArray[n].id, tempObjectIds, adds, updates, deletes); + if(that._featureCollectionUsageFlag){ + // Note: when the feature layer is created with a feature collection we have to handle applyEdits() differently + promises[n] = that._internalApplyEditsFeatureCollection(layer, tempArray[n].id, tempObjectIds, adds, updates, deletes); + } + else { + promises[n] = that._internalApplyEdits(layer, tempArray[n].id, tempObjectIds, adds, updates, deletes); + } } // wait for all requests to finish @@ -1735,7 +1771,7 @@ define([ }, /** - * Executes the _applyEdits() method + * Executes the _applyEdits() method when a feature layer is created using a REST endpoint * @param layer * @param id the unique id that identifies the Graphic in the database * @param tempObjectIds @@ -1790,6 +1826,126 @@ define([ return dfd.promise; }, + /** + * Executes the _applyEdits() method when a feature layer is created using a feature collection. + * This works around specific behaviors in esri.layers.FeatureLayer when using the pattern + * new FeatureLayer(featureCollectionObject). + * + * Details on the specific behaviors can be found here: + * https://developers.arcgis.com/javascript/jsapi/featurelayer-amd.html#featurelayer2 + * + * @param layer + * @param id + * @param tempObjectIds + * @param adds + * @param updates + * @param deletes + * @returns {*|r} + * @private + */ + _internalApplyEditsFeatureCollection: function (layer, id, tempObjectIds, adds, updates, deletes) { + var dfd = new Deferred(); + + this._makeEditRequest(layer.url, adds, updates, deletes, + function (addResults, updateResults, deleteResults) { + layer._phantomLayer.clear(); + + var newObjectIds = addResults.map(function (r) { + return r.objectId; + }); + + // We use a different pattern if the attachmentsStore is valid and the layer has attachments + if (layer._attachmentsStore != null && layer.hasAttachments && tempObjectIds.length > 0) { + layer._replaceFeatureIds(tempObjectIds, newObjectIds, function (success) { + dfd.resolve({ + id: id, + layer: layer.url, + tempId: tempObjectIds, // let's us internally match an ADD to it's new ObjectId + addResults: addResults, + updateResults: updateResults, + deleteResults: deleteResults + }); // wrap three arguments in a single object + }); + } + else { + dfd.resolve({ + id: id, + layer: layer.url, + tempId: tempObjectIds, // let's us internally match an ADD to it's new ObjectId + addResults: addResults, + updateResults: updateResults, + deleteResults: deleteResults + }); // wrap three arguments in a single object + } + }, + function (error) { + layer.onEditsComplete = layer.__onEditsComplete; + delete layer.__onEditsComplete; + + dfd.reject(error); + } + ); + return dfd.promise; + }, + + /** + * Used when a feature layer is created with a feature collection. + * + * In the current version of the ArcGIS JSAPI 3.12+ the applyEdit() method doesn't send requests + * to the server when a feature layer is created with a feature collection. + * + * The use case for using this is: clean start app > go offline and make edits > offline restart browser > + * go online. + * + * @param url + * @param adds + * @param updates + * @param deletes + * @returns {*|r} + * @private + */ + _makeEditRequest: function(url,adds, updates, deletes, callback, errback) { + + //var dfd = new Deferred(); + + var data = new FormData(); + data.append("f", "json"); + if(adds.length > 0) { + data.append("adds", JSON.stringify(adds)); + } + if(updates.length > 0) { + data.append("updates", JSON.stringify(updates)); + } + if(deletes.length > 0) { + data.append("deletes", JSON.stringify(deletes)); + } + + var req = new XMLHttpRequest(); + req.open("POST", url + "/applyEdits", true); + req.onload = function() + { + if( req.status === 200 && req.responseText !== "") + { + var obj = JSON.parse(this.response); + //dfd.resolve(obj); + callback(obj.addResults, obj.updateResults, obj.deleteResults); + //callback(this.response); + //Object.keys(this.response).forEach(function(key) { + // console.log(key, this.response[key]); + //}); + } + }; + req.onerror = function(e) + { + console.log("_getTileInfoPrivate failed: " + e); + errback(e); + //dfd.error(e); + }; + req.send(data); + + //return dfd.promise; + }, + /** * Deprecated @ v2.5. Internal-use only * @returns {string} @@ -2784,7 +2940,7 @@ O.esri.Edit.EditStore = function () { this._db = event.target.result; this._isDBInit = true; console.log("database opened successfully"); - callback(true); + callback(true, null); }.bind(this); }; diff --git a/dist/offline-tiles-advanced-min.js b/dist/offline-tiles-advanced-min.js index ee08a7fd..455d7ec8 100644 --- a/dist/offline-tiles-advanced-min.js +++ b/dist/offline-tiles-advanced-min.js @@ -1,4 +1,4 @@ -/*! offline-editor-js - v2.7.1 - 2015-04-29 +/*! offline-editor-js - v2.8 - 2015-05-05 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ define(["dojo/query","dojo/request","dojo/_base/declare","esri/layers/LOD","esri/geometry/Point","esri/geometry/Extent","esri/layers/TileInfo","esri/SpatialReference","esri/geometry/Polygon","esri/layers/TiledMapServiceLayer"],function(a,b,c,d,e,f,g,h,i,j){"use strict";return c("O.esri.Tiles.OfflineTileEnablerLayer",[j],{tileInfo:null,_imageType:"",_level:null,_minZoom:null,_maxZoom:null,_tilesCore:null,constructor:function(a,b,c){this._isLocalStorage()===!1&&(alert("OfflineTiles Library not supported on this browser."),b(!1)),this._tilesCore=new O.esri.Tiles.TilesCore,Array.prototype.sortNumber=function(){return this.sort(function(a,b){return a-b})},this._self=this,this._lastTileUrl="",this._imageType="",this._getTileUrl=this.getTileUrl;var d=!0;return("undefined"!=typeof c||null!=c)&&(d=c),this.offline={online:d,store:new O.esri.Tiles.TilesStore,proxyPath:null},this.offline.store.isSupported()?void this.offline.store.init(function(c){c&&this._getTileInfoPrivate(a,function(a){void 0==localStorage.__offlineTileInfo&&0!=a&&(localStorage.__offlineTileInfo=a),0==this.offline.online&&0==a&&void 0!=localStorage.__offlineTileInfo?a=localStorage.__offlineTileInfo:0==this.offline.online&&0==a&&void 0==localStorage.__offlineTileInfo&&alert("There was a problem retrieving tiled map info in OfflineTilesEnablerLayer."),this._tilesCore._parseGetTileInfo(a,function(a){this.layerInfos=a.resultObj.layers,this.minScale=a.resultObj.minScale,this.maxScale=a.resultObj.maxScale,this.tileInfo=a.tileInfo,this._imageType=this.tileInfo.format.toLowerCase(),this.fullExtent=a.fullExtent,this.spatialReference=this.tileInfo.spatialReference,this.initialExtent=a.initExtent,this.loaded=!0,this.onLoad(this),b(!0)}.bind(this._self))}.bind(this._self))}.bind(this._self)):b(!1,"indexedDB not supported")},getTileUrl:function(b,c,d){this._level=b;var e=this.url+"/tile/"+b+"/"+c+"/"+d;if(this.offline.online)return this._lastTileUrl=e,e;e=e.split("?")[0];var f="void:/"+b+"/"+c+"/"+d,g=null;return this._tilesCore._getTiles(g,this._imageType,e,f,this.offline.store,a),f},getBasemapLayer:function(a){var b=a.layerIds[0];return a.getLayer(b)},getLevelEstimation:function(a,b,c){var d=new O.esri.Tiles.TilingScheme(this),e=d.getAllCellIdsInExtent(a,b),f={level:b,tileCount:e.length,sizeBytes:e.length*c};return f},getLevel:function(){return this._level},getMaxZoom:function(a){null==this._maxZoom&&(this._maxZoom=this.tileInfo.lods[this.tileInfo.lods.length-1].level),a(this._maxZoom)},getMinZoom:function(a){null==this._minZoom&&(this._minZoom=this.tileInfo.lods[0].level),a(this._minZoom)},getMinMaxLOD:function(a,b){var c={},d=this.getMap(),e=d.getLevel()+a,f=d.getLevel()+b;return null!=this._maxZoom&&null!=this._minZoom?(c.max=Math.min(this._maxZoom,f),c.min=Math.max(this._minZoom,e)):(this.getMinZoom(function(a){c.min=Math.max(a,e)}),this.getMaxZoom(function(a){c.max=Math.min(a,f)})),c},prepareForOffline:function(a,b,c,d){this._tilesCore._createCellsForOffline(this,a,b,c,function(a){this._doNextTile(0,a,d)}.bind(this))},goOffline:function(){this.offline.online=!1},goOnline:function(){this.offline.online=!0,this.refresh()},isOnline:function(){return this.offline.online},deleteAllTiles:function(a){var b=this.offline.store;b.deleteAll(a)},getOfflineUsage:function(a){var b=this.offline.store;b.usedSpace(a)},getTilePolygons:function(a){this._tilesCore._getTilePolygons(this.offline.store,this.url,this,a)},saveToFile:function(a,b){this._tilesCore._saveToFile(a,this.offline.store,b)},loadFromFile:function(a,b){this._tilesCore._loadFromFile(a,this.offline.store,b)},estimateTileSize:function(a){this._tilesCore._estimateTileSize(b,this._lastTileUrl,this.offline.proxyPath,a)},getExtentBuffer:function(a,b){return b.xmin-=a,b.ymin-=a,b.xmax+=a,b.ymax+=a,b},getTileUrlsByExtent:function(a,b){var c=new O.esri.Tiles.TilingScheme(this),d=c.getAllCellIdsInExtent(a,b),e=[];return d.forEach(function(a){e.push(this.url+"/"+b+"/"+a[1]+"/"+a[0])}.bind(this)),e},_doNextTile:function(a,b,c){var d=b[a],e=this._getTileUrl(d.level,d.row,d.col);this._tilesCore._storeTile(e,this.offline.proxyPath,this.offline.store,function(e,f){e||(f={cell:d,msg:f});var g=c({countNow:a,countMax:b.length,cell:d,error:f,finishedDownloading:!1});g||a===b.length-1?c({finishedDownloading:!0,cancelRequested:g}):this._doNextTile(a+1,b,c)}.bind(this))},_isLocalStorage:function(){var a="test";try{return localStorage.setItem(a,a),localStorage.removeItem(a),!0}catch(b){return!1}},_getTileInfoPrivate:function(a,b){var c=new XMLHttpRequest,a=null!=this.offline.proxyPath?this.offline.proxyPath+"?"+a+"?f=pjson":a+"?f=pjson";c.open("GET",a,!0),c.onload=function(){b(200===c.status&&""!==c.responseText?this.response:!1)},c.onerror=function(a){b(!1)},c.send(null)}})}),"undefined"!=typeof O?O.esri.Tiles={}:(O={},O.esri={Tiles:{}}),O.esri.Tiles.Base64Utils={},O.esri.Tiles.Base64Utils.outputTypes={Base64:0,Hex:1,String:2,Raw:3},O.esri.Tiles.Base64Utils.addWords=function(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c},O.esri.Tiles.Base64Utils.stringToWord=function(a){for(var b=8,c=(1<e;e+=b)d[e>>5]|=(a.charCodeAt(e/b)&c)<e;e+=b)d.push(String.fromCharCode(a[e>>5]>>>e%32&c));return d.join("")},O.esri.Tiles.Base64Utils.wordToHex=function(a){for(var b="0123456789abcdef",c=[],d=0,e=4*a.length;e>d;d++)c.push(b.charAt(a[d>>2]>>d%4*8+4&15)+b.charAt(a[d>>2]>>d%4*8&15));return c.join("")},O.esri.Tiles.Base64Utils.wordToBase64=function(a){for(var b="=",c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",d=[],e=0,f=4*a.length;f>e;e+=3)for(var g=(a[e>>2]>>8*(e%4)&255)<<16|(a[e+1>>2]>>8*((e+1)%4)&255)<<8|a[e+2>>2]>>8*((e+2)%4)&255,h=0;4>h;h++)d.push(8*e+6*h>32*a.length?b:c.charAt(g>>6*(3-h)&63));return d.join("")},/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ diff --git a/dist/offline-tiles-advanced-src.js b/dist/offline-tiles-advanced-src.js index 8e2aa48b..fda72011 100644 --- a/dist/offline-tiles-advanced-src.js +++ b/dist/offline-tiles-advanced-src.js @@ -1,4 +1,4 @@ -/*! offline-editor-js - v2.7.1 - 2015-04-29 +/*! offline-editor-js - v2.8 - 2015-05-05 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ define([ diff --git a/dist/offline-tiles-basic-min.js b/dist/offline-tiles-basic-min.js index 89aa4df8..04070761 100644 --- a/dist/offline-tiles-basic-min.js +++ b/dist/offline-tiles-basic-min.js @@ -1,4 +1,4 @@ -/*! offline-editor-js - v2.7.1 - 2015-04-29 +/*! offline-editor-js - v2.8 - 2015-05-05 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ define(["dojo/query","dojo/request","esri/geometry/Polygon","dojo/_base/declare"],function(a,b,c,d){"use strict";return d("O.esri.Tiles.OfflineTilesEnabler",[],{getBasemapLayer:function(a){var b=a.layerIds[0];return a.getLayer(b)},extend:function(c,d,e){c._tilesCore=new O.esri.Tiles.TilesCore,c._lastTileUrl="",c._imageType="",c._minZoom=null,c._maxZoom=null,c._getTileUrl=c.getTileUrl;var f=!0;return"undefined"!=typeof e&&(f=e),c.offline={online:f,store:new O.esri.Tiles.TilesStore,proxyPath:null},c.offline.store.isSupported()?(c.offline.store.init(function(b){b&&(c.resampling=!1,c.getTileUrl=function(b,d,e){var f=this._getTileUrl(b,d,e);if(this.offline.online)return""==c._imageType&&(c._imageType=this.tileInfo.format.toLowerCase()),c._lastTileUrl=f,f;f=f.split("?")[0];var g="void:/"+b+"/"+d+"/"+e,h=null;return c._tilesCore._getTiles(h,this._imageType,f,g,this.offline.store,a),g},d&&d(!0))}.bind(this)),c.getLevelEstimation=function(a,b,c){var d=new O.esri.Tiles.TilingScheme(this),e=d.getAllCellIdsInExtent(a,b),f={level:b,tileCount:e.length,sizeBytes:e.length*c};return f},c.prepareForOffline=function(a,b,d,e){c._tilesCore._createCellsForOffline(this,a,b,d,function(a){this._doNextTile(0,a,e)}.bind(this))},c.goOffline=function(){this.offline.online=!1},c.goOnline=function(){this.offline.online=!0,this.refresh()},c.isOnline=function(){return this.offline.online},c.deleteAllTiles=function(a){var b=this.offline.store;b.deleteAll(a)},c.getOfflineUsage=function(a){var b=this.offline.store;b.usedSpace(a)},c.getTilePolygons=function(a){c._tilesCore._getTilePolygons(this.offline.store,c.url,this,a)},c.saveToFile=function(a,b){c._tilesCore._saveToFile(a,this.offline.store,b)},c.loadFromFile=function(a,b){c._tilesCore._loadFromFile(a,this.offline.store,b)},c.getMaxZoom=function(a){null==this._maxZoom&&(this._maxZoom=c.tileInfo.lods[c.tileInfo.lods.length-1].level),a(this._maxZoom)},c.getMinZoom=function(a){null==this._minZoom&&(this._minZoom=c.tileInfo.lods[0].level),a(this._minZoom)},c.getMinMaxLOD=function(a,b){var d={},e=c.getMap(),f=e.getLevel()+a,g=e.getLevel()+b;return null!=this._maxZoom&&null!=this._minZoom?(d.max=Math.min(this._maxZoom,g),d.min=Math.max(this._minZoom,f)):(c.getMinZoom(function(a){d.min=Math.max(a,f)}),c.getMaxZoom(function(a){d.max=Math.min(a,g)})),d},c.estimateTileSize=function(a){c._tilesCore._estimateTileSize(b,this._lastTileUrl,this.offline.proxyPath,a)},c.getExtentBuffer=function(a,b){return b.xmin-=a,b.ymin-=a,b.xmax+=a,b.ymax+=a,b},c.getTileUrlsByExtent=function(a,b){var d=new O.esri.Tiles.TilingScheme(c),e=d.getAllCellIdsInExtent(a,b),f=[];return e.forEach(function(a){f.push(c.url+"/"+b+"/"+a[1]+"/"+a[0])}.bind(this)),f},void(c._doNextTile=function(a,b,d){var e=b[a],f=this._getTileUrl(e.level,e.row,e.col);c._tilesCore._storeTile(f,this.offline.proxyPath,this.offline.store,function(c,f){c||(f={cell:e,msg:f});var g=d({countNow:a,countMax:b.length,cell:e,error:f,finishedDownloading:!1});g||a===b.length-1?d({finishedDownloading:!0,cancelRequested:g}):this._doNextTile(a+1,b,d)}.bind(this))})):d(!1,"indexedDB not supported")}})}),"undefined"!=typeof O?O.esri.Tiles={}:(O={},O.esri={Tiles:{}}),O.esri.Tiles.Base64Utils={},O.esri.Tiles.Base64Utils.outputTypes={Base64:0,Hex:1,String:2,Raw:3},O.esri.Tiles.Base64Utils.addWords=function(a,b){var c=(65535&a)+(65535&b),d=(a>>16)+(b>>16)+(c>>16);return d<<16|65535&c},O.esri.Tiles.Base64Utils.stringToWord=function(a){for(var b=8,c=(1<e;e+=b)d[e>>5]|=(a.charCodeAt(e/b)&c)<e;e+=b)d.push(String.fromCharCode(a[e>>5]>>>e%32&c));return d.join("")},O.esri.Tiles.Base64Utils.wordToHex=function(a){for(var b="0123456789abcdef",c=[],d=0,e=4*a.length;e>d;d++)c.push(b.charAt(a[d>>2]>>d%4*8+4&15)+b.charAt(a[d>>2]>>d%4*8&15));return c.join("")},O.esri.Tiles.Base64Utils.wordToBase64=function(a){for(var b="=",c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",d=[],e=0,f=4*a.length;f>e;e+=3)for(var g=(a[e>>2]>>8*(e%4)&255)<<16|(a[e+1>>2]>>8*((e+1)%4)&255)<<8|a[e+2>>2]>>8*((e+2)%4)&255,h=0;4>h;h++)d.push(8*e+6*h>32*a.length?b:c.charAt(g>>6*(3-h)&63));return d.join("")},/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ diff --git a/dist/offline-tiles-basic-src.js b/dist/offline-tiles-basic-src.js index d1679ebe..070a4899 100644 --- a/dist/offline-tiles-basic-src.js +++ b/dist/offline-tiles-basic-src.js @@ -1,4 +1,4 @@ -/*! offline-editor-js - v2.7.1 - 2015-04-29 +/*! offline-editor-js - v2.8 - 2015-05-05 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ define([ diff --git a/dist/offline-tpk-min.js b/dist/offline-tpk-min.js index 4c0e6491..a99fc13a 100644 --- a/dist/offline-tpk-min.js +++ b/dist/offline-tpk-min.js @@ -1,4 +1,4 @@ -/*! offline-editor-js - v2.7.1 - 2015-04-29 +/*! offline-editor-js - v2.8 - 2015-05-05 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ define(["dojo/_base/declare","esri/geometry/Extent","dojo/query","esri/SpatialReference","esri/layers/TileInfo","esri/layers/TiledMapServiceLayer","dojo/Deferred","dojo/promise/all","dojo/Evented"],function(a,b,c,d,e,f,g,h,i){return a("O.esri.TPK.TPKLayer",[f,i],{map:null,store:null,MAX_DB_SIZE:75,TILE_PATH:"",RECENTER_DELAY:350,PARSING_ERROR:"parsingError",DB_INIT_ERROR:"dbInitError",DB_FULL_ERROR:"dbFullError",NO_SUPPORT_ERROR:"libNotSupportedError",PROGRESS_START:"start",PROGRESS_END:"end",WINDOW_VALIDATED:"windowValidated",DB_VALIDATED:"dbValidated",DATABASE_ERROR_EVENT:"databaseErrorEvent",VALIDATION_EVENT:"validationEvent",PROGRESS_EVENT:"progress",_maxDBSize:75,_isDBWriteable:!0,_isDBValid:!1,_autoCenter:null,_fileEntriesLength:0,_inMemTilesObject:null,_inMemTilesObjectLength:0,_zeroLengthFileCounter:0,constructor:function(){this._self=this,this._inMemTilesIndex=[],this._inMemTilesObject={},this.store=new O.esri.Tiles.TilesStore,this._validate()},extend:function(a){this._fileEntriesLength=a.length,this.emit(this.PROGRESS_EVENT,this.PROGRESS_START),this._parseInMemFiles(a,function(){this._parseConfCdi(function(a){this.initialExtent=this.fullExtent=a,this._parseConfXml(function(a){this.tileInfo=new e(a),this.spatialReference=new d({wkid:this.tileInfo.spatialReference.wkid}),this.loaded=!0,this.onLoad(this),this.emit(this.PROGRESS_EVENT,this.PROGRESS_END)}.bind(this._self))}.bind(this._self))}.bind(this._self))},getTileUrl:function(a,b,d){this.emit(this.PROGRESS_EVENT,this.PROGRESS_START);var e=this._self.TILE_PATH+"_alllayers",f=this._getCacheFilePath(e,a,b,d);if(this._inMemTilesObject!={}){var g="void:/"+a+"/"+b+"/"+d;return null==this.map&&(this.map=this.getMap()),null==this._autoCenter&&(this._autoCenter=new O.esri.TPK.autoCenterMap(this.map,this.RECENTER_DELAY),this._autoCenter.init()),this._getInMemTiles(f,e,a,b,d,g,function(a,b,d){var e=c("img[src="+b+"]")[0];"undefined"==typeof e&&(e=new Image);var f;if(a){var g="data:image/png;base64,";switch(this.tileInfo.format){case"JPEG":f="data:image/jpg;base64,"+a;break;case"PNG":f=g+a;break;case"PNG8":f=g+a;break;case"PNG24":f=g+a;break;case"PNG32":f=g+a;break;default:f="data:image/jpg;base64,"+a}e.style.borderColor="blue"}else e.style.borderColor="green",f="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAABQdJREFUeNrs2yFv6mocwOH/ualYRUVJRrKKCRATCCZqJ/mOfKQJBGaiYkcguoSJigoQTc4VN222Mdhu7l0ysudJjqFAD13669u37a/lcvkngB8piYhYLBa2BPxAf9kEIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIAPxsiU3wfbRtG1mWnVzedV3kef7q9a7rYrvdxm63i4iILMtiNBpFkiQfftdnZFkWbdtGRAzr7j+fZdnR9Xy0jiRJTv5eBOBHqaoqsiyLm5ubo8ubponFYjG8Vtd1VFV1sKMlSRI3NzdRFMXJ7/qMsixjtVpFRAzr7j9fluVBkD67jjzPoyxLf3gBoLfZbGI8Hh/dqV6q6zoeHh4iSZKYTCYxGo0iImK73Q7Luq6L6+vrg88WRfFqHfv9Puq6jjRN4+rq6tV7Ly4u/tNvKori3e9I09QfXAB4a71ex93d3ckhfNd1UVXVcIR+OZTO8zyKooj7+/uoqiouLy8Pdra3I4OmaaKu67i4uIjpdPq//p63seH7MAn4DXVdF+v1+sOjf390f+88Osuy4ci/2WxsVATgXEwmk2ia5uSOu91uIyJiPB4ffU+/rJ/AA6cAZ2A6ncbz83NUVRV5nr97hO8n104Nrftln53s+ypVVR2czpj8MwLghPl8HkmSDBN556xt22ia5tU/jAA4IU3TmE6nUVVVVFUVs9nsbH/LqUuFGAFwxPX1deR5HnVdD+f8LwPx0fl9f2OQy20IwJm6vb0dTgX2+/3wej8vcCoA/VDb3XYIwLmeoyVJzGaz6LpuOKJHRFxeXkbEP5cDj+mX9e8FAThD4/H44HJfURSRpmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg==";return e.style.visibility="visible",e.src=f,this.emit(this.PROGRESS_EVENT,this.PROGRESS_END),""}.bind(this._self)),g}},setMaxDBSize:function(a){var b=/^\d+$/;b.test(a)&&a<=this.MAX_DB_SIZE&&(this._maxDBSize=a)},getDBSize:function(a){this.store.usedSpace(function(b,c){a(b,c)}.bind(this))},setDBWriteable:function(a){this._isDBWriteable=a},isDBValid:function(){return this._validate(),this._isDBValid},loadFromURL:function(a,b){this.isDBValid()?this.store.store(a,function(a,c){a?b(!0,""):b(!1,c)}):b(!1,"not supported")},_validate:function(){window.File||window.FileReader||window.Blob||window.btoa||window.DataView?this.emit(this.VALIDATION_EVENT,{msg:this.WINDOW_VALIDATED,err:null}):this.emit(this.VALIDATION_EVENT,{msg:this.NO_SUPPORT_ERROR,err:null}),this.store.isSupported()?this.store.init(function(a){0==a?this.emit(this.DATABASE_ERROR_EVENT,{msg:this.DB_INIT_ERROR,err:null}):this.store.usedSpace(function(a,b){var c=this._bytes2MBs(a.sizeBytes);c>this.MAX_DB_SIZE&&this.emit(this.DATABASE_ERROR_EVENT,{msg:this.DB_FULL_ERROR,err:b}),this.emit(this.VALIDATION_EVENT,{msg:this.DB_VALIDATED,err:null}),this._isDBValid=!0}.bind(this))}.bind(this)):this.emit(this.VALIDATION_EVENT,{msg:this.NO_SUPPORT_ERROR,err:null})},_parseInMemFiles:function(a,b){var c=this._fileEntriesLength;this._zeroLengthFileCounter=0;for(var d=[],e=0;c>e;e++){var f=new g,i=a[e].filename.toLocaleUpperCase(),j=i.indexOf("_ALLLAYERS",0);-1!=j&&(this.TILE_PATH=i.slice(0,j)),0==a[e].compressedSize&&this._zeroLengthFileCounter++;var k=i.indexOf("CONF.CDI",0),l=i.indexOf("CONF.XML",0),m=i.indexOf("BUNDLE",0),n=i.indexOf("BUNDLX",0);-1!=k||-1!=l?this._unzipConfFiles(a,e,f,function(a,b){a.resolve(b)}):-1!=m||-1!=n?this._unzipTileFiles(a,e,f,function(a,b){a.resolve(b)}):f.resolve(e),d.push(f)}h(d).then(function(a){b&&b(a)})},ObjectSize:function(a){var b,c=0;for(b in a)a.hasOwnProperty(b)&&c++;return c},_unzipConfFiles:function(a,b,c,d){a[b].getData(new O.esri.zip.TextWriter(b),function(b){this._inMemTilesIndex.push("blank");var e=a[b.token].filename.toLocaleUpperCase();this._inMemTilesObject[e]=b.string;var f=this.ObjectSize(this._inMemTilesObject);f>0&&d(c,b.token)}.bind(this))},_unzipTileFiles:function(a,b,c,d){var e=this;a[b].getData(new O.esri.zip.BlobWriter(b),function(b){if(0!=b.size){var f=new FileReader;f.token=b.token,f.onerror=function(a){e.emit(e.PARSING_ERROR,{msg:"Error parsing file: ",err:a.target.error})},f.addEventListener("loadend",function(f){if(void 0!=this.token){e._inMemTilesIndex.push("blank");var g=a[this.token].filename.toLocaleUpperCase();e._inMemTilesObject[g]=this.result;var h=e.ObjectSize(e._inMemTilesObject);h>0&&d(c,b.token)}}),f.readAsArrayBuffer(b)}})},_parseConfCdi:function(a){var c=this._inMemTilesObject[this.TILE_PATH+"CONF.CDI"],e=new O.esri.TPK.X2JS,f=e.xml_str2json(c),g=f.EnvelopeN,h=parseFloat(g.XMin),i=parseFloat(g.YMin),j=parseFloat(g.XMax),k=parseFloat(g.YMax),l=parseInt(g.SpatialReference.WKID),m=new b(h,i,j,k,new d({wkid:l}));a(m)},_parseConfXml:function(a){var b=this._inMemTilesObject[this.TILE_PATH+"CONF.XML"],c=new O.esri.TPK.X2JS,d=c.xml_str2json(b),e=d.CacheInfo,f={};f.rows=parseInt(e.TileCacheInfo.TileRows),f.cols=parseInt(e.TileCacheInfo.TileCols),f.dpi=parseInt(e.TileCacheInfo.DPI),f.format=e.TileImageInfo.CacheTileFormat,f.compressionQuality=parseInt(e.TileImageInfo.CompressionQuality),f.origin={x:parseInt(e.TileCacheInfo.TileOrigin.X),y:parseInt(e.TileCacheInfo.TileOrigin.Y)},f.spatialReference={wkid:parseInt(e.TileCacheInfo.SpatialReference.WKID)};for(var g=e.TileCacheInfo.LODInfos.LODInfo,h=[],i=0;im;m+=3)f=i[m]<<16|i[m+1]<<8|i[m+2],b=(16515072&f)>>18,c=(258048&f)>>12,d=(4032&f)>>6,e=63&f,g+=h[b]+h[c]+h[d]+h[e];return 1==k?(f=i[l],b=(252&f)>>2,c=(3&f)<<4,g+=h[b]+h[c]+"=="):2==k&&(f=i[l]<<8|i[l+1],b=(64512&f)>>10,c=(1008&f)>>4,d=(15&f)<<2,g+=h[b]+h[c]+h[d]+"="),g},_buffer2Base64:function(a,b,c){var d=new DataView(a,b),e=d.getInt32(0,!0),f=d.buffer.slice(b+4,b+4+e),g=this._base64ArrayBuffer(f);c(g)},_int2HexString:function(a){var b=a.toString(16).toUpperCase();return 1===b.length?"000"+b:2===b.length?"00"+b:3===b.length?"0"+b:b.substr(0,b.length)},_getOffset:function(a,b,c,d,e){var f=128*(c-e)+(b-d);return 16+5*f},_getCacheFilePath:function(a,b,c,d){var e=[];return e.push(a),e.push("/"),e.push("L"),e.push(10>b?"0"+b:b),e.push("/"),e.push("R"),e.push(this._int2HexString(c)),e.push("C"),e.push(this._int2HexString(d)),e.join("")},_bytes2MBs:function(a){return(a>>>20)+"."+(2046&a)}})}),"undefined"!=typeof O?O.esri.TPK={}:(O={},O.esri={TPK:{},Tiles:{}}),O.esri.Tiles.TilesStore=function(){this._db=null;var a="offline_tile_store";this.isSupported=function(){return window.indexedDB||window.openDatabase?!0:!1},this.store=function(a,b){try{var c=this._db.transaction(["tilepath"],"readwrite");c.oncomplete=function(){b(!0)},c.onerror=function(a){b(!1,a.target.error.message)};var d=c.objectStore("tilepath"),e=d.put(a);e.onsuccess=function(){}}catch(f){b(!1,f.stack)}},this.retrieve=function(a,b){if(null!==this._db){var c=this._db.transaction(["tilepath"]).objectStore("tilepath"),d=c.get(a);d.onsuccess=function(a){var c=a.target.result;void 0==c?b(!1,"not found"):b(!0,c)},d.onerror=function(a){b(!1,a)}}},this.deleteAll=function(a){if(null!==this._db){var b=this._db.transaction(["tilepath"],"readwrite").objectStore("tilepath").clear();b.onsuccess=function(){a(!0)},b.onerror=function(b){a(!1,b)}}else a(!1,null)},this["delete"]=function(a,b){if(null!==this._db){var c=this._db.transaction(["tilepath"],"readwrite").objectStore("tilepath")["delete"](a);c.onsuccess=function(){b(!0)},c.onerror=function(a){b(!1,a)}}else b(!1,null)},this.getAllTiles=function(a){if(null!==this._db){var b=this._db.transaction(["tilepath"]).objectStore("tilepath").openCursor();b.onsuccess=function(b){var c=b.target.result;if(c){var d=c.value.url,e=c.value.img;a(d,e,null),c["continue"]()}else a(null,null,"end")}.bind(this),b.onerror=function(b){a(null,null,b)}}else a(null,null,"no db")},this.usedSpace=function(a){if(null!==this._db){var b={sizeBytes:0,tileCount:0},c=this._db.transaction(["tilepath"]).objectStore("tilepath").openCursor();c.onsuccess=function(c){var d=c.target.result;if(d){var e=d.value,f=JSON.stringify(e);b.sizeBytes+=this._stringBytes(f),b.tileCount+=1,d["continue"]()}else a(b,null)}.bind(this),c.onerror=function(b){a(null,b)}}else a(null,null)},this._stringBytes=function(a){return a.length},this.init=function(b){var c=indexedDB.open(a,4);b=b||function(a){}.bind(this),c.onerror=function(a){b(!1,a.target.errorCode)}.bind(this),c.onupgradeneeded=function(a){var b=a.target.result;b.objectStoreNames.contains("tilepath")&&b.deleteObjectStore("tilepath"),b.createObjectStore("tilepath",{keyPath:"url"})}.bind(this),c.onsuccess=function(a){this._db=a.target.result,b(!0)}.bind(this)}},function(a){function b(){var a=-1,b=this;b.append=function(c){var d,e=b.table;for(d=0;d>>8^e[255&(a^c[d])]},b.get=function(){return~a}}function c(a,b,c){return a.slice?a.slice(b,b+c):a.webkitSlice?a.webkitSlice(b,b+c):a.mozSlice?a.mozSlice(b,b+c):a.msSlice?a.msSlice(b,b+c):void 0}function d(a,b){var c,d;return c=new ArrayBuffer(a),d=new Uint8Array(c),b&&d.set(b,0),{buffer:c,array:d,view:new DataView(c)}}function e(){}function f(a){function b(b,c){var f=new Blob([a],{type:M});d=new h(f),d.init(function(){e.size=d.size,b()},c)}function c(a,b,c,e){d.readUint8Array(a,b,c,e)}var d,e=this;e.size=0,e.init=b,e.readUint8Array=c}function g(b){function c(a){for(var c=b.length;"="==b.charAt(c-1);)c--;f=b.indexOf(",")+1,g.size=Math.floor(.75*(c-f)),a()}function e(c,e,g){var h,i=d(e),j=4*Math.floor(c/3),k=4*Math.ceil((c+e)/3),l=a.atob(b.substring(j+f,k+f)),m=c-3*Math.floor(j/4);for(h=m;m+e>h;h++)i.array[h-m]=l.charCodeAt(h);g(i.array)}var f,g=this;g.size=0,g.init=c,g.readUint8Array=e}function h(a){function b(b){this.size=a.size,b()}function d(b,d,e,f){var g=new FileReader;g.onload=function(a){e(new Uint8Array(a.target.result))},g.onerror=f,g.readAsArrayBuffer(c(a,b,d))}var e=this;e.size=0,e.init=b,e.readUint8Array=d}function i(){}function j(a,b){function c(a){f=new Blob([],{type:M}),a()}function d(a,b){f=new Blob([f,A?a:a.buffer],{type:M}),b()}function e(c,d){var e=new FileReader;e.onload=function(b){var d={string:b.target.result,token:a};c(d)},e.onerror=d,e.readAsText(f,b)}var f,g=this;g.init=c,g.writeUint8Array=d,g.getData=e}function k(b){function c(a){g+="data:"+(b||"")+";base64,",a()}function d(b,c){var d,e=h.length,f=h;for(h="",d=0;d<3*Math.floor((e+b.length)/3)-e;d++)f+=String.fromCharCode(b[d]);for(;d2?g+=a.btoa(f):h=f,c()}function e(b){b(g+a.btoa(h))}var f=this,g="",h="";f.init=c,f.writeUint8Array=d,f.getData=e}function l(a,b){function c(a){f=new Blob([],{type:b}),a()}function d(c,d){f=new Blob([f,A?c:c.buffer],{type:b}),f.token=a,d()}function e(a){a(f)}var f,g=this;g.init=c,g.writeUint8Array=d,g.getData=e}function m(a,b,c,d,e,f,g,h,i,j){function k(){a.removeEventListener(N,l,!1),h(o)}function l(a){var b=a.data,d=b.data;b.onappend&&(o+=d.length,c.writeUint8Array(d,function(){f(!1,d),m()},j)),b.onflush&&(d?(o+=d.length,c.writeUint8Array(d,function(){f(!1,d),k()},j)):k()),b.progress&&g&&g(n+b.current,e)}function m(){n=p*J,e>n?b.readUint8Array(d+n,Math.min(J,e-n),function(b){a.postMessage({append:!0,data:b}),p++,g&&g(n,e),f(!0,b)},i):a.postMessage({flush:!0})}var n,o,p=0;o=0,a.addEventListener(N,l,!1),m()}function n(a,b,c,d,e,f,g,h,i,j){function k(){var o;l=m*J,e>l?b.readUint8Array(d+l,Math.min(J,e-l),function(b){var h=a.append(b,function(){g&&g(d+l,e)});n+=h.length,f(!0,b),c.writeUint8Array(h,function(){f(!1,h),m++,setTimeout(k,1)},j),g&&g(l,e)},i):(o=a.flush(),o?(n+=o.length,c.writeUint8Array(o,function(){f(!1,o),h(n)},j)):h(n))}var l,m=0,n=0;k()}function o(c,d,e,f,g,h,i,j,k){function l(a,b){g&&!a&&q.append(b)}function o(a){h(a,q.get())}var p,q=new b;return a.zip.useWebWorkers?(p=new Worker(a.zip.workerScriptsPath+K),m(p,c,d,e,f,l,i,o,j,k)):n(new a.zip.Inflater,c,d,e,f,l,i,o,j,k),p}function p(c,d,e,f,g,h,i){function j(a,b){a&&p.append(b)}function k(a){f(a,p.get())}function l(){o.removeEventListener(N,l,!1),m(o,c,d,0,c.size,j,g,k,h,i)}var o,p=new b;return a.zip.useWebWorkers?(o=new Worker(a.zip.workerScriptsPath+L),o.addEventListener(N,l,!1),o.postMessage({init:!0,level:e})):n(new a.zip.Deflater,c,d,0,c.size,j,g,k,h,i),o}function q(a,c,d,e,f,g,h,i,j){function k(){var b=l*J;e>b?a.readUint8Array(d+b,Math.min(J,e-b),function(a){f&&m.append(a),h&&h(b,e,a),c.writeUint8Array(a,function(){l++,k()},j)},i):g(e,m.get())}var l=0,m=new b;k()}function r(a){var b,c,d="",e=["Ç","ü","é","â","ä","à","å","ç","ê","ë","è","ï","î","ì","Ä","Å","É","æ","Æ","ô","ö","ò","û","ù","ÿ","Ö","Ü","ø","£","Ø","×","ƒ","á","í","ó","ú","ñ","Ñ","ª","º","¿","®","¬","½","¼","¡","«","»","_","_","_","¦","¦","Á","Â","À","©","¦","¦","+","+","¢","¥","+","+","-","-","+","-","+","ã","Ã","+","+","-","-","¦","-","+","¤","ð","Ð","Ê","Ë","È","i","Í","Î","Ï","+","+","_","_","¦","Ì","_","Ó","ß","Ô","Ò","õ","Õ","µ","þ","Þ","Ú","Û","Ù","ý","Ý","¯","´","­","±","_","¾","¶","§","÷","¸","°","¨","·","¹","³","²","_"," "];for(b=0;b127?e[c-128]:String.fromCharCode(c);return d}function s(a){return decodeURIComponent(escape(a))}function t(a){var b,c="";for(b=0;b>16,c=65535&a;try{return new Date(1980+((65024&b)>>9),((480&b)>>5)-1,31&b,(63488&c)>>11,(2016&c)>>5,2*(31&c),0)}catch(d){}}function v(a,b,c,d,e){return a.version=b.view.getUint16(c,!0),a.bitFlag=b.view.getUint16(c+2,!0),a.compressionMethod=b.view.getUint16(c+4,!0),a.lastModDateRaw=b.view.getUint32(c+6,!0),a.lastModDate=u(a.lastModDateRaw),1===(1&a.bitFlag)?void e(C):((d||8!=(8&a.bitFlag))&&(a.crc32=b.view.getUint32(c+10,!0),a.compressedSize=b.view.getUint32(c+14,!0),a.uncompressedSize=b.view.getUint32(c+18,!0)),4294967295===a.compressedSize||4294967295===a.uncompressedSize?void e(D):(a.filenameLength=b.view.getUint16(c+22,!0),void(a.extraFieldLength=b.view.getUint16(c+24,!0))))}function w(a,b){function c(){}function e(c,f){a.readUint8Array(a.size-c,c,function(a){var b=d(a.length,a).view;1347093766!=b.getUint32(0)?e(c+1,f):f(b)},function(){b(E)})}return c.prototype.getData=function(c,e,f,g){function h(a,b){m&&m.terminate(),m=null,a&&a(b)}function i(a){var b=d(4);return b.view.setUint32(0,a),n.crc32==b.view.getUint32(0)}function j(a,b){g&&!i(b)?k():c.getData(function(a){h(e,a)})}function k(){h(b,H)}function l(){h(b,G)}var m,n=this;a.readUint8Array(n.offset,30,function(e){var h,i=d(e.length,e);return 1347093252!=i.view.getUint32(0)?void b(B):(v(n,i,4,!1,b),h=n.offset+30+n.filenameLength+n.extraFieldLength,void c.init(function(){0===n.compressionMethod?q(a,c,h,n.compressedSize,g,j,f,k,l):m=o(a,c,h,n.compressedSize,g,j,f,k,l)},l))},k)},{getEntries:function(f){return a.size<22?void b(B):void e(22,function(e){var g,h;g=e.getUint32(16,!0),h=e.getUint16(8,!0),a.readUint8Array(g,a.size-g,function(a){var e,g,i,j,k=0,l=[],m=d(a.length,a);for(e=0;h>e;e++){if(g=new c,1347092738!=m.view.getUint32(k))return void b(B);v(g,m,k+6,!0,b),g.commentLength=m.view.getUint16(k+32,!0),g.directory=16==(16&m.view.getUint8(k+38)),g.offset=m.view.getUint32(k+42,!0),i=t(m.array.subarray(k+46,k+46+g.filenameLength)),g.filename=2048===(2048&g.bitFlag)?s(i):r(i),g.directory||"/"!=g.filename.charAt(g.filename.length-1)||(g.directory=!0),j=t(m.array.subarray(k+46+g.filenameLength+g.extraFieldLength,k+46+g.filenameLength+g.extraFieldLength+g.commentLength)),g.comment=2048===(2048&g.bitFlag)?s(j):r(j),l.push(g),k+=46+g.filenameLength+g.extraFieldLength+g.commentLength}f(l)},function(){b(E)})})},close:function(a){a&&a()}}}function x(a){return unescape(encodeURIComponent(a))}function y(a){var b,c=[];for(b=0;ba;a++){for(c=a,b=0;8>b;b++)1&c?c=c>>>1^3988292384:c>>>=1;d[a]=c}return d}(),f.prototype=new e,f.prototype.constructor=f,g.prototype=new e,g.prototype.constructor=g,h.prototype=new e,h.prototype.constructor=h,i.prototype.getData=function(a){a(this.data)},j.prototype=new i,j.prototype.constructor=j,k.prototype=new i,k.prototype.constructor=k,l.prototype=new i,l.prototype.constructor=l,a.zip={Reader:e,Writer:i,BlobReader:h,Data64URIReader:g,TextReader:f,BlobWriter:l,Data64URIWriter:k,TextWriter:j,createReader:function(a,b,c){a.init(function(){b(w(a,c))},c)},createWriter:function(a,b,c,d){a.init(function(){b(z(a,c,d))},c)},workerScriptsPath:"",useWebWorkers:!0}}(O.esri),O.esri.TPK.autoCenterMap=function(a,b){function c(a){var b="onorientationchange"in window,c=b?"orientationchange":"resize";window.addEventListener(c,e(function(){d()},a))}function d(){var b=i().split(","),c=a.spatialReference.wkid,d=null;4326==c?d=new esri.geometry.Point(b[1],b[0]):(c=102100)&&(d=new esri.geometry.Point(b[0],b[1],new esri.SpatialReference({wkid:c}))),a.centerAt(d)}function e(a,b,c){var d;return function(){var e=this,f=arguments;clearTimeout(d),d=setTimeout(function(){d=null,c||a.apply(e,f)},b),c&&!d&&a.apply(e,f)}}function f(){a.on("pan-end",function(){var b=a.extent.getCenter();h(b.x,b.y,a.spatialReference.wkid)})}function g(){a.on("zoom-end",function(){var b=a.extent.getCenter();h(b.x,b.y,a.spatialReference.wkid),a.setZoom(a.getZoom())}.bind(self))}function h(a,b,c){localStorage.setItem("_centerPtX",a),localStorage.setItem("_centerPtY",b),localStorage.setItem("_spatialReference",c)}function i(){var a=null;try{a=localStorage.getItem("_centerPtX")+","+localStorage.getItem("_centerPtY")+","+localStorage.getItem("_spatialReference")}catch(b){}return a}this.init=function(){f(),g(),c(b);var d=a.extent.getCenter();h(d.x,d.y,a.spatialReference.wkid)}},O.esri.TPK.inflate=function(a){function b(){function a(a,b,c,d,j,k,l,n,p,r,s){var t,u,v,w,x,y,z,A,C,D,E,F,G,H,I;D=0,x=c;do e[a[b+D]]++,D++,x--;while(0!==x);if(e[0]==c)return l[0]=-1,n[0]=0,i;for(A=n[0],y=1;B>=y&&0===e[y];y++);for(z=y,y>A&&(A=y),x=B;0!==x&&0===e[x];x--);for(v=x,A>x&&(A=x),n[0]=A,H=1<y;y++,H<<=1)if((H-=e[y])<0)return m;if((H-=e[x])<0)return m;for(e[x]+=H,h[1]=y=0,D=1,G=2;0!==--x;)h[G]=y+=e[D],G++,D++;x=0,D=0;do 0!==(y=a[b+D])&&(s[h[y]++]=x),D++;while(++x=z;z++)for(t=e[z];0!==t--;){for(;z>F+A;){if(w++,F+=A,I=v-F,I=I>A?A:I,(u=1<<(y=z-F))>t+1&&(u-=t+1,G=z,I>y))for(;++yq)return m;g[w]=E=r[0],r[0]+=I,0!==w?(h[w]=x,f[0]=y,f[1]=A,y=x>>>F-A,f[2]=E-g[w-1]-y,p.set(f,3*(g[w-1]+y))):l[0]=E}for(f[1]=z-F,D>=c?f[0]=192:s[D]>>F;I>y;y+=u)p.set(f,3*(E+y));for(y=1<>>=1)x^=y;for(x^=y,C=(1<b;b++)d[b]=0;for(b=0;B+1>b;b++)e[b]=0;for(b=0;3>b;b++)f[b]=0;g.set(e.subarray(0,B),0),h.set(e.subarray(0,B+1),0)}var c,d,e,f,g,h,j=this;j.inflate_trees_bits=function(e,f,g,h,i){var j;return b(19),c[0]=0,j=a(e,0,19,19,null,null,g,f,h,c,d),j==m?i.msg="oversubscribed dynamic bit lengths tree":(j==o||0===f[0])&&(i.msg="incomplete dynamic bit lengths tree",j=m),j},j.inflate_trees_dynamic=function(e,f,g,h,j,k,l,p,q){var r;return b(288),c[0]=0,r=a(g,0,e,257,x,y,k,h,p,c,d),r!=i||0===h[0]?(r==m?q.msg="oversubscribed literal/length tree":r!=n&&(q.msg="incomplete literal/length tree",r=m),r):(b(288),r=a(g,e,f,0,z,A,l,j,p,c,d),r!=i||0===j[0]&&e>257?(r==m?q.msg="oversubscribed distance tree":r==o?(q.msg="incomplete distance tree",r=m):r!=n&&(q.msg="empty distance tree with lengths",r=m),r):i)}}function c(){function a(a,b,c,d,e,f,g,h){var k,l,n,o,q,r,s,t,u,v,w,x,y,z,A,B;s=h.next_in_index,t=h.avail_in,q=g.bitb,r=g.bitk,u=g.write,v=ur;)t--,q|=(255&h.read_byte(s++))<>=l[B+1],r-=l[B+1],0!==(16&o)){for(o&=15,y=l[B+2]+(q&p[o]),q>>=o,r-=o;15>r;)t--,q|=(255&h.read_byte(s++))<>=l[B+1],r-=l[B+1],0!==(16&o)){for(o&=15;o>r;)t--,q|=(255&h.read_byte(s++))<>=o,r-=o,v-=y,u>=z)A=u-z,u-A>0&&2>u-A?(g.window[u++]=g.window[A++],g.window[u++]=g.window[A++],y-=2):(g.window.set(g.window.subarray(A,A+2),u),u+=2,A+=2,y-=2);else{A=u-z;do A+=g.end;while(0>A);if(o=g.end-A,y>o){if(y-=o,u-A>0&&o>u-A){do g.window[u++]=g.window[A++];while(0!==--o)}else g.window.set(g.window.subarray(A,A+o),u),u+=o,A+=o,o=0;A=0}}if(u-A>0&&y>u-A){do g.window[u++]=g.window[A++];while(0!==--y)}else g.window.set(g.window.subarray(A,A+y),u),u+=y,A+=y,y=0;break}if(0!==(64&o))return h.msg="invalid distance code",y=h.avail_in-t,y=y>r>>3?r>>3:y,t+=y,s-=y,r-=y<<3,g.bitb=q,g.bitk=r,h.avail_in=t,h.total_in+=s-h.next_in_index,h.next_in_index=s,g.write=u,m;k+=l[B+2],k+=q&p[o],B=3*(n+k),o=l[B]}break}if(0!==(64&o))return 0!==(32&o)?(y=h.avail_in-t,y=y>r>>3?r>>3:y,t+=y,s-=y,r-=y<<3,g.bitb=q,g.bitk=r,h.avail_in=t,h.total_in+=s-h.next_in_index,h.next_in_index=s,g.write=u,j):(h.msg="invalid literal/length code",y=h.avail_in-t,y=y>r>>3?r>>3:y,t+=y,s-=y,r-=y<<3,g.bitb=q,g.bitk=r,h.avail_in=t,h.total_in+=s-h.next_in_index,h.next_in_index=s,g.write=u,m);if(k+=l[B+2],k+=q&p[o],B=3*(n+k),0===(o=l[B])){q>>=l[B+1],r-=l[B+1],g.window[u++]=l[B+2],v--;break}}else q>>=l[B+1],r-=l[B+1],g.window[u++]=l[B+2],v--}while(v>=258&&t>=10);return y=h.avail_in-t,y=y>r>>3?r>>3:y,t+=y,s-=y,r-=y<<3,g.bitb=q,g.bitk=r,h.avail_in=t,h.total_in+=s-h.next_in_index,h.next_in_index=s,g.write=u,i}var b,c,d,e,f=this,g=0,h=0,k=0,n=0,o=0,q=0,r=0,s=0,t=0,u=0;f.init=function(a,f,g,h,i,j){b=C,r=a,s=f,d=g,t=h,e=i,u=j,c=null},f.proc=function(f,v,w){var x,y,z,A,B,M,N,O=0,P=0,Q=0;for(Q=v.next_in_index,A=v.avail_in,O=f.bitb,P=f.bitk,B=f.write,M=B=258&&A>=10&&(f.bitb=O,f.bitk=P,v.avail_in=A,v.total_in+=Q-v.next_in_index,v.next_in_index=Q,f.write=B,w=a(r,s,d,t,e,u,f,v),Q=v.next_in_index,A=v.avail_in,O=f.bitb,P=f.bitk,B=f.write,M=BP;){if(0===A)return f.bitb=O,f.bitk=P,v.avail_in=A,v.total_in+=Q-v.next_in_index,v.next_in_index=Q,f.write=B,f.inflate_flush(v,w);w=i,A--,O|=(255&v.read_byte(Q++))<>>=c[y+1],P-=c[y+1],z=c[y],0===z){n=c[y+2],b=I;break}if(0!==(16&z)){o=15&z,g=c[y+2],b=E;break}if(0===(64&z)){k=z,h=y/3+c[y+2];break}if(0!==(32&z)){b=J;break}return b=L,v.msg="invalid literal/length code",w=m,f.bitb=O,f.bitk=P,v.avail_in=A,v.total_in+=Q-v.next_in_index, diff --git a/dist/offline-tpk-src.js b/dist/offline-tpk-src.js index 7c4a2111..db907c5d 100644 --- a/dist/offline-tpk-src.js +++ b/dist/offline-tpk-src.js @@ -1,4 +1,4 @@ -/*! offline-editor-js - v2.7.1 - 2015-04-29 +/*! offline-editor-js - v2.8 - 2015-05-05 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ /** diff --git a/package.json b/package.json index e7578551..4e6ce3d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "offline-editor-js", - "version": "2.7.1", + "version": "2.8", "description": "Lightweight set of libraries for working offline with map tiles and ArcGIS feature services", "author": "Andy Gup (http://blog.andygup.net)", "license": "Apache 2", diff --git a/samples/package.json b/samples/package.json index d0500b94..8eb59647 100644 --- a/samples/package.json +++ b/samples/package.json @@ -9,7 +9,7 @@ "appHomePage": "appcache-features.html", "optimizedApiURL": "../samples/jsolib", "arcGISBaseURL": "http://js.arcgis.com/3.11", - "version": "2.7.1", + "version": "2.8", "private": true, "description": "manifest generator project", "repository": {