diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index 2f52ed4..f1e3cad 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 317c3d4..5304682 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; +// }); } });