diff --git a/CHANGELOG.md b/CHANGELOG.md index 331da73f..eeb05bb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,40 @@ # offline-editor-js - Changelog +## Version 3.0 - Nov. 23, 2015 + +Has many breaking changes due to new naming conventions. + +This version adds a new, lightweight (14Kb) editing library aimed at intermittent offline-only workflows. And, there was a significant amount of refactoring aimed at fixing and simplifying the library's naming conventions. + +This version implements a consistent naming convention that uses "basic" and "advanced". If `basic` is in the name of the distribution library that means support for intermittent offline-only. `advanced` in the name means support for both intermittent and full offline usage including browser restarts while offline. + +In general, migrating from v2.x to 3.x should be a straightforward exercise in simple refactoring of library names. + +**Enhancements** +* Creates a new `OfflineEditBasic` library. This lightweight (14Kb) library is designed specifically for easy use and intermittent offline-only editing use cases. +* Created `offline-edit-basic` and `offline-edit-advanced` for both `src` and `min` versions. +* Updates documentation, samples and unit tests to reflect the name changes. + +**Refactored** +* `offlineFeaturesManager.js` renamed `OfflineEditAdvanced.js`. No other changes were made to this library. +* `offlineTilesEnabler.js` renamed `OfflineTilesBasic.js`. +* `OfflineTilesEnablerLayer.js` renamed `OfflineTilesAdvanced`. No other changes made. +* `tiles-indexed-db.html` renamed to `simple-tiles.html`. +* All samples have been updated to reflect the new library names. +* All test specs have been updated and refactored to reflect the new library names. + +**Deprecated** +* Deprecated `offline-edit-min.js` and `offline-edit-src.js`. +* In `OfflineEditAdvanced.js` removed deprecated functions `_cleanSuccessfulEditsDatabaseRecords()`, `_internalApplyEdits()`. +* In `editStore.js` removed deprecated function `resetLimitedPhantomGraphicsQueue()`. +* In `editStore.js` removed unused functions `_serialize()`, `_deserialize()`. +* Deleted `/demo/samples` directory. +* Removed Offline.js and IndexedDBShim from sub-module depencies. You'll need to reference these via their respective CDNs for gh-pages URL. + +**Bug Fixes** +* Minor - `OfflineEditAdvanced` no longer throws an error when reinitialized with a featureCollection. + + ## Version 2.16 - Oct. 29, 2015 No breaking changes. Recommended update. diff --git a/README.md b/README.md index ea5e961e..38b64183 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Online samples and getting started tutorials are available here: **[http://esri. # Libraries -This repo contains the following libraries in the `/dist` directory. +This repo contains the following libraries in the `/dist` directory. The use of `basic` in the name indicates intermittent offline-only, and `advanced` indicates the library can be used for both intermittent and full offline. Use_Case | Name, Description and gh-pages URL --- | --- @@ -22,7 +22,7 @@ Basic map tiles | **`offline-tiles-basic-min.js`** Caches map tiles for simple, Advanced map tiles | **`offline-tiles-advanced-min.js`** Used for intermittent and full offline tile caching. Extends any ArcGIS Tiled Map Service. This library should be used in conjunction with an HTML5 Application Cache Manifest coding pattern.

[`http://esri.github.io/offline-editor-js/dist/offline-tiles-advanced-min.js`](http://esri.github.io/offline-editor-js/dist/offline-tiles-advanced-min.js) TPK files | **`offline-tpk-min.js`** Reads TPK files and displays and caches them as a tiled map layer.

[`http://esri.github.io/offline-editor-js/dist/offline-tpk-min.js`](http://esri.github.io/offline-editor-js/dist/offline-tpk-min.js) -`src` files are for software development-only. The`min` versions are minified and should be used in production. The use of `basic` in the name indicates intermittent offline-only, and `advanced` indicates the library can be used for both intermittent and full offline. +`src` files are for software development-only. The`min` versions are minified and should be used in production. #Workflows Supported The following workflow is currently supported for both both features and tiles: @@ -70,6 +70,7 @@ Go __[here](https://github.com/Esri/offline-editor-js/wiki/FAQ)__ for answers to ##Limitations * Currently does not support related tables, domains or subtypes. The ArcGIS Runtime SDKs fully support these and more. +* There are browser limitations and technical dependencies. The offline capabilities in this toolkit depend on certain JavaScript capabilities being present in the browser. Go [here](doc/dependencies.md) for a detailed breakdown. * Attachments are supported with some limitations listed [here](./doc/attachments.md). * Browser storage space on mobile devices is a known limitation. This applies to stand-alone web applications and hybrid applications. @@ -79,17 +80,15 @@ Go __[here](https://github.com/Esri/offline-editor-js/wiki/FAQ)__ for answers to ##Dependencies -* ArcGIS API for JavaScript (v3.12+) +* [ArcGIS API for JavaScript (v3.12+)](https://developers.arcgis.com/javascript/) * [Offline.js](http://github.hubspot.com/offline/docs/welcome/) - it allows detection of the online/offline condition and provides events to hook callbacks on when this condition changes * Node.js required for building the source -* NOTE: browser limitations and technical dependencies. The offline capabilities in this toolkit depend on certain HTML5 capabilities being present in the browser. Go [here](doc/dependencies.md) for a detailed breakdown of the information. +* [IndexedDBShim](https://github.com/axemclion/IndexedDBShim) - polyfill to simulate indexedDB functionality in browsers/platforms where it is not supported notably older versions desktop Safari and iOS Safari. * Sub-modules (see `/vendor` directory) - * [IndexedDBShim](https://github.com/axemclion/IndexedDBShim) - polyfill to simulate indexedDB functionality in browsers/platforms where it is not supported (notably desktop Safari and iOS Safari) - - IMPORTANT: There are known [issues](https://github.com/axemclion/IndexedDBShim/issues/115) with IndexedDBShim on Safari. For Safari, the storage error workaround is to switch from using /dist/IndexedDBShim.min.js to just using IndexedDBShim.js and then search for and modify the line that defines the value for `DEFAULT_DB_SIZE`. Set this to more appropriate size that will meet all your storage needs, for example: ```var DEFAULT_DB_SIZE = 24 * 1024 * 1024``` * [jasmine.async](https://github.com/derickbailey/jasmine.async.git) - Used specifically for unit testing. -* Non sub-module based libraries +* Non sub-module based libraries that are used internally by this project * [FileSaver.js](https://github.com/Esri/offline-editor-js/blob/master/lib/tiles/README.md) - library to assist with uploading and downloading of files containing tile information. * [grunt-manifest](https://github.com/gunta/grunt-manifest) node.js library to assist with the creation of manifest files. * [zip](http://gildas-lormeau.github.io/zip.js/) A library for zipping and unzipping files. diff --git a/dist/offline-edit-advanced-min.js b/dist/offline-edit-advanced-min.js new file mode 100644 index 00000000..a105dd15 --- /dev/null +++ b/dist/offline-edit-advanced-min.js @@ -0,0 +1,226 @@ +/*! esri-offline-maps - v3.0.0 - 2015-11-23 +* Copyright (c) 2015 Environmental Systems Research Institute, Inc. +* Apache License*/ +Offline.options={checks:{image:{url:function(){return"http://esri.github.io/offline-editor-js/tiny-image.png?_="+Math.floor(1e9*Math.random())}},active:"image"}},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.OfflineEditAdvanced",[a],{_onlineStatus:"online",_featureLayers:{},_featureCollectionUsageFlag:!1,_editStore:new O.esri.Edit.EditStore,_defaultXhrTimeout:15e3,ONLINE:"online",OFFLINE:"offline",RECONNECTING:"reconnecting",attachmentsStore:null,proxyPath:null,ENABLE_FEATURECOLLECTION:!1,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=[],n=this +a.offlineExtended=!0,!a.loaded,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=n.attachmentsStore.TYPE.UPDATE,k=new b +return 0>c&&(j=n.attachmentsStore.TYPE.ADD),n.attachmentsStore.store(this.url,c,a,i,j,function(b,d){var g={attachmentId:c,objectId:a,success:b} +if(b){n.emit(n.events.ATTACHMENT_ENQUEUED,g),e&&e(g),k.resolve(g) +var i=this._url.path+"/"+a+"/attachments/"+c,j=h("[href="+i+"]") +j.attr("href",d.url)}else g.error="layer.updateAttachment::attachmentStore can't store attachment",f&&f(g),k.reject(g)}.bind(this)),k}},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(d,e,f,g,h){var i=[] +if(n.getOnlineStatus()===n.ONLINE){var j=this._applyEdits(d,e,f,function(){n.emit(n.events.EDITS_SENT,arguments),g&&g.apply(this,arguments)},h) +return j}var k=new b,l={addResults:[],updateResults:[],deleteResults:[]},m={},o=d||[] +return o.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,l,d,c):c.resolve(!0)},function(a){c.reject(a)}),i.push(c)},this),e=e||[],e.forEach(function(a){var c=new b,d=a.attributes[this.objectIdField] +m[d]=a +var e=this +this._validateFeature(a,this.url,n._editStore.UPDATE).then(function(b){b.success?e._pushValidatedUpdateFeatureToDB(e,a,b.operation,l,d,c):c.resolve(!0)},function(a){c.reject(a)}),i.push(c)},this),f=f||[],f.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,l,d,c):c.resolve(!0)},function(a){c.reject(a)}),i.push(c)},this),c(i).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._pushFeatureCollections=function(b){n._editStore._getFeatureCollections(function(c,d){var e={featureLayerUrl:a.url,featureLayerCollection:a.toJson()},f=[e],g={id:n._editStore.FEATURE_COLLECTION_ID,featureCollections:f} +if(a.hasAttachments=e.featureLayerCollection.layerDefinition.hasAttachments,c){for(var h=0,i=0;id;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&&o?this.ENABLE_FEATURECOLLECTION?a._pushFeatureCollections(function(a){a?d(!0,null):d(!1,null)}):d(!0,null):b[0].success&&!o?this._editStore.getFeatureLayerJSON(function(b,c){b?(this._featureLayers[c.__featureLayerURL]=a,a.url=c.__featureLayerURL,this.ENABLE_FEATURECOLLECTION?a._pushFeatureCollections(function(a){a?d(!0,null):d(!1,null)}):d(!0,null)):d(!1,c)}.bind(this)):b[0].success&&(this.ENABLE_FEATURECOLLECTION?a._pushFeatureCollections(function(a){a?d(!0,null):d(!1,null)}):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){d.attachments={success:b,responses:c,dbResponses:e},a&&a(d)}.bind(this)):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}}},getFeatureCollections:function(a){this._editStore._isDBInit?this._editStore._getFeatureCollections(function(b,c){a(b,c)}):this._initializeDB(null,null).then(function(b){b.success&&this._editStore._getFeatureCollections(function(b,c){a(b,c)})}.bind(this),function(b){a(!1,b)})},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]},_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){var j=b.map(function(a){return a.objectId}) +a._replaceFeatureIds(d,j,function(a){})}if(b.length>0){var l=new k(e[0].geometry,null,e[0].attributes) +a.add(l)}h._cleanDatabase(a,d,b,f,g).then(function(e){i.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:f,deleteResults:g,databaseResults:e,databaseErrors:null,syncError:null})},function(e){i.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:f,deleteResults:g,databaseResults:null,databaseErrors:e,syncError:e})})},function(b){a.onEditsComplete=a.__onEditsComplete,delete a.__onEditsComplete,i.reject(b)}),i.promise},_cleanDatabase:function(a,c,d,e,f){var g=new b,h=null +e.length>0&&e[0].success&&(h=e[0].objectId),f.length>0&&f[0].success&&(h=f[0].objectId),d.length>0&&d[0].success&&(h=c) +var i={} +return i.attributes={},i.attributes[this.DB_UID]=h,this._editStore["delete"](a.url,i,function(a,b){if(a){var c=this._editStore.PHANTOM_GRAPHIC_PREFIX+this._editStore._PHANTOM_PREFIX_TOKEN+i.attributes[this.DB_UID] +this._editStore.deletePhantomGraphic(c,function(a,b){a?g.resolve({success:!0,error:null,id:c}):g.reject({success:!1,error:b,id:c})})}else g.reject({success:!1,error:b,id:c})}.bind(this)),g.promise},_makeEditRequest:function(a,b,c,d,f,g){var h="f=json",i="",j="",k="" +if(b.length>0&&(e.forEach(b,function(a){a.hasOwnProperty("infoTemplate")&&delete a.infoTemplate},this),i="&adds="+JSON.stringify(b)),c.length>0&&(e.forEach(c,function(a){a.hasOwnProperty("infoTemplate")&&delete a.infoTemplate},this),j="&updates="+JSON.stringify(c)),d.length>0){var l=d[0].attributes[this.DB_UID] +k="&deletes="+l}var m=h+i+j+k +a.hasOwnProperty("credential")&&a.credential&&a.credential.hasOwnProperty("token")&&a.credential.token&&(m=m+"&token="+a.credential.token) +var n=new XMLHttpRequest +n.open("POST",a.url+"/applyEdits",!0),n.setRequestHeader("Content-type","application/x-www-form-urlencoded"),n.onload=function(){if(200===n.status&&""!==n.responseText)try{var a=JSON.parse(this.response) +f(a.addResults,a.updateResults,a.deleteResults)}catch(b){g("Unable to parse xhr response",n)}},n.onerror=function(a){g(a)},n.ontimeout=function(){g("xhr timeout error")},n.timeout=this._defaultXhrTimeout,n.send(m)},_parseResponsesArray:function(a){var c=new b,d=0 +for(var e in a)a.hasOwnProperty(e)&&(a[e].addResults.map(function(a){a.success||d++}),a[e].updateResults.map(function(a){a.success||d++}),a[e].deleteResults.map(function(a){a.success||d++})) +return d>0?c.resolve(!1):c.resolve(!0),c.promise}})}),"undefined"!=typeof O?O.esri.Edit={}:(O={},O.esri={Edit:{}}),O.esri.Edit.EditStore=function(){"use strict" +this._db=null,this._isDBInit=!1,this.dbName="features_store",this.objectStoreName="features",this.objectId="objectid",this.ADD="add",this.UPDATE="update",this.DELETE="delete",this.FEATURE_LAYER_JSON_ID="feature-layer-object-1001",this.FEATURE_COLLECTION_ID="feature-collection-object-1001",this.PHANTOM_GRAPHIC_PREFIX="phantom-layer",this._PHANTOM_PREFIX_TOKEN="|@|",this.isSupported=function(){return window.indexedDB?!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 offlineEdit.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){a(!1,{message:"object was not deleted."})},function(b){a(!0,{message:"id does not exist"})})},function(b){a(!1,{message:"id does not exist"})}),d.editExists(e).then(function(a){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})}},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){d.then(function(c){e.editExists(a).then(function(a){b(!1,"item was not deleted")},function(a){b(!0,"item successfully deleted")})},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,"item doesn't exist in db")})})},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) +if("undefined"==typeof a)return void b(!1,"id is undefined.") +var d=c.get(a) +d.onsuccess=function(){var c=d.result +c&&c.id==a?b(!0,c):b(!1,"Id not found")},d.onerror=function(a){b(!1,a)}},this.getAllEdits=function(a){if(null!==this._db){var b=this.FEATURE_LAYER_JSON_ID,c=this.FEATURE_COLLECTION_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.hasOwnProperty("value")&&f.value.hasOwnProperty("id")?(f.value.id!==b&&f.value.id!==c&&-1==f.value.id.indexOf(d)&&a(f.value,null),f["continue"]()):a(null,"end")}.bind(this),e.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.FEATURE_COLLECTION_ID,e=this.PHANTOM_GRAPHIC_PREFIX,f=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor() +f.onsuccess=function(f){var g=f.target.result +g&&g.value&&g.value.id?(g.value.id!==c&&g.value.id!==d&&-1==g.value.id.indexOf(e)&&b.push(g.value),g["continue"]()):a(b,"end")}.bind(this),f.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){e.then(function(a){f.editExists(g).then(function(a){c(!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.FEATURE_COLLECTION_ID,e=this.PHANTOM_GRAPHIC_PREFIX,f=this._db.transaction([this.objectStoreName],"readwrite"),g=f.objectStore(this.objectStoreName) +g.openCursor().onsuccess=function(f){var g=f.target.result +g&&g.value&&g.value.id&&-1==g.value.id.indexOf(e)?(g.value.id!==c&&g.value.id!==d&&b++,g["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:"objectId 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.FEATURE_COLLECTION_ID,d=this.PHANTOM_GRAPHIC_PREFIX,e={sizeBytes:0,editCount:0},f=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor() +f.onsuccess=function(f){var g=f.target.result +if(g&&g.value&&g.value.id){var h=g.value,i=JSON.stringify(h) +e.sizeBytes+=i.length,-1==g.value.id.indexOf(d)&&g.value.id!==b&&g.value.id!==c&&(e.editCount+=1),g["continue"]()}else a(e,null)},f.onerror=function(b){a(null,b)}},this._pushFeatureCollections=function(a,b){var c=this._db.transaction([this.objectStoreName],"readwrite") +c.oncomplete=function(a){b(!0)},c.onerror=function(a){b(!1,a.target.error.message)} +var d=c.objectStore(this.objectStoreName) +d.put(a)},this._getFeatureCollections=function(a){var b=this._db.transaction([this.objectStoreName],"readonly").objectStore(this.objectStoreName),c=b.get(this.FEATURE_COLLECTION_ID) +c.onsuccess=function(){var b=c.result +"undefined"!=typeof b?a(!0,b):a(!1,null)},c.onerror=function(b){a(!1,b)}},this.init=function(a){var b=indexedDB.open(this.dbName,11) +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),b.createObjectStore(this.objectStoreName,{keyPath:"id"})}.bind(this),b.onsuccess=function(b){this._db=b.target.result,this._isDBInit=!0,a(!0,null)}.bind(this)}},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)} +try{j.objectStore(this.objectStoreName).put(i)}catch(k){f(!1,k)}}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 +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)}} diff --git a/dist/offline-edit-advanced-src.js b/dist/offline-edit-advanced-src.js new file mode 100644 index 00000000..841b4250 --- /dev/null +++ b/dist/offline-edit-advanced-src.js @@ -0,0 +1,3430 @@ +/*! esri-offline-maps - v3.0.0 - 2015-11-23 +* Copyright (c) 2015 Environmental Systems Research Institute, Inc. +* Apache License*/ +// Configure offline/online detection +// Requires: http://github.hubspot.com/offline/docs/welcome/ + +Offline.options = { // jshint ignore:line + checks: { + image: { + url: function() { + return 'http://esri.github.io/offline-editor-js/tiny-image.png?_=' + (Math.floor(Math.random() * 1000000000)); + } + }, + active: 'image' + } +}; +/*jshint -W030 */ +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 (Evented, Deferred, all, declare, array, domAttr, domStyle, query, + esriConfig, GraphicsLayer, Graphic, esriRequest, SimpleMarkerSymbol, SimpleLineSymbol, SimpleFillSymbol, urlUtils) { + "use strict"; + return declare("O.esri.Edit.OfflineEditAdvanced", [Evented], + { + _onlineStatus: "online", + _featureLayers: {}, + _featureCollectionUsageFlag: false, // if a feature collection was used to create the feature layer. + _editStore: new O.esri.Edit.EditStore(), + _defaultXhrTimeout: 15000, // ms + + ONLINE: "online", // all edits will directly go to the server + OFFLINE: "offline", // edits will be enqueued + RECONNECTING: "reconnecting", // sending stored edits to the server + attachmentsStore: null, // indexedDB for storing attachments + proxyPath: null, // by default we use CORS and therefore proxyPath is null + + ENABLE_FEATURECOLLECTION: false, // Set this to true for full offline use if you want to use the + // getFeatureCollections() pattern of reconstituting a feature layer. + + // Database properties + DB_NAME: "features_store", // Sets the database name. + DB_OBJECTSTORE_NAME: "features",// Represents an object store that allows access to a set of data in the IndexedDB database + DB_UID: "objectid", // Set this based on the unique identifier is set up in the feature service + + 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 (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. + + // manager emits event when... + events: { + EDITS_SENT: "edits-sent", // ...whenever any edit is actually sent to the server + EDITS_ENQUEUED: "edits-enqueued", // ...when an edit is enqueued (and not sent to the server) + EDITS_ENQUEUED_ERROR: "edits-enqueued-error", // ...when there is an error during the queing process + 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", + }, + + /** + * Need to call this method only if you want to support offline attachments + * it is optional + * @param callback(success) + * @returns void + */ + initAttachments: function (callback) { + callback = callback || function (success) { + console.log("attachments inited ", success ? "ok" : "failed"); + }; + + if (!this._checkFileAPIs()) { + return callback(false, "File APIs not supported"); + } + + try { + this.attachmentsStore = new O.esri.Edit.AttachmentsStore(); + this.attachmentsStore.dbName = this.ATTACHMENTS_DB_NAME; + this.attachmentsStore.objectStoreName = this.ATTACHMENTS_DB_OBJECTSTORE_NAME; + + if (/*false &&*/ this.attachmentsStore.isSupported()) { + this.attachmentsStore.init(callback); + } + else { + return callback(false, "indexedDB not supported"); + } + } + catch (err) { + console.log("problem! " + err.toString()); + } + }, + + /** + * Overrides a feature layer. Call this AFTER the FeatureLayer's 'update-end' event. + * IMPORTANT: If dataStore is specified it will be saved to the database. Any complex + * objects such as [esri.Graphic] will need to be serialized or you will get an IndexedDB error. + * @param layer + * @param updateEndEvent The FeatureLayer's update-end event object + * @param callback {true, null} or {false, errorString} Traps whether or not the database initialized + * @param dataStore Optional configuration Object. Added @ v2.5. There is only one reserved object key and that is "id". + * Use this option to store featureLayerJSON and any other configuration information you'll need access to after + * a full offline browser restart. + * @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 + + if(!layer.loaded) { + console.error("Make sure to initialize OfflineEditAdvanced after layer loaded and feature layer update-end event."); + } + + // NOTE: At v2.6.1 we've discovered that not all feature layers support objectIdField. + // However, to try to be consistent here with how the library is managing Ids + // 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 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) { + extendPromises.push(this._initializeDB(dataStore, url)); + } + + // replace the applyEdits() method + layer._applyEdits = layer.applyEdits; + + + // attachments + layer._addAttachment = layer.addAttachment; + layer._queryAttachmentInfos = layer.queryAttachmentInfos; + layer._deleteAttachments = layer.deleteAttachments; + layer._updateAttachment = layer.updateAttachment; + + /* + operations supported offline: + 1. add a new attachment to an existing feature (DONE) + 2. add a new attachment to a new feature (DONE) + 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 (DONE) + + concerns: + - manage the relationship between offline features and attachments: what if the user wants to add + an attachment to a feature that is still offline? we need to keep track of objectids so that when + 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) + */ + + // + // attachments + // + layer.queryAttachmentInfos = function (objectId, callback, errback) { + if (self.getOnlineStatus() === self.ONLINE) { + var def = this._queryAttachmentInfos(objectId, + function () { + console.log(arguments); + self.emit(self.events.ATTACHMENTS_INFO, arguments); + callback && callback.apply(this, arguments); + }, + errback); + return def; + } + + if (!self.attachmentsStore) { + console.log("in order to support attachments you need to call initAttachments() method of OfflineEditAdvanced"); + return; + } + + // will only return LOCAL attachments + var deferred = new Deferred(); + self.attachmentsStore.getAttachmentsByFeatureId(this.url, objectId, function (attachments) { + callback && callback(attachments); + deferred.resolve(attachments); + }); + return deferred; + }; + + layer.addAttachment = function (objectId, formNode, callback, errback) { + + if (self.getOnlineStatus() === self.ONLINE) { + return this._addAttachment(objectId, formNode, + function () { + console.log(arguments); + self.emit(self.events.ATTACHMENTS_SENT, arguments); + callback && callback.apply(this, arguments); + }, + function (err) { + console.log("addAttachment: " + err); + errback && errback.apply(this, arguments); + } + ); + } + + if (!self.attachmentsStore) { + console.error("in order to support attachments you need to call initAttachments() method of OfflineEditAdvanced"); + return; + } + + var files = this._getFilesFromForm(formNode); + var file = files[0]; // addAttachment can only add one file, so the rest -if any- are ignored + + var deferred = new Deferred(); + var attachmentId = this._getNextTempId(); + self.attachmentsStore.store(this.url, attachmentId, objectId, file,self.attachmentsStore.TYPE.ADD, function (success, newAttachment) { + var returnValue = {attachmentId: attachmentId, objectId: objectId, success: success}; + if (success) { + self.emit(self.events.ATTACHMENT_ENQUEUED, returnValue); + callback && callback(returnValue); + deferred.resolve(returnValue); + + // replace the default URL that is set by attachmentEditor with the local file URL + var attachmentUrl = this._url.path + "/" + objectId + "/attachments/" + attachmentId; + var attachmentElement = query("[href=" + attachmentUrl + "]"); + attachmentElement.attr("href", newAttachment.url); + } + else { + returnValue.error = "can't store attachment"; + errback && errback(returnValue); + deferred.reject(returnValue); + } + }.bind(this)); + + return deferred; + }; + + layer.updateAttachment = function(objectId, attachmentId, formNode, callback, errback) { + if (self.getOnlineStatus() === self.ONLINE) { + return this._updateAttachment(objectId, attachmentId, formNode, + function () { + callback && callback.apply(this, arguments); + }, + function (err) { + console.log("updateAttachment: " + err); + errback && errback.apply(this, arguments); + }); + //return def; + } + + if (!self.attachmentsStore) { + console.error("in order to support attachments you need to call initAttachments() method of OfflineEditAdvanced"); + return; + } + + var files = this._getFilesFromForm(formNode); + var file = files[0]; // addAttachment can only add one file, so the rest -if any- are ignored + var action = self.attachmentsStore.TYPE.UPDATE; // Is this an ADD or an UPDATE? + + var deferred = new Deferred(); + + // If the attachment has a temporary ID we want to keep it's action as an ADD. + // Otherwise we'll get an error when we try to UPDATE an ObjectId that doesn't exist in ArcGIS Online or Server. + if(attachmentId < 0) { + action = self.attachmentsStore.TYPE.ADD; + } + + self.attachmentsStore.store(this.url, attachmentId, objectId, file, action, function (success, newAttachment) { + var returnValue = {attachmentId: attachmentId, objectId: objectId, success: success}; + if (success) { + self.emit(self.events.ATTACHMENT_ENQUEUED, returnValue); + callback && callback(returnValue); + deferred.resolve(returnValue); + + // replace the default URL that is set by attachmentEditor with the local file URL + var attachmentUrl = this._url.path + "/" + objectId + "/attachments/" + attachmentId; + var attachmentElement = query("[href=" + attachmentUrl + "]"); + attachmentElement.attr("href", newAttachment.url); + } + else { + returnValue.error = "layer.updateAttachment::attachmentStore can't store attachment"; + errback && errback(returnValue); + deferred.reject(returnValue); + } + }.bind(this)); + + return deferred; + }; + + layer.deleteAttachments = function (objectId, attachmentsIds, callback, errback) { + if (self.getOnlineStatus() === self.ONLINE) { + var def = this._deleteAttachments(objectId, attachmentsIds, + function () { + callback && callback.apply(this, arguments); + }, + function (err) { + console.log("deleteAttachments: " + err); + errback && errback.apply(this, arguments); + }); + return def; + } + + if (!self.attachmentsStore) { + console.error("in order to support attachments you need to call initAttachments() method of OfflineEditAdvanced"); + return; + } + + // case 1.- it is a new attachment + // case 2.- it is an already existing attachment + + // asynchronously delete each of the attachments + var promises = []; + attachmentsIds.forEach(function (attachmentId) { + attachmentId = parseInt(attachmentId, 10); // to number + + var deferred = new Deferred(); + + // IMPORTANT: If attachmentId < 0 then it's a local/new attachment + // and we can simply delete it from the attachmentsStore. + // However, if the attachmentId > 0 then we need to store the DELETE + // so that it can be processed and sync'd correctly during _uploadAttachments(). + if(attachmentId < 0) { + self.attachmentsStore.delete(attachmentId, function (success) { + var result = {objectId: objectId, attachmentId: attachmentId, success: success}; + deferred.resolve(result); + }); + } + else { + var dummyBlob = new Blob([],{type: "image/png"}); //TO-DO just a placeholder. Need to consider add a null check. + self.attachmentsStore.store(this.url, attachmentId, objectId, dummyBlob,self.attachmentsStore.TYPE.DELETE, function (success, newAttachment) { + var returnValue = {attachmentId: attachmentId, objectId: objectId, success: success}; + if (success) { + deferred.resolve(returnValue); + } + else { + deferred.reject(returnValue); + } + }.bind(this)); + } + //console.assert(attachmentId < 0, "we only support deleting local attachments"); + promises.push(deferred); + }, this); + + // call callback once all deletes have finished + // IMPORTANT: This returns an array!!! + var allPromises = all(promises); + allPromises.then(function (results) { + callback && callback(results); + }); + + return allPromises; + }; + + // + // other functions + // + + /** + * Overrides the ArcGIS API for JavaSccript applyEdits() method. + * @param adds Creates a new edit entry. + * @param updates Updates an existing entry. + * @param deletes Deletes an existing entry. + * @param callback Called when the operation is complete. + * @param errback An error object is returned if an error occurs + * @returns {*} deferred + * @event EDITS_ENQUEUED boolean if all edits successfully stored while offline + * @event EDITS_ENQUEUED_ERROR string message if there was an error while storing an edit while offline + */ + layer.applyEdits = function (adds, updates, deletes, callback, errback) { + // inside this method, 'this' will be the FeatureLayer + // and 'self' will be the offlineFeatureLayer object + var promises = []; + + if (self.getOnlineStatus() === self.ONLINE) { + var def = this._applyEdits(adds, updates, deletes, + function () { + self.emit(self.events.EDITS_SENT, arguments); + callback && callback.apply(this, arguments); + }, + errback); + return def; + } + + var deferred1 = new Deferred(); + var results = {addResults: [], updateResults: [], deleteResults: []}; + var updatesMap = {}; + + var _adds = adds || []; + _adds.forEach(function (addEdit) { + var deferred = new Deferred(); + + var objectId = this._getNextTempId(); + + addEdit.attributes[this.objectIdField] = objectId; + + var thisLayer = this; + + // We need to run some validation tests against each feature being added. + // Adding the same feature multiple times results in the last edit wins. LIFO. + this._validateFeature(addEdit,this.url,self._editStore.ADD).then(function(result){ + console.log("EDIT ADD IS BACK!!! " ); + + if(result.success){ + thisLayer._pushValidatedAddFeatureToDB(thisLayer,addEdit,result.operation,results,objectId,deferred); + } + else{ + // If we get here then we deleted an edit that was added offline. + // We also have deleted the phantom graphic. + deferred.resolve(true); + } + + },function(error){ + console.log("_validateFeature: Unable to validate!"); + deferred.reject(error); + }); + + promises.push(deferred); + }, this); + + updates = updates || []; + updates.forEach(function (updateEdit) { + var deferred = new Deferred(); + + var objectId = updateEdit.attributes[this.objectIdField]; + updatesMap[objectId] = updateEdit; + + var thisLayer = this; + + // We need to run some validation tests against each feature being updated. + // If we have added a feature and we need to update it then we change it's operation type to "add" + // and the last edits wins. LIFO. + this._validateFeature(updateEdit,this.url,self._editStore.UPDATE).then(function(result){ + console.log("EDIT UPDATE IS BACK!!! " ); + + if(result.success){ + thisLayer._pushValidatedUpdateFeatureToDB(thisLayer,updateEdit,result.operation,results,objectId,deferred); + } + else{ + // If we get here then we deleted an edit that was added offline. + // We also have deleted the phantom graphic. + deferred.resolve(true); + } + + },function(error){ + console.log("_validateFeature: Unable to validate!"); + deferred.reject(error); + }); + + promises.push(deferred); + }, this); + + deletes = deletes || []; + deletes.forEach(function (deleteEdit) { + var deferred = new Deferred(); + + var objectId = deleteEdit.attributes[this.objectIdField]; + + var thisLayer = this; + + // We need to run some validation tests against each feature being deleted. + // If we have added a feature and then deleted it in the app then we go ahead + // and delete it and its phantom graphic from the database. + // NOTE: at this time we don't handle attachments automatically + this._validateFeature(deleteEdit,this.url,self._editStore.DELETE).then(function(result){ + console.log("EDIT DELETE IS BACK!!! " ); + + if(result.success){ + thisLayer._pushValidatedDeleteFeatureToDB(thisLayer,deleteEdit,result.operation,results,objectId,deferred); + } + else{ + // If we get here then we deleted an edit that was added offline. + // We also have deleted the phantom graphic. + deferred.resolve(true); + } + + },function(error){ + console.log("_validateFeature: Unable to validate!"); + deferred.reject(error); + }); + + promises.push(deferred); + }, this); + + all(promises).then(function (r) { + // Make sure all edits were successful. If not throw an error. + var promisesSuccess = true; + for (var v = 0; v < r.length; v++) { + if (r[v] === false) { + promisesSuccess = false; + } + } + + layer._pushFeatureCollections(function(success){ + console.log("All edits done"); + + if(success && promisesSuccess){ + self.emit(self.events.EDITS_ENQUEUED, results); + } + else { + if(!success){ + console.log("applyEdits() there was a problem with _pushFeatureCollections."); + } + self.emit(self.events.EDITS_ENQUEUED_ERROR, results); + } + + //promisesSuccess === true ? self.emit(self.events.EDITS_ENQUEUED, results) : self.emit(self.events.EDITS_ENQUEUED_ERROR, results); + + // we already pushed the edits into the database, now we let the FeatureLayer to do the local updating of the layer graphics + this._editHandler(results, _adds, updatesMap, callback, errback, deferred1); + }.bind(this)); + + //success === true ? self.emit(self.events.EDITS_ENQUEUED, results) : self.emit(self.events.EDITS_ENQUEUED_ERROR, results); + // EDITS_ENQUEUED = callback(true, edit), and EDITS_ENQUEUED_ERROR = callback(false, /*String */ error) + //this._editHandler(results, _adds, updatesMap, callback, errback, deferred1); + }.bind(this)); + + return deferred1; + + }; // layer.applyEdits() + + /** + * Converts an array of graphics/features into JSON + * @param features + * @param updateEndEvent The layer's 'update-end' event + * @param callback + */ + layer.convertGraphicLayerToJSON = function (features, updateEndEvent, callback) { + var layerDefinition = {}; + + // We want to be consistent, but we've discovered that not all feature layers have an objectIdField + if(updateEndEvent.target.hasOwnProperty("objectIdField")) + { + layerDefinition.objectIdFieldName = updateEndEvent.target.objectIdField; + }else { + layerDefinition.objectIdFieldName = this.objectIdField; + } + + layerDefinition.globalIdFieldName = updateEndEvent.target.globalIdField; + layerDefinition.geometryType = updateEndEvent.target.geometryType; + layerDefinition.spatialReference = updateEndEvent.target.spatialReference; + layerDefinition.fields = updateEndEvent.target.fields; + + var length = features.length; + var jsonArray = []; + for (var i = 0; i < length; i++) { + var jsonGraphic = features[i].toJson(); + jsonArray.push(jsonGraphic); + if (i == (length - 1)) { + var featureJSON = JSON.stringify(jsonArray); + var layerDefJSON = JSON.stringify(layerDefinition); + callback(featureJSON, layerDefJSON); + break; + } + } + }; + + /** + * Retrieves f=json from the feature layer + * @param url FeatureLayer's URL + * @param callback + * @private + */ + layer.getFeatureLayerJSON = function (url, callback) { + require(["esri/request"], function (esriRequest) { + var request = esriRequest({ + url: url, + content: {f: "json"}, + handleAs: "json", + callbackParamName: "callback" + }); + + request.then(function (response) { + console.log("Success: ", response); + callback(true, response); + }, function (error) { + console.log("Error: ", error.message); + callback(false, error.message); + }); + }); + }; + + /** + * Sets the optional feature layer storage object + * @param jsonObject + * @param callback + */ + layer.setFeatureLayerJSONDataStore = function(jsonObject, callback){ + self._editStore.pushFeatureLayerJSON(jsonObject,function(success,error){ + callback(success,error); + }); + }; + + /** + * Retrieves the optional feature layer storage object + * @param callback callback(true, object) || callback(false, error) + */ + layer.getFeatureLayerJSONDataStore = function(callback){ + self._editStore.getFeatureLayerJSON(function(success,message){ + callback(success,message); + }); + }; + + /** + * Sets the phantom layer with new features. + * Used to restore PhantomGraphicsLayer after offline restart + * @param graphicsArray an array of Graphics + */ + layer.setPhantomLayerGraphics = function (graphicsArray) { + var length = graphicsArray.length; + + if (length > 0) { + for (var i = 0; i < length; i++) { + var graphic = new Graphic(graphicsArray[i]); + this._phantomLayer.add(graphic); + } + } + }; + + /** + * Returns the array of graphics from the phantom graphics layer. + * This layer identifies features that have been modified + * while offline. + * @returns {array} + */ + layer.getPhantomLayerGraphics = function (callback) { + //return layer._phantomLayer.graphics; + var graphics = layer._phantomLayer.graphics; + var length = layer._phantomLayer.graphics.length; + var jsonArray = []; + for (var i = 0; i < length; i++) { + var jsonGraphic = graphics[i].toJson(); + jsonArray.push(jsonGraphic); + if (i == (length - 1)) { + var graphicsJSON = JSON.stringify(jsonArray); + callback(graphicsJSON); + break; + } + } + }; + + /** + * Returns an array of phantom graphics from the database. + * @param callback callback (true, array) or (false, errorString) + */ + layer.getPhantomGraphicsArray = function(callback){ + self._editStore.getPhantomGraphicsArray(function(array,message){ + if(message == "end"){ + callback(true,array); + } + else{ + callback(false,message); + } + }); + }; + + /** + * Returns the approximate size of the attachments database in bytes + * @param callback callback({usage}, error) Whereas, the usage Object is {sizeBytes: number, attachmentCount: number} + */ + layer.getAttachmentsUsage = function(callback) { + self.attachmentsStore.getUsage(function(usage,error){ + callback(usage,error); + }); + }; + + /** + * Full attachments database reset. + * CAUTION! If some attachments weren't successfully sent, then their record + * will still exist in the database. If you use this function you + * will also delete those records. + * @param callback (boolean, error) + */ + layer.resetAttachmentsDatabase = function(callback){ + self.attachmentsStore.resetAttachmentsQueue(function(result,error){ + callback(result,error); + }); + }; + + /** + * Returns the approximate size of the edits database in bytes + * @param callback callback({usage}, error) Whereas, the usage Object is {sizeBytes: number, editCount: number} + */ + layer.getUsage = function(callback){ + self._editStore.getUsage(function(usage,error){ + callback(usage,error); + }); + }; + + /** + * Full edits database reset. + * CAUTION! If some edits weren't successfully sent, then their record + * will still exist in the database. If you use this function you + * will also delete those records. + * @param callback (boolean, error) + */ + layer.resetDatabase = function(callback){ + self._editStore.resetEditsQueue(function(result,error){ + callback(result,error); + }); + }; + + /** + * Returns the number of edits pending in the database. + * @param callback callback( int ) + */ + layer.pendingEditsCount = function(callback){ + self._editStore.pendingEditsCount(function(count){ + callback(count); + }); + }; + + /** + * Create a featureDefinition + * @param featureLayer + * @param featuresArr + * @param geometryType + * @param callback + */ + layer.getFeatureDefinition = function (/* Object */ featureLayer, /* Array */ featuresArr, /* String */ geometryType, callback) { + + var featureDefinition = { + "layerDefinition": featureLayer, + "featureSet": { + "features": featuresArr, + "geometryType": geometryType + } + + }; + + callback(featureDefinition); + }; + + /** + * Returns an iterable array of all edits stored in the database + * Each item in the array is an object and contains: + * { + * id: "internal ID", + * operation: "add, update or delete", + * layer: "layerURL", + * type: "esri Geometry Type", + * graphic: "esri.Graphic converted to JSON then serialized" + * } + * @param callback (true, array) or (false, errorString) + */ + layer.getAllEditsArray = function(callback){ + self._editStore.getAllEditsArray(function(array,message){ + if(message == "end"){ + callback(true,array); + } + else{ + callback(false,message); + } + }); + }; + + /* internal methods */ + + /** + * Automatically creates a set of featureLayerCollections. This is specifically for + * use with offline browser restarts. You can retrieve the collections and use them + * to reconstitute a featureLayer and then redisplay all the associated features. + * + * To retrieve use OfflineEditAdvanced.getFeatureCollections(). + * @param callback (boolean) + * @private + */ + layer._pushFeatureCollections = function(callback){ + + // First let's see if any collections exists + self._editStore._getFeatureCollections(function(success, result) { + + var featureCollection = + { + featureLayerUrl: layer.url, + featureLayerCollection: layer.toJson() + }; + + // An array of feature collections, of course :-) + var featureCollectionsArray = [ + featureCollection + ]; + + // An object for storing multiple feature collections + var featureCollectionsObject = { + // The id is required because the editsStore keypath + // uses it as a UID for all entries in the database + id: self._editStore.FEATURE_COLLECTION_ID, + featureCollections: featureCollectionsArray + }; + + // THIS IS A HACK. + // There is a bug in JS API 3.11+ when you create a feature layer from a featureCollectionObject + // the hasAttachments property does not get properly repopulated. + layer.hasAttachments = featureCollection.featureLayerCollection.layerDefinition.hasAttachments; + + // If the featureCollectionsObject already exists + if(success){ + var count = 0; + for(var i = 0; i < result.featureCollections.length; i++) { + + // Update the current feature collection + if(result.featureCollections[i].featureLayerUrl === layer.url) { + count++; + result.featureCollections[i] = featureCollection; + } + } + + // If we have a new feature layer then add it to the featureCollections array + if(count === 0) { + result.featureCollections.push(featureCollection); + } + } + // If it does not exist then we need to add a featureCollectionsObject + else if(!success && result === null) { + result = featureCollectionsObject; + } + else { + console.error("There was a problem retrieving the featureCollections from editStore."); + } + + // Automatically update the featureCollectionsObject in the database with every ADD, UPDATE + // and DELETE. It can be retrieved via OfflineEditAdvanced.getFeatureCollections(); + self._editStore._pushFeatureCollections(result, function(success, error) { + if(!success){ + console.error("There was a problem creating the featureCollectionObject: " + error); + callback(false); + } + else { + callback(true); + } + }.bind(this)); + }); + }; + + /** + * Pushes a DELETE request to the database after it's been validated + * @param layer + * @param deleteEdit + * @param operation + * @param resultsArray + * @param objectId + * @param deferred + * @private + */ + layer._pushValidatedDeleteFeatureToDB = function(layer,deleteEdit,operation,resultsArray,objectId,deferred){ + self._editStore.pushEdit(operation, layer.url, deleteEdit, function (result, error) { + + if(result){ + resultsArray.deleteResults.push({success: true, error: null, objectId: objectId}); + + // Use the correct key as set by self.DB_UID + var tempIdObject = {}; + tempIdObject[self.DB_UID] = objectId; + + var phantomDelete = new Graphic( + deleteEdit.geometry, + self._getPhantomSymbol(deleteEdit.geometry, self._editStore.DELETE), + tempIdObject + ); + + layer._phantomLayer.add(phantomDelete); + + // Add phantom graphic to the database + self._editStore.pushPhantomGraphic(phantomDelete, function (result) { + if (!result) { + console.log("There was a problem adding phantom graphic id: " + objectId); + } + else{ + console.log("Phantom graphic " + objectId + " added to database as a deletion."); + domAttr.set(phantomDelete.getNode(), "stroke-dasharray", "4,4"); + domStyle.set(phantomDelete.getNode(), "pointer-events", "none"); + } + }); + + if (self.attachmentsStore) { + // delete local attachments of this feature, if any... we just launch the delete and don't wait for it to complete + self.attachmentsStore.deleteAttachmentsByFeatureId(layer.url, objectId, function (deletedCount) { + console.log("deleted", deletedCount, "attachments of feature", objectId); + }); + } + } + else{ + // If we can't push edit to database then we don't create a phantom graphic + resultsArray.deleteResults.push({success: false, error: error, objectId: objectId}); + } + + deferred.resolve(result); + }); + }; + + /** + * Pushes an UPDATE request to the database after it's been validated + * @param layer + * @param updateEdit + * @param operation + * @param resultsArray + * @param objectId + * @param deferred + * @private + */ + layer._pushValidatedUpdateFeatureToDB = function(layer,updateEdit,operation,resultsArray,objectId,deferred){ + self._editStore.pushEdit(operation, layer.url, updateEdit, function (result, error) { + + if(result){ + resultsArray.updateResults.push({success: true, error: null, objectId: objectId}); + + // Use the correct key as set by self.DB_UID + var tempIdObject = {}; + tempIdObject[self.DB_UID] = objectId; + + var phantomUpdate = new Graphic( + updateEdit.geometry, + self._getPhantomSymbol(updateEdit.geometry, self._editStore.UPDATE), + tempIdObject + ); + + layer._phantomLayer.add(phantomUpdate); + + // Add phantom graphic to the database + self._editStore.pushPhantomGraphic(phantomUpdate, function (result) { + if (!result) { + console.log("There was a problem adding phantom graphic id: " + objectId); + } + else{ + console.log("Phantom graphic " + objectId + " added to database as an update."); + domAttr.set(phantomUpdate.getNode(), "stroke-dasharray", "5,2"); + domStyle.set(phantomUpdate.getNode(), "pointer-events", "none"); + } + }); + + } + else{ + // If we can't push edit to database then we don't create a phantom graphic + resultsArray.updateResults.push({success: false, error: error, objectId: objectId}); + } + + deferred.resolve(result); + }); + }; + + /** + * Pushes an ADD request to the database after it's been validated + * @param layer + * @param addEdit + * @param operation + * @param resultsArray + * @param objectId + * @param deferred + * @private + */ + layer._pushValidatedAddFeatureToDB = function(layer,addEdit,operation,resultsArray,objectId,deferred){ + self._editStore.pushEdit(operation, layer.url, addEdit, function (result, error) { + if(result){ + resultsArray.addResults.push({success: true, error: null, objectId: objectId}); + + // Use the correct key as set by self.DB_UID + var tempIdObject = {}; + tempIdObject[self.DB_UID] = objectId; + + var phantomAdd = new Graphic( + addEdit.geometry, + self._getPhantomSymbol(addEdit.geometry, self._editStore.ADD), + tempIdObject + ); + + // Add phantom graphic to the layer + layer._phantomLayer.add(phantomAdd); + + // Add phantom graphic to the database + self._editStore.pushPhantomGraphic(phantomAdd, function (result) { + if (!result) { + console.log("There was a problem adding phantom graphic id: " + objectId); + } + else{ + console.log("Phantom graphic " + objectId + " added to database as an add."); + domAttr.set(phantomAdd.getNode(), "stroke-dasharray", "10,4"); + domStyle.set(phantomAdd.getNode(), "pointer-events", "none"); + } + + }); + } + else{ + // If we can't push edit to database then we don't create a phantom graphic + resultsArray.addResults.push({success: false, error: error, objectId: objectId}); + } + + deferred.resolve(result); + }); + }; + + /** + * Validates duplicate entries. Last edit on same feature can overwite any previous values. + * Note: if an edit was already added offline and you delete it then we return success == false + * @param graphic esri.Graphic. + * @param layerUrl the URL of the feature service + * @param operation add, update or delete action on an edit + * @returns deferred {success:boolean,graphic:graphic,operation:add|update|delete} + * @private + */ + layer._validateFeature = function (graphic,layerUrl,operation) { + + var deferred = new Deferred(); + + var id = layerUrl + "/" + graphic.attributes[self.DB_UID]; + + self._editStore.getEdit(id,function(success,result){ + if (success) { + switch( operation ) + { + case self._editStore.ADD: + // Not good - however we'll allow the new ADD to replace/overwrite existing edit + // and pass it through unmodified. Last ADD wins. + deferred.resolve({"success":true,"graphic":graphic,"operation":operation}); + break; + case self._editStore.UPDATE: + // If we are doing an update on a feature that has not been added to + // the server yet, then we need to maintain its operation as an ADD + // and not an UPDATE. This avoids the potential for an error if we submit + // an update operation on a feature that has not been added to the + // database yet. + if(result.operation == self._editStore.ADD){ + graphic.operation = self._editStore.ADD; + operation = self._editStore.ADD; + } + deferred.resolve({"success":true,"graphic":graphic,"operation":operation}); + break; + case self._editStore.DELETE: + + var resolved = true; + + if(result.operation == self._editStore.ADD){ + // If we are deleting a new feature that has not been added to the + // server yet we need to delete it and its phantom graphic. + layer._deleteTemporaryFeature(graphic,function(success){ + if(!success){ + resolved = false; + } + }); + } + deferred.resolve({"success":resolved,"graphic":graphic,"operation":operation}); + break; + } + } + else if(result == "Id not found"){ + // Let's simply pass the graphic back as good-to-go. + // No modifications needed because the graphic does not + // already exist in the database. + deferred.resolve({"success":true,"graphic":graphic,"operation":operation}); + } + else{ + deferred.reject(graphic); + } + }); + + return deferred; + }; + + /** + * Delete a graphic and its associated phantom graphic that has been added while offline. + * @param graphic + * @param callback + * @private + */ + layer._deleteTemporaryFeature = function(graphic,callback){ + + var phantomGraphicId = self._editStore.PHANTOM_GRAPHIC_PREFIX + self._editStore._PHANTOM_PREFIX_TOKEN + graphic.attributes[self.DB_UID]; + + function _deleteGraphic(){ + var deferred = new Deferred(); + self._editStore.delete(layer.url,graphic,function(success,error){ + if(success){ + deferred.resolve(true); + } + else{ + deferred.resolve(false); + } + }); + return deferred.promise; + } + + function _deletePhantomGraphic(){ + var deferred = new Deferred(); + self._editStore.deletePhantomGraphic(phantomGraphicId,function(success){ + if(success) { + deferred.resolve(true); + } + else { + deferred.resolve(false); + } + }, function(error) { + deferred.resolve(false); + }); + return deferred.promise; + } + + all([_deleteGraphic(),_deletePhantomGraphic()]).then(function (results) { + callback(results); + }); + + }; + + layer._getFilesFromForm = function (formNode) { + var files = []; + var inputNodes = array.filter(formNode.elements, function (node) { + return node.type === "file"; + }); + inputNodes.forEach(function (inputNode) { + files.push.apply(files, inputNode.files); + }, this); + return files; + }; + + layer._replaceFeatureIds = function (tempObjectIds, newObjectIds, callback) { + console.log("replacing ids of attachments", tempObjectIds, newObjectIds); + console.assert(tempObjectIds.length === newObjectIds.length, "arrays must be the same length"); + + if (!tempObjectIds.length) { + console.log("no ids to replace!"); + callback(0); + } + + var i, n = tempObjectIds.length; + var count = n; + var successCount = 0; + for (i = 0; i < n; i++) { + self.attachmentsStore.replaceFeatureId(this.url, tempObjectIds[i], newObjectIds[i], function (success) { + --count; + successCount += (success ? 1 : 0); + if (count === 0) { + callback(successCount); + } + }.bind(this)); + } + }; + + // we need to identify ADDs before sending them to the server + // we assign temporary ids (using negative numbers to distinguish them from real ids) + layer._nextTempId = -1; + layer._getNextTempId = function () { + return this._nextTempId--; + }; + + function _initPhantomLayer() { + try { + layer._phantomLayer = new GraphicsLayer({opacity: 0.8}); + layer._map.addLayer(layer._phantomLayer); + } + catch (err) { + console.log("Unable to init PhantomLayer: " + err.message); + } + } + + _initPhantomLayer(); + + // We are currently only passing in a single deferred. + all(extendPromises).then(function (r) { + + // DB already initialized + if(r.length === 0 && url){ + // Initialize the internal featureLayerCollectionObject + if(this.ENABLE_FEATURECOLLECTION) { + layer._pushFeatureCollections(function(success){ + if(success){ + callback(true, null); + } + else { + callback(false, null); + } + }); + } + else { + 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; + + // Initialize the internal featureLayerCollectionObject + if(this.ENABLE_FEATURECOLLECTION) { + layer._pushFeatureCollections(function(success){ + if(success){ + callback(true, null); + } + else { + callback(false, null); + } + }); + } + else { + callback(true, null); + } + } + else { + // NOTE: We have to have a valid feature layer URL in order to initialize the featureLayerCollectionObject + console.error("getFeatureLayerJSON() failed and unable to create featureLayerCollectionObject."); + callback(false, message); + } + }.bind(this)); + } + else if(r[0].success){ + + if(this.ENABLE_FEATURECOLLECTION) { + layer._pushFeatureCollections(function(success){ + if(success){ + callback(true, null); + } + else { + callback(false, null); + } + }); + } + else { + callback(true, null); + } + } + }.bind(this)); + + }, // extend + + /** + * Forces library into an offline state. Any edits applied during this condition will be stored locally + */ + goOffline: function () { + console.log("offlineFeatureManager going offline"); + this._onlineStatus = this.OFFLINE; + }, + + /** + * 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 + * @param callback callback( boolean, errors ) + */ + goOnline: function (callback) { + console.log("OfflineEditAdvanced going online"); + this._onlineStatus = this.RECONNECTING; + this._replayStoredEdits(function (success, responses) { + var result = {success: success, responses: responses}; + this._onlineStatus = this.ONLINE; + if (this.attachmentsStore != null) { + console.log("sending attachments"); + this._sendStoredAttachments(function (success, uploadedResponses, dbResponses) { + //this._onlineStatus = this.ONLINE; + result.attachments = {success: success, responses: uploadedResponses, dbResponses: dbResponses}; + callback && callback(result); + }.bind(this)); + } + else { + //this._onlineStatus = this.ONLINE; + callback && callback(result); + } + }.bind(this)); + }, + + /** + * Determines if offline or online condition exists + * @returns {string} ONLINE or OFFLINE + */ + getOnlineStatus: function () { + return this._onlineStatus; + }, + + /** + * Serialize the feature layer graphics + * @param features Array of features + * @param callback Returns a JSON string + */ + serializeFeatureGraphicsArray: function (features, callback) { + var length = features.length; + var jsonArray = []; + for (var i = 0; i < length; i++) { + var jsonGraphic = features[i].toJson(); + jsonArray.push(jsonGraphic); + if (i == (length - 1)) { + var featureJSON = JSON.stringify(jsonArray); + callback(featureJSON); + break; + } + } + }, + + /** + * Retrieves the feature collection object. Specifically used in offline browser restarts. + * This is an object created automatically by the library and is updated with every ADD, UPDATE and DELETE. + * Attachments are handled separately and not part of the feature collection created here. + * + * It has the following signature: {id: "feature-collection-object-1001", + * featureLayerCollections: [{ featureCollection: [Object], featureLayerUrl: String }]} + * + * @param callback + */ + getFeatureCollections: function(callback){ + if(!this._editStore._isDBInit){ + + this._initializeDB(null,null).then(function(result){ + if(result.success){ + this._editStore._getFeatureCollections(function(success,message){ + callback(success,message); + }); + } + }.bind(this), function(err){ + callback(false, err); + }); + } + else { + this._editStore._getFeatureCollections(function(success,message){ + callback(success,message); + }); + } + }, + + /** + * Retrieves the optional feature layer storage object + * For use in full offline scenarios. + * @param callback callback(true, object) || callback(false, error) + */ + getFeatureLayerJSONDataStore: function(callback){ + if(!this._editStore._isDBInit){ + + this._initializeDB(null,null).then(function(result){ + if(result.success){ + this._editStore.getFeatureLayerJSON(function(success,message){ + callback(success,message); + }); + } + }.bind(this), function(err){ + callback(false, err); + }); + } + else { + this._editStore.getFeatureLayerJSON(function(success,message){ + callback(success,message); + }); + } + }, + + /* internal methods */ + + /** + * 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,url,callback){ + _initializeDB: function(dataStore,url){ + var deferred = new Deferred(); + + var editStore = this._editStore; + + // Configure the database + editStore.dbName = this.DB_NAME; + editStore.objectStoreName = this.DB_OBJECTSTORE_NAME; + editStore.objectId = this.DB_UID; + + // Attempt to initialize the database + editStore.init(function (result, error) { + + //////////////////////////////////////////////////// + // OFFLINE RESTART CONFIGURATION + // Added @ v2.5 + // + // Configure database for offline restart + // dataStore object allows you to store data that you'll + // use after an offline browser restart. + // + // If dataStore Object is not defined then do nothing. + // + //////////////////////////////////////////////////// + + 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) { + deferred.resolve({success:true, error: null}); + } + else { + deferred.reject({success:false, error: err}); + } + }); + } + else if(result){ + deferred.resolve({success:true, error: null}); + } + else{ + deferred.reject({success:false, error: null}); + } + }); + + return deferred; + }, + + /** + * internal method that checks if this browser supports everything that is needed to handle offline attachments + * it also extends XMLHttpRequest with sendAsBinary() method, needed in Chrome + */ + _checkFileAPIs: function () { + if (window.File && window.FileReader && window.FileList && window.Blob) { + console.log("All APIs supported!"); + + if (!XMLHttpRequest.prototype.sendAsBinary) { + // https://code.google.com/p/chromium/issues/detail?id=35705#c40 + XMLHttpRequest.prototype.sendAsBinary = function (datastr) { + function byteValue(x) { + return x.charCodeAt(0) & 0xff; // jshint ignore:line + } + + var ords = Array.prototype.map.call(datastr, byteValue); + var ui8a = new Uint8Array(ords); + this.send(ui8a.buffer); + }; + console.log("extending XMLHttpRequest"); + } + return true; + } + console.log("The File APIs are not fully supported in this browser."); + return false; + }, + + /** + * internal method that extends an object with sendAsBinary() method + * sometimes extending XMLHttpRequest.prototype is not enough, as ArcGIS JS lib seems to mess with this object too + * @param oAjaxReq object to extend + */ + _extendAjaxReq: function (oAjaxReq) { + oAjaxReq.sendAsBinary = XMLHttpRequest.prototype.sendAsBinary; + console.log("extending XMLHttpRequest"); + }, + + // + // phantom symbols + // + + _phantomSymbols: [], + + _getPhantomSymbol: function (geometry, operation) { + if (this._phantomSymbols.length === 0) { + var color = [0, 255, 0, 255]; + var width = 1.5; + + this._phantomSymbols.point = []; + this._phantomSymbols.point[this._editStore.ADD] = new SimpleMarkerSymbol({ + "type": "esriSMS", "style": "esriSMSCross", + "xoffset": 10, "yoffset": 10, + "color": [255, 255, 255, 0], "size": 15, + "outline": {"color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid"} + }); + this._phantomSymbols.point[this._editStore.UPDATE] = new SimpleMarkerSymbol({ + "type": "esriSMS", "style": "esriSMSCircle", + "xoffset": 0, "yoffset": 0, + "color": [255, 255, 255, 0], "size": 15, + "outline": {"color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid"} + }); + this._phantomSymbols.point[this._editStore.DELETE] = new SimpleMarkerSymbol({ + "type": "esriSMS", "style": "esriSMSX", + "xoffset": 0, "yoffset": 0, + "color": [255, 255, 255, 0], "size": 15, + "outline": {"color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid"} + }); + this._phantomSymbols.multipoint = null; + + this._phantomSymbols.polyline = []; + this._phantomSymbols.polyline[this._editStore.ADD] = new SimpleLineSymbol({ + "type": "esriSLS", "style": "esriSLSSolid", + "color": color, "width": width + }); + this._phantomSymbols.polyline[this._editStore.UPDATE] = new SimpleLineSymbol({ + "type": "esriSLS", "style": "esriSLSSolid", + "color": color, "width": width + }); + this._phantomSymbols.polyline[this._editStore.DELETE] = new SimpleLineSymbol({ + "type": "esriSLS", "style": "esriSLSSolid", + "color": color, "width": width + }); + + this._phantomSymbols.polygon = []; + this._phantomSymbols.polygon[this._editStore.ADD] = new SimpleFillSymbol({ + "type": "esriSFS", + "style": "esriSFSSolid", + "color": [255, 255, 255, 0], + "outline": {"type": "esriSLS", "style": "esriSLSSolid", "color": color, "width": width} + }); + this._phantomSymbols.polygon[this._editStore.UPDATE] = new SimpleFillSymbol({ + "type": "esriSFS", + "style": "esriSFSSolid", + "color": [255, 255, 255, 0], + "outline": {"type": "esriSLS", "style": "esriSLSDash", "color": color, "width": width} + }); + this._phantomSymbols.polygon[this._editStore.DELETE] = new SimpleFillSymbol({ + "type": "esriSFS", + "style": "esriSFSSolid", + "color": [255, 255, 255, 0], + "outline": {"type": "esriSLS", "style": "esriSLSDot", "color": color, "width": width} + }); + } + + return this._phantomSymbols[geometry.type][operation]; + }, + + // + // methods to handle attachment uploads + // + + _uploadAttachment: function (attachment) { + var dfd = new Deferred(); + + var layer = this._featureLayers[attachment.featureLayerUrl]; + + var formData = new FormData(); + formData.append("attachment",attachment.file); + + switch(attachment.type){ + case this.attachmentsStore.TYPE.ADD: + layer.addAttachment(attachment.objectId,formData,function(evt){ + dfd.resolve({attachmentResult:evt,id:attachment.id}); + },function(err){ + dfd.reject(err); + }); + break; + case this.attachmentsStore.TYPE.UPDATE: + formData.append("attachmentId", attachment.id); + + // NOTE: + // We need to handle updates different from ADDS and DELETES because of how the JS API + // parses the DOM formNode property. + layer._sendAttachment("update",/* objectid */attachment.objectId, formData,function(evt){ + dfd.resolve({attachmentResult:evt,id:attachment.id}); + },function(err){ + dfd.reject(err); + }); + + break; + case this.attachmentsStore.TYPE.DELETE: + // IMPORTANT: This method returns attachmentResult as an Array. Whereas ADD and UPDATE do not!! + layer.deleteAttachments(attachment.objectId,[attachment.id],function(evt){ + dfd.resolve({attachmentResult:evt,id:attachment.id}); + },function(err){ + dfd.reject(err); + }); + break; + } + + return dfd.promise; + }, + + _deleteAttachmentFromDB: function (attachmentId, uploadResult) { + var dfd = new Deferred(); + + console.log("upload complete", uploadResult, attachmentId); + this.attachmentsStore.delete(attachmentId, function (success) { + console.assert(success === true, "can't delete attachment already uploaded"); + console.log("delete complete", success); + dfd.resolve({success:success,result:uploadResult}); + }); + + return dfd; + }, + + /** + * Removes attachments from DB if they were successfully uploaded + * @param results promises.results + * @callback callback callback( {errors: boolean, attachmentsDBResults: results, uploadResults: results} ) + * @private + */ + _cleanAttachmentsDB: function(results,callback){ + + var self = this; + var promises = []; + var count = 0; + + results.forEach(function(value){ + + if(typeof value.attachmentResult == "object" && value.attachmentResult.success){ + // Delete an attachment from the database if it was successfully + // submitted to the server. + promises.push(self._deleteAttachmentFromDB(value.id,null)); + } + // NOTE: layer.deleteAttachments returns an array rather than an object + else if(value.attachmentResult instanceof Array){ + + // Because we get an array we have to cycle thru it to verify all results + value.attachmentResult.forEach(function(deleteValue){ + if(deleteValue.success){ + // Delete an attachment from the database if it was successfully + // submitted to the server. + promises.push(self._deleteAttachmentFromDB(value.id,null)); + } + else { + count++; + } + }); + } + else{ + // Do nothing. Don't delete attachments from DB if we can't upload them + count++; + } + }); + + var allPromises = all(promises); + allPromises.then(function(dbResults){ + if(count > 0){ + // If count is greater than zero then we have errors and need to set errors to true + callback({errors: true, attachmentsDBResults: dbResults, uploadResults: results}); + } + else{ + callback({errors: false, attachmentsDBResults: dbResults, uploadResults: results}); + } + }); + }, + + /** + * Attempts to upload stored attachments when the library goes back on line. + * @param callback callback({success: boolean, uploadResults: results, dbResults: results}) + * @private + */ + _sendStoredAttachments: function (callback) { + this.attachmentsStore.getAllAttachments(function (attachments) { + + var self = this; + + console.log("we have", attachments.length, "attachments to upload"); + + var promises = []; + attachments.forEach(function (attachment) { + console.log("sending attachment", attachment.id, "to feature", attachment.featureId); + + var uploadAttachmentComplete = this._uploadAttachment(attachment); + promises.push(uploadAttachmentComplete); + }, this); + console.log("promises", promises.length); + var allPromises = all(promises); + allPromises.then(function (uploadResults) { + console.log(uploadResults); + self._cleanAttachmentsDB(uploadResults,function(dbResults){ + if(dbResults.errors){ + callback && callback(false, uploadResults,dbResults); + } + else{ + callback && callback(true, uploadResults,dbResults); + } + }); + }, + function (err) { + console.log("error!", err); + callback && callback(false, err); + }); + }.bind(this)); + }, + + // + // methods to send features back to the server + // + + /** + * Attempts to send any edits in the database. Monitor events for success or failure. + * @param callback + * @event ALL_EDITS_SENT when all edits have been successfully sent. Contains {[addResults],[updateResults],[deleteResults]} + * @event EDITS_SENT_ERROR some edits were not sent successfully. Contains {msg: error} + * @private + */ + _replayStoredEdits: function (callback) { + var promises = {}; + var that = this; + + // + // send edits for each of the layers + // + var layer; + var adds = [], updates = [], deletes = []; + var tempObjectIds = []; + var tempArray = []; + var featureLayers = this._featureLayers; + var attachmentsStore = this.attachmentsStore; + var editStore = this._editStore; + + this._editStore.getAllEditsArray(function (result, err) { + if (result.length > 0) { + tempArray = result; + + var length = tempArray.length; + + for (var n = 0; n < length; n++) { + layer = featureLayers[tempArray[n].layer]; + + // If the layer has attachments then check to see if the attachmentsStore has been initialized + if (attachmentsStore == null && layer.hasAttachments) { + console.log("NOTICE: you may need to run OfflineEditAdvanced.initAttachments(). Check the Attachments doc for more info. Layer id: " + layer.id + " accepts attachments"); + } + + // Assign the attachmentsStore to the layer as a private var so we can access it from + // the promises applyEdits() method. + layer._attachmentsStore = attachmentsStore; + + layer.__onEditsComplete = layer.onEditsComplete; + layer.onEditsComplete = function () { + console.log("intercepting events onEditsComplete"); + }; + + // Let's zero everything out + adds = [], updates = [], deletes = [], tempObjectIds = []; + + // IMPORTANT: reconstitute the graphic JSON into an actual esri.Graphic object + // NOTE: we are only sending one Graphic per loop! + var graphic = new Graphic(tempArray[n].graphic); + + switch (tempArray[n].operation) { + case editStore.ADD: + for (var i = 0; i < layer.graphics.length; i++) { + var g = layer.graphics[i]; + if (g.attributes[layer.objectIdField] === graphic.attributes[layer.objectIdField]) { + layer.remove(g); + break; + } + } + tempObjectIds.push(graphic.attributes[layer.objectIdField]); + delete graphic.attributes[layer.objectIdField]; + adds.push(graphic); + break; + case editStore.UPDATE: + updates.push(graphic); + break; + case editStore.DELETE: + deletes.push(graphic); + break; + } + + //if(that._featureCollectionUsageFlag){ + // Note: when the feature layer is created with a feature collection we have to handle applyEdits() differently + // TO-DO rename this method. + promises[n] = that._internalApplyEditsAll(layer, tempArray[n].id, tempObjectIds, adds, updates, deletes); + } + + // wait for all requests to finish + // responses contain {id,layer,tempId,addResults,updateResults,deleteResults} + var allPromises = all(promises); + allPromises.then( + function (responses) { + console.log("OfflineEditAdvanced sync - all responses are back"); + + this._parseResponsesArray(responses).then(function(result) { + if(result) { + this.emit(this.events.ALL_EDITS_SENT,responses); + } + else { + this.emit(this.events.EDITS_SENT_ERROR, {msg: "Not all edits synced", respones: responses}); + } + callback && callback(true, responses); + }.bind(this)); + }.bind(that), + function (errors) { + console.log("OfflineEditAdvanced._replayStoredEdits - ERROR!!"); + console.log(errors); + callback && callback(false, errors); + }.bind(that) + ); + + } + else{ + // No edits were found + callback(true,[]); + } + }); + }, + + /** + * Deletes edits from database. + * This does not handle phantom graphics! + * @param edit + * @returns {l.Deferred.promise|*|c.promise|q.promise|promise} + * @private + */ + _updateDatabase: function (edit) { + var dfd = new Deferred(); + var fakeGraphic = {}; + fakeGraphic.attributes = {}; + + // Use the correct attributes key! + fakeGraphic.attributes[this.DB_UID] = edit.id; + + this._editStore.delete(edit.layer, fakeGraphic, function (success, error) { + if (success) { + dfd.resolve({success: true, error: null}); + } + else { + dfd.reject({success: false, error: error}); + } + }.bind(this)); + + return dfd.promise; + + }, + + /** + * Retrieves f=json from the feature layer + * @param url FeatureLayer's URL + * @param callback + * @private + */ + getFeatureLayerJSON: function (url, callback) { + require(["esri/request"], function (esriRequest) { + var request = esriRequest({ + url: url, + content: {f: "json"}, + handleAs: "json", + callbackParamName: "callback" + }); + + request.then(function (response) { + console.log("Success: ", response); + callback(true, response); + }, function (error) { + console.log("Error: ", error.message); + callback(false, error.message); + }); + }); + }, + + /** + * Applies edits. This works with both standard feature layers and 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 + */ + _internalApplyEditsAll: function (layer, id, tempObjectIds, adds, updates, deletes) { + var that = this; + var dfd = new Deferred(); + + this._makeEditRequest(layer, adds, updates, deletes, + function (addResults, updateResults, deleteResults) { + layer._phantomLayer.clear(); + + // We use a different pattern if the attachmentsStore is valid and the layer has attachments + if (layer._attachmentsStore != null && layer.hasAttachments && tempObjectIds.length > 0) { + + var newObjectIds = addResults.map(function (r) { + return r.objectId; + }); + + layer._replaceFeatureIds(tempObjectIds, newObjectIds, function (count) { + console.log("Done replacing feature ids. Total count = " + count); + }); + } + + if(addResults.length > 0) { + var graphic = new Graphic(adds[0].geometry,null,adds[0].attributes); + layer.add(graphic); + } + + that._cleanDatabase(layer, tempObjectIds, addResults, updateResults, deleteResults).then(function(results){ + 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, + databaseResults: results, + databaseErrors: null, + syncError: null + }); + }, function(error) { + 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, + databaseResults: null, + databaseErrors: error, + syncError: error + }); + }); + + }, + function (error) { + layer.onEditsComplete = layer.__onEditsComplete; + delete layer.__onEditsComplete; + + dfd.reject(error); + } + ); + return dfd.promise; + }, + + _cleanDatabase: function(layer, tempId, addResults, updateResults, deleteResults) { + + var dfd = new Deferred(); + var id = null; + + if (updateResults.length > 0) { + if (updateResults[0].success) { + id = updateResults[0].objectId; + } + } + if (deleteResults.length > 0) { + if (deleteResults[0].success) { + id = deleteResults[0].objectId; + } + } + if (addResults.length > 0) { + if (addResults[0].success) { + id = tempId; + } + } + + var fakeGraphic = {}; + fakeGraphic.attributes = {}; + + // Use the correct attributes key! + fakeGraphic.attributes[this.DB_UID] = id; + + // Delete the edit from the database + this._editStore.delete(layer.url, fakeGraphic, function (success, error) { + if (success) { + + var id = this._editStore.PHANTOM_GRAPHIC_PREFIX + this._editStore._PHANTOM_PREFIX_TOKEN + fakeGraphic.attributes[this.DB_UID]; + + // Delete the phantom graphic associated with the dit + this._editStore.deletePhantomGraphic(id, function(success,error){ + if(!success) { + console.log("_cleanDatabase delete phantom graphic error: " + error); + dfd.reject({success: false, error: error, id: id}); + } + else { + console.log("_cleanDatabase success: " + id); + dfd.resolve({success: true, error: null, id: id}); + } + }); + } + else { + dfd.reject({success: false, error: error, id: id}); + } + }.bind(this)); + + 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 layer + * @param adds + * @param updates + * @param deletes + * @returns {*|r} + * @private + */ + _makeEditRequest: function(layer,adds, updates, deletes, callback, errback) { + + var f = "f=json", a = "", u = "", d = ""; + + if(adds.length > 0) { + array.forEach(adds, function(add){ + if(add.hasOwnProperty("infoTemplate")){ // if the add has an infoTemplate attached, + delete add.infoTemplate; // delete it to reduce payload size. + } + }, this); + a = "&adds=" + JSON.stringify((adds)); + } + if(updates.length > 0) { + array.forEach(updates, function(update){ + if(update.hasOwnProperty("infoTemplate")){ // if the update has an infoTemplate attached, + delete update.infoTemplate; // delete it to reduce payload size. + } + }, this); + u = "&updates=" + JSON.stringify(updates); + } + if(deletes.length > 0) { + var id = deletes[0].attributes[this.DB_UID]; + d = "&deletes=" + id; + } + + var params = f + a + u + d; + + if(layer.hasOwnProperty("credential") && layer.credential){ + if(layer.credential.hasOwnProperty("token") && layer.credential.token){ + params = params + "&token=" + layer.credential.token; + } + } + + var req = new XMLHttpRequest(); + req.open("POST", layer.url + "/applyEdits", true); + req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + req.onload = function() + { + if( req.status === 200 && req.responseText !== "") + { + try { + var obj = JSON.parse(this.response); + callback(obj.addResults, obj.updateResults, obj.deleteResults); + } + catch(err) { + console.error("EDIT REQUEST REPONSE WAS NOT SUCCESSFUL:", req); + errback("Unable to parse xhr response", req); + } + } + + }; + req.onerror = function(e) + { + console.error("_makeEditRequest failed: " + e); + errback(e); + }; + req.ontimeout = function() { + errback("xhr timeout error"); + }; + req.timeout = this._defaultXhrTimeout; + req.send(params); + }, + + /** + * Parses the respones related to going back online and cleaning up the database. + * @param responses + * @returns {promise} True means all was successful. False indicates there was a problem. + * @private + */ + _parseResponsesArray: function(responses) { + + var dfd = new Deferred(); + var err = 0; + + for (var key in responses) { + if (responses.hasOwnProperty(key)) { + responses[key].addResults.map(function(result){ + if(!result.success) { + err++; + } + }); + + responses[key].updateResults.map(function(result){ + if(!result.success) { + err++; + } + }); + + responses[key].deleteResults.map(function(result){ + if(!result.success) { + err++; + } + }); + } + } + + if(err > 0){ + dfd.resolve(false); + } + else { + dfd.resolve(true); + } + + return dfd.promise; + } + }); // declare + }); // define +/** + * Creates a namespace for the non-AMD libraries in this directory + */ +/*jshint -W020 */ +if(typeof O != "undefined"){ + O.esri.Edit = {}; +} +else{ + O = {}; + O.esri = { + Edit: {} + }; +} +/*global indexedDB */ +/*jshint -W030 */ +O.esri.Edit.EditStore = function () { + + "use strict"; + + this._db = null; + this._isDBInit = false; + + // Public properties + + this.dbName = "features_store"; + this.objectStoreName = "features"; + this.objectId = "objectid"; // set this depending on how your feature service is configured; + + //var _dbIndex = "featureId"; // @private + + // ENUMs + + this.ADD = "add"; + this.UPDATE = "update"; + this.DELETE = "delete"; + + this.FEATURE_LAYER_JSON_ID = "feature-layer-object-1001"; + this.FEATURE_COLLECTION_ID = "feature-collection-object-1001"; + this.PHANTOM_GRAPHIC_PREFIX = "phantom-layer"; + this._PHANTOM_PREFIX_TOKEN = "|@|"; + + this.isSupported = function () { + if (!window.indexedDB) { + return false; + } + return true; + }; + + /** + * Commit an edit to the database + * @param operation add, update or delete + * @param layerUrl the URL of the feature layer + * @param graphic esri/graphic. The method will serialize to JSON + * @param callback callback(true, edit) or callback(false, error) + */ + this.pushEdit = function (operation, layerUrl, graphic, callback) { + + var edit = { + id: layerUrl + "/" + graphic.attributes[this.objectId], + operation: operation, + layer: layerUrl, + type: graphic.geometry.type, + graphic: graphic.toJson() + }; + + if(typeof graphic.attributes[this.objectId] === "undefined") { + console.error("editsStore.pushEdit() - failed to insert undefined objectId into database. Did you set offlineEdit.DB_UID? " + JSON.stringify(graphic.attributes)); + callback(false,"editsStore.pushEdit() - failed to insert undefined objectId into database. Did you set offlineEdit.DB_UID? " + JSON.stringify(graphic.attributes)); + } + else{ + var transaction = this._db.transaction([this.objectStoreName], "readwrite"); + + transaction.oncomplete = function (event) { + callback(true); + }; + + transaction.onerror = function (event) { + callback(false, event.target.error.message); + }; + + var objectStore = transaction.objectStore(this.objectStoreName); + objectStore.put(edit); + } + }; + + /** + * Use this to store any static FeatureLayer or related JSON data related to your app that will assist in restoring + * a FeatureLayer. + * + * Handles both adds and updates. It copies any object properties, so it will not, by default, overwrite the entire object. + * + * Example 1: If you just submit {featureLayerRenderer: {someJSON}} it will only update the featureLayerRenderer property + * Example 2: This is a full example + * { + * featureLayerJSON: ..., + * graphics: ..., // Serialized Feature Layer graphics. Must be serialized! + * renderer: ..., + * opacity: ..., + * outfields: ..., + * mode: ..., + * extent: ..., + * zoom: 7, + * lastEdit: ... + * } + * + * NOTE: "dataObject.id" is a reserved property. If you use "id" in your object this method will break. + * @param dataStore Object + * @param callback callback(true, null) or callback(false, error) + */ + this.pushFeatureLayerJSON = function (dataStore /*Object*/, callback) { + + console.assert(this._db !== null, "indexeddb not initialized"); + if (typeof dataStore != "object") { + callback(false, "dataObject type is not an object."); + } + + var db = this._db; + dataStore.id = this.FEATURE_LAYER_JSON_ID; + + this.getFeatureLayerJSON(function (success, result) { + + var objectStore; + + if (success && typeof result !== "undefined") { + + objectStore = db.transaction([this.objectStoreName], "readwrite").objectStore(this.objectStoreName); + + // Make a copy of the object + for (var key in dataStore) { + if (dataStore.hasOwnProperty(key)) { + result[key] = dataStore[key]; + } + } + + // Insert the update into the database + var updateFeatureLayerDataRequest = objectStore.put(result); + + updateFeatureLayerDataRequest.onsuccess = function () { + callback(true, null); + }; + + updateFeatureLayerDataRequest.onerror = function (err) { + callback(false, err); + }; + } + else { + + var transaction = db.transaction([this.objectStoreName], "readwrite"); + + transaction.oncomplete = function (event) { + callback(true, null); + }; + + transaction.onerror = function (event) { + callback(false, event.target.error.message); + }; + + objectStore = transaction.objectStore(this.objectStoreName); + + // Protect against data cloning errors since we don't validate the input object + // Example: if you attempt to use an esri.Graphic in its native form you'll get a data clone error + try { + objectStore.put(dataStore); + } + catch (err) { + callback(false, JSON.stringify(err)); + } + } + }.bind(this)); + }; + + /** + * Retrieve the FeatureLayer data object + * @param callback callback(true, object) || callback(false, error) + */ + this.getFeatureLayerJSON = function (callback) { + + console.assert(this._db !== null, "indexeddb not initialized"); + + var objectStore = this._db.transaction([this.objectStoreName], "readwrite").objectStore(this.objectStoreName); + + //Get the entry associated with the graphic + var objectStoreGraphicRequest = objectStore.get(this.FEATURE_LAYER_JSON_ID); + + objectStoreGraphicRequest.onsuccess = function () { + var object = objectStoreGraphicRequest.result; + if (typeof object != "undefined") { + callback(true, object); + } + else { + callback(false, "nothing found"); + } + }; + + objectStoreGraphicRequest.onerror = function (msg) { + callback(false, msg); + }; + }; + + /** + * Safe delete. Checks if id exists, then reverifies. + * @param callback callback(boolean, {message: String}) + */ + this.deleteFeatureLayerJSON = function (callback) { + // NOTE: the implementation of the IndexedDB spec has a design fault with respect to + // handling deletes. The result of a delete operation is always designated as undefined. + // What this means is that there is no way to tell if an operation was successful or not. + // And, it will always return 'true.' + // + // In order to get around this we have to verify if after the attempted deletion operation + // if the record is or is not in the database. Kinda dumb, but that's how IndexedDB works. + //http://stackoverflow.com/questions/17137879/is-there-a-way-to-get-information-on-deleted-record-when-calling-indexeddbs-obj + + var db = this._db; + var deferred = null; + var self = this; + + var id = this.FEATURE_LAYER_JSON_ID; + + require(["dojo/Deferred"], function (Deferred) { + deferred = new Deferred(); + + // Step 4 - Then we check to see if the record actually exists or not. + deferred.then(function (result) { + // IF the delete was successful, then the record should return an error because it doesn't exist. + // We aren't 100% sure how all platforms will perform so we also trap the promise for return results. + self.editExists(id).then(function (results) { + // If edit does exist then we have not been successful in deleting the object. + callback(false, {message: "object was not deleted."}); + }, + function (err) { + // If the result is false then in theory the id no longer exists + // and we should return 'true' to indicate a successful delete operation. + callback(true, {message: "id does not exist"}); //because we want this test to throw an error. That means item deleted. + }); + }, + // There was a problem with the delete operation on the database + // This error message will come from editExists(); + function (err) { + callback(false, {message: "id does not exist"}); + }); + + // Step 1 - lets see if record exits. If it does not then return callback. Otherwise, + // continue on with the deferred. + self.editExists(id).then(function (result) { + + var objectStore = db.transaction([self.objectStoreName], "readwrite").objectStore(self.objectStoreName); + + // Step 2 - go ahead and delete graphic + var objectStoreDeleteRequest = objectStore.delete(id); + + // Step 3 - We know that the onsuccess will always fire unless something serious goes wrong. + // So we go ahead and resolve the deferred here. + objectStoreDeleteRequest.onsuccess = function () { + deferred.resolve(true); + }; + + objectStoreDeleteRequest.onerror = function (msg) { + deferred.reject({success: false, error: msg}); + }; + + }, + // If there is an error in editExists() + function (err) { + deferred.reject({success: false, message: err}); + }.bind(this)); + }); + }; + + /** + * Add a phantom graphic to the store. + * IMPORTANT! Requires graphic to have an objectId + * @param graphic + * @param callback + */ + this.pushPhantomGraphic = function (graphic, callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var db = this._db; + var id = this.PHANTOM_GRAPHIC_PREFIX + this._PHANTOM_PREFIX_TOKEN + graphic.attributes[this.objectId]; + + var object = { + id: id, + graphic: graphic.toJson() + }; + + var transaction = db.transaction([this.objectStoreName], "readwrite"); + + transaction.oncomplete = function (event) { + callback(true, null); + }; + + transaction.onerror = function (event) { + callback(false, event.target.error.message); + }; + + var objectStore = transaction.objectStore(this.objectStoreName); + objectStore.put(object); + + }; + + /** + * Return an array of phantom graphics + * @param callback + */ + this.getPhantomGraphicsArray = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + var editsArray = []; + + if (this._db !== null) { + + var phantomGraphicPrefix = this.PHANTOM_GRAPHIC_PREFIX; + + var transaction = this._db.transaction([this.objectStoreName]) + .objectStore(this.objectStoreName) + .openCursor(); + + transaction.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor && cursor.value && cursor.value.id) { + + // Make sure we are not return FeatureLayer JSON data or a Phantom Graphic + if (cursor.value.id.indexOf(phantomGraphicPrefix) != -1) { + editsArray.push(cursor.value); + + } + cursor.continue(); + } + else { + callback(editsArray, "end"); + } + }.bind(this); + transaction.onerror = function (err) { + callback(null, err); + }; + } + else { + callback(null, "no db"); + } + }; + + /** + * Internal method that returns an array of id's only + * @param callback + * @private + */ + this._getPhantomGraphicsArraySimple = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + var editsArray = []; + + if (this._db !== null) { + + var phantomGraphicPrefix = this.PHANTOM_GRAPHIC_PREFIX; + + var transaction = this._db.transaction([this.objectStoreName]) + .objectStore(this.objectStoreName) + .openCursor(); + + transaction.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor && cursor.value && cursor.value.id) { + + // Make sure we are not return FeatureLayer JSON data or a Phantom Graphic + if (cursor.value.id.indexOf(phantomGraphicPrefix) != -1) { + editsArray.push(cursor.value.id); + + } + cursor.continue(); + } + else { + callback(editsArray, "end"); + } + }.bind(this); + transaction.onerror = function (err) { + callback(null, err); + }; + } + else { + callback(null, "no db"); + } + }; + + /** + * Deletes an individual graphic from the phantom layer + * @param id Internal ID + * @param callback callback(boolean, message) + */ + this.deletePhantomGraphic = function (id, callback) { + // NOTE: the implementation of the IndexedDB spec has a design fault with respect to + // handling deletes. The result of a delete operation is always designated as undefined. + // What this means is that there is no way to tell if an operation was successful or not. + // And, it will always return 'true.' + // + // In order to get around this we have to verify if after the attempted deletion operation + // if the record is or is not in the database. Kinda dumb, but that's how IndexedDB works. + //http://stackoverflow.com/questions/17137879/is-there-a-way-to-get-information-on-deleted-record-when-calling-indexeddbs-obj + + var db = this._db; + var deferred = null; + var self = this; + + require(["dojo/Deferred"], function (Deferred) { + deferred = new Deferred(); + + // Step 1 - lets see if record exits. If it does then return callback. + self.editExists(id).then(function (result) { + + // Step 4 - Then we check to see if the record actually exists or not. + deferred.then(function (result) { + + // IF the delete was successful, then the record should return 'false' because it doesn't exist. + self.editExists(id).then(function (results) { + callback(false, "item was not deleted"); // item is still in the database!! + }, + function (err) { + callback(true, "item successfully deleted"); //because we want this test to throw an error. That means item deleted. + }); + }, + // There was a problem with the delete operation on the database + function (err) { + callback(false, err); + }); + + var objectStore = db.transaction([self.objectStoreName], "readwrite").objectStore(self.objectStoreName); + + // Step 2 - go ahead and delete graphic + var objectStoreDeleteRequest = objectStore.delete(id); + + // Step 3 - We know that the onsuccess will always fire unless something serious goes wrong. + // So we go ahead and resolve the deferred here. + objectStoreDeleteRequest.onsuccess = function () { + deferred.resolve(true); + }; + + objectStoreDeleteRequest.onerror = function (msg) { + deferred.reject({success: false, error: msg}); + }; + }, + // If there is an error in editExists() + function (err) { + callback(false, "item doesn't exist in db"); + }); + }); + }; + + /** + * Removes all phantom graphics from database + * @param callback boolean + */ + this.resetPhantomGraphicsQueue = function (callback) { + + var db = this._db; + + // First we need to get the array of graphics that are stored in the database + // so that we can cycle thru them. + this._getPhantomGraphicsArraySimple(function (array) { + if (array != []) { + + var errors = 0; + var tx = db.transaction([this.objectStoreName], "readwrite"); + var objectStore = tx.objectStore(this.objectStoreName); + + objectStore.onerror = function () { + errors++; + }; + + tx.oncomplete = function () { + errors === 0 ? callback(true) : callback(false); + }; + + var length = array.length; + for (var i = 0; i < length; i++) { + objectStore.delete(array[i]); + } + } + else { + callback(true); + } + }.bind(this)); + }; + + /** + * Retrieve an edit by its internal ID + * @param id String identifier + * @param callback callback(true,graphic) or callback(false, error) + */ + this.getEdit = function(id,callback){ + + console.assert(this._db !== null, "indexeddb not initialized"); + var objectStore = this._db.transaction([this.objectStoreName], "readwrite").objectStore(this.objectStoreName); + + if(typeof id === "undefined"){ + callback(false,"id is undefined."); + return; + } + + //Get the entry associated with the graphic + var objectStoreGraphicRequest = objectStore.get(id); + + objectStoreGraphicRequest.onsuccess = function () { + var graphic = objectStoreGraphicRequest.result; + if (graphic && (graphic.id == id)) { + callback(true,graphic); + } + else { + callback(false,"Id not found"); + } + }; + + objectStoreGraphicRequest.onerror = function (msg) { + callback(false,msg); + }; + }; + + /** + * Returns all the edits recursively via the callback + * @param callback {value, message} + */ + this.getAllEdits = function (callback) { + + console.assert(this._db !== null, "indexeddb not initialized"); + + if (this._db !== null) { + + var fLayerJSONId = this.FEATURE_LAYER_JSON_ID; + var fCollectionId = this.FEATURE_COLLECTION_ID; + var phantomGraphicPrefix = this.PHANTOM_GRAPHIC_PREFIX; + + var transaction = this._db.transaction([this.objectStoreName]) + .objectStore(this.objectStoreName) + .openCursor(); + + transaction.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor && cursor.hasOwnProperty("value") && cursor.value.hasOwnProperty("id")) { + + // Make sure we are not return FeatureLayer JSON data or a Phantom Graphic + if (cursor.value.id !== fLayerJSONId && cursor.value.id !== fCollectionId && cursor.value.id.indexOf(phantomGraphicPrefix) == -1) { + callback(cursor.value, null); + } + cursor.continue(); + } + else { + callback(null, "end"); + } + }.bind(this); + transaction.onerror = function (err) { + callback(null, err); + }; + } + else { + callback(null, "no db"); + } + }; + + /** + * Returns all the edits as a single Array via the callback + * @param callback {array, messageString} or {null, messageString} + */ + this.getAllEditsArray = function (callback) { + + console.assert(this._db !== null, "indexeddb not initialized"); + var editsArray = []; + + if (this._db !== null) { + + var fLayerJSONId = this.FEATURE_LAYER_JSON_ID; + var fCollectionId = this.FEATURE_COLLECTION_ID; + var phantomGraphicPrefix = this.PHANTOM_GRAPHIC_PREFIX; + + var transaction = this._db.transaction([this.objectStoreName]) + .objectStore(this.objectStoreName) + .openCursor(); + + transaction.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor && cursor.value && cursor.value.id) { + + // Make sure we are not return FeatureLayer JSON data or a Phantom Graphic + if (cursor.value.id !== fLayerJSONId && cursor.value.id !== fCollectionId && cursor.value.id.indexOf(phantomGraphicPrefix) == -1) { + editsArray.push(cursor.value); + + } + cursor.continue(); + } + else { + callback(editsArray, "end"); + } + }.bind(this); + transaction.onerror = function (err) { + callback(null, err); + }; + } + else { + callback(null, "no db"); + } + }; + + /** + * Update an edit already exists in the database + * @param operation add, update or delete + * @param layer the URL of the feature layer + * @param graphic esri/graphic. The method will serialize to JSON + * @param callback {true, edit} or {false, error} + */ + this.updateExistingEdit = function (operation, layer, graphic, callback) { + + console.assert(this._db !== null, "indexeddb not initialized"); + + var objectStore = this._db.transaction([this.objectStoreName], "readwrite").objectStore(this.objectStoreName); + + //Let's get the entry associated with the graphic + var objectStoreGraphicRequest = objectStore.get(graphic.attributes[this.objectId]); + objectStoreGraphicRequest.onsuccess = function () { + + //Grab the data object returned as a result + // TO-DO Do we keep this?? + objectStoreGraphicRequest.result; + + //Create a new update object + var update = { + id: layer + "/" + graphic.attributes[this.objectId], + operation: operation, + layer: layer, + graphic: graphic.toJson() + }; + + // Insert the update into the database + var updateGraphicRequest = objectStore.put(update); + + updateGraphicRequest.onsuccess = function () { + callback(true); + }; + + updateGraphicRequest.onerror = function (err) { + callback(false, err); + }; + }.bind(this); + }; + + /** + * Delete a pending edit's record from the database. + * IMPORTANT: Be aware of false negatives. See Step 4 in this function. + * + * @param layerUrl + * @param graphic Graphic + * @param callback {boolean, error} + */ + this.delete = function (layerUrl, graphic, callback) { + + // NOTE: the implementation of the IndexedDB spec has a design fault with respect to + // handling deletes. The result of a delete operation is always designated as undefined. + // What this means is that there is no way to tell if an operation was successful or not. + // And, it will always return 'true.' + // + // In order to get around this we have to verify if after the attempted deletion operation + // if the record is or is not in the database. Kinda dumb, but that's how IndexedDB works. + //http://stackoverflow.com/questions/17137879/is-there-a-way-to-get-information-on-deleted-record-when-calling-indexeddbs-obj + + var db = this._db; + var deferred = null; + var self = this; + + var id = layerUrl + "/" + graphic.attributes[this.objectId]; + + require(["dojo/Deferred"], function (Deferred) { + deferred = new Deferred(); + + // Step 1 - lets see if record exits. If it does then return callback. + self.editExists(id).then(function (result) { + + // Step 4 - Then we check to see if the record actually exists or not. + deferred.then(function (result) { + + // IF the delete was successful, then the record should return 'false' because it doesn't exist. + self.editExists(id).then(function (results) { + callback(false); + }, + function (err) { + callback(true); //because we want this test to throw an error. That means item deleted. + }); + }, + // There was a problem with the delete operation on the database + function (err) { + callback(false, err); + }); + + var objectStore = db.transaction([self.objectStoreName], "readwrite").objectStore(self.objectStoreName); + + // Step 2 - go ahead and delete graphic + var objectStoreDeleteRequest = objectStore.delete(id); + + // Step 3 - We know that the onsuccess will always fire unless something serious goes wrong. + // So we go ahead and resolve the deferred here. + objectStoreDeleteRequest.onsuccess = function () { + deferred.resolve(true); + }; + + objectStoreDeleteRequest.onerror = function (msg) { + deferred.reject({success: false, error: msg}); + }; + + }, + // If there is an error in editExists() + function (err) { + callback(false); + }); + }); + }; + + /** + * Full database reset. + * CAUTION! If some edits weren't successfully sent, then their record + * will still exist in the database. If you use this function you + * will also delete those records. + * @param callback boolean + */ + this.resetEditsQueue = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var request = this._db.transaction([this.objectStoreName], "readwrite") + .objectStore(this.objectStoreName) + .clear(); + request.onsuccess = function (event) { + setTimeout(function () { + callback(true); + }, 0); + }; + request.onerror = function (err) { + callback(false, err); + }; + }; + + this.pendingEditsCount = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var count = 0; + var id = this.FEATURE_LAYER_JSON_ID; + var fCollectionId = this.FEATURE_COLLECTION_ID; + var phantomGraphicPrefix = this.PHANTOM_GRAPHIC_PREFIX; + + var transaction = this._db.transaction([this.objectStoreName], "readwrite"); + var objectStore = transaction.objectStore(this.objectStoreName); + objectStore.openCursor().onsuccess = function (evt) { + var cursor = evt.target.result; + + // IMPORTANT: + // Remember that we have feature layer JSON and Phantom Graphics in the same database + if (cursor && cursor.value && cursor.value.id && cursor.value.id.indexOf(phantomGraphicPrefix) == -1) { + if (cursor.value.id !== id && cursor.value.id !== fCollectionId) { + count++; + } + cursor.continue(); + } + else { + callback(count); + } + }; + }; + + /** + * Verify is an edit already exists in the database. Checks the objectId. + * @param id + * @returns {deferred} {success: boolean, error: message} + * @private + */ + this.editExists = function (id) { + + var db = this._db; + var deferred = null; + var self = this; + + require(["dojo/Deferred"], function (Deferred) { + deferred = new Deferred(); + + var objectStore = db.transaction([self.objectStoreName], "readwrite").objectStore(self.objectStoreName); + + //Get the entry associated with the graphic + var objectStoreGraphicRequest = objectStore.get(id); + + objectStoreGraphicRequest.onsuccess = function () { + var graphic = objectStoreGraphicRequest.result; + if (graphic && (graphic.id == id)) { + deferred.resolve({success: true, error: null}); + } + else { + deferred.reject({success: false, error: "objectId is not a match."}); + } + }; + + objectStoreGraphicRequest.onerror = function (msg) { + deferred.reject({success: false, error: msg}); + }; + }); + + //We return a deferred object so that when calling this function you can chain it with a then() statement. + return deferred; + }; + + /** + * Returns the approximate size of the database in bytes + * IMPORTANT: Currently requires all data be serialized! + * @param callback callback({usage}, error) Whereas, the usage Object is {sizeBytes: number, editCount: number} + */ + this.getUsage = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var id = this.FEATURE_LAYER_JSON_ID; + var fCollectionId = this.FEATURE_COLLECTION_ID; + var phantomGraphicPrefix = this.PHANTOM_GRAPHIC_PREFIX; + + var usage = {sizeBytes: 0, editCount: 0}; + + var transaction = this._db.transaction([this.objectStoreName]) + .objectStore(this.objectStoreName) + .openCursor(); + + console.log("dumping keys"); + + transaction.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor && cursor.value && cursor.value.id) { + var storedObject = cursor.value; + var json = JSON.stringify(storedObject); + usage.sizeBytes += json.length; + + if (cursor.value.id.indexOf(phantomGraphicPrefix) == -1 && cursor.value.id !== id && cursor.value.id !== fCollectionId) { + usage.editCount += 1; + } + + cursor.continue(); + } + else { + callback(usage, null); + } + }; + transaction.onerror = function (err) { + callback(null, err); + }; + }; + + // + // internal methods + // + + /** + * The library automatically keeps a copy of the featureLayerCollection and its + * associated layer.url. + * + * There should be only one featureLayerCollection Object per feature layer. + * @param featureCollectionObject + * @param callback + * @private + */ + this._pushFeatureCollections = function(featureCollectionObject, callback){ + var transaction = this._db.transaction([this.objectStoreName], "readwrite"); + + transaction.oncomplete = function (event) { + callback(true); + }; + + transaction.onerror = function (event) { + callback(false, event.target.error.message); + }; + + var objectStore = transaction.objectStore(this.objectStoreName); + objectStore.put(featureCollectionObject); + }; + + this._getFeatureCollections = function(callback){ + var objectStore = this._db.transaction([this.objectStoreName], "readonly").objectStore(this.objectStoreName); + + //Get the entry associated with the graphic + var objectStoreGraphicRequest = objectStore.get(this.FEATURE_COLLECTION_ID); + + objectStoreGraphicRequest.onsuccess = function () { + var object = objectStoreGraphicRequest.result; + if (typeof object != "undefined") { + callback(true, object); + } + else { + callback(false, null); + } + }; + + objectStoreGraphicRequest.onerror = function (msg) { + callback(false, msg); + }; + }; + + this.init = function (callback) { + console.log("init editsStore.js"); + + var request = indexedDB.open(this.dbName, 11); + callback = callback || function (success) { + console.log("EditsStore::init() success:", success); + }.bind(this); + + request.onerror = function (event) { + console.log("indexedDB error: " + event.target.errorCode); + callback(false, event.target.errorCode); + }.bind(this); + + request.onupgradeneeded = function (event) { + var db = event.target.result; + + if (db.objectStoreNames.contains(this.objectStoreName)) { + db.deleteObjectStore(this.objectStoreName); + } + + db.createObjectStore(this.objectStoreName, {keyPath: "id"}); + }.bind(this); + + request.onsuccess = function (event) { + this._db = event.target.result; + this._isDBInit = true; + console.log("database opened successfully"); + callback(true, null); + }.bind(this); + }; +}; + + + +/*global IDBKeyRange,indexedDB */ + +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 () { + if (!window.indexedDB) { + return false; + } + return true; + }; + + /** + * Stores an attachment in the database. + * In theory, this abides by the query-attachment-infos-complete Object which can be found here: + * https://developers.arcgis.com/javascript/jsapi/featurelayer-amd.html#event-query-attachment-infos-complete + * @param featureLayerUrl + * @param attachmentId The temporary or actual attachmentId issued by the feature service + * @param objectId The actual ObjectId issues by the feature service + * @param attachmentFile + * @param type Type of operation: "add", "update" or "delete" + * @param callback + */ + this.store = function (featureLayerUrl, attachmentId, objectId, attachmentFile, type, callback) { + try { + // Avoid allowing the wrong type to be stored + if(type == this.TYPE.ADD || type == this.TYPE.UPDATE || type == this.TYPE.DELETE) { + + // first of all, read file content + this._readFile(attachmentFile, function (success, fileContent) { + + if (success) { + // now, store it in the db + var newAttachment = + { + id: attachmentId, + objectId: objectId, + type: type, + + // Unique ID - don't use the ObjectId + // multiple features services could have an a feature with the same ObjectId + featureId: featureLayerUrl + "/" + objectId, + contentType: attachmentFile.type, + name: attachmentFile.name, + size: attachmentFile.size, + featureLayerUrl: featureLayerUrl, + content: fileContent, + file: attachmentFile + }; + + var transaction = this._db.transaction([this.objectStoreName], "readwrite"); + + transaction.oncomplete = function (event) { + callback(true, newAttachment); + }; + + transaction.onerror = function (event) { + callback(false, event.target.error.message); + }; + + try { + transaction.objectStore(this.objectStoreName).put(newAttachment); + } + catch(err) { + callback(false, err); + } + } + else { + callback(false, fileContent); + } + }.bind(this)); + } + else{ + console.error("attachmentsStore.store() Invalid type in the constructor!"); + callback(false,"attachmentsStore.store() Invalid type in the constructor!"); + } + } + catch (err) { + console.log("AttachmentsStore: " + err.stack); + callback(false, err.stack); + } + }; + + this.retrieve = function (attachmentId, callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var objectStore = this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName); + var request = objectStore.get(attachmentId); + request.onsuccess = function (event) { + var result = event.target.result; + if (!result) { + callback(false, "not found"); + } + else { + callback(true, result); + } + }; + request.onerror = function (err) { + console.log(err); + callback(false, err); + }; + }; + + this.getAttachmentsByFeatureId = function (featureLayerUrl, objectId, callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var featureId = featureLayerUrl + "/" + objectId; + var attachments = []; + + var objectStore = this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName); + var index = objectStore.index("featureId"); + var keyRange = IDBKeyRange.only(featureId); + index.openCursor(keyRange).onsuccess = function (evt) { + var cursor = evt.target.result; + if (cursor) { + attachments.push(cursor.value); + cursor.continue(); + } + else { + callback(attachments); + } + }; + }; + + this.getAttachmentsByFeatureLayer = function (featureLayerUrl, callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var attachments = []; + + var objectStore = this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName); + var index = objectStore.index("featureLayerUrl"); + var keyRange = IDBKeyRange.only(featureLayerUrl); + index.openCursor(keyRange).onsuccess = function (evt) { + var cursor = evt.target.result; + if (cursor) { + attachments.push(cursor.value); + cursor.continue(); + } + else { + callback(attachments); + } + }; + }; + + this.getAllAttachments = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var attachments = []; + + var objectStore = this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName); + objectStore.openCursor().onsuccess = function (evt) { + var cursor = evt.target.result; + if (cursor) { + attachments.push(cursor.value); + cursor.continue(); + } + else { + callback(attachments); + } + }; + }; + + this.deleteAttachmentsByFeatureId = function (featureLayerUrl, objectId, callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var featureId = featureLayerUrl + "/" + objectId; + + var objectStore = this._db.transaction([this.objectStoreName], "readwrite").objectStore(this.objectStoreName); + var index = objectStore.index("featureId"); + var keyRange = IDBKeyRange.only(featureId); + var deletedCount = 0; + index.openCursor(keyRange).onsuccess = function (evt) { + var cursor = evt.target.result; + if (cursor) { + //var attachment = cursor.value; + //this._revokeLocalURL(attachment); + objectStore.delete(cursor.primaryKey); + deletedCount++; + cursor.continue(); + } + else { + setTimeout(function () { + callback(deletedCount); + }, 0); + } + }.bind(this); + }; + + this.delete = function (attachmentId, callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + // before deleting an attachment we must revoke the blob URL that it contains + // in order to free memory in the browser + this.retrieve(attachmentId, function (success, attachment) { + if (!success) { + callback(false, "attachment " + attachmentId + " not found"); + return; + } + + //this._revokeLocalURL(attachment); + + var request = this._db.transaction([this.objectStoreName], "readwrite") + .objectStore(this.objectStoreName) + .delete(attachmentId); + request.onsuccess = function (event) { + setTimeout(function () { + callback(true); + }, 0); + }; + request.onerror = function (err) { + callback(false, err); + }; + }.bind(this)); + }; + + this.deleteAll = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + this.getAllAttachments(function (attachments) { + //attachments.forEach(function (attachment) { + // this._revokeLocalURL(attachment); + //}, this); + + var request = this._db.transaction([this.objectStoreName], "readwrite") + .objectStore(this.objectStoreName) + .clear(); + request.onsuccess = function (event) { + setTimeout(function () { + callback(true); + }, 0); + }; + request.onerror = function (err) { + callback(false, err); + }; + }.bind(this)); + }; + + this.replaceFeatureId = function (featureLayerUrl, oldId, newId, callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var featureId = featureLayerUrl + "/" + oldId; + + var objectStore = this._db.transaction([this.objectStoreName], "readwrite").objectStore(this.objectStoreName); + var index = objectStore.index("featureId"); + var keyRange = IDBKeyRange.only(featureId); + var replacedCount = 0; + index.openCursor(keyRange).onsuccess = function (evt) { + var cursor = evt.target.result; + if (cursor) { + var newFeatureId = featureLayerUrl + "/" + newId; + var updated = cursor.value; + updated.featureId = newFeatureId; + updated.objectId = newId; + objectStore.put(updated); + replacedCount++; + cursor.continue(); + } + else { + // If no records match then evt.target.result = null + // allow time for all changes to persist... + setTimeout(function () { + callback(replacedCount); + }, 1); + } + }; + }; + + this.getUsage = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var usage = {sizeBytes: 0, attachmentCount: 0}; + + var transaction = this._db.transaction([this.objectStoreName]) + .objectStore(this.objectStoreName) + .openCursor(); + + console.log("dumping keys"); + + transaction.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor) { + console.log(cursor.value.id, cursor.value.featureId, cursor.value.objectId); + var storedObject = cursor.value; + var json = JSON.stringify(storedObject); + usage.sizeBytes += json.length; + usage.attachmentCount += 1; + cursor.continue(); + } + else { + callback(usage, null); + } + }.bind(this); + transaction.onerror = function (err) { + callback(null, err); + }; + }; + + /** + * Full attachments database reset. + * CAUTION! If some attachments weren't successfully sent, then their record + * will still exist in the database. If you use this function you + * will also delete those records. + * @param callback boolean + */ + this.resetAttachmentsQueue = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var request = this._db.transaction([this.objectStoreName], "readwrite") + .objectStore(this.objectStoreName) + .clear(); + request.onsuccess = function (event) { + setTimeout(function () { + callback(true); + }, 0); + }; + request.onerror = function (err) { + callback(false, err); + }; + }; + + // internal methods + + this._readFile = function (attachmentFile, callback) { + var reader = new FileReader(); + reader.onload = function (evt) { + callback(true,evt.target.result); + }; + reader.onerror = function (evt) { + callback(false,evt.target.result); + }; + reader.readAsBinaryString(attachmentFile); + }; + + // Deprecated @ v2.7 + //this._createLocalURL = function (attachmentFile) { + // return window.URL.createObjectURL(attachmentFile); + //}; + + //this._revokeLocalURL = function (attachment) { + // window.URL.revokeObjectURL(attachment.url); + //}; + + this.init = function (callback) { + console.log("init AttachmentStore"); + + var request = indexedDB.open(this.dbName, 12); + callback = callback || function (success) { + console.log("AttachmentsStore::init() success:", success); + }.bind(this); + + request.onerror = function (event) { + console.log("indexedDB error: " + event.target.errorCode); + callback(false, event.target.errorCode); + }.bind(this); + + request.onupgradeneeded = function (event) { + var db = event.target.result; + + if (db.objectStoreNames.contains(this.objectStoreName)) { + db.deleteObjectStore(this.objectStoreName); + } + + var objectStore = db.createObjectStore(this.objectStoreName, {keyPath: "id"}); + objectStore.createIndex("featureId", "featureId", {unique: false}); + objectStore.createIndex("featureLayerUrl", "featureLayerUrl", {unique: false}); + }.bind(this); + + request.onsuccess = function (event) { + this._db = event.target.result; + console.log("database opened successfully"); + callback(true); + }.bind(this); + }; +}; + diff --git a/dist/offline-edit-basic-min.js b/dist/offline-edit-basic-min.js new file mode 100644 index 00000000..70f0eef5 --- /dev/null +++ b/dist/offline-edit-basic-min.js @@ -0,0 +1,97 @@ +/*! esri-offline-maps - v3.0.0 - 2015-11-23 +* Copyright (c) 2015 Environmental Systems Research Institute, Inc. +* Apache License*/ +Offline.options={checks:{image:{url:function(){return"http://esri.github.io/offline-editor-js/tiny-image.png?_="+Math.floor(1e9*Math.random())}},active:"image"}},define(["dojo/Evented","dojo/_base/Deferred","dojo/promise/all","dojo/_base/declare","dojo/_base/array","dojo/dom-attr","dojo/dom-style","dojo/query","dojo/on","esri/config","esri/layers/GraphicsLayer","esri/layers/FeatureLayer","esri/graphic"],function(a,b,c,d,e,f,g,h,i,j,k,l,m){"use strict" +return d("O.esri.Edit.OfflineEditBasic",[a],{_onlineStatus:"online",_featureLayers:{},_editStore:new O.esri.Edit.EditStorePOLS,_defaultXhrTimeout:15e3,_autoOfflineDetect:!0,ONLINE:"online",OFFLINE:"offline",RECONNECTING:"reconnecting",proxyPath:null,DB_NAME:"features_store",DB_OBJECTSTORE_NAME:"features",DB_UID:"objectid",events:{EDITS_SENT:"edits-sent",EDITS_ENQUEUED:"edits-enqueued",EDITS_ENQUEUED_ERROR:"edits-enqueued-error"},constructor:function(a){a&&a.hasOwnProperty("autoDetect")&&(this._autoOfflineDetect=a.autoDetect)},extend:function(a,d){var f=[],g=this +a.offlineExtended=!0,!a.loaded||null===a._url,a.objectIdField=this.DB_UID +var h=null +a.url&&(h=a.url,this._featureLayers[a.url]=a),this._editStore._isDBInit||f.push(this._initializeDB(h)),a._applyEdits=a.applyEdits,a.applyEdits=function(d,e,f,h,i){var j=[] +if(g.getOnlineStatus()===g.ONLINE){var k=a._applyEdits(d,e,f,function(){g.emit(g.events.EDITS_SENT,arguments),h&&h.apply(this,arguments)},i) +return k}var l=new b,m={addResults:[],updateResults:[],deleteResults:[]},n={},o=d||[] +return o.forEach(function(a){var c=new b,d=this._getNextTempId() +a.attributes[this.objectIdField]=d +var e=this +this._validateFeature(a,this.url,g._editStore.ADD).then(function(b){b.success?e._pushValidatedAddFeatureToDB(e,a,b.operation,m,d,c):c.resolve(!0)},function(a){c.reject(a)}),j.push(c)},this),e=e||[],e.forEach(function(a){var c=new b,d=a.attributes[this.objectIdField] +n[d]=a +var e=this +this._validateFeature(a,this.url,g._editStore.UPDATE).then(function(b){b.success?e._pushValidatedUpdateFeatureToDB(e,a,b.operation,m,d,c):c.resolve(!0)},function(a){c.reject(a)}),j.push(c)},this),f=f||[],f.forEach(function(a){var c=new b,d=a.attributes[this.objectIdField],e=this +this._validateFeature(a,this.url,g._editStore.DELETE).then(function(b){b.success?e._pushValidatedDeleteFeatureToDB(e,a,b.operation,m,d,c):c.resolve(!0)},function(a){c.reject(a)}),j.push(c)},this),c(j).then(function(a){for(var b=!0,c=0;c0){j=n +for(var p=j.length,q=0;p>q;q++){b=k[j[q].layer],b.__onEditsComplete=b.onEditsComplete,b.onEditsComplete=function(){},f=[],g=[],h=[],i=[] +var r=new m(j[q].graphic) +switch(j[q].operation){case l.ADD:for(var s=0;s0&&(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},_internalApplyEditsAll:function(a,c,d,e,f,g){var h=this,i=new b +return this._makeEditRequest(a,e,f,g,function(b,f,g){if(b.length>0){var j=new m(e[0].geometry,null,e[0].attributes) +a.add(j)}h._cleanDatabase(a,d,b,f,g).then(function(e){i.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:f,deleteResults:g,databaseResults:e,databaseErrors:null,syncError:null})},function(e){i.resolve({id:c,layer:a.url,tempId:d,addResults:b,updateResults:f,deleteResults:g,databaseResults:null,databaseErrors:e,syncError:e})})},function(b){a.onEditsComplete=a.__onEditsComplete,delete a.__onEditsComplete,i.reject(b)}),i.promise},_cleanDatabase:function(a,c,d,e,f){var g=new b,h=null +e.length>0&&e[0].success&&(h=e[0].objectId),f.length>0&&f[0].success&&(h=f[0].objectId),d.length>0&&d[0].success&&(h=c) +var i={} +return i.attributes={},i.attributes[this.DB_UID]=h,this._editStore["delete"](a.url,i,function(a,b){a?g.resolve({success:!0,error:null,id:h}):g.reject({success:!1,error:b,id:h})}),g.promise},_makeEditRequest:function(a,b,c,d,f,g){var h="f=json",i="",j="",k="" +if(b.length>0&&(e.forEach(b,function(a){a.hasOwnProperty("infoTemplate")&&delete a.infoTemplate},this),i="&adds="+JSON.stringify(b)),c.length>0&&(e.forEach(c,function(a){a.hasOwnProperty("infoTemplate")&&delete a.infoTemplate},this),j="&updates="+JSON.stringify(c)),d.length>0){var l=d[0].attributes[this.DB_UID] +k="&deletes="+l}var m=h+i+j+k +a.hasOwnProperty("credential")&&a.credential&&a.credential.hasOwnProperty("token")&&a.credential.token&&(m=m+"&token="+a.credential.token) +var n=new XMLHttpRequest +n.open("POST",a.url+"/applyEdits",!0),n.setRequestHeader("Content-type","application/x-www-form-urlencoded"),n.onload=function(){if(200===n.status&&""!==n.responseText)try{var a=JSON.parse(this.response) +f(a.addResults,a.updateResults,a.deleteResults)}catch(b){g("Unable to parse xhr response",n)}},n.onerror=function(a){g(a)},n.ontimeout=function(){g("xhr timeout error")},n.timeout=this._defaultXhrTimeout,n.send(m)},_parseResponsesArray:function(a,b){var c=0 +for(var d in a)a.hasOwnProperty(d)&&(a[d].addResults.forEach(function(a){a.success||c++}),a[d].updateResults.forEach(function(a){a.success||c++}),a[d].deleteResults.forEach(function(a){a.success||c++})) +b(c>0?!1:!0)}})}),"undefined"!=typeof O?O.esri.Edit={}:(O={},O.esri={Edit:{}}),O.esri.Edit.EditStorePOLS=function(){"use strict" +this._db=null,this._isDBInit=!1,this.dbName="features_store",this.objectStoreName="features",this.objectId="objectid",this.ADD="add",this.UPDATE="update",this.DELETE="delete",this.FEATURE_LAYER_JSON_ID="feature-layer-object-1001",this.FEATURE_COLLECTION_ID="feature-collection-object-1001",this.isSupported=function(){return window.indexedDB?!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 offlineEdit.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.getEdit=function(a,b){var c=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName) +if("undefined"==typeof a)return void b(!1,"id is undefined.") +var d=c.get(a) +d.onsuccess=function(){var c=d.result +c&&c.id==a?b(!0,c):b(!1,"Id not found")},d.onerror=function(a){b(!1,a)}},this.getAllEditsArray=function(a){var b=[] +if(null!==this._db){var c=this.FEATURE_LAYER_JSON_ID,d=this.FEATURE_COLLECTION_ID,e=this._db.transaction([this.objectStoreName]).objectStore(this.objectStoreName).openCursor() +e.onsuccess=function(e){var f=e.target.result +f&&f.value&&f.value.id?(f.value.id!==c&&f.value.id!==d&&b.push(f.value),f["continue"]()):a(b,"end")}.bind(this),e.onerror=function(b){a(null,b)}}else a(null,"no db")},this.updateExistingEdit=function(a,b,c,d){var e=this._db.transaction([this.objectStoreName],"readwrite").objectStore(this.objectStoreName),f=e.get(c.attributes[this.objectId]) +f.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){e.then(function(a){f.editExists(g).then(function(a){c(!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,a)})})},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.FEATURE_COLLECTION_ID,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?(f.value.id!==c&&f.value.id!==d&&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:"objectId 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.FEATURE_COLLECTION_ID,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,f.value.id!==b&&f.value.id!==c&&(d.editCount+=1),f["continue"]()}else a(d,null)},e.onerror=function(b){a(null,b)}},this.init=function(a){var b=indexedDB.open(this.dbName,11) +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),b.createObjectStore(this.objectStoreName,{keyPath:"id"})}.bind(this),b.onsuccess=function(b){this._db=b.target.result,this._isDBInit=!0,a(!0,null)}.bind(this)}} diff --git a/dist/offline-edit-basic-src.js b/dist/offline-edit-basic-src.js new file mode 100644 index 00000000..c125af10 --- /dev/null +++ b/dist/offline-edit-basic-src.js @@ -0,0 +1,1457 @@ +/*! esri-offline-maps - v3.0.0 - 2015-11-23 +* Copyright (c) 2015 Environmental Systems Research Institute, Inc. +* Apache License*/ +// Configure offline/online detection +// Requires: http://github.hubspot.com/offline/docs/welcome/ + +Offline.options = { // jshint ignore:line + checks: { + image: { + url: function() { + return 'http://esri.github.io/offline-editor-js/tiny-image.png?_=' + (Math.floor(Math.random() * 1000000000)); + } + }, + active: 'image' + } +}; +/*jshint -W030 */ +/** + * This library is optimized for Partial Offline Support ONLY + */ +define([ + "dojo/Evented", + "dojo/_base/Deferred", + "dojo/promise/all", + "dojo/_base/declare", + "dojo/_base/array", + "dojo/dom-attr", + "dojo/dom-style", + "dojo/query", + "dojo/on", + "esri/config", + "esri/layers/GraphicsLayer", + "esri/layers/FeatureLayer", + "esri/graphic"], + function (Evented, Deferred, all, declare, array, domAttr, domStyle, query, on, + esriConfig, GraphicsLayer, FeatureLayer, Graphic) { + "use strict"; + return declare("O.esri.Edit.OfflineEditBasic", [Evented], + { + _onlineStatus: "online", + _featureLayers: {}, + _editStore: new O.esri.Edit.EditStorePOLS(), + _defaultXhrTimeout: 15000, // ms + _autoOfflineDetect: true, + + ONLINE: "online", // all edits will directly go to the server + OFFLINE: "offline", // edits will be enqueued + RECONNECTING: "reconnecting", // sending stored edits to the server + proxyPath: null, // by default we use CORS and therefore proxyPath is null + + // Database properties + DB_NAME: "features_store", // Sets the database name. + DB_OBJECTSTORE_NAME: "features",// Represents an object store that allows access to a set of data in the IndexedDB database + DB_UID: "objectid", // Set this based on the unique identifier is set up in the feature service + + // manager emits event when... + events: { + EDITS_SENT: "edits-sent", // ...whenever any edit is actually sent to the server + EDITS_ENQUEUED: "edits-enqueued", // ...when an edit is enqueued (and not sent to the server) + EDITS_ENQUEUED_ERROR: "edits-enqueued-error", // ...when there is an error during the queing process + }, + + constructor: function(options){ + if(options && options.hasOwnProperty("autoDetect")){ + this._autoOfflineDetect = options.autoDetect; + } + }, + + /** + * Overrides a feature layer. Call this AFTER the FeatureLayer's 'update-end' event. + * objects such as [esri.Graphic] will need to be serialized or you will get an IndexedDB error. + * @param layer + * @param updateEndEvent The FeatureLayer's update-end event object + * @param callback {true, null} or {false, errorString} Traps whether or not the database initialized + * @returns deferred + */ + extend: function (layer, callback) { + + var extendPromises = []; // deferred promises related to initializing this method + + var self = this; + layer.offlineExtended = true; // to identify layer has been extended + + if(!layer.loaded || layer._url === null) { + console.error("Make sure to initialize OfflineEditBasic after layer loaded and feature layer update-end event."); + } + + // NOTE: At v2.6.1 we've discovered that not all feature layers support objectIdField. + // However, to try to be consistent here with how the library is managing Ids + // 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 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; + } + + // Initialize the database as well as set offline data. + if(!this._editStore._isDBInit) { + extendPromises.push(this._initializeDB(url)); + } + + // replace the applyEdits() method + layer._applyEdits = layer.applyEdits; + + /** + * Overrides the ArcGIS API for JavaSccript applyEdits() method. + * @param adds Creates a new edit entry. + * @param updates Updates an existing entry. + * @param deletes Deletes an existing entry. + * @param callback Called when the operation is complete. + * @param errback An error object is returned if an error occurs + * @returns {*} deferred + * @event EDITS_ENQUEUED boolean if all edits successfully stored while offline + * @event EDITS_ENQUEUED_ERROR string message if there was an error while storing an edit while offline + */ + layer.applyEdits = function (adds, updates, deletes, callback, errback) { + // inside this method, 'this' will be the FeatureLayer + // and 'self' will be the offlineFeatureLayer object + var promises = []; + + if (self.getOnlineStatus() === self.ONLINE) { + var def = layer._applyEdits(adds, updates, deletes, + function () { + self.emit(self.events.EDITS_SENT, arguments); + callback && callback.apply(this, arguments); + }, + errback); + return def; + } + + var deferred1 = new Deferred(); + var results = {addResults: [], updateResults: [], deleteResults: []}; + var updatesMap = {}; + + var _adds = adds || []; + _adds.forEach(function (addEdit) { + var deferred = new Deferred(); + + var objectId = this._getNextTempId(); + + addEdit.attributes[this.objectIdField] = objectId; + + var thisLayer = this; + + // We need to run some validation tests against each feature being added. + // Adding the same feature multiple times results in the last edit wins. LIFO. + this._validateFeature(addEdit,this.url,self._editStore.ADD).then(function(result){ + console.log("EDIT ADD IS BACK!!! " ); + + if(result.success){ + thisLayer._pushValidatedAddFeatureToDB(thisLayer,addEdit,result.operation,results,objectId,deferred); + } + else{ + // If we get here then we deleted an edit that was added offline. + deferred.resolve(true); + } + + },function(error){ + console.log("_validateFeature: Unable to validate!"); + deferred.reject(error); + }); + + promises.push(deferred); + }, this); + + updates = updates || []; + updates.forEach(function (updateEdit) { + var deferred = new Deferred(); + + var objectId = updateEdit.attributes[this.objectIdField]; + updatesMap[objectId] = updateEdit; + + var thisLayer = this; + + // We need to run some validation tests against each feature being updated. + // If we have added a feature and we need to update it then we change it's operation type to "add" + // and the last edits wins. LIFO. + this._validateFeature(updateEdit,this.url,self._editStore.UPDATE).then(function(result){ + console.log("EDIT UPDATE IS BACK!!! " ); + + if(result.success){ + thisLayer._pushValidatedUpdateFeatureToDB(thisLayer,updateEdit,result.operation,results,objectId,deferred); + } + else{ + // If we get here then we deleted an edit that was added offline. + deferred.resolve(true); + } + + },function(error){ + console.log("_validateFeature: Unable to validate!"); + deferred.reject(error); + }); + + promises.push(deferred); + }, this); + + deletes = deletes || []; + deletes.forEach(function (deleteEdit) { + var deferred = new Deferred(); + + var objectId = deleteEdit.attributes[this.objectIdField]; + + var thisLayer = this; + + // We need to run some validation tests against each feature being deleted. + // If we have added a feature and then deleted it in the app + this._validateFeature(deleteEdit,this.url,self._editStore.DELETE).then(function(result){ + console.log("EDIT DELETE IS BACK!!! " ); + + if(result.success){ + thisLayer._pushValidatedDeleteFeatureToDB(thisLayer,deleteEdit,result.operation,results,objectId,deferred); + } + else{ + // If we get here then we deleted an edit that was added offline. + deferred.resolve(true); + } + + },function(error){ + console.log("_validateFeature: Unable to validate!"); + deferred.reject(error); + }); + + promises.push(deferred); + }, this); + + all(promises).then(function (r) { + // Make sure all edits were successful. If not throw an error. + var promisesSuccess = true; + for (var v = 0; v < r.length; v++) { + if (r[v] === false) { + promisesSuccess = false; + } + } + + promisesSuccess === true ? self.emit(self.events.EDITS_ENQUEUED, results) : self.emit(self.events.EDITS_ENQUEUED_ERROR, results); + this._editHandler(results, _adds, updatesMap, callback, errback, deferred1); + }.bind(this)); + + return deferred1; + + }; // layer.applyEdits() + + /** + * Returns the approximate size of the edits database in bytes + * @param callback callback({usage}, error) Whereas, the usage Object is {sizeBytes: number, editCount: number} + */ + layer.getUsage = function(callback){ + self._editStore.getUsage(function(usage,error){ + callback(usage,error); + }); + }; + + /** + * Full edits database reset. + * CAUTION! If some edits weren't successfully sent, then their record + * will still exist in the database. If you use this function you + * will also delete those records. + * @param callback (boolean, error) + */ + layer.resetDatabase = function(callback){ + self._editStore.resetEditsQueue(function(result,error){ + callback(result,error); + }); + }; + + /** + * Returns the number of edits pending in the database. + * @param callback callback( int ) + */ + layer.pendingEditsCount = function(callback){ + self._editStore.pendingEditsCount(function(count){ + callback(count); + }); + }; + + /** + * Create a featureDefinition + * @param featureLayer + * @param featuresArr + * @param geometryType + * @param callback + */ + layer.getFeatureDefinition = function (/* Object */ featureLayer, /* Array */ featuresArr, /* String */ geometryType, callback) { + + var featureDefinition = { + "layerDefinition": featureLayer, + "featureSet": { + "features": featuresArr, + "geometryType": geometryType + } + + }; + + callback(featureDefinition); + }; + + /** + * Returns an iterable array of all edits stored in the database + * Each item in the array is an object and contains: + * { + * id: "internal ID", + * operation: "add, update or delete", + * layer: "layerURL", + * type: "esri Geometry Type", + * graphic: "esri.Graphic converted to JSON then serialized" + * } + * @param callback (true, array) or (false, errorString) + */ + layer.getAllEditsArray = function(callback){ + self._editStore.getAllEditsArray(function(array,message){ + if(message == "end"){ + callback(true,array); + } + else{ + callback(false,message); + } + }); + }; + + /* internal methods */ + + /** + * Pushes a DELETE request to the database after it's been validated + * @param layer + * @param deleteEdit + * @param operation + * @param resultsArray + * @param objectId + * @param deferred + * @private + */ + layer._pushValidatedDeleteFeatureToDB = function(layer,deleteEdit,operation,resultsArray,objectId,deferred){ + self._editStore.pushEdit(operation, layer.url, deleteEdit, function (result, error) { + + if(result){ + resultsArray.deleteResults.push({success: true, error: null, objectId: objectId}); + + // Use the correct key as set by self.DB_UID + var tempIdObject = {}; + tempIdObject[self.DB_UID] = objectId; + } + else{ + resultsArray.deleteResults.push({success: false, error: error, objectId: objectId}); + } + + deferred.resolve(result); + }); + }; + + /** + * Pushes an UPDATE request to the database after it's been validated + * @param layer + * @param updateEdit + * @param operation + * @param resultsArray + * @param objectId + * @param deferred + * @private + */ + layer._pushValidatedUpdateFeatureToDB = function(layer,updateEdit,operation,resultsArray,objectId,deferred){ + self._editStore.pushEdit(operation, layer.url, updateEdit, function (result, error) { + + if(result){ + resultsArray.updateResults.push({success: true, error: null, objectId: objectId}); + + // Use the correct key as set by self.DB_UID + var tempIdObject = {}; + tempIdObject[self.DB_UID] = objectId; + } + else{ + resultsArray.updateResults.push({success: false, error: error, objectId: objectId}); + } + + deferred.resolve(result); + }); + }; + + /** + * Pushes an ADD request to the database after it's been validated + * @param layer + * @param addEdit + * @param operation + * @param resultsArray + * @param objectId + * @param deferred + * @private + */ + layer._pushValidatedAddFeatureToDB = function(layer,addEdit,operation,resultsArray,objectId,deferred){ + self._editStore.pushEdit(operation, layer.url, addEdit, function (result, error) { + if(result){ + resultsArray.addResults.push({success: true, error: null, objectId: objectId}); + + // Use the correct key as set by self.DB_UID + var tempIdObject = {}; + tempIdObject[self.DB_UID] = objectId; + } + else{ + resultsArray.addResults.push({success: false, error: error, objectId: objectId}); + } + + deferred.resolve(result); + }); + }; + + /** + * Validates duplicate entries. Last edit on same feature can overwite any previous values. + * Note: if an edit was already added offline and you delete it then we return success == false + * @param graphic esri.Graphic. + * @param layerUrl the URL of the feature service + * @param operation add, update or delete action on an edit + * @returns deferred {success:boolean,graphic:graphic,operation:add|update|delete} + * @private + */ + layer._validateFeature = function (graphic,layerUrl,operation) { + + var deferred = new Deferred(); + + var id = layerUrl + "/" + graphic.attributes[self.DB_UID]; + + self._editStore.getEdit(id,function(success,result){ + if (success) { + switch( operation ) + { + case self._editStore.ADD: + // Not good - however we'll allow the new ADD to replace/overwrite existing edit + // and pass it through unmodified. Last ADD wins. + deferred.resolve({"success":true,"graphic":graphic,"operation":operation}); + break; + case self._editStore.UPDATE: + // If we are doing an update on a feature that has not been added to + // the server yet, then we need to maintain its operation as an ADD + // and not an UPDATE. This avoids the potential for an error if we submit + // an update operation on a feature that has not been added to the + // database yet. + if(result.operation == self._editStore.ADD){ + graphic.operation = self._editStore.ADD; + operation = self._editStore.ADD; + } + deferred.resolve({"success":true,"graphic":graphic,"operation":operation}); + break; + case self._editStore.DELETE: + + var resolved = true; + + if(result.operation == self._editStore.ADD){ + // If we are deleting a new feature that has not been added to the + // server yet we need to delete it + layer._deleteTemporaryFeature(graphic,function(success, error){ + if(!success){ + resolved = false; + console.log("Unable to delete feature: " + JSON.stringify(error)); + } + }); + } + deferred.resolve({"success":resolved,"graphic":graphic,"operation":operation}); + break; + } + } + else if(result == "Id not found"){ + // Let's simply pass the graphic back as good-to-go. + // No modifications needed because the graphic does not + // already exist in the database. + deferred.resolve({"success":true,"graphic":graphic,"operation":operation}); + } + else{ + deferred.reject(graphic); + } + }); + + return deferred; + }; + + /** + * Delete a graphic that has been added while offline. + * @param graphic + * @param callback + * @private + */ + layer._deleteTemporaryFeature = function(graphic,callback){ + self._editStore.delete(layer.url,graphic,function(success,error){ + callback(success, error); + }); + }; + + layer._getFilesFromForm = function (formNode) { + var files = []; + var inputNodes = array.filter(formNode.elements, function (node) { + return node.type === "file"; + }); + inputNodes.forEach(function (inputNode) { + files.push.apply(files, inputNode.files); + }, this); + return files; + }; + + // we need to identify ADDs before sending them to the server + // we assign temporary ids (using negative numbers to distinguish them from real ids) + layer._nextTempId = -1; + layer._getNextTempId = function () { + return this._nextTempId--; + }; + + // We are currently only passing in a single deferred. + all(extendPromises).then(function (r) { + + if(self._autoOfflineDetect){ + Offline.on('up', function(){ // jshint ignore:line + + self.goOnline(function(success,error){ // jshint ignore:line + console.log("GOING ONLINE"); + }); + }); + + Offline.on('down', function(){ // jshint ignore:line + self.goOffline(); // jshint ignore:line + }); + } + + callback(true, null); + }); + + }, // extend + + /** + * Forces library into an offline state. Any edits applied during this condition will be stored locally + */ + goOffline: function () { + console.log("offlineFeatureManager going offline"); + this._onlineStatus = this.OFFLINE; + }, + + /** + * 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 + * @param callback callback( boolean, errors ) + */ + goOnline: function (callback) { + console.log("OfflineEditBasic going online"); + this._onlineStatus = this.RECONNECTING; + this._replayStoredEdits(function (success, responses) { + //var result = {success: success, responses: responses}; + this._onlineStatus = this.ONLINE; + + //this._onlineStatus = this.ONLINE; + callback && callback(success,responses); + + }.bind(this)); + }, + + /** + * Determines if offline or online condition exists + * @returns {string} ONLINE or OFFLINE + */ + getOnlineStatus: function () { + return this._onlineStatus; + }, + + /* internal methods */ + + /** + * Initialize the database and push featureLayer JSON to DB if required. + * @param url Feature Layer's url. This is used by this library for internal feature identification. + * @return deferred + * @private + */ + _initializeDB: function(url){ + var deferred = new Deferred(); + + var editStore = this._editStore; + + // Configure the database + editStore.dbName = this.DB_NAME; + editStore.objectStoreName = this.DB_OBJECTSTORE_NAME; + editStore.objectId = this.DB_UID; + + // Attempt to initialize the database + editStore.init(function (result, error) { + + if(result){ + deferred.resolve({success:true, error: null}); + } + else{ + deferred.reject({success:false, error: null}); + } + }); + + return deferred; + }, + + // + // methods to send features back to the server + // + + /** + * Attempts to send any edits in the database. Monitor events for success or failure. + * @param callback + * @event ALL_EDITS_SENT when all edits have been successfully sent. Contains {[addResults],[updateResults],[deleteResults]} + * @event EDITS_SENT_ERROR some edits were not sent successfully. Contains {msg: error} + * @private + */ + _replayStoredEdits: function (callback) { + var promises = {}; + var that = this; + + // + // send edits for each of the layers + // + var layer; + var adds = [], updates = [], deletes = []; + var tempObjectIds = []; + var tempArray = []; + var featureLayers = this._featureLayers; + + var editStore = this._editStore; + + this._editStore.getAllEditsArray(function (result, err) { + if (result.length > 0) { + tempArray = result; + + var length = tempArray.length; + + for (var n = 0; n < length; n++) { + layer = featureLayers[tempArray[n].layer]; + layer.__onEditsComplete = layer.onEditsComplete; + layer.onEditsComplete = function () { + console.log("intercepting events onEditsComplete"); + }; + + // Let's zero everything out + adds = [], updates = [], deletes = [], tempObjectIds = []; + + // IMPORTANT: reconstitute the graphic JSON into an actual esri.Graphic object + // NOTE: we are only sending one Graphic per loop! + var graphic = new Graphic(tempArray[n].graphic); + + switch (tempArray[n].operation) { + case editStore.ADD: + for (var i = 0; i < layer.graphics.length; i++) { + var g = layer.graphics[i]; + if (g.attributes[layer.objectIdField] === graphic.attributes[layer.objectIdField]) { + layer.remove(g); + break; + } + } + tempObjectIds.push(graphic.attributes[layer.objectIdField]); + delete graphic.attributes[layer.objectIdField]; + adds.push(graphic); + break; + case editStore.UPDATE: + updates.push(graphic); + break; + case editStore.DELETE: + deletes.push(graphic); + break; + } + + // Note: when the feature layer is created with a feature collection we have to handle applyEdits() differently + // TO-DO rename this method. + promises[n] = that._internalApplyEditsAll(layer, tempArray[n].id, tempObjectIds, adds, updates, deletes); + } + + // wait for all requests to finish + // responses contain {id,layer,tempId,addResults,updateResults,deleteResults} + var allPromises = all(promises); + allPromises.then( + function (responses) { + console.log("OfflineEditBasic sync - all responses are back"); + callback(true, responses); + }, + function (errors) { + console.log("OfflineEditBasic._replayStoredEdits - ERROR!!"); + callback(false, errors); + } + ); + + } + else{ + // No edits were found + callback(true,[]); + } + }); + }, + + /** + * DEPRECATED as of v2.11 - + * TO-DO remove in next release + * Only delete items from database that were verified as successfully updated on the server. + * @param responses Object + * @param callback callback(true, responses) or callback(false, responses) + * @private + */ + _cleanSuccessfulEditsDatabaseRecords: function (responses, callback) { + if (Object.keys(responses).length !== 0) { + + var editsArray = []; + var editsFailedArray = []; + + for (var key in responses) { + if (responses.hasOwnProperty(key)) { + + var edit = responses[key]; + var tempResult = {}; + + if (edit.updateResults.length > 0) { + if (edit.updateResults[0].success) { + tempResult.layer = edit.layer; + tempResult.id = edit.updateResults[0].objectId; + editsArray.push(tempResult); + } + else { + editsFailedArray.push(edit); + } + } + if (edit.deleteResults.length > 0) { + if (edit.deleteResults[0].success) { + tempResult.layer = edit.layer; + tempResult.id = edit.deleteResults[0].objectId; + editsArray.push(tempResult); + } + else { + editsFailedArray.push(edit); + } + } + if (edit.addResults.length > 0) { + if (edit.addResults[0].success) { + tempResult.layer = edit.layer; + tempResult.id = edit.tempId; + editsArray.push(tempResult); + } + else { + editsFailedArray.push(edit); + } + } + } + } + + var promises = {}; + var length = editsArray.length; + for (var i = 0; i < length; i++) { + promises[i] = this._updateDatabase(editsArray[i]); + } + //console.log("EDIT LIST " + JSON.stringify(editsArray)); + + // wait for all requests to finish + // + var allPromises = all(promises); + allPromises.then( + function (responses) { + editsFailedArray.length > 0 ? callback(false, responses) : callback(true, responses); + }, + function (errors) { + callback(false, errors); + } + ); + } + else { + callback(true, {}); + } + }, + + /** + * Deletes edits from database. + * @param edit + * @returns {l.Deferred.promise|*|c.promise|q.promise|promise} + * @private + */ + _updateDatabase: function (edit) { + var dfd = new Deferred(); + var fakeGraphic = {}; + fakeGraphic.attributes = {}; + + // Use the correct attributes key! + fakeGraphic.attributes[this.DB_UID] = edit.id; + + this._editStore.delete(edit.layer, fakeGraphic, function (success, error) { + if (success) { + dfd.resolve({success: true, error: null}); + } + else { + dfd.reject({success: false, error: error}); + } + }.bind(this)); + + return dfd.promise; + + }, + + /** + * Applies edits. This works with both standard feature layers and 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 + */ + _internalApplyEditsAll: function (layer, id, tempObjectIds, adds, updates, deletes) { + var that = this; + var dfd = new Deferred(); + + this._makeEditRequest(layer, adds, updates, deletes, + function (addResults, updateResults, deleteResults) { + + if(addResults.length > 0) { + var graphic = new Graphic(adds[0].geometry,null,adds[0].attributes); + layer.add(graphic); + } + + that._cleanDatabase(layer, tempObjectIds, addResults, updateResults, deleteResults).then(function(results){ + 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, + databaseResults: results, + databaseErrors: null, + syncError: null + }); + }, function(error) { + 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, + databaseResults: null, + databaseErrors: error, + syncError: error + }); + }); + + }, + function (error) { + layer.onEditsComplete = layer.__onEditsComplete; + delete layer.__onEditsComplete; + + dfd.reject(error); + } + ); + return dfd.promise; + }, + + _cleanDatabase: function(layer, tempId, addResults, updateResults, deleteResults) { + + var dfd = new Deferred(); + var id = null; + + if (updateResults.length > 0) { + if (updateResults[0].success) { + id = updateResults[0].objectId; + } + } + if (deleteResults.length > 0) { + if (deleteResults[0].success) { + id = deleteResults[0].objectId; + } + } + if (addResults.length > 0) { + if (addResults[0].success) { + id = tempId; + } + } + + var fakeGraphic = {}; + fakeGraphic.attributes = {}; + + // Use the correct attributes key! + fakeGraphic.attributes[this.DB_UID] = id; + + // Delete the edit from the database + this._editStore.delete(layer.url, fakeGraphic, function (success, error) { + if (success) { + dfd.resolve({success: true, error: null, id: id}); + } + else { + dfd.reject({success: false, error: error, id: id}); + } + }); + + 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 layer + * @param adds + * @param updates + * @param deletes + * @returns {*|r} + * @private + */ + _makeEditRequest: function(layer,adds, updates, deletes, callback, errback) { + + var f = "f=json", a = "", u = "", d = ""; + + if(adds.length > 0) { + array.forEach(adds, function(add){ + if(add.hasOwnProperty("infoTemplate")){ // if the add has an infoTemplate attached, + delete add.infoTemplate; // delete it to reduce payload size. + } + }, this); + a = "&adds=" + JSON.stringify((adds)); + } + if(updates.length > 0) { + array.forEach(updates, function(update){ + if(update.hasOwnProperty("infoTemplate")){ // if the update has an infoTemplate attached, + delete update.infoTemplate; // delete it to reduce payload size. + } + }, this); + u = "&updates=" + JSON.stringify(updates); + } + if(deletes.length > 0) { + var id = deletes[0].attributes[this.DB_UID]; + d = "&deletes=" + id; + } + + var params = f + a + u + d; + + if(layer.hasOwnProperty("credential") && layer.credential){ + if(layer.credential.hasOwnProperty("token") && layer.credential.token){ + params = params + "&token=" + layer.credential.token; + } + } + + var req = new XMLHttpRequest(); + req.open("POST", layer.url + "/applyEdits", true); + req.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + req.onload = function() + { + if( req.status === 200 && req.responseText !== "") + { + try { + var obj = JSON.parse(this.response); + callback(obj.addResults, obj.updateResults, obj.deleteResults); + } + catch(err) { + console.error("EDIT REQUEST REPONSE WAS NOT SUCCESSFUL:", req); + errback("Unable to parse xhr response", req); + } + } + + }; + req.onerror = function(e) + { + console.error("_makeEditRequest failed: " + e); + errback(e); + }; + req.ontimeout = function() { + errback("xhr timeout error"); + }; + req.timeout = this._defaultXhrTimeout; + req.send(params); + }, + + /** + * Parses the respones related to going back online and cleaning up the database. + * @param responses + * @returns {promise} True means all was successful. False indicates there was a problem. + * @private + */ + _parseResponsesArray: function(responses,callback) { + + var err = 0; + + for (var key in responses) { + if (responses.hasOwnProperty(key)) { + responses[key].addResults.forEach(function(result){ + if(!result.success) { + err++; + } + }); + + responses[key].updateResults.forEach(function(result){ + if(!result.success) { + err++; + } + }); + + responses[key].deleteResults.forEach(function(result){ + if(!result.success) { + err++; + } + }); + } + } + + if(err > 0){ + callback(false); + } + else { + callback(true); + } + } + }); // declare + }); // define +/** + * Creates a namespace for the non-AMD libraries in this directory + */ +/*jshint -W020 */ +if(typeof O != "undefined"){ + O.esri.Edit = {}; +} +else{ + O = {}; + O.esri = { + Edit: {} + }; +} +/*global indexedDB */ +/*jshint -W030 */ +/** + * This library is optimized for Partial Offline Support ONLY + * @constructor + */ +O.esri.Edit.EditStorePOLS = function () { + + "use strict"; + + this._db = null; + this._isDBInit = false; + + // Public properties + + this.dbName = "features_store"; + this.objectStoreName = "features"; + this.objectId = "objectid"; // set this depending on how your feature service is configured; + + //var _dbIndex = "featureId"; // @private + + // ENUMs + + this.ADD = "add"; + this.UPDATE = "update"; + this.DELETE = "delete"; + + this.FEATURE_LAYER_JSON_ID = "feature-layer-object-1001"; + this.FEATURE_COLLECTION_ID = "feature-collection-object-1001"; + + this.isSupported = function () { + if (!window.indexedDB) { + return false; + } + return true; + }; + + /** + * Commit an edit to the database + * @param operation add, update or delete + * @param layerUrl the URL of the feature layer + * @param graphic esri/graphic. The method will serialize to JSON + * @param callback callback(true, edit) or callback(false, error) + */ + this.pushEdit = function (operation, layerUrl, graphic, callback) { + + var edit = { + id: layerUrl + "/" + graphic.attributes[this.objectId], + operation: operation, + layer: layerUrl, + type: graphic.geometry.type, + graphic: graphic.toJson() + }; + + if(typeof graphic.attributes[this.objectId] === "undefined") { + console.error("editsStore.pushEdit() - failed to insert undefined objectId into database. Did you set offlineEdit.DB_UID? " + JSON.stringify(graphic.attributes)); + callback(false,"editsStore.pushEdit() - failed to insert undefined objectId into database. Did you set offlineEdit.DB_UID? " + JSON.stringify(graphic.attributes)); + } + else{ + var transaction = this._db.transaction([this.objectStoreName], "readwrite"); + + transaction.oncomplete = function (event) { + callback(true); + }; + + transaction.onerror = function (event) { + callback(false, event.target.error.message); + }; + + var objectStore = transaction.objectStore(this.objectStoreName); + objectStore.put(edit); + } + }; + + /** + * Retrieve an edit by its internal ID + * @param id String identifier + * @param callback callback(true,graphic) or callback(false, error) + */ + this.getEdit = function(id,callback){ + + console.assert(this._db !== null, "indexeddb not initialized"); + var objectStore = this._db.transaction([this.objectStoreName], "readwrite").objectStore(this.objectStoreName); + + if(typeof id === "undefined"){ + callback(false,"id is undefined."); + return; + } + + //Get the entry associated with the graphic + var objectStoreGraphicRequest = objectStore.get(id); + + objectStoreGraphicRequest.onsuccess = function () { + var graphic = objectStoreGraphicRequest.result; + if (graphic && (graphic.id == id)) { + callback(true,graphic); + } + else { + callback(false,"Id not found"); + } + }; + + objectStoreGraphicRequest.onerror = function (msg) { + callback(false,msg); + }; + }; + + /** + * Returns all the edits as a single Array via the callback + * @param callback {array, messageString} or {null, messageString} + */ + this.getAllEditsArray = function (callback) { + + console.assert(this._db !== null, "indexeddb not initialized"); + var editsArray = []; + + if (this._db !== null) { + + var fLayerJSONId = this.FEATURE_LAYER_JSON_ID; + var fCollectionId = this.FEATURE_COLLECTION_ID; + + var transaction = this._db.transaction([this.objectStoreName]) + .objectStore(this.objectStoreName) + .openCursor(); + + transaction.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor && cursor.value && cursor.value.id) { + + // Make sure we are not return FeatureLayer JSON data + if (cursor.value.id !== fLayerJSONId && cursor.value.id !== fCollectionId) { + editsArray.push(cursor.value); + + } + cursor.continue(); + } + else { + callback(editsArray, "end"); + } + }.bind(this); + transaction.onerror = function (err) { + callback(null, err); + }; + } + else { + callback(null, "no db"); + } + }; + + /** + * Update an edit already exists in the database + * @param operation add, update or delete + * @param layer the URL of the feature layer + * @param graphic esri/graphic. The method will serialize to JSON + * @param callback {true, edit} or {false, error} + */ + this.updateExistingEdit = function (operation, layer, graphic, callback) { + + console.assert(this._db !== null, "indexeddb not initialized"); + + var objectStore = this._db.transaction([this.objectStoreName], "readwrite").objectStore(this.objectStoreName); + + //Let's get the entry associated with the graphic + var objectStoreGraphicRequest = objectStore.get(graphic.attributes[this.objectId]); + objectStoreGraphicRequest.onsuccess = function () { + + //Grab the data object returned as a result + // TO-DO Do we keep this?? + objectStoreGraphicRequest.result; + + //Create a new update object + var update = { + id: layer + "/" + graphic.attributes[this.objectId], + operation: operation, + layer: layer, + graphic: graphic.toJson() + }; + + // Insert the update into the database + var updateGraphicRequest = objectStore.put(update); + + updateGraphicRequest.onsuccess = function () { + callback(true); + }; + + updateGraphicRequest.onerror = function (err) { + callback(false, err); + }; + }.bind(this); + }; + + /** + * Delete a pending edit's record from the database. + * IMPORTANT: Be aware of false negatives. See Step 4 in this function. + * + * @param layerUrl + * @param graphic Graphic + * @param callback {boolean, error} + */ + this.delete = function (layerUrl, graphic, callback) { + + // NOTE: the implementation of the IndexedDB spec has a design fault with respect to + // handling deletes. The result of a delete operation is always designated as undefined. + // What this means is that there is no way to tell if an operation was successful or not. + // And, it will always return 'true.' + // + // In order to get around this we have to verify if after the attempted deletion operation + // if the record is or is not in the database. Kinda dumb, but that's how IndexedDB works. + //http://stackoverflow.com/questions/17137879/is-there-a-way-to-get-information-on-deleted-record-when-calling-indexeddbs-obj + + var db = this._db; + var deferred = null; + var self = this; + + var id = layerUrl + "/" + graphic.attributes[this.objectId]; + + require(["dojo/Deferred"], function (Deferred) { + deferred = new Deferred(); + + // Step 1 - lets see if record exits. If it does then return callback. + self.editExists(id).then(function (result) { + + // Step 4 - Then we check to see if the record actually exists or not. + deferred.then(function (result) { + + // IF the delete was successful, then the record should return 'false' because it doesn't exist. + self.editExists(id).then(function (results) { + callback(false); + }, + function (err) { + callback(true); //because we want this test to throw an error. That means item deleted. + }); + }, + // There was a problem with the delete operation on the database + function (err) { + callback(false, err); + }); + + var objectStore = db.transaction([self.objectStoreName], "readwrite").objectStore(self.objectStoreName); + + // Step 2 - go ahead and delete graphic + var objectStoreDeleteRequest = objectStore.delete(id); + + // Step 3 - We know that the onsuccess will always fire unless something serious goes wrong. + // So we go ahead and resolve the deferred here. + objectStoreDeleteRequest.onsuccess = function () { + deferred.resolve(true); + }; + + objectStoreDeleteRequest.onerror = function (msg) { + deferred.reject({success: false, error: msg}); + }; + + }, + // If there is an error in editExists() + function (err) { + callback(false, err); + }); + }); + }; + + /** + * Full database reset. + * CAUTION! If some edits weren't successfully sent, then their record + * will still exist in the database. If you use this function you + * will also delete those records. + * @param callback boolean + */ + this.resetEditsQueue = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var request = this._db.transaction([this.objectStoreName], "readwrite") + .objectStore(this.objectStoreName) + .clear(); + request.onsuccess = function (event) { + setTimeout(function () { + callback(true); + }, 0); + }; + request.onerror = function (err) { + callback(false, err); + }; + }; + + this.pendingEditsCount = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var count = 0; + var id = this.FEATURE_LAYER_JSON_ID; + var fCollectionId = this.FEATURE_COLLECTION_ID; + + var transaction = this._db.transaction([this.objectStoreName], "readwrite"); + var objectStore = transaction.objectStore(this.objectStoreName); + objectStore.openCursor().onsuccess = function (evt) { + var cursor = evt.target.result; + if (cursor && cursor.value && cursor.value.id) { + if (cursor.value.id !== id && cursor.value.id !== fCollectionId) { + count++; + } + cursor.continue(); + } + else { + callback(count); + } + }; + }; + + /** + * Verify is an edit already exists in the database. Checks the objectId. + * @param id + * @returns {deferred} {success: boolean, error: message} + * @private + */ + this.editExists = function (id) { + + var db = this._db; + var deferred = null; + var self = this; + + require(["dojo/Deferred"], function (Deferred) { + deferred = new Deferred(); + + var objectStore = db.transaction([self.objectStoreName], "readwrite").objectStore(self.objectStoreName); + + //Get the entry associated with the graphic + var objectStoreGraphicRequest = objectStore.get(id); + + objectStoreGraphicRequest.onsuccess = function () { + var graphic = objectStoreGraphicRequest.result; + if (graphic && (graphic.id == id)) { + deferred.resolve({success: true, error: null}); + } + else { + deferred.reject({success: false, error: "objectId is not a match."}); + } + }; + + objectStoreGraphicRequest.onerror = function (msg) { + deferred.reject({success: false, error: msg}); + }; + }); + + //We return a deferred object so that when calling this function you can chain it with a then() statement. + return deferred; + }; + + /** + * Returns the approximate size of the database in bytes + * IMPORTANT: Currently requires all data be serialized! + * @param callback callback({usage}, error) Whereas, the usage Object is {sizeBytes: number, editCount: number} + */ + this.getUsage = function (callback) { + console.assert(this._db !== null, "indexeddb not initialized"); + + var id = this.FEATURE_LAYER_JSON_ID; + var fCollectionId = this.FEATURE_COLLECTION_ID; + + var usage = {sizeBytes: 0, editCount: 0}; + + var transaction = this._db.transaction([this.objectStoreName]) + .objectStore(this.objectStoreName) + .openCursor(); + + console.log("dumping keys"); + + transaction.onsuccess = function (event) { + var cursor = event.target.result; + if (cursor && cursor.value && cursor.value.id) { + var storedObject = cursor.value; + var json = JSON.stringify(storedObject); + usage.sizeBytes += json.length; + + if (cursor.value.id !== id && cursor.value.id !== fCollectionId) { + usage.editCount += 1; + } + + cursor.continue(); + } + else { + callback(usage, null); + } + }; + transaction.onerror = function (err) { + callback(null, err); + }; + }; + + this.init = function (callback) { + console.log("init editsStore.js"); + + var request = indexedDB.open(this.dbName, 11); + callback = callback || function (success) { + console.log("EditsStore::init() success:", success); + }.bind(this); + + request.onerror = function (event) { + console.log("indexedDB error: " + event.target.errorCode); + callback(false, event.target.errorCode); + }.bind(this); + + request.onupgradeneeded = function (event) { + var db = event.target.result; + + if (db.objectStoreNames.contains(this.objectStoreName)) { + db.deleteObjectStore(this.objectStoreName); + } + + db.createObjectStore(this.objectStoreName, {keyPath: "id"}); + }.bind(this); + + request.onsuccess = function (event) { + this._db = event.target.result; + this._isDBInit = true; + console.log("database opened successfully"); + callback(true, null); + }.bind(this); + }; +}; + + diff --git a/dist/offline-tiles-advanced-min.js b/dist/offline-tiles-advanced-min.js index fde704bc..5e24e446 100644 --- a/dist/offline-tiles-advanced-min.js +++ b/dist/offline-tiles-advanced-min.js @@ -1,5 +1,92 @@ -/*! esri-offline-maps - v2.16.0 - 2015-10-29 +/*! esri-offline-maps - v3.0.0 - 2015-11-23 * 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,_secure:!1,constructor:function(a,b,c,d){this._isLocalStorage()===!1?(alert("OfflineTiles Library not supported on this browser."),b(!1)):window.localStorage.offline_id_manager="",void 0===d||null===d?(this.DB_NAME="offline_tile_store",this.DB_OBJECTSTORE_NAME="tilepath"):(this.DB_NAME=d.dbName,this.DB_OBJECTSTORE_NAME=d.objectStoreName),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 e=!0;return("undefined"!=typeof c||null!=c)&&(e=c),this.showBlankTiles=!0,this.offline={online:e,store:new O.esri.Tiles.TilesStore,proxyPath:null},this.offline.store.isSupported()?(this.offline.store.dbName=this.DB_NAME,this.offline.store.objectStoreName=this.DB_OBJECTSTORE_NAME,this.offline.store.init(function(c){c&&this._getTileInfoPrivate(a,function(a){b(a)})}.bind(this._self)),void 0):b(!1,"indexedDB not supported")},getTileUrl:function(b,c,d){this._level=b;var e,f=this,g=window.localStorage.offline_id_manager;if(void 0===g||""===g)e="";else{var h=JSON.parse(g);h.credentials.forEach(function(a){-1!==f.url.indexOf(a.server)&&(e="?token="+a.token)})}var i=this.url+"/tile/"+b+"/"+c+"/"+d+e;if(this.offline.online)return this._lastTileUrl=i,i;i=i.split("?")[0];var j="void:/"+b+"/"+c+"/"+d,k=null;return this._tilesCore._getTiles(k,this._imageType,i,j,this.offline.store,a,this.showBlankTiles),j},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()-Math.abs(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}},_parseTileInfo:function(a,b,c){b.offline.online===!1&&a===!1&&void 0!==localStorage.__offlineTileInfo?a=localStorage.__offlineTileInfo:b.offline.online===!1&&a===!1&&void 0===localStorage.__offlineTileInfo&&alert("There was a problem retrieving tiled map info in OfflineTilesEnablerLayer."),b._tilesCore._parseGetTileInfo(a,function(a){b.layerInfos=a.resultObj.layers,b.minScale=a.resultObj.minScale,b.maxScale=a.resultObj.maxScale,b.tileInfo=a.tileInfo,b._imageType=b.tileInfo.format.toLowerCase(),b.fullExtent=a.fullExtent,b.spatialReference=b.tileInfo.spatialReference,b.initialExtent=a.initExtent,b.loaded=!0,b.onLoad(b),c(!0)})},_getTileInfoPrivate:function(a,b){var c,d=this,e=new XMLHttpRequest,f=window.localStorage.offline_id_manager;if(void 0===f||""===f)c="";else{var g=JSON.parse(f);g.credentials.forEach(function(b){-1!==a.indexOf(b.server)&&(c="&token="+b.token)})}var h=null!=d.offline.proxyPath?d.offline.proxyPath+"?"+a+"?f=pjson"+c:a+"?f=pjson"+c;e.open("GET",h,!0),e.onload=function(){if(200===e.status&&""!==e.responseText){var c=this.response,f=this.response.replace(/\\'/g,"'"),g=JSON.parse(f);"error"in g?"code"in g.error&&(499==g.error.code||498==g.error.code)&&require(["esri/IdentityManager"],function(c){var e=c.findCredential(a);void 0===e?c.getCredential(a).then(function(){d._secure=!0,window.localStorage.offline_id_manager=JSON.stringify(c.toJson()),d._getTileInfoPrivate(a,b)}):d._getTileInfoPrivate(a,b)}):d._parseTileInfo(c,d,b)}else b(!1)},e.onerror=function(a){b(!1)},e.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++)8*e+6*h>32*a.length?d.push(b):d.push(c.charAt(g>>6*(3-h)&63));return d.join("")},/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ -O.esri.Tiles.saveAs=function(a){"use strict";var b=a.document,c=function(){return a.URL||a.webkitURL||a},d=a.URL||a.webkitURL||a,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),f=!a.externalHost&&"download"in e,g=a.webkitRequestFileSystem,h=a.requestFileSystem||g||a.mozRequestFileSystem,i=function(b){(a.setImmediate||a.setTimeout)(function(){throw b},0)},j="application/octet-stream",k=0,l=[],m=function(){for(var a=l.length;a--;){var b=l[a];"string"==typeof b?d.revokeObjectURL(b):b.remove()}l.length=0},n=function(a,b,c){b=[].concat(b);for(var d=b.length;d--;){var e=a["on"+b[d]];if("function"==typeof e)try{e.call(a,c||a)}catch(f){i(f)}}},o=function(d,i){var m,o,p,q=this,r=d.type,s=!1,t=function(){var a=c().createObjectURL(d);return l.push(a),a},u=function(){n(q,"writestart progress write writeend".split(" "))},v=function(){(s||!m)&&(m=t(d)),o?o.location.href=m:window.open(m,"_blank"),q.readyState=q.DONE,u()},w=function(a){return function(){return q.readyState!==q.DONE?a.apply(this,arguments):void 0}},x={create:!0,exclusive:!1};if(q.readyState=q.INIT,i||(i="download"),f){m=t(d),b=a.document,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),e.href=m,e.download=i;var y=b.createEvent("MouseEvents");return y.initMouseEvent("click",!0,!1,a,0,0,0,0,0,!1,!1,!1,!1,0,null),e.dispatchEvent(y),q.readyState=q.DONE,void u()}return a.chrome&&r&&r!==j&&(p=d.slice||d.webkitSlice,d=p.call(d,0,d.size,j),s=!0),g&&"download"!==i&&(i+=".download"),(r===j||g)&&(o=a),h?(k+=d.size,void h(a.TEMPORARY,k,w(function(a){a.root.getDirectory("saved",x,w(function(a){var b=function(){a.getFile(i,x,w(function(a){a.createWriter(w(function(b){b.onwriteend=function(b){o.location.href=a.toURL(),l.push(a),q.readyState=q.DONE,n(q,"writeend",b)},b.onerror=function(){var a=b.error;a.code!==a.ABORT_ERR&&v()},"writestart progress write abort".split(" ").forEach(function(a){b["on"+a]=q["on"+a]}),b.write(d),q.abort=function(){b.abort(),q.readyState=q.DONE},q.readyState=q.WRITING}),v)}),v)};a.getFile(i,{create:!1},w(function(a){a.remove(),b()}),w(function(a){a.code===a.NOT_FOUND_ERR?b():v()}))}),v)}),v)):void v()},p=o.prototype,q=function(a,b){return new o(a,b)};return p.abort=function(){var a=this;a.readyState=a.DONE,n(a,"abort")},p.readyState=p.INIT=0,p.WRITING=1,p.DONE=2,p.error=p.onwritestart=p.onprogress=p.onwrite=p.onabort=p.onerror=p.onwriteend=null,a.addEventListener("unload",m,!1),q}(this.self||this.window||this.content),O.esri.Tiles.TilesCore=function(){this._getTiles=function(a,b,c,d,e,f,g){e.retrieve(c,function(c,e){a=f("img[src="+d+"]")[0];var h;return c?(a.style.borderColor="blue",h="data:image/"+b+";base64,"+e.img):g?(a.style.borderColor="green",h="data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="):h="data:image/png;base64,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AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDwAwMBPAABGrpAUwAAAABJRU5ErkJggg==",a.style.visibility="visible",a.src=h,""})},this._storeTile=function(a,b,c,d){a=a.split("?")[0];var e=b?b+"?"+a:a,f=new XMLHttpRequest;f.open("GET",e,!0),f.overrideMimeType("text/plain; charset=x-user-defined"),f.onload=function(){if(200===f.status&&""!==f.responseText){var b=O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(this.responseText)),g={url:a,img:b};c.store(g,d)}else d(!1,f.status+" "+f.statusText+": "+f.response+" when downloading "+e)},f.onerror=function(a){d(!1,a)},f.send(null)},this._createCellsForOffline=function(a,b,c,d,e){for(var f=new O.esri.Tiles.TilingScheme(a),g=[],h=b;c>=h;h++){var i=f.getAllCellIdsInExtent(d,h);if(i.forEach(function(a){g.push({level:h,row:a[1],col:a[0]})}),g.length>5e3&&h!==c)break}e(g)},this._saveToFile=function(a,b,c){var d=[];d.push("url,img"),b.getAllTiles(function(b,e,f){if("end"===f){var g=new Blob([d.join("\r\n")],{type:"text/plain;charset=utf-8"}),h=O.esri.Tiles.saveAs(g,a);if(h.readyState===h.DONE)return h.error?c(!1,"Error saving file "+a):c(!0,"Saved "+(d.length-1)+" tiles ("+Math.floor(g.size/1024/1024*100)/100+" Mb) into "+a);h.onerror=function(){c(!1,"Error saving file "+a)},h.onwriteend=function(){c(!0,"Saved "+(d.length-1)+" tiles ("+Math.floor(g.size/1024/1024*100)/100+" Mb) into "+a)}}else d.push(b+","+e)})},this._estimateTileSize=function(a,b,c,d){if(b){var e,f=window.localStorage.offline_id_manager;if(void 0===f||""===f)e="";else{var g=JSON.parse(f);g.credentials.forEach(function(a){-1!==b.indexOf(a.server)&&(e="?token="+a.token)})}var h=c?c+"?"+b+e:b+e;a.get(h,{handleAs:"text/plain; charset=x-user-defined",headers:{"X-Requested-With":""},timeout:2e3}).then(function(a){var b=O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(a));d(b.length+h.length,null)},function(a){d(null,a)})}else d(NaN)},this._loadFromFile=function(a,b,c){if(window.File&&window.FileReader&&window.FileList&&window.Blob){var d=new FileReader;d.onload=function(d){var e,f,g=d.target.result,h=g.split("\r\n"),i=0;if("url,img"!==h[0])return c(!1,"File "+a.name+" doesn't contain tiles that can be loaded");for(var j=1;j=c;c++)for(d=i;j>=d;d++)k.push([c,d]);return k}}; \ No newline at end of file +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.OfflineTilesAdvanced",[j],{tileInfo:null,_imageType:"",_level:null,_minZoom:null,_maxZoom:null,_tilesCore:null,_secure:!1,constructor:function(a,b,c,d){this._isLocalStorage()===!1?(alert("OfflineTiles Library not supported on this browser."),b(!1)):window.localStorage.offline_id_manager="",void 0===d||null===d?(this.DB_NAME="offline_tile_store",this.DB_OBJECTSTORE_NAME="tilepath"):(this.DB_NAME=d.dbName,this.DB_OBJECTSTORE_NAME=d.objectStoreName),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 e=!0 +return("undefined"!=typeof c||null!=c)&&(e=c),this.showBlankTiles=!0,this.offline={online:e,store:new O.esri.Tiles.TilesStore,proxyPath:null},this.offline.store.isSupported()?(this.offline.store.dbName=this.DB_NAME,this.offline.store.objectStoreName=this.DB_OBJECTSTORE_NAME,this.offline.store.init(function(c){c&&this._getTileInfoPrivate(a,function(a){b(a)})}.bind(this._self)),void 0):b(!1,"indexedDB not supported")},getTileUrl:function(b,c,d){this._level=b +var e,f=this,g=window.localStorage.offline_id_manager +if(void 0===g||""===g)e="" +else{var h=JSON.parse(g) +h.credentials.forEach(function(a){-1!==f.url.indexOf(a.server)&&(e="?token="+a.token)})}var i=this.url+"/tile/"+b+"/"+c+"/"+d+e +if(this.offline.online)return this._lastTileUrl=i,i +i=i.split("?")[0] +var j="void:/"+b+"/"+c+"/"+d,k=null +return this._tilesCore._getTiles(k,this._imageType,i,j,this.offline.store,a,this.showBlankTiles),j},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()-Math.abs(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}},_parseTileInfo:function(a,b,c){b.offline.online===!1&&a===!1&&void 0!==localStorage.__offlineTileInfo?a=localStorage.__offlineTileInfo:b.offline.online===!1&&a===!1&&void 0===localStorage.__offlineTileInfo&&alert("There was a problem retrieving tiled map info in OfflineTilesEnablerLayer."),b._tilesCore._parseGetTileInfo(a,function(a){b.layerInfos=a.resultObj.layers,b.minScale=a.resultObj.minScale,b.maxScale=a.resultObj.maxScale,b.tileInfo=a.tileInfo,b._imageType=b.tileInfo.format.toLowerCase(),b.fullExtent=a.fullExtent,b.spatialReference=b.tileInfo.spatialReference,b.initialExtent=a.initExtent,b.loaded=!0,b.onLoad(b),c(!0)})},_getTileInfoPrivate:function(a,b){var c,d=this,e=new XMLHttpRequest,f=window.localStorage.offline_id_manager +if(void 0===f||""===f)c="" +else{var g=JSON.parse(f) +g.credentials.forEach(function(b){-1!==a.indexOf(b.server)&&(c="&token="+b.token)})}var h=null!=d.offline.proxyPath?d.offline.proxyPath+"?"+a+"?f=pjson"+c:a+"?f=pjson"+c +e.open("GET",h,!0),e.onload=function(){if(200===e.status&&""!==e.responseText){var c=this.response,f=this.response.replace(/\\'/g,"'"),g=JSON.parse(f) +"error"in g?"code"in g.error&&(499==g.error.code||498==g.error.code)&&require(["esri/IdentityManager"],function(c){var e=c.findCredential(a) +void 0===e?c.getCredential(a).then(function(){d._secure=!0,window.localStorage.offline_id_manager=JSON.stringify(c.toJson()),d._getTileInfoPrivate(a,b)}):d._getTileInfoPrivate(a,b)}):d._parseTileInfo(c,d,b)}else b(!1)},e.onerror=function(a){b(!1)},e.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++)8*e+6*h>32*a.length?d.push(b):d.push(c.charAt(g>>6*(3-h)&63)) +return d.join("")},/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ +O.esri.Tiles.saveAs=function(a){"use strict" +var b=a.document,c=function(){return a.URL||a.webkitURL||a},d=a.URL||a.webkitURL||a,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),f=!a.externalHost&&"download"in e,g=a.webkitRequestFileSystem,h=a.requestFileSystem||g||a.mozRequestFileSystem,i=function(b){(a.setImmediate||a.setTimeout)(function(){throw b},0)},j="application/octet-stream",k=0,l=[],m=function(){for(var a=l.length;a--;){var b=l[a] +"string"==typeof b?d.revokeObjectURL(b):b.remove()}l.length=0},n=function(a,b,c){b=[].concat(b) +for(var d=b.length;d--;){var e=a["on"+b[d]] +if("function"==typeof e)try{e.call(a,c||a)}catch(f){i(f)}}},o=function(d,i){var m,o,p,q=this,r=d.type,s=!1,t=function(){var a=c().createObjectURL(d) +return l.push(a),a},u=function(){n(q,"writestart progress write writeend".split(" "))},v=function(){(s||!m)&&(m=t(d)),o?o.location.href=m:window.open(m,"_blank"),q.readyState=q.DONE,u()},w=function(a){return function(){return q.readyState!==q.DONE?a.apply(this,arguments):void 0}},x={create:!0,exclusive:!1} +if(q.readyState=q.INIT,i||(i="download"),f){m=t(d),b=a.document,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),e.href=m,e.download=i +var y=b.createEvent("MouseEvents") +return y.initMouseEvent("click",!0,!1,a,0,0,0,0,0,!1,!1,!1,!1,0,null),e.dispatchEvent(y),q.readyState=q.DONE,void u()}return a.chrome&&r&&r!==j&&(p=d.slice||d.webkitSlice,d=p.call(d,0,d.size,j),s=!0),g&&"download"!==i&&(i+=".download"),(r===j||g)&&(o=a),h?(k+=d.size,void h(a.TEMPORARY,k,w(function(a){a.root.getDirectory("saved",x,w(function(a){var b=function(){a.getFile(i,x,w(function(a){a.createWriter(w(function(b){b.onwriteend=function(b){o.location.href=a.toURL(),l.push(a),q.readyState=q.DONE,n(q,"writeend",b)},b.onerror=function(){var a=b.error +a.code!==a.ABORT_ERR&&v()},"writestart progress write abort".split(" ").forEach(function(a){b["on"+a]=q["on"+a]}),b.write(d),q.abort=function(){b.abort(),q.readyState=q.DONE},q.readyState=q.WRITING}),v)}),v)} +a.getFile(i,{create:!1},w(function(a){a.remove(),b()}),w(function(a){a.code===a.NOT_FOUND_ERR?b():v()}))}),v)}),v)):void v()},p=o.prototype,q=function(a,b){return new o(a,b)} +return p.abort=function(){var a=this +a.readyState=a.DONE,n(a,"abort")},p.readyState=p.INIT=0,p.WRITING=1,p.DONE=2,p.error=p.onwritestart=p.onprogress=p.onwrite=p.onabort=p.onerror=p.onwriteend=null,a.addEventListener("unload",m,!1),q}(this.self||this.window||this.content),O.esri.Tiles.TilesCore=function(){this._getTiles=function(a,b,c,d,e,f,g){e.retrieve(c,function(c,e){a=f("img[src="+d+"]")[0] +var h +return c?(a.style.borderColor="blue",h="data:image/"+b+";base64,"+e.img):g?(a.style.borderColor="green",h="data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="):h="data:image/png;base64,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AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDwAwMBPAABGrpAUwAAAABJRU5ErkJggg==",a.style.visibility="visible",a.src=h,""})},this._storeTile=function(a,b,c,d){a=a.split("?")[0] +var e=b?b+"?"+a:a,f=new XMLHttpRequest +f.open("GET",e,!0),f.overrideMimeType("text/plain; charset=x-user-defined"),f.onload=function(){if(200===f.status&&""!==f.responseText){var b=O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(this.responseText)),g={url:a,img:b} +c.store(g,d)}else d(!1,f.status+" "+f.statusText+": "+f.response+" when downloading "+e)},f.onerror=function(a){d(!1,a)},f.send(null)},this._createCellsForOffline=function(a,b,c,d,e){for(var f=new O.esri.Tiles.TilingScheme(a),g=[],h=b;c>=h;h++){var i=f.getAllCellIdsInExtent(d,h) +if(i.forEach(function(a){g.push({level:h,row:a[1],col:a[0]})}),g.length>5e3&&h!==c)break}e(g)},this._saveToFile=function(a,b,c){var d=[] +d.push("url,img"),b.getAllTiles(function(b,e,f){if("end"===f){var g=new Blob([d.join("\r\n")],{type:"text/plain;charset=utf-8"}),h=O.esri.Tiles.saveAs(g,a) +if(h.readyState===h.DONE)return h.error?c(!1,"Error saving file "+a):c(!0,"Saved "+(d.length-1)+" tiles ("+Math.floor(g.size/1024/1024*100)/100+" Mb) into "+a) +h.onerror=function(){c(!1,"Error saving file "+a)},h.onwriteend=function(){c(!0,"Saved "+(d.length-1)+" tiles ("+Math.floor(g.size/1024/1024*100)/100+" Mb) into "+a)}}else d.push(b+","+e)})},this._estimateTileSize=function(a,b,c,d){if(b){var e,f=window.localStorage.offline_id_manager +if(void 0===f||""===f)e="" +else{var g=JSON.parse(f) +g.credentials.forEach(function(a){-1!==b.indexOf(a.server)&&(e="?token="+a.token)})}var h=c?c+"?"+b+e:b+e +a.get(h,{handleAs:"text/plain; charset=x-user-defined",headers:{"X-Requested-With":""},timeout:2e3}).then(function(a){var b=O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(a)) +d(b.length+h.length,null)},function(a){d(null,a)})}else d(NaN)},this._loadFromFile=function(a,b,c){if(window.File&&window.FileReader&&window.FileList&&window.Blob){var d=new FileReader +d.onload=function(d){var e,f,g=d.target.result,h=g.split("\r\n"),i=0 +if("url,img"!==h[0])return c(!1,"File "+a.name+" doesn't contain tiles that can be loaded") +for(var j=1;j=c;c++)for(d=i;j>=d;d++)k.push([c,d]) +return k}} diff --git a/dist/offline-tiles-advanced-src.js b/dist/offline-tiles-advanced-src.js index 1d7e17ee..b4b96bcb 100644 --- a/dist/offline-tiles-advanced-src.js +++ b/dist/offline-tiles-advanced-src.js @@ -1,4 +1,4 @@ -/*! esri-offline-maps - v2.16.0 - 2015-10-29 +/*! esri-offline-maps - v3.0.0 - 2015-11-23 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ define([ @@ -15,7 +15,7 @@ define([ ], function(query, request, declare,LOD,Point,Extent,TileInfo,SpatialReference,Polygon,TiledMapServerLayer) { "use strict"; - return declare("O.esri.Tiles.OfflineTileEnablerLayer",[TiledMapServerLayer],{ + return declare("O.esri.Tiles.OfflineTilesAdvanced",[TiledMapServerLayer],{ tileInfo: null, _imageType: "", @@ -898,8 +898,8 @@ O.esri.Tiles.saveAs = /** - * This library contains common core code between offlineTilesEnabler.js - * and OfflineTilesEnablerLayer.js + * This library contains common core code between OfflineTilesBasic.js + * and OfflineTilesAdvanced.js */ O.esri.Tiles.TilesCore = function(){ diff --git a/dist/offline-tiles-basic-min.js b/dist/offline-tiles-basic-min.js index 3d28da50..97334721 100644 --- a/dist/offline-tiles-basic-min.js +++ b/dist/offline-tiles-basic-min.js @@ -1,5 +1,81 @@ -/*! esri-offline-maps - v2.16.0 - 2015-10-29 +/*! esri-offline-maps - v3.0.0 - 2015-11-23 * 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,f){c._tilesCore=new O.esri.Tiles.TilesCore,c._lastTileUrl="",c._imageType="",c._minZoom=null,c._maxZoom=null,void 0===f||null===f?(c.DB_NAME="offline_tile_store",c.DB_OBJECTSTORE_NAME="tilepath"):(c.DB_NAME=f.dbName,c.DB_OBJECTSTORE_NAME=f.objectStoreName),c._getTileUrl=c.getTileUrl;var g=!0;return"undefined"!=typeof e&&(g=e),c.showBlankTiles=!0,c.offline={online:g,store:new O.esri.Tiles.TilesStore,proxyPath:null},c.offline.store.isSupported()?(c.offline.store.dbName=c.DB_NAME,c.offline.store.objectStoreName=c.DB_OBJECTSTORE_NAME,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,c.showBlankTiles),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()-Math.abs(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++)8*e+6*h>32*a.length?d.push(b):d.push(c.charAt(g>>6*(3-h)&63));return d.join("")},/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ -O.esri.Tiles.saveAs=function(a){"use strict";var b=a.document,c=function(){return a.URL||a.webkitURL||a},d=a.URL||a.webkitURL||a,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),f=!a.externalHost&&"download"in e,g=a.webkitRequestFileSystem,h=a.requestFileSystem||g||a.mozRequestFileSystem,i=function(b){(a.setImmediate||a.setTimeout)(function(){throw b},0)},j="application/octet-stream",k=0,l=[],m=function(){for(var a=l.length;a--;){var b=l[a];"string"==typeof b?d.revokeObjectURL(b):b.remove()}l.length=0},n=function(a,b,c){b=[].concat(b);for(var d=b.length;d--;){var e=a["on"+b[d]];if("function"==typeof e)try{e.call(a,c||a)}catch(f){i(f)}}},o=function(d,i){var m,o,p,q=this,r=d.type,s=!1,t=function(){var a=c().createObjectURL(d);return l.push(a),a},u=function(){n(q,"writestart progress write writeend".split(" "))},v=function(){(s||!m)&&(m=t(d)),o?o.location.href=m:window.open(m,"_blank"),q.readyState=q.DONE,u()},w=function(a){return function(){return q.readyState!==q.DONE?a.apply(this,arguments):void 0}},x={create:!0,exclusive:!1};if(q.readyState=q.INIT,i||(i="download"),f){m=t(d),b=a.document,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),e.href=m,e.download=i;var y=b.createEvent("MouseEvents");return y.initMouseEvent("click",!0,!1,a,0,0,0,0,0,!1,!1,!1,!1,0,null),e.dispatchEvent(y),q.readyState=q.DONE,void u()}return a.chrome&&r&&r!==j&&(p=d.slice||d.webkitSlice,d=p.call(d,0,d.size,j),s=!0),g&&"download"!==i&&(i+=".download"),(r===j||g)&&(o=a),h?(k+=d.size,void h(a.TEMPORARY,k,w(function(a){a.root.getDirectory("saved",x,w(function(a){var b=function(){a.getFile(i,x,w(function(a){a.createWriter(w(function(b){b.onwriteend=function(b){o.location.href=a.toURL(),l.push(a),q.readyState=q.DONE,n(q,"writeend",b)},b.onerror=function(){var a=b.error;a.code!==a.ABORT_ERR&&v()},"writestart progress write abort".split(" ").forEach(function(a){b["on"+a]=q["on"+a]}),b.write(d),q.abort=function(){b.abort(),q.readyState=q.DONE},q.readyState=q.WRITING}),v)}),v)};a.getFile(i,{create:!1},w(function(a){a.remove(),b()}),w(function(a){a.code===a.NOT_FOUND_ERR?b():v()}))}),v)}),v)):void v()},p=o.prototype,q=function(a,b){return new o(a,b)};return p.abort=function(){var a=this;a.readyState=a.DONE,n(a,"abort")},p.readyState=p.INIT=0,p.WRITING=1,p.DONE=2,p.error=p.onwritestart=p.onprogress=p.onwrite=p.onabort=p.onerror=p.onwriteend=null,a.addEventListener("unload",m,!1),q}(this.self||this.window||this.content),O.esri.Tiles.TilesCore=function(){this._getTiles=function(a,b,c,d,e,f,g){e.retrieve(c,function(c,e){a=f("img[src="+d+"]")[0];var h;return c?(a.style.borderColor="blue",h="data:image/"+b+";base64,"+e.img):g?(a.style.borderColor="green",h="data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="):h="data:image/png;base64,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AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDwAwMBPAABGrpAUwAAAABJRU5ErkJggg==",a.style.visibility="visible",a.src=h,""})},this._storeTile=function(a,b,c,d){a=a.split("?")[0];var e=b?b+"?"+a:a,f=new XMLHttpRequest;f.open("GET",e,!0),f.overrideMimeType("text/plain; charset=x-user-defined"),f.onload=function(){if(200===f.status&&""!==f.responseText){var b=O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(this.responseText)),g={url:a,img:b};c.store(g,d)}else d(!1,f.status+" "+f.statusText+": "+f.response+" when downloading "+e)},f.onerror=function(a){d(!1,a)},f.send(null)},this._createCellsForOffline=function(a,b,c,d,e){for(var f=new O.esri.Tiles.TilingScheme(a),g=[],h=b;c>=h;h++){var i=f.getAllCellIdsInExtent(d,h);if(i.forEach(function(a){g.push({level:h,row:a[1],col:a[0]})}),g.length>5e3&&h!==c)break}e(g)},this._saveToFile=function(a,b,c){var d=[];d.push("url,img"),b.getAllTiles(function(b,e,f){if("end"===f){var g=new Blob([d.join("\r\n")],{type:"text/plain;charset=utf-8"}),h=O.esri.Tiles.saveAs(g,a);if(h.readyState===h.DONE)return h.error?c(!1,"Error saving file "+a):c(!0,"Saved "+(d.length-1)+" tiles ("+Math.floor(g.size/1024/1024*100)/100+" Mb) into "+a);h.onerror=function(){c(!1,"Error saving file "+a)},h.onwriteend=function(){c(!0,"Saved "+(d.length-1)+" tiles ("+Math.floor(g.size/1024/1024*100)/100+" Mb) into "+a)}}else d.push(b+","+e)})},this._estimateTileSize=function(a,b,c,d){if(b){var e,f=window.localStorage.offline_id_manager;if(void 0===f||""===f)e="";else{var g=JSON.parse(f);g.credentials.forEach(function(a){-1!==b.indexOf(a.server)&&(e="?token="+a.token)})}var h=c?c+"?"+b+e:b+e;a.get(h,{handleAs:"text/plain; charset=x-user-defined",headers:{"X-Requested-With":""},timeout:2e3}).then(function(a){var b=O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(a));d(b.length+h.length,null)},function(a){d(null,a)})}else d(NaN)},this._loadFromFile=function(a,b,c){if(window.File&&window.FileReader&&window.FileList&&window.Blob){var d=new FileReader;d.onload=function(d){var e,f,g=d.target.result,h=g.split("\r\n"),i=0;if("url,img"!==h[0])return c(!1,"File "+a.name+" doesn't contain tiles that can be loaded");for(var j=1;j=c;c++)for(d=i;j>=d;d++)k.push([c,d]);return k}}; \ No newline at end of file +define(["dojo/query","dojo/request","esri/geometry/Polygon","dojo/_base/declare"],function(a,b,c,d){"use strict" +return d("O.esri.Tiles.OfflineTilesBasic",[],{getBasemapLayer:function(a){var b=a.layerIds[0] +return a.getLayer(b)},extend:function(c,d,e,f){c._tilesCore=new O.esri.Tiles.TilesCore,c._lastTileUrl="",c._imageType="",c._minZoom=null,c._maxZoom=null,void 0===f||null===f?(c.DB_NAME="offline_tile_store",c.DB_OBJECTSTORE_NAME="tilepath"):(c.DB_NAME=f.dbName,c.DB_OBJECTSTORE_NAME=f.objectStoreName),c._getTileUrl=c.getTileUrl +var g=!0 +return"undefined"!=typeof e&&(g=e),c.showBlankTiles=!0,c.offline={online:g,store:new O.esri.Tiles.TilesStore,proxyPath:null},c.offline.store.isSupported()?(c.offline.store.dbName=c.DB_NAME,c.offline.store.objectStoreName=c.DB_OBJECTSTORE_NAME,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,c.showBlankTiles),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()-Math.abs(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++)8*e+6*h>32*a.length?d.push(b):d.push(c.charAt(g>>6*(3-h)&63)) +return d.join("")},/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ +O.esri.Tiles.saveAs=function(a){"use strict" +var b=a.document,c=function(){return a.URL||a.webkitURL||a},d=a.URL||a.webkitURL||a,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),f=!a.externalHost&&"download"in e,g=a.webkitRequestFileSystem,h=a.requestFileSystem||g||a.mozRequestFileSystem,i=function(b){(a.setImmediate||a.setTimeout)(function(){throw b},0)},j="application/octet-stream",k=0,l=[],m=function(){for(var a=l.length;a--;){var b=l[a] +"string"==typeof b?d.revokeObjectURL(b):b.remove()}l.length=0},n=function(a,b,c){b=[].concat(b) +for(var d=b.length;d--;){var e=a["on"+b[d]] +if("function"==typeof e)try{e.call(a,c||a)}catch(f){i(f)}}},o=function(d,i){var m,o,p,q=this,r=d.type,s=!1,t=function(){var a=c().createObjectURL(d) +return l.push(a),a},u=function(){n(q,"writestart progress write writeend".split(" "))},v=function(){(s||!m)&&(m=t(d)),o?o.location.href=m:window.open(m,"_blank"),q.readyState=q.DONE,u()},w=function(a){return function(){return q.readyState!==q.DONE?a.apply(this,arguments):void 0}},x={create:!0,exclusive:!1} +if(q.readyState=q.INIT,i||(i="download"),f){m=t(d),b=a.document,e=b.createElementNS("http://www.w3.org/1999/xhtml","a"),e.href=m,e.download=i +var y=b.createEvent("MouseEvents") +return y.initMouseEvent("click",!0,!1,a,0,0,0,0,0,!1,!1,!1,!1,0,null),e.dispatchEvent(y),q.readyState=q.DONE,void u()}return a.chrome&&r&&r!==j&&(p=d.slice||d.webkitSlice,d=p.call(d,0,d.size,j),s=!0),g&&"download"!==i&&(i+=".download"),(r===j||g)&&(o=a),h?(k+=d.size,void h(a.TEMPORARY,k,w(function(a){a.root.getDirectory("saved",x,w(function(a){var b=function(){a.getFile(i,x,w(function(a){a.createWriter(w(function(b){b.onwriteend=function(b){o.location.href=a.toURL(),l.push(a),q.readyState=q.DONE,n(q,"writeend",b)},b.onerror=function(){var a=b.error +a.code!==a.ABORT_ERR&&v()},"writestart progress write abort".split(" ").forEach(function(a){b["on"+a]=q["on"+a]}),b.write(d),q.abort=function(){b.abort(),q.readyState=q.DONE},q.readyState=q.WRITING}),v)}),v)} +a.getFile(i,{create:!1},w(function(a){a.remove(),b()}),w(function(a){a.code===a.NOT_FOUND_ERR?b():v()}))}),v)}),v)):void v()},p=o.prototype,q=function(a,b){return new o(a,b)} +return p.abort=function(){var a=this +a.readyState=a.DONE,n(a,"abort")},p.readyState=p.INIT=0,p.WRITING=1,p.DONE=2,p.error=p.onwritestart=p.onprogress=p.onwrite=p.onabort=p.onerror=p.onwriteend=null,a.addEventListener("unload",m,!1),q}(this.self||this.window||this.content),O.esri.Tiles.TilesCore=function(){this._getTiles=function(a,b,c,d,e,f,g){e.retrieve(c,function(c,e){a=f("img[src="+d+"]")[0] +var h +return c?(a.style.borderColor="blue",h="data:image/"+b+";base64,"+e.img):g?(a.style.borderColor="green",h="data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="):h="data:image/png;base64,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AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDwAwMBPAABGrpAUwAAAABJRU5ErkJggg==",a.style.visibility="visible",a.src=h,""})},this._storeTile=function(a,b,c,d){a=a.split("?")[0] +var e=b?b+"?"+a:a,f=new XMLHttpRequest +f.open("GET",e,!0),f.overrideMimeType("text/plain; charset=x-user-defined"),f.onload=function(){if(200===f.status&&""!==f.responseText){var b=O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(this.responseText)),g={url:a,img:b} +c.store(g,d)}else d(!1,f.status+" "+f.statusText+": "+f.response+" when downloading "+e)},f.onerror=function(a){d(!1,a)},f.send(null)},this._createCellsForOffline=function(a,b,c,d,e){for(var f=new O.esri.Tiles.TilingScheme(a),g=[],h=b;c>=h;h++){var i=f.getAllCellIdsInExtent(d,h) +if(i.forEach(function(a){g.push({level:h,row:a[1],col:a[0]})}),g.length>5e3&&h!==c)break}e(g)},this._saveToFile=function(a,b,c){var d=[] +d.push("url,img"),b.getAllTiles(function(b,e,f){if("end"===f){var g=new Blob([d.join("\r\n")],{type:"text/plain;charset=utf-8"}),h=O.esri.Tiles.saveAs(g,a) +if(h.readyState===h.DONE)return h.error?c(!1,"Error saving file "+a):c(!0,"Saved "+(d.length-1)+" tiles ("+Math.floor(g.size/1024/1024*100)/100+" Mb) into "+a) +h.onerror=function(){c(!1,"Error saving file "+a)},h.onwriteend=function(){c(!0,"Saved "+(d.length-1)+" tiles ("+Math.floor(g.size/1024/1024*100)/100+" Mb) into "+a)}}else d.push(b+","+e)})},this._estimateTileSize=function(a,b,c,d){if(b){var e,f=window.localStorage.offline_id_manager +if(void 0===f||""===f)e="" +else{var g=JSON.parse(f) +g.credentials.forEach(function(a){-1!==b.indexOf(a.server)&&(e="?token="+a.token)})}var h=c?c+"?"+b+e:b+e +a.get(h,{handleAs:"text/plain; charset=x-user-defined",headers:{"X-Requested-With":""},timeout:2e3}).then(function(a){var b=O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(a)) +d(b.length+h.length,null)},function(a){d(null,a)})}else d(NaN)},this._loadFromFile=function(a,b,c){if(window.File&&window.FileReader&&window.FileList&&window.Blob){var d=new FileReader +d.onload=function(d){var e,f,g=d.target.result,h=g.split("\r\n"),i=0 +if("url,img"!==h[0])return c(!1,"File "+a.name+" doesn't contain tiles that can be loaded") +for(var j=1;j=c;c++)for(d=i;j>=d;d++)k.push([c,d]) +return k}} diff --git a/dist/offline-tiles-basic-src.js b/dist/offline-tiles-basic-src.js index 43064592..23423f46 100644 --- a/dist/offline-tiles-basic-src.js +++ b/dist/offline-tiles-basic-src.js @@ -1,4 +1,4 @@ -/*! esri-offline-maps - v2.16.0 - 2015-10-29 +/*! esri-offline-maps - v3.0.0 - 2015-11-23 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ define([ @@ -9,7 +9,7 @@ define([ ], function(query, request, Polygon,declare) { "use strict"; - return declare("O.esri.Tiles.OfflineTilesEnabler",[],{ + return declare("O.esri.Tiles.OfflineTilesBasic",[],{ /** * Utility method to get the basemap layer reference * @param map @@ -730,8 +730,8 @@ O.esri.Tiles.saveAs = /** - * This library contains common core code between offlineTilesEnabler.js - * and OfflineTilesEnablerLayer.js + * This library contains common core code between OfflineTilesBasic.js + * and OfflineTilesAdvanced.js */ O.esri.Tiles.TilesCore = function(){ diff --git a/dist/offline-tpk-min.js b/dist/offline-tpk-min.js index 8fa2951c..2f0159da 100644 --- a/dist/offline-tpk-min.js +++ b/dist/offline-tpk-min.js @@ -1,5 +1,342 @@ -/*! esri-offline-maps - v2.16.0 - 2015-10-29 +/*! esri-offline-maps - v3.0.0 - 2015-11-23 * 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,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//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){a===!1?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.onloadend=function(g){if(void 0!==f.token){e._inMemTilesIndex.push("blank");var h=a[f.token].filename.toLocaleUpperCase();e._inMemTilesObject[h]=f.result;var i=e.ObjectSize(e._inMemTilesObject);i>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,this.dbName="offline_tile_store",this.objectStoreName="tilepath",this.isSupported=function(){return window.indexedDB||window.openDatabase?!0:!1},this.store=function(a,b){try{var c=this._db.transaction([this.objectStoreName],"readwrite");c.oncomplete=function(){b(!0)},c.onerror=function(a){b(!1,a.target.error.message)};var d=c.objectStore(this.objectStoreName),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([this.objectStoreName]).objectStore(this.objectStoreName),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([this.objectStoreName],"readwrite").objectStore(this.objectStoreName).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([this.objectStoreName],"readwrite").objectStore(this.objectStoreName)["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([this.objectStoreName]).objectStore(this.objectStoreName).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([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+=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(a){var b=indexedDB.open(this.dbName,4);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),b.createObjectStore(this.objectStoreName,{keyPath:"url"})}.bind(this),b.onsuccess=function(b){this._db=b.target.result,a(!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(){require(["esri/geometry/Point","esri/SpatialReference"],function(b,c){var d=i().split(","),e=a.spatialReference.wkid,f=null;4326==e?f=new b(d[1],d[0]):102100==e&&(f=new b(d[0],d[1],new c({wkid:e}))),a.centerAt(f)})}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())})}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,v.next_in_index=Q,f.write=B,f.inflate_flush(v,w);case E:for(x=o;x>P;){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++))<>=x,P-=x,k=s,c=e,h=u,b=F;case F:for(x=k;x>P;){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!==(16&z)){o=15&z,q=c[y+2],b=G;break}if(0===(64&z)){k=z,h=y/3+c[y+2];break}return b=L,v.msg="invalid distance code",w=m,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);case G:for(x=o;x>P;){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++))<>=x,P-=x,b=H;case H:for(N=B-q;0>N;)N+=f.end;for(;0!==g;){if(0===M&&(B==f.end&&0!==f.read&&(B=0,M=B7&&(P-=8,A++,Q--),f.write=B,w=f.inflate_flush(v,w),B=f.write,M=Ba.avail_out&&(c=a.avail_out),0!==c&&b==o&&(b=i),a.avail_out-=c,a.total_out+=c,a.next_out.set(f.window.subarray(e,e+c),d),d+=c,e+=c,e==f.end&&(e=0,f.write==f.end&&(f.write=0),c=f.write-e,c>a.avail_out&&(c=a.avail_out),0!==c&&b==o&&(b=i),a.avail_out-=c,a.total_out+=c,a.next_out.set(f.window.subarray(e,e+c),d),d+=c,e+=c),a.next_out_index=d,f.read=e,b},f.proc=function(a,c){var d,o,q,w,y,z,A,B;for(w=a.next_in_index,y=a.avail_in,o=f.bitb,q=f.bitk,z=f.write,A=zq;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);c=i,y--,o|=(255&a.read_byte(w++))<>>1){case 0:o>>>=3,q-=3,d=7&q,o>>>=d,q-=d,g=O;break;case 1:var C=[],D=[],E=[[]],F=[[]];b.inflate_trees_fixed(C,D,E,F),t.init(C[0],D[0],E[0],0,F[0],0),o>>>=3,q-=3,g=T;break;case 2:o>>>=3,q-=3,g=Q;break;case 3:return o>>>=3,q-=3,g=W,a.msg="invalid block type",c=m,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c)}break;case O:for(;32>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);c=i,y--,o|=(255&a.read_byte(w++))<>>16&65535)!=(65535&o))return g=W,a.msg="invalid stored block lengths",c=m,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);h=65535&o,o=q=0,g=0!==h?P:0!==u?U:N;break;case P:if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);if(0===A&&(z==f.end&&0!==f.read&&(z=0,A=zy&&(d=y),d>A&&(d=A),f.window.set(a.read_buf(w,d),z),w+=d,y-=d,z+=d,A-=d,0!==(h-=d))break;g=0!==u?U:N;break;case Q:for(;14>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);c=i,y--,o|=(255&a.read_byte(w++))<29||(d>>5&31)>29)return g=W,a.msg="too many length or distance symbols",c=m,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);if(d=258+(31&d)+(d>>5&31),!e||e.lengthB;B++)e[B]=0;o>>>=14,q-=14,n=0,g=R;case R:for(;4+(k>>>10)>n;){for(;3>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);c=i,y--,o|=(255&a.read_byte(w++))<>>=3,q-=3}for(;19>n;)e[M[n++]]=0;if(r[0]=7,d=x.inflate_trees_bits(e,r,s,v,a),d!=i)return c=d,c==m&&(e=null,g=W),f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);n=0,g=S;case S:for(;;){if(d=k,!(258+(31&d)+(d>>5&31)>n))break;var G,H;for(d=r[0];d>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);c=i,y--,o|=(255&a.read_byte(w++))<H)o>>>=d,q-=d,e[n++]=H;else{for(B=18==H?7:H-14,G=18==H?11:3;d+B>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);c=i,y--,o|=(255&a.read_byte(w++))<>>=d,q-=d,G+=o&p[B],o>>>=B,q-=B,B=n,d=k,B+G>258+(31&d)+(d>>5&31)||16==H&&1>B)return e=null,g=W,a.msg="invalid bit length repeat",c=m,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);H=16==H?e[B-1]:0;do e[B++]=H;while(0!==--G);n=B}}s[0]=-1;var I=[],J=[],K=[],L=[];if(I[0]=9,J[0]=6,d=k,d=x.inflate_trees_dynamic(257+(31&d),1+(d>>5&31),e,I,J,K,L,v,a),d!=i)return d==m&&(e=null,g=W),c=d,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c);t.init(I[0],J[0],v,K[0],v,L[0]),g=T;case T:if(f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,(c=t.proc(f,a,c))!=j)return f.inflate_flush(a,c);if(c=i,t.free(a),w=a.next_in_index,y=a.avail_in,o=f.bitb,q=f.bitk,z=f.write,A=ze||e>15?(b.inflateEnd(c),l):(b.wbits=e,c.istate.blocks=new d(c,1<>4)+8>a.istate.wbits){a.istate.mode=ga,a.msg="invalid window size",a.istate.marker=5;break}a.istate.mode=$;case $:if(0===a.avail_in)return c;if(c=b,a.avail_in--,a.total_in++,d=255&a.read_byte(a.next_in_index++),((a.istate.method<<8)+d)%31!==0){a.istate.mode=ga,a.msg="incorrect header check",a.istate.marker=5;break}if(0===(d&X)){a.istate.mode=ea;break}a.istate.mode=_;case _:if(0===a.avail_in)return c;c=b,a.avail_in--,a.total_in++,a.istate.need=(255&a.read_byte(a.next_in_index++))<<24&4278190080,a.istate.mode=aa;case aa:if(0===a.avail_in)return c;c=b,a.avail_in--,a.total_in++,a.istate.need+=(255&a.read_byte(a.next_in_index++))<<16&16711680,a.istate.mode=ba;case ba:if(0===a.avail_in)return c;c=b,a.avail_in--,a.total_in++,a.istate.need+=(255&a.read_byte(a.next_in_index++))<<8&65280,a.istate.mode=ca;case ca:return 0===a.avail_in?c:(c=b,a.avail_in--,a.total_in++,a.istate.need+=255&a.read_byte(a.next_in_index++),a.istate.mode=da,k);case da:return a.istate.mode=ga,a.msg="need dictionary",a.istate.marker=0,l;case ea:if(c=a.istate.blocks.proc(a,c),c==m){a.istate.mode=ga,a.istate.marker=0;break}if(c==i&&(c=b),c!=j)return c;c=b,a.istate.blocks.reset(a,a.istate.was),a.istate.mode=fa;case fa:return j;case ga:return m;default:return l}},b.inflateSetDictionary=function(a,b,c){var d=0,e=c;return a&&a.istate&&a.istate.mode==da?(e>=1<e;)b.read_byte(d)==ha[e]?e++:e=0!==b.read_byte(d)?0:4-e,d++,c--;return b.total_in+=d-b.next_in_index,b.next_in_index=d,b.avail_in=c,b.istate.marker=e,4!=e?m:(f=b.total_in,g=b.total_out,a(b),b.total_in=f,b.total_out=g,b.istate.mode=ea,i)},b.inflateSyncPoint=function(a){return a&&a.istate&&a.istate.blocks?a.istate.blocks.sync_point():l}}function f(){}function g(){var a=this,b=new f,c=512,d=r,e=new Uint8Array(c),g=!1;b.inflateInit(),b.next_out=e,a.append=function(a,f){var h,k,l=[],m=0,n=0,p=0;if(0!==a.length){b.next_in_index=0,b.next_in=a,b.avail_in=a.length;do{if(b.next_out_index=0,b.avail_out=c,0!==b.avail_in||g||(b.next_in_index=0,g=!0),h=b.inflate(d),g&&h==o)return-1;if(h!=i&&h!=j)throw"inflating: "+b.msg;if((g||h==j)&&b.avail_in==a.length)return-1;b.next_out_index&&(b.next_out_index==c?l.push(new Uint8Array(e)):l.push(new Uint8Array(e.subarray(0,b.next_out_index)))),p+=b.next_out_index,f&&b.next_in_index>0&&b.next_in_index!=m&&(f(b.next_in_index),m=b.next_in_index)}while(b.avail_in>0||0===b.avail_out);return k=new Uint8Array(p),l.forEach(function(a){k.set(a,n),n+=a.length}),k}},a.flush=function(){b.inflateEnd()}}var h=15,i=0,j=1,k=2,l=-2,m=-3,n=-4,o=-5,p=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],q=1440,r=0,s=4,t=9,u=5,v=[96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,192,80,7,10,0,8,96,0,8,32,0,9,160,0,8,0,0,8,128,0,8,64,0,9,224,80,7,6,0,8,88,0,8,24,0,9,144,83,7,59,0,8,120,0,8,56,0,9,208,81,7,17,0,8,104,0,8,40,0,9,176,0,8,8,0,8,136,0,8,72,0,9,240,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,200,81,7,13,0,8,100,0,8,36,0,9,168,0,8,4,0,8,132,0,8,68,0,9,232,80,7,8,0,8,92,0,8,28,0,9,152,84,7,83,0,8,124,0,8,60,0,9,216,82,7,23,0,8,108,0,8,44,0,9,184,0,8,12,0,8,140,0,8,76,0,9,248,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,196,81,7,11,0,8,98,0,8,34,0,9,164,0,8,2,0,8,130,0,8,66,0,9,228,80,7,7,0,8,90,0,8,26,0,9,148,84,7,67,0,8,122,0,8,58,0,9,212,82,7,19,0,8,106,0,8,42,0,9,180,0,8,10,0,8,138,0,8,74,0,9,244,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,204,81,7,15,0,8,102,0,8,38,0,9,172,0,8,6,0,8,134,0,8,70,0,9,236,80,7,9,0,8,94,0,8,30,0,9,156,84,7,99,0,8,126,0,8,62,0,9,220,82,7,27,0,8,110,0,8,46,0,9,188,0,8,14,0,8,142,0,8,78,0,9,252,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,194,80,7,10,0,8,97,0,8,33,0,9,162,0,8,1,0,8,129,0,8,65,0,9,226,80,7,6,0,8,89,0,8,25,0,9,146,83,7,59,0,8,121,0,8,57,0,9,210,81,7,17,0,8,105,0,8,41,0,9,178,0,8,9,0,8,137,0,8,73,0,9,242,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,202,81,7,13,0,8,101,0,8,37,0,9,170,0,8,5,0,8,133,0,8,69,0,9,234,80,7,8,0,8,93,0,8,29,0,9,154,84,7,83,0,8,125,0,8,61,0,9,218,82,7,23,0,8,109,0,8,45,0,9,186,0,8,13,0,8,141,0,8,77,0,9,250,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,198,81,7,11,0,8,99,0,8,35,0,9,166,0,8,3,0,8,131,0,8,67,0,9,230,80,7,7,0,8,91,0,8,27,0,9,150,84,7,67,0,8,123,0,8,59,0,9,214,82,7,19,0,8,107,0,8,43,0,9,182,0,8,11,0,8,139,0,8,75,0,9,246,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,206,81,7,15,0,8,103,0,8,39,0,9,174,0,8,7,0,8,135,0,8,71,0,9,238,80,7,9,0,8,95,0,8,31,0,9,158,84,7,99,0,8,127,0,8,63,0,9,222,82,7,27,0,8,111,0,8,47,0,9,190,0,8,15,0,8,143,0,8,79,0,9,254,96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,193,80,7,10,0,8,96,0,8,32,0,9,161,0,8,0,0,8,128,0,8,64,0,9,225,80,7,6,0,8,88,0,8,24,0,9,145,83,7,59,0,8,120,0,8,56,0,9,209,81,7,17,0,8,104,0,8,40,0,9,177,0,8,8,0,8,136,0,8,72,0,9,241,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,201,81,7,13,0,8,100,0,8,36,0,9,169,0,8,4,0,8,132,0,8,68,0,9,233,80,7,8,0,8,92,0,8,28,0,9,153,84,7,83,0,8,124,0,8,60,0,9,217,82,7,23,0,8,108,0,8,44,0,9,185,0,8,12,0,8,140,0,8,76,0,9,249,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,197,81,7,11,0,8,98,0,8,34,0,9,165,0,8,2,0,8,130,0,8,66,0,9,229,80,7,7,0,8,90,0,8,26,0,9,149,84,7,67,0,8,122,0,8,58,0,9,213,82,7,19,0,8,106,0,8,42,0,9,181,0,8,10,0,8,138,0,8,74,0,9,245,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,205,81,7,15,0,8,102,0,8,38,0,9,173,0,8,6,0,8,134,0,8,70,0,9,237,80,7,9,0,8,94,0,8,30,0,9,157,84,7,99,0,8,126,0,8,62,0,9,221,82,7,27,0,8,110,0,8,46,0,9,189,0,8,14,0,8,142,0,8,78,0,9,253,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,195,80,7,10,0,8,97,0,8,33,0,9,163,0,8,1,0,8,129,0,8,65,0,9,227,80,7,6,0,8,89,0,8,25,0,9,147,83,7,59,0,8,121,0,8,57,0,9,211,81,7,17,0,8,105,0,8,41,0,9,179,0,8,9,0,8,137,0,8,73,0,9,243,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,203,81,7,13,0,8,101,0,8,37,0,9,171,0,8,5,0,8,133,0,8,69,0,9,235,80,7,8,0,8,93,0,8,29,0,9,155,84,7,83,0,8,125,0,8,61,0,9,219,82,7,23,0,8,109,0,8,45,0,9,187,0,8,13,0,8,141,0,8,77,0,9,251,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,199,81,7,11,0,8,99,0,8,35,0,9,167,0,8,3,0,8,131,0,8,67,0,9,231,80,7,7,0,8,91,0,8,27,0,9,151,84,7,67,0,8,123,0,8,59,0,9,215,82,7,19,0,8,107,0,8,43,0,9,183,0,8,11,0,8,139,0,8,75,0,9,247,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,207,81,7,15,0,8,103,0,8,39,0,9,175,0,8,7,0,8,135,0,8,71,0,9,239,80,7,9,0,8,95,0,8,31,0,9,159,84,7,99,0,8,127,0,8,63,0,9,223,82,7,27,0,8,111,0,8,47,0,9,191,0,8,15,0,8,143,0,8,79,0,9,255],w=[80,5,1,87,5,257,83,5,17,91,5,4097,81,5,5,89,5,1025,85,5,65,93,5,16385,80,5,3,88,5,513,84,5,33,92,5,8193,82,5,9,90,5,2049,86,5,129,192,5,24577,80,5,2,87,5,385,83,5,25,91,5,6145,81,5,7,89,5,1537,85,5,97,93,5,24577,80,5,4,88,5,769,84,5,49,92,5,12289,82,5,13,90,5,3073,86,5,193,192,5,24577],x=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],y=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,112,112],z=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],A=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],B=15;b.inflate_trees_fixed=function(a,b,c,d){return a[0]=t,b[0]=u,c[0]=v,d[0]=w,i};var C=0,D=1,E=2,F=3,G=4,H=5,I=6,J=7,K=8,L=9,M=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],N=0,O=1,P=2,Q=3,R=4,S=5,T=6,U=7,V=8,W=9,X=32,Y=8,Z=0,$=1,_=2,aa=3,ba=4,ca=5,da=6,ea=7,fa=12,ga=13,ha=[0,0,255,255];f.prototype={inflateInit:function(a){var b=this;return b.istate=new e,a||(a=h),b.istate.inflateInit(b,a)},inflate:function(a){var b=this;return b.istate?b.istate.inflate(b,a):l},inflateEnd:function(){var a=this;if(!a.istate)return l;var b=a.istate.inflateEnd(a);return a.istate=null,b},inflateSync:function(){var a=this;return a.istate?a.istate.inflateSync(a):l},inflateSetDictionary:function(a,b){var c=this;return c.istate?c.istate.inflateSetDictionary(c,a,b):l},read_byte:function(a){var b=this;return b.next_in.subarray(a,a+1)[0]},read_buf:function(a,b){var c=this;return c.next_in.subarray(a,a+b)}};var ia;a.zip?a.zip.Inflater=g:(ia=new g,a.addEventListener("message",function(b){var c=b.data;c.append&&a.postMessage({onappend:!0,data:ia.append(c.data,function(b){a.postMessage({progress:!0,current:b})})}),c.flush&&(ia.flush(),a.postMessage({onflush:!0}))},!1))},O.esri.TPK.___test=O.esri.TPK.inflate.toString(),O.esri.TPK.___blobURL=URL.createObjectURL(new Blob(["(",O.esri.TPK.___test,")(this)"],{type:"application/javascript"})),O.esri.zip.workerScriptsPath=O.esri.TPK.___blobURL,O.esri.TPK.X2JS=function(a){"use strict";function b(){void 0===a.escapeMode&&(a.escapeMode=!0),a.attributePrefix=a.attributePrefix||"_",a.arrayAccessForm=a.arrayAccessForm||"none",a.emptyNodeForm=a.emptyNodeForm||"text",void 0===a.enableToStringFunc&&(a.enableToStringFunc=!0),a.arrayAccessFormPaths=a.arrayAccessFormPaths||[],void 0===a.skipEmptyTextNodesForObj&&(a.skipEmptyTextNodesForObj=!0),void 0===a.stripWhitespaces&&(a.stripWhitespaces=!0),a.datetimeAccessFormPaths=a.datetimeAccessFormPaths||[]}function c(){function a(a){var b=String(a);return 1===b.length&&(b="0"+b),b}"function"!=typeof String.prototype.trim&&(String.prototype.trim=function(){return this.replace(/^\s+|^\n+|(\s|\n)+$/g,"")}),"function"!=typeof Date.prototype.toISOString&&(Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+a(this.getUTCMonth()+1)+"-"+a(this.getUTCDate())+"T"+a(this.getUTCHours())+":"+a(this.getUTCMinutes())+":"+a(this.getUTCSeconds())+"."+String((this.getUTCMilliseconds()/1e3).toFixed(3)).slice(2,5)+"Z"})}function d(a){var b=a.localName;return null==b&&(b=a.baseName),(null==b||""==b)&&(b=a.nodeName),b}function e(a){return a.prefix}function f(a){return"string"==typeof a?a.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/"):a}function g(a){return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(///g,"/")}function h(b,c,d){switch(a.arrayAccessForm){case"property":b[c]instanceof Array?b[c+"_asArray"]=b[c]:b[c+"_asArray"]=[b[c]]}if(!(b[c]instanceof Array)&&a.arrayAccessFormPaths.length>0){for(var e=0;e1&&c.setMilliseconds(d[1]),b[6]&&b[7]){var e=60*b[6]+Number(b[7]),f=/\d\d-\d\d:\d\d$/.test(a)?"-":"+";e=0+("-"==f?-1*e:e),c.setMinutes(c.getMinutes()-e-c.getTimezoneOffset())}else-1!==a.indexOf("Z",a.length-1)&&(c=new Date(Date.UTC(c.getFullYear(),c.getMonth(),c.getDate(),c.getHours(),c.getMinutes(),c.getSeconds(),c.getMilliseconds())));return c}function j(b,c,d){if(a.datetimeAccessFormPaths.length>0){for(var e=d.split(".#")[0],f=0;f1&&null!=f.__text&&a.skipEmptyTextNodesForObj&&(a.stripWhitespaces&&""==f.__text||""==f.__text.trim())&&delete f.__text,delete f.__cnt,!a.enableToStringFunc||null==f.__text&&null==f.__cdata||(f.toString=function(){return(null!=this.__text?this.__text:"")+(null!=this.__cdata?this.__cdata:"")}),f}return b.nodeType==w.TEXT_NODE||b.nodeType==w.CDATA_SECTION_NODE?b.nodeValue:void 0}function l(b,c,d,e){var g="<"+(null!=b&&null!=b.__prefix?b.__prefix+":":"")+c;if(null!=d)for(var h=0;h":">"}function m(a,b){return""}function n(a,b){return-1!==a.indexOf(b,a.length-b.length)}function o(b,c){return"property"==a.arrayAccessForm&&n(c.toString(),"_asArray")||0==c.toString().indexOf(a.attributePrefix)||0==c.toString().indexOf("__")||b[c]instanceof Function?!0:!1}function p(a){var b=0;if(a instanceof Object)for(var c in a)o(a,c)||b++;return b}function q(b){var c=[];if(b instanceof Object)for(var d in b)-1==d.toString().indexOf("__")&&0==d.toString().indexOf(a.attributePrefix)&&c.push(d);return c}function r(b){var c="";return null!=b.__cdata&&(c+=""),null!=b.__text&&(c+=a.escapeMode?f(b.__text):b.__text),c}function s(b){var c="";return b instanceof Object?c+=r(b):null!=b&&(c+=a.escapeMode?f(b):b),c}function t(a,b,c){var d="";if(0==a.length)d+=l(a,b,c,!0);else for(var e=0;e0)for(var d in a)if(!o(a,d)){var e=a[d],f=q(e);if(null==e||void 0==e)b+=l(e,d,f,!0);else if(e instanceof Object)if(e instanceof Array)b+=t(e,d,f);else if(e instanceof Date)b+=l(e,d,f,!1),b+=e.toISOString(),b+=m(e,d);else{var g=p(e);g>0||null!=e.__text||null!=e.__cdata?(b+=l(e,d,f,!1),b+=u(e),b+=m(e,d)):b+=l(e,d,f,!0)}else b+=l(e,d,f,!1),b+=s(e),b+=m(e,d)}return b+=s(a)}var v="1.1.5",a=a||{};b(),c();var w={ELEMENT_NODE:1,TEXT_NODE:3,CDATA_SECTION_NODE:4,COMMENT_NODE:8,DOCUMENT_NODE:9};this.parseXmlString=function(a){var b=window.ActiveXObject||"ActiveXObject"in window;if(void 0===a)return null;var c;if(window.DOMParser){var d=new window.DOMParser,e=null;if(!b)try{e=d.parseFromString("INVALID","text/xml").childNodes[0].namespaceURI}catch(f){e=null}try{c=d.parseFromString(a,"text/xml"),null!=e&&c.getElementsByTagNameNS(e,"parsererror").length>0&&(c=null)}catch(f){c=null}}else 0==a.indexOf("")+2)),c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(a);return c},this.asArray=function(a){return a instanceof Array?a:[a]},this.toXmlDateTime=function(a){return a instanceof Date?a.toISOString():"number"==typeof a?new Date(a).toISOString():null},this.asDateTime=function(a){return"string"==typeof a?i(a):a},this.xml2json=function(a){return k(a)},this.xml_str2json=function(a){var b=this.parseXmlString(a);return null!=b?this.xml2json(b):null},this.json2xml_str=function(a){return u(a)},this.json2xml=function(a){var b=this.json2xml_str(a);return this.parseXmlString(b)},this.getVersion=function(){return v}}; \ No newline at end of file +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,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//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){a===!1?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.onloadend=function(g){if(void 0!==f.token){e._inMemTilesIndex.push("blank") +var h=a[f.token].filename.toLocaleUpperCase() +e._inMemTilesObject[h]=f.result +var i=e.ObjectSize(e._inMemTilesObject) +i>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,this.dbName="offline_tile_store",this.objectStoreName="tilepath",this.isSupported=function(){return window.indexedDB||window.openDatabase?!0:!1},this.store=function(a,b){try{var c=this._db.transaction([this.objectStoreName],"readwrite") +c.oncomplete=function(){b(!0)},c.onerror=function(a){b(!1,a.target.error.message)} +var d=c.objectStore(this.objectStoreName),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([this.objectStoreName]).objectStore(this.objectStoreName),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([this.objectStoreName],"readwrite").objectStore(this.objectStoreName).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([this.objectStoreName],"readwrite").objectStore(this.objectStoreName)["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([this.objectStoreName]).objectStore(this.objectStoreName).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([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+=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(a){var b=indexedDB.open(this.dbName,4) +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),b.createObjectStore(this.objectStoreName,{keyPath:"url"})}.bind(this),b.onsuccess=function(b){this._db=b.target.result,a(!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(){require(["esri/geometry/Point","esri/SpatialReference"],function(b,c){var d=i().split(","),e=a.spatialReference.wkid,f=null +4326==e?f=new b(d[1],d[0]):102100==e&&(f=new b(d[0],d[1],new c({wkid:e}))),a.centerAt(f)})}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())})}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,v.next_in_index=Q,f.write=B,f.inflate_flush(v,w) +case E:for(x=o;x>P;){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++))<>=x,P-=x,k=s,c=e,h=u,b=F +case F:for(x=k;x>P;){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!==(16&z)){o=15&z,q=c[y+2],b=G +break}if(0===(64&z)){k=z,h=y/3+c[y+2] +break}return b=L,v.msg="invalid distance code",w=m,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) +case G:for(x=o;x>P;){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++))<>=x,P-=x,b=H +case H:for(N=B-q;0>N;)N+=f.end +for(;0!==g;){if(0===M&&(B==f.end&&0!==f.read&&(B=0,M=B7&&(P-=8,A++,Q--),f.write=B,w=f.inflate_flush(v,w),B=f.write,M=Ba.avail_out&&(c=a.avail_out),0!==c&&b==o&&(b=i),a.avail_out-=c,a.total_out+=c,a.next_out.set(f.window.subarray(e,e+c),d),d+=c,e+=c,e==f.end&&(e=0,f.write==f.end&&(f.write=0),c=f.write-e,c>a.avail_out&&(c=a.avail_out),0!==c&&b==o&&(b=i),a.avail_out-=c,a.total_out+=c,a.next_out.set(f.window.subarray(e,e+c),d),d+=c,e+=c),a.next_out_index=d,f.read=e,b},f.proc=function(a,c){var d,o,q,w,y,z,A,B +for(w=a.next_in_index,y=a.avail_in,o=f.bitb,q=f.bitk,z=f.write,A=zq;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +c=i,y--,o|=(255&a.read_byte(w++))<>>1){case 0:o>>>=3,q-=3,d=7&q,o>>>=d,q-=d,g=O +break +case 1:var C=[],D=[],E=[[]],F=[[]] +b.inflate_trees_fixed(C,D,E,F),t.init(C[0],D[0],E[0],0,F[0],0),o>>>=3,q-=3,g=T +break +case 2:o>>>=3,q-=3,g=Q +break +case 3:return o>>>=3,q-=3,g=W,a.msg="invalid block type",c=m,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c)}break +case O:for(;32>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +c=i,y--,o|=(255&a.read_byte(w++))<>>16&65535)!=(65535&o))return g=W,a.msg="invalid stored block lengths",c=m,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +h=65535&o,o=q=0,g=0!==h?P:0!==u?U:N +break +case P:if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +if(0===A&&(z==f.end&&0!==f.read&&(z=0,A=zy&&(d=y),d>A&&(d=A),f.window.set(a.read_buf(w,d),z),w+=d,y-=d,z+=d,A-=d,0!==(h-=d))break +g=0!==u?U:N +break +case Q:for(;14>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +c=i,y--,o|=(255&a.read_byte(w++))<29||(d>>5&31)>29)return g=W,a.msg="too many length or distance symbols",c=m,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +if(d=258+(31&d)+(d>>5&31),!e||e.lengthB;B++)e[B]=0 +o>>>=14,q-=14,n=0,g=R +case R:for(;4+(k>>>10)>n;){for(;3>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +c=i,y--,o|=(255&a.read_byte(w++))<>>=3,q-=3}for(;19>n;)e[M[n++]]=0 +if(r[0]=7,d=x.inflate_trees_bits(e,r,s,v,a),d!=i)return c=d,c==m&&(e=null,g=W),f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +n=0,g=S +case S:for(;;){if(d=k,!(258+(31&d)+(d>>5&31)>n))break +var G,H +for(d=r[0];d>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +c=i,y--,o|=(255&a.read_byte(w++))<H)o>>>=d,q-=d,e[n++]=H +else{for(B=18==H?7:H-14,G=18==H?11:3;d+B>q;){if(0===y)return f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +c=i,y--,o|=(255&a.read_byte(w++))<>>=d,q-=d,G+=o&p[B],o>>>=B,q-=B,B=n,d=k,B+G>258+(31&d)+(d>>5&31)||16==H&&1>B)return e=null,g=W,a.msg="invalid bit length repeat",c=m,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +H=16==H?e[B-1]:0 +do e[B++]=H +while(0!==--G) +n=B}}s[0]=-1 +var I=[],J=[],K=[],L=[] +if(I[0]=9,J[0]=6,d=k,d=x.inflate_trees_dynamic(257+(31&d),1+(d>>5&31),e,I,J,K,L,v,a),d!=i)return d==m&&(e=null,g=W),c=d,f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,f.inflate_flush(a,c) +t.init(I[0],J[0],v,K[0],v,L[0]),g=T +case T:if(f.bitb=o,f.bitk=q,a.avail_in=y,a.total_in+=w-a.next_in_index,a.next_in_index=w,f.write=z,(c=t.proc(f,a,c))!=j)return f.inflate_flush(a,c) +if(c=i,t.free(a),w=a.next_in_index,y=a.avail_in,o=f.bitb,q=f.bitk,z=f.write,A=ze||e>15?(b.inflateEnd(c),l):(b.wbits=e,c.istate.blocks=new d(c,1<>4)+8>a.istate.wbits){a.istate.mode=ga,a.msg="invalid window size",a.istate.marker=5 +break}a.istate.mode=$ +case $:if(0===a.avail_in)return c +if(c=b,a.avail_in--,a.total_in++,d=255&a.read_byte(a.next_in_index++),((a.istate.method<<8)+d)%31!==0){a.istate.mode=ga,a.msg="incorrect header check",a.istate.marker=5 +break}if(0===(d&X)){a.istate.mode=ea +break}a.istate.mode=_ +case _:if(0===a.avail_in)return c +c=b,a.avail_in--,a.total_in++,a.istate.need=(255&a.read_byte(a.next_in_index++))<<24&4278190080,a.istate.mode=aa +case aa:if(0===a.avail_in)return c +c=b,a.avail_in--,a.total_in++,a.istate.need+=(255&a.read_byte(a.next_in_index++))<<16&16711680,a.istate.mode=ba +case ba:if(0===a.avail_in)return c +c=b,a.avail_in--,a.total_in++,a.istate.need+=(255&a.read_byte(a.next_in_index++))<<8&65280,a.istate.mode=ca +case ca:return 0===a.avail_in?c:(c=b,a.avail_in--,a.total_in++,a.istate.need+=255&a.read_byte(a.next_in_index++),a.istate.mode=da,k) +case da:return a.istate.mode=ga,a.msg="need dictionary",a.istate.marker=0,l +case ea:if(c=a.istate.blocks.proc(a,c),c==m){a.istate.mode=ga,a.istate.marker=0 +break}if(c==i&&(c=b),c!=j)return c +c=b,a.istate.blocks.reset(a,a.istate.was),a.istate.mode=fa +case fa:return j +case ga:return m +default:return l}},b.inflateSetDictionary=function(a,b,c){var d=0,e=c +return a&&a.istate&&a.istate.mode==da?(e>=1<e;)b.read_byte(d)==ha[e]?e++:e=0!==b.read_byte(d)?0:4-e,d++,c-- +return b.total_in+=d-b.next_in_index,b.next_in_index=d,b.avail_in=c,b.istate.marker=e,4!=e?m:(f=b.total_in,g=b.total_out,a(b),b.total_in=f,b.total_out=g,b.istate.mode=ea,i)},b.inflateSyncPoint=function(a){return a&&a.istate&&a.istate.blocks?a.istate.blocks.sync_point():l}}function f(){}function g(){var a=this,b=new f,c=512,d=r,e=new Uint8Array(c),g=!1 +b.inflateInit(),b.next_out=e,a.append=function(a,f){var h,k,l=[],m=0,n=0,p=0 +if(0!==a.length){b.next_in_index=0,b.next_in=a,b.avail_in=a.length +do{if(b.next_out_index=0,b.avail_out=c,0!==b.avail_in||g||(b.next_in_index=0,g=!0),h=b.inflate(d),g&&h==o)return-1 +if(h!=i&&h!=j)throw"inflating: "+b.msg +if((g||h==j)&&b.avail_in==a.length)return-1 +b.next_out_index&&(b.next_out_index==c?l.push(new Uint8Array(e)):l.push(new Uint8Array(e.subarray(0,b.next_out_index)))),p+=b.next_out_index,f&&b.next_in_index>0&&b.next_in_index!=m&&(f(b.next_in_index),m=b.next_in_index)}while(b.avail_in>0||0===b.avail_out) +return k=new Uint8Array(p),l.forEach(function(a){k.set(a,n),n+=a.length}),k}},a.flush=function(){b.inflateEnd()}}var h=15,i=0,j=1,k=2,l=-2,m=-3,n=-4,o=-5,p=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],q=1440,r=0,s=4,t=9,u=5,v=[96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,192,80,7,10,0,8,96,0,8,32,0,9,160,0,8,0,0,8,128,0,8,64,0,9,224,80,7,6,0,8,88,0,8,24,0,9,144,83,7,59,0,8,120,0,8,56,0,9,208,81,7,17,0,8,104,0,8,40,0,9,176,0,8,8,0,8,136,0,8,72,0,9,240,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,200,81,7,13,0,8,100,0,8,36,0,9,168,0,8,4,0,8,132,0,8,68,0,9,232,80,7,8,0,8,92,0,8,28,0,9,152,84,7,83,0,8,124,0,8,60,0,9,216,82,7,23,0,8,108,0,8,44,0,9,184,0,8,12,0,8,140,0,8,76,0,9,248,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,196,81,7,11,0,8,98,0,8,34,0,9,164,0,8,2,0,8,130,0,8,66,0,9,228,80,7,7,0,8,90,0,8,26,0,9,148,84,7,67,0,8,122,0,8,58,0,9,212,82,7,19,0,8,106,0,8,42,0,9,180,0,8,10,0,8,138,0,8,74,0,9,244,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,204,81,7,15,0,8,102,0,8,38,0,9,172,0,8,6,0,8,134,0,8,70,0,9,236,80,7,9,0,8,94,0,8,30,0,9,156,84,7,99,0,8,126,0,8,62,0,9,220,82,7,27,0,8,110,0,8,46,0,9,188,0,8,14,0,8,142,0,8,78,0,9,252,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,194,80,7,10,0,8,97,0,8,33,0,9,162,0,8,1,0,8,129,0,8,65,0,9,226,80,7,6,0,8,89,0,8,25,0,9,146,83,7,59,0,8,121,0,8,57,0,9,210,81,7,17,0,8,105,0,8,41,0,9,178,0,8,9,0,8,137,0,8,73,0,9,242,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,202,81,7,13,0,8,101,0,8,37,0,9,170,0,8,5,0,8,133,0,8,69,0,9,234,80,7,8,0,8,93,0,8,29,0,9,154,84,7,83,0,8,125,0,8,61,0,9,218,82,7,23,0,8,109,0,8,45,0,9,186,0,8,13,0,8,141,0,8,77,0,9,250,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,198,81,7,11,0,8,99,0,8,35,0,9,166,0,8,3,0,8,131,0,8,67,0,9,230,80,7,7,0,8,91,0,8,27,0,9,150,84,7,67,0,8,123,0,8,59,0,9,214,82,7,19,0,8,107,0,8,43,0,9,182,0,8,11,0,8,139,0,8,75,0,9,246,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,206,81,7,15,0,8,103,0,8,39,0,9,174,0,8,7,0,8,135,0,8,71,0,9,238,80,7,9,0,8,95,0,8,31,0,9,158,84,7,99,0,8,127,0,8,63,0,9,222,82,7,27,0,8,111,0,8,47,0,9,190,0,8,15,0,8,143,0,8,79,0,9,254,96,7,256,0,8,80,0,8,16,84,8,115,82,7,31,0,8,112,0,8,48,0,9,193,80,7,10,0,8,96,0,8,32,0,9,161,0,8,0,0,8,128,0,8,64,0,9,225,80,7,6,0,8,88,0,8,24,0,9,145,83,7,59,0,8,120,0,8,56,0,9,209,81,7,17,0,8,104,0,8,40,0,9,177,0,8,8,0,8,136,0,8,72,0,9,241,80,7,4,0,8,84,0,8,20,85,8,227,83,7,43,0,8,116,0,8,52,0,9,201,81,7,13,0,8,100,0,8,36,0,9,169,0,8,4,0,8,132,0,8,68,0,9,233,80,7,8,0,8,92,0,8,28,0,9,153,84,7,83,0,8,124,0,8,60,0,9,217,82,7,23,0,8,108,0,8,44,0,9,185,0,8,12,0,8,140,0,8,76,0,9,249,80,7,3,0,8,82,0,8,18,85,8,163,83,7,35,0,8,114,0,8,50,0,9,197,81,7,11,0,8,98,0,8,34,0,9,165,0,8,2,0,8,130,0,8,66,0,9,229,80,7,7,0,8,90,0,8,26,0,9,149,84,7,67,0,8,122,0,8,58,0,9,213,82,7,19,0,8,106,0,8,42,0,9,181,0,8,10,0,8,138,0,8,74,0,9,245,80,7,5,0,8,86,0,8,22,192,8,0,83,7,51,0,8,118,0,8,54,0,9,205,81,7,15,0,8,102,0,8,38,0,9,173,0,8,6,0,8,134,0,8,70,0,9,237,80,7,9,0,8,94,0,8,30,0,9,157,84,7,99,0,8,126,0,8,62,0,9,221,82,7,27,0,8,110,0,8,46,0,9,189,0,8,14,0,8,142,0,8,78,0,9,253,96,7,256,0,8,81,0,8,17,85,8,131,82,7,31,0,8,113,0,8,49,0,9,195,80,7,10,0,8,97,0,8,33,0,9,163,0,8,1,0,8,129,0,8,65,0,9,227,80,7,6,0,8,89,0,8,25,0,9,147,83,7,59,0,8,121,0,8,57,0,9,211,81,7,17,0,8,105,0,8,41,0,9,179,0,8,9,0,8,137,0,8,73,0,9,243,80,7,4,0,8,85,0,8,21,80,8,258,83,7,43,0,8,117,0,8,53,0,9,203,81,7,13,0,8,101,0,8,37,0,9,171,0,8,5,0,8,133,0,8,69,0,9,235,80,7,8,0,8,93,0,8,29,0,9,155,84,7,83,0,8,125,0,8,61,0,9,219,82,7,23,0,8,109,0,8,45,0,9,187,0,8,13,0,8,141,0,8,77,0,9,251,80,7,3,0,8,83,0,8,19,85,8,195,83,7,35,0,8,115,0,8,51,0,9,199,81,7,11,0,8,99,0,8,35,0,9,167,0,8,3,0,8,131,0,8,67,0,9,231,80,7,7,0,8,91,0,8,27,0,9,151,84,7,67,0,8,123,0,8,59,0,9,215,82,7,19,0,8,107,0,8,43,0,9,183,0,8,11,0,8,139,0,8,75,0,9,247,80,7,5,0,8,87,0,8,23,192,8,0,83,7,51,0,8,119,0,8,55,0,9,207,81,7,15,0,8,103,0,8,39,0,9,175,0,8,7,0,8,135,0,8,71,0,9,239,80,7,9,0,8,95,0,8,31,0,9,159,84,7,99,0,8,127,0,8,63,0,9,223,82,7,27,0,8,111,0,8,47,0,9,191,0,8,15,0,8,143,0,8,79,0,9,255],w=[80,5,1,87,5,257,83,5,17,91,5,4097,81,5,5,89,5,1025,85,5,65,93,5,16385,80,5,3,88,5,513,84,5,33,92,5,8193,82,5,9,90,5,2049,86,5,129,192,5,24577,80,5,2,87,5,385,83,5,25,91,5,6145,81,5,7,89,5,1537,85,5,97,93,5,24577,80,5,4,88,5,769,84,5,49,92,5,12289,82,5,13,90,5,3073,86,5,193,192,5,24577],x=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],y=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,112,112],z=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577],A=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],B=15 +b.inflate_trees_fixed=function(a,b,c,d){return a[0]=t,b[0]=u,c[0]=v,d[0]=w,i} +var C=0,D=1,E=2,F=3,G=4,H=5,I=6,J=7,K=8,L=9,M=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],N=0,O=1,P=2,Q=3,R=4,S=5,T=6,U=7,V=8,W=9,X=32,Y=8,Z=0,$=1,_=2,aa=3,ba=4,ca=5,da=6,ea=7,fa=12,ga=13,ha=[0,0,255,255] +f.prototype={inflateInit:function(a){var b=this +return b.istate=new e,a||(a=h),b.istate.inflateInit(b,a)},inflate:function(a){var b=this +return b.istate?b.istate.inflate(b,a):l},inflateEnd:function(){var a=this +if(!a.istate)return l +var b=a.istate.inflateEnd(a) +return a.istate=null,b},inflateSync:function(){var a=this +return a.istate?a.istate.inflateSync(a):l},inflateSetDictionary:function(a,b){var c=this +return c.istate?c.istate.inflateSetDictionary(c,a,b):l},read_byte:function(a){var b=this +return b.next_in.subarray(a,a+1)[0]},read_buf:function(a,b){var c=this +return c.next_in.subarray(a,a+b)}} +var ia +a.zip?a.zip.Inflater=g:(ia=new g,a.addEventListener("message",function(b){var c=b.data +c.append&&a.postMessage({onappend:!0,data:ia.append(c.data,function(b){a.postMessage({progress:!0,current:b})})}),c.flush&&(ia.flush(),a.postMessage({onflush:!0}))},!1))},O.esri.TPK.___test=O.esri.TPK.inflate.toString(),O.esri.TPK.___blobURL=URL.createObjectURL(new Blob(["(",O.esri.TPK.___test,")(this)"],{type:"application/javascript"})),O.esri.zip.workerScriptsPath=O.esri.TPK.___blobURL,O.esri.TPK.X2JS=function(a){"use strict" +function b(){void 0===a.escapeMode&&(a.escapeMode=!0),a.attributePrefix=a.attributePrefix||"_",a.arrayAccessForm=a.arrayAccessForm||"none",a.emptyNodeForm=a.emptyNodeForm||"text",void 0===a.enableToStringFunc&&(a.enableToStringFunc=!0),a.arrayAccessFormPaths=a.arrayAccessFormPaths||[],void 0===a.skipEmptyTextNodesForObj&&(a.skipEmptyTextNodesForObj=!0),void 0===a.stripWhitespaces&&(a.stripWhitespaces=!0),a.datetimeAccessFormPaths=a.datetimeAccessFormPaths||[]}function c(){function a(a){var b=String(a) +return 1===b.length&&(b="0"+b),b}"function"!=typeof String.prototype.trim&&(String.prototype.trim=function(){return this.replace(/^\s+|^\n+|(\s|\n)+$/g,"")}),"function"!=typeof Date.prototype.toISOString&&(Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+a(this.getUTCMonth()+1)+"-"+a(this.getUTCDate())+"T"+a(this.getUTCHours())+":"+a(this.getUTCMinutes())+":"+a(this.getUTCSeconds())+"."+String((this.getUTCMilliseconds()/1e3).toFixed(3)).slice(2,5)+"Z"})}function d(a){var b=a.localName +return null==b&&(b=a.baseName),(null==b||""==b)&&(b=a.nodeName),b}function e(a){return a.prefix}function f(a){return"string"==typeof a?a.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/"):a}function g(a){return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(///g,"/")}function h(b,c,d){switch(a.arrayAccessForm){case"property":b[c]instanceof Array?b[c+"_asArray"]=b[c]:b[c+"_asArray"]=[b[c]]}if(!(b[c]instanceof Array)&&a.arrayAccessFormPaths.length>0){for(var e=0;e1&&c.setMilliseconds(d[1]),b[6]&&b[7]){var e=60*b[6]+Number(b[7]),f=/\d\d-\d\d:\d\d$/.test(a)?"-":"+" +e=0+("-"==f?-1*e:e),c.setMinutes(c.getMinutes()-e-c.getTimezoneOffset())}else-1!==a.indexOf("Z",a.length-1)&&(c=new Date(Date.UTC(c.getFullYear(),c.getMonth(),c.getDate(),c.getHours(),c.getMinutes(),c.getSeconds(),c.getMilliseconds()))) +return c}function j(b,c,d){if(a.datetimeAccessFormPaths.length>0){for(var e=d.split(".#")[0],f=0;f1&&null!=f.__text&&a.skipEmptyTextNodesForObj&&(a.stripWhitespaces&&""==f.__text||""==f.__text.trim())&&delete f.__text,delete f.__cnt,!a.enableToStringFunc||null==f.__text&&null==f.__cdata||(f.toString=function(){return(null!=this.__text?this.__text:"")+(null!=this.__cdata?this.__cdata:"")}),f}return b.nodeType==w.TEXT_NODE||b.nodeType==w.CDATA_SECTION_NODE?b.nodeValue:void 0}function l(b,c,d,e){var g="<"+(null!=b&&null!=b.__prefix?b.__prefix+":":"")+c +if(null!=d)for(var h=0;h":">"}function m(a,b){return""}function n(a,b){return-1!==a.indexOf(b,a.length-b.length)}function o(b,c){return"property"==a.arrayAccessForm&&n(c.toString(),"_asArray")||0==c.toString().indexOf(a.attributePrefix)||0==c.toString().indexOf("__")||b[c]instanceof Function?!0:!1}function p(a){var b=0 +if(a instanceof Object)for(var c in a)o(a,c)||b++ +return b}function q(b){var c=[] +if(b instanceof Object)for(var d in b)-1==d.toString().indexOf("__")&&0==d.toString().indexOf(a.attributePrefix)&&c.push(d) +return c}function r(b){var c="" +return null!=b.__cdata&&(c+=""),null!=b.__text&&(c+=a.escapeMode?f(b.__text):b.__text),c}function s(b){var c="" +return b instanceof Object?c+=r(b):null!=b&&(c+=a.escapeMode?f(b):b),c}function t(a,b,c){var d="" +if(0==a.length)d+=l(a,b,c,!0) +else for(var e=0;e0)for(var d in a)if(!o(a,d)){var e=a[d],f=q(e) +if(null==e||void 0==e)b+=l(e,d,f,!0) +else if(e instanceof Object)if(e instanceof Array)b+=t(e,d,f) +else if(e instanceof Date)b+=l(e,d,f,!1),b+=e.toISOString(),b+=m(e,d) +else{var g=p(e) +g>0||null!=e.__text||null!=e.__cdata?(b+=l(e,d,f,!1),b+=u(e),b+=m(e,d)):b+=l(e,d,f,!0)}else b+=l(e,d,f,!1),b+=s(e),b+=m(e,d)}return b+=s(a)}var v="1.1.5",a=a||{} +b(),c() +var w={ELEMENT_NODE:1,TEXT_NODE:3,CDATA_SECTION_NODE:4,COMMENT_NODE:8,DOCUMENT_NODE:9} +this.parseXmlString=function(a){var b=window.ActiveXObject||"ActiveXObject"in window +if(void 0===a)return null +var c +if(window.DOMParser){var d=new window.DOMParser,e=null +if(!b)try{e=d.parseFromString("INVALID","text/xml").childNodes[0].namespaceURI}catch(f){e=null}try{c=d.parseFromString(a,"text/xml"),null!=e&&c.getElementsByTagNameNS(e,"parsererror").length>0&&(c=null)}catch(f){c=null}}else 0==a.indexOf("")+2)),c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(a) +return c},this.asArray=function(a){return a instanceof Array?a:[a]},this.toXmlDateTime=function(a){return a instanceof Date?a.toISOString():"number"==typeof a?new Date(a).toISOString():null},this.asDateTime=function(a){return"string"==typeof a?i(a):a},this.xml2json=function(a){return k(a)},this.xml_str2json=function(a){var b=this.parseXmlString(a) +return null!=b?this.xml2json(b):null},this.json2xml_str=function(a){return u(a)},this.json2xml=function(a){var b=this.json2xml_str(a) +return this.parseXmlString(b)},this.getVersion=function(){return v}} diff --git a/dist/offline-tpk-src.js b/dist/offline-tpk-src.js index 14a139f6..199cf2bc 100644 --- a/dist/offline-tpk-src.js +++ b/dist/offline-tpk-src.js @@ -1,4 +1,4 @@ -/*! esri-offline-maps - v2.16.0 - 2015-10-29 +/*! esri-offline-maps - v3.0.0 - 2015-11-23 * Copyright (c) 2015 Environmental Systems Research Institute, Inc. * Apache License*/ /** @@ -147,7 +147,7 @@ define([ // sets img visibility to 'hidden', so we need to show the image back once we have put the data:image img.style.visibility = "visible"; img.src = imgURL; - console.log("URL length " + imgURL.length + ", image: " + imgURL); + //console.log("URL length " + imgURL.length + ", image: " + imgURL); this.emit(this.PROGRESS_EVENT,this.PROGRESS_END); return ""; /* this result goes nowhere, seriously */ @@ -204,7 +204,7 @@ define([ }, /** - * Reads a tile into tile database. Works with offlineTilesEnabler.js and OfflineTilesEnablerLayer.js + * Reads a tile into tile database. Works with OfflineTilesBasic.js and OfflineTilesAdvanced.js * saveToFile() functionality. * IMPORTANT! The tile must confirm to an object using the pattern shown in _storeTile(). * @param file diff --git a/package.json b/package.json index c78b67a1..60349aae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "esri-offline-maps", - "version": "2.16.0", + "version": "3.0.0", "description": "Lightweight set of libraries for working offline with map tiles and editing with ArcGIS feature services", "author": "Andy Gup (http://blog.andygup.net)", "license": "Apache 2.0",