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/doc/howtouseeditlibrary.md b/doc/howtouseeditlibrary.md index 34f51b3a..62b748b7 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 @@ -86,7 +88,7 @@ NOTE: You can also monitor standard ArcGIS API for JavaScript layer events using // 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."); } @@ -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 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.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. + 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 6319d0cb..d2151ed2 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` 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)` | `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( 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/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); }; diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index b77ce411..b34e2c64 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -11,17 +11,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 @@ -37,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. @@ -50,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 }, /** @@ -98,23 +101,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; @@ -132,7 +156,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 @@ -140,9 +164,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) */ // @@ -281,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; } @@ -465,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; } @@ -720,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 @@ -1032,6 +1052,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 /** @@ -1051,7 +1098,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"); @@ -1102,13 +1149,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){ @@ -1120,12 +1170,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; @@ -1150,22 +1204,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; }, /** @@ -1413,23 +1477,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 +1492,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); @@ -1505,10 +1539,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. @@ -1547,7 +1577,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 @@ -1732,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 @@ -1787,6 +1823,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} 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/appcache-features.html b/samples/appcache-features.html index 3f2c66b5..14c79c57 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -143,8 +143,8 @@
- - + +
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; @@ -221,7 +222,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 +272,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); @@ -288,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); @@ -301,8 +303,7 @@ //on a mobile device. busStopFeatureLayer.setRenderer(new SimpleRenderer(defaultSymbol)); - initFeatureUpdateEndListener(); - initMapLoadListener(); + _listener = busStopFeatureLayer.on("update-end",initFeatureUpdateEndListener); map.addLayer(busStopFeatureLayer); } @@ -315,62 +316,56 @@ */ function initFeatureUpdateEndListener() { - busStopFeatureLayer.on("update-end",function(evt){ + _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){ + // 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); + // 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."); - } - }); - } - }); - } + 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; - }); } /** @@ -404,33 +399,33 @@ 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, - 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. 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); @@ -461,16 +456,16 @@ // 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); } 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 // @@ -488,16 +483,26 @@ 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 == true){ - offlineFeaturesManager.goOnline(); - } + // 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); } @@ -505,7 +510,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); } /** @@ -572,10 +577,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 } } @@ -644,7 +651,6 @@ } else { globalState.downloadState = 'downloaded'; - btnOnlineOffline.disabled = false; alert("Tile download complete"); } @@ -669,6 +675,10 @@ loading.style.visibility = "visible"; + if(busStopFeatureLayer && busStopFeatureLayer.offlineExtended) { + + } + offlineFeaturesManager.goOnline(function(success,error) { if(error === undefined) { setUIOnline(); @@ -735,33 +745,22 @@ }) } - /** - * 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 * @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); } }; @@ -778,28 +777,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() { - busStopFeatureLayer.pendingEditsCount(function(count) { - pendingEdits.innerHTML = count; - }); + + // 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("pendingEditsCount" in busStopFeatureLayer) { + busStopFeatureLayer.pendingEditsCount(function(count) { + pendingEdits.innerHTML = count; + }); + } } }); 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": { 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); 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))