From 042a6c74d48beb759638debd18bd25bf94a0fca6 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 30 Jul 2014 16:53:16 -0600 Subject: [PATCH 01/86] fixes #219 --- samples/appcache-features.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/appcache-features.html b/samples/appcache-features.html index 33c7854..2929d4e 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -558,7 +558,7 @@ }); updateOfflineUsage(); - if(typeof baseMapLayer != "undefined") baseMapLayer.goOnline(); + if(typeof tileLayer != "undefined") tileLayer.goOnline(); } function goOffline(){ From 76bb3e2d6b7f3469d2eb274bb60c31c2e6764a06 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 11 Aug 2014 11:09:10 -0600 Subject: [PATCH 02/86] initial commit of TilesCore.js --- lib/tiles/TilesCore.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 lib/tiles/TilesCore.js diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js new file mode 100644 index 0000000..d705aac --- /dev/null +++ b/lib/tiles/TilesCore.js @@ -0,0 +1,12 @@ +/** + * This library contains common core code between offlineTilesEnabler.js + * and OfflineTilesEnablerLayer.js + */ +define([ + "dojo/_base/declare"], + function(declare){ + "use strict"; + return declare([],{ + + }) +}) From deaac929b9cd597beff547bf77d77867ddf994cf Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 11 Aug 2014 13:56:52 -0600 Subject: [PATCH 03/86] refactored _storeTile() to TilesCore --- lib/tiles/OfflineTilesEnablerLayer.js | 49 +++---------------- lib/tiles/TilesCore.js | 46 +++++++++++++++-- lib/tiles/offlineTilesEnabler.js | 47 +++--------------- test/SpecRunner.offlineTilesEnabler.html | 3 +- test/SpecRunner.offlineTilesEnablerLayer.html | 6 ++- test/spec/offlineTilesEnablerLayerSpec.js | 10 +++- test/spec/offlineTilesEnablerSpec.js | 13 +++-- 7 files changed, 79 insertions(+), 95 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index 385dce4..98078cf 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -2,6 +2,7 @@ define([ "dojo/query", "dojo/request", "dojo/_base/declare", + "tiles/TilesCore", "tiles/base64utils", "tiles/TilesStore", "tiles/tilingScheme", @@ -12,7 +13,7 @@ define([ "esri/layers/TileInfo", "esri/SpatialReference", "esri/layers/TiledMapServiceLayer" -], function(query, request, declare,Base64Utils,TilesStore,TilingScheme, +], function(query, request, declare,TilesCore,Base64Utils,TilesStore,TilingScheme, FileSaver,LOD,Point,Extent,TileInfo,SpatialReference,TiledMapServerLayer) { "use strict"; @@ -23,6 +24,7 @@ define([ _level: null, //current zoom level _minZoom: null, _maxZoom: null, + _tilesCore:null, constructor:function(url,callback,state){ @@ -31,6 +33,8 @@ define([ callback(false); } + this._tilesCore = new TilesCore(); + //For calculating minZoom and maxZoom Array.prototype.sortNumber = function(){return this.sort(function(a,b){return a - b})}; @@ -545,7 +549,9 @@ define([ { var cell = cells[i]; - this._storeTile(cell.level,cell.row,cell.col, function(success, error) + var url = this._getTileUrl(cell.level,cell.row,cell.col); + + this._tilesCore._storeTile(url,this.offline.proxyPath,this.offline.store,function(success, error) { if(!success) { @@ -567,45 +573,6 @@ define([ }.bind(this)); }, - _storeTile : function(level,row,col,callback) // callback(success, msg) - { - var store = this.offline.store; - var url = this._getTileUrl(level,row,col); - url = url.split("?")[0]; - - /* download the tile */ - var imgurl = this.offline.proxyPath? this.offline.proxyPath + "?" + url : url; - var req = new XMLHttpRequest(); - req.open("GET", imgurl, true); - req.overrideMimeType("text/plain; charset=x-user-defined"); // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM%2FXMLHttpRequest%2FUsing_XMLHttpRequest#Handling_binary_data - - req.onload = function() - { - if( req.status === 200 && req.responseText !== "") - { - var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(this.responseText)); - - var tile = { - url: url, - img: img - }; - - store.store(tile,callback); - } - else - { - console.log("xhr failed for", imgurl); - callback(false, req.status + " " + req.statusText + ": " + req.response + " when downloading " + imgurl); - } - }; - req.onerror = function(e) - { - console.log("xhr failed for", imgurl); - callback(false, e); - }; - req.send(null); - }, - parseGetTileInfo: function(data,callback){ var fixedResponse = data.replace(/\\'/g, "'"); diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index d705aac..964e67b 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -3,10 +3,46 @@ * and OfflineTilesEnablerLayer.js */ define([ - "dojo/_base/declare"], - function(declare){ + "dojo/query", + "tiles/base64utils", + ], + function(query,Base64Utils){ "use strict"; - return declare([],{ + var TilesCore = function(){ + this._storeTile= function(url,proxyPath,store,callback) // callback(success, msg) + { + url = url.split("?")[0]; - }) -}) + /* download the tile */ + var imgurl = proxyPath ? proxyPath + "?" + url : url; + var req = new XMLHttpRequest(); + req.open("GET", imgurl, true); + req.overrideMimeType("text/plain; charset=x-user-defined"); // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM%2FXMLHttpRequest%2FUsing_XMLHttpRequest#Handling_binary_data + + req.onload = function () { + if (req.status === 200 && req.responseText !== "") { + var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(this.responseText)); + + var tile = { + url: url, + img: img + }; + + store.store(tile, callback); + } + else { + console.log("xhr failed for", imgurl); + callback(false, req.status + " " + req.statusText + ": " + req.response + " when downloading " + imgurl); + } + }; + req.onerror = function (e) { + console.log("xhr failed for", imgurl); + callback(false, e); + }; + req.send(null); + } + } + + return TilesCore; + } +) diff --git a/lib/tiles/offlineTilesEnabler.js b/lib/tiles/offlineTilesEnabler.js index d732e85..f5bcaff 100644 --- a/lib/tiles/offlineTilesEnabler.js +++ b/lib/tiles/offlineTilesEnabler.js @@ -2,11 +2,12 @@ define([ "dojo/query", "dojo/request", "dojo/_base/declare", + "tiles/TilesCore", "tiles/base64utils", "tiles/TilesStore", "tiles/tilingScheme", "tiles/FileSaver" - ], function(query, request, declare,Base64Utils,TilesStore,TilingScheme,FileSaver) + ], function(query, request, declare,TilesCore,Base64Utils,TilesStore,TilingScheme,FileSaver) { "use strict"; return declare([],{ @@ -34,6 +35,7 @@ define([ { console.log("extending layer", layer.url); + layer._tilesCore = new TilesCore(); layer._lastTileUrl = ""; layer._imageType = ""; @@ -461,7 +463,9 @@ define([ { var cell = cells[i]; - this._storeTile(cell.level,cell.row,cell.col, function(success, error) + var url = this._getTileUrl(cell.level,cell.row,cell.col); + + layer._tilesCore._storeTile(url,this.offline.proxyPath,this.offline.store, function(success, error) { if(!success) { @@ -482,45 +486,6 @@ define([ }.bind(this)); }; - - layer._storeTile = function(level,row,col,callback) // callback(success, msg) - { - var store = this.offline.store; - var url = this._getTileUrl(level,row,col); - url = url.split("?")[0]; - - /* download the tile */ - var imgurl = this.offline.proxyPath? this.offline.proxyPath + "?" + url : url; - var req = new XMLHttpRequest(); - req.open("GET", imgurl, true); - req.overrideMimeType("text/plain; charset=x-user-defined"); // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM%2FXMLHttpRequest%2FUsing_XMLHttpRequest#Handling_binary_data - - req.onload = function() - { - if( req.status === 200 && req.responseText !== "") - { - var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(this.responseText)); - - var tile = { - url: url, - img: img - }; - - store.store(tile,callback); - } - else - { - console.log("xhr failed for", imgurl); - callback(false, req.status + " " + req.statusText + ": " + req.response + " when downloading " + imgurl); - } - }; - req.onerror = function(e) - { - console.log("xhr failed for", imgurl); - callback(false, e); - }; - req.send(null); - }; } }); // declare }); // define diff --git a/test/SpecRunner.offlineTilesEnabler.html b/test/SpecRunner.offlineTilesEnabler.html index 36e3967..606e5b7 100755 --- a/test/SpecRunner.offlineTilesEnabler.html +++ b/test/SpecRunner.offlineTilesEnabler.html @@ -28,6 +28,7 @@ var g_map; var g_basemapLayer; var g_offlineTilesEnabler; + var g_tilesCore; require(["esri/map", "esri/layers/GraphicsLayer", "esri/graphic", "esri/symbols/SimpleFillSymbol", @@ -60,7 +61,7 @@ var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; - jasmineEnv.defaultTimeoutInterval = 10000; // 10 sec + jasmineEnv.defaultTimeoutInterval = 15000; // 15 sec var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); diff --git a/test/SpecRunner.offlineTilesEnablerLayer.html b/test/SpecRunner.offlineTilesEnablerLayer.html index 77616c6..e74353f 100644 --- a/test/SpecRunner.offlineTilesEnablerLayer.html +++ b/test/SpecRunner.offlineTilesEnablerLayer.html @@ -27,6 +27,7 @@ var g_map; var g_basemapLayer; + var tilesCore; require(["esri/map", "esri/layers/GraphicsLayer", "esri/graphic", "esri/symbols/SimpleFillSymbol", @@ -34,13 +35,14 @@ "dojo/dom", "dojo/on", "dojo/query", "esri/urlUtils", "esri/geometry/webMercatorUtils", "tiles/OfflineTilesEnablerLayer", + "tiles/TilesCore", "dojo/dom-construct", "dojo/domReady!"], function(Map, GraphicsLayer, Graphic, SimpleFillSymbol, Scalebar, esriUtils, geometry, dom, on, query, urlUtils, webMercatorUtils, - OfflineTilesEnablerLayer, + OfflineTilesEnablerLayer,TilesCore, domConstruct) { g_basemapLayer = new OfflineTilesEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ @@ -57,6 +59,8 @@ g_map.addLayer(g_basemapLayer); + tilesCore = new TilesCore(); + function test() { var jasmineEnv = jasmine.getEnv(); diff --git a/test/spec/offlineTilesEnablerLayerSpec.js b/test/spec/offlineTilesEnablerLayerSpec.js index 6ec1c88..4933a94 100644 --- a/test/spec/offlineTilesEnablerLayerSpec.js +++ b/test/spec/offlineTilesEnablerLayerSpec.js @@ -78,7 +78,10 @@ describe("offline enabler custom layer library", function() g_basemapLayer.getOfflineUsage(function(usage) { expect(usage.tileCount).toEqual(0); - g_basemapLayer._storeTile(14,6177,8023, function(success) + + var url = g_basemapLayer._getTileUrl(14,6177,8023); + + tilesCore._storeTile(url,g_basemapLayer.offline.proxyPath,g_basemapLayer.offline.store, function(success) { expect(success).toEqual(true); g_basemapLayer.getOfflineUsage(function(usage) @@ -95,7 +98,10 @@ describe("offline enabler custom layer library", function() g_basemapLayer.getOfflineUsage(function(usage) { expect(usage.tileCount).toEqual(1); - g_basemapLayer._storeTile(14,6177,8023, function(success) + + var url = g_basemapLayer._getTileUrl(14,6177,8023); + + tilesCore._storeTile(url,g_basemapLayer.offline.proxyPath,g_basemapLayer.offline.store, function(success) { expect(success).toEqual(true); g_basemapLayer.getOfflineUsage(function(usage) diff --git a/test/spec/offlineTilesEnablerSpec.js b/test/spec/offlineTilesEnablerSpec.js index aefed18..9f399be 100644 --- a/test/spec/offlineTilesEnablerSpec.js +++ b/test/spec/offlineTilesEnablerSpec.js @@ -29,10 +29,10 @@ describe("offline enabler library", function() expect(g_basemapLayer.getTileUrl).toEqual(jasmine.any(Function)); expect(g_basemapLayer._getTileUrl).toEqual(jasmine.any(Function)); expect(g_basemapLayer.prepareForOffline).toEqual(jasmine.any(Function)); - expect(g_basemapLayer._storeTile).toEqual(jasmine.any(Function)); expect(g_basemapLayer.deleteAllTiles).toEqual(jasmine.any(Function)); expect(g_basemapLayer.offline).toEqual(jasmine.any(Object)); expect(g_basemapLayer.offline.store).toEqual(jasmine.any(Object)); + expect(g_basemapLayer._tilesCore._storeTile).toEqual(jasmine.any(Function)); g_basemapLayer.offline.proxyPath = "../lib/resource-proxy/proxy.php"; done(); @@ -78,7 +78,10 @@ describe("offline enabler library", function() g_basemapLayer.getOfflineUsage(function(usage) { expect(usage.tileCount).toEqual(0); - g_basemapLayer._storeTile(14,6177,8023, function(success) + + var url = g_basemapLayer._getTileUrl(14,6177,8023); + + g_basemapLayer._tilesCore._storeTile(url,g_basemapLayer.offline.proxyPath,g_basemapLayer.offline.store, function(success) { expect(success).toEqual(true); g_basemapLayer.getOfflineUsage(function(usage) @@ -95,8 +98,10 @@ describe("offline enabler library", function() g_basemapLayer.getOfflineUsage(function(usage) { expect(usage.tileCount).toEqual(1); - g_basemapLayer._storeTile(14,6177,8023, function(success) - { + var url = g_basemapLayer._getTileUrl(14,6177,8023); + + g_basemapLayer._tilesCore._storeTile(url,g_basemapLayer.offline.proxyPath,g_basemapLayer.offline.store, function(success) + { expect(success).toEqual(true); g_basemapLayer.getOfflineUsage(function(usage) { From 8e148eb426ab7b73216d1b25813231a2bc6560b6 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 11 Aug 2014 14:37:22 -0600 Subject: [PATCH 04/86] refactored parseGetTiledInfo to TilesCore --- lib/tiles/OfflineTilesEnablerLayer.js | 49 +------------------- lib/tiles/TilesCore.js | 55 ++++++++++++++++++++++- test/spec/offlineTilesEnablerLayerSpec.js | 2 +- 3 files changed, 56 insertions(+), 50 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index 98078cf..7c5a057 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -79,7 +79,7 @@ define([ alert("There was a problem retrieving tiled map info in OfflineTilesEnablerLayer."); } - this.parseGetTileInfo(result,function(tileResult){ + this._tilesCore._parseGetTileInfo(result,function(tileResult){ this.layerInfos = tileResult.resultObj.layers; this.minScale = tileResult.resultObj.minScale; this.maxScale = tileResult.resultObj.maxScale; @@ -573,53 +573,6 @@ define([ }.bind(this)); }, - parseGetTileInfo: function(data,callback){ - - var fixedResponse = data.replace(/\\'/g, "'"); - var resultObj = JSON.parse(fixedResponse); - var spatialRef = new SpatialReference({wkid:resultObj.spatialReference.wkid}); - - var lods = []; - - var lodsObj = JSON.parse(data,function(key,value){ - if(((typeof key == 'number') || (key % 1 == 0)) && (typeof value === "object")){ - var l = new LOD(); - l.level = value.level; - l.resolution = value.resolution; - l.scale = value.scale; - - if(value.hasOwnProperty("level")) lods.push(l); - return value; - } - else{ - return value; - } - }); - - var initialExtent = new Extent( - parseFloat(resultObj.initialExtent.xmin), - parseFloat(resultObj.initialExtent.ymin), - parseFloat(resultObj.initialExtent.xmax), - parseFloat(resultObj.initialExtent.ymax), - spatialRef - ); - - var fullExtent = new Extent( - parseFloat(resultObj.fullExtent.xmin), - parseFloat(resultObj.fullExtent.ymin), - parseFloat(resultObj.fullExtent.xmax), - parseFloat(resultObj.fullExtent.ymax), - spatialRef - ); - - var tileInfo = new TileInfo(resultObj.tileInfo); - var origin = new Point(tileInfo.origin.x,tileInfo.origin.y,spatialRef) - tileInfo.origin = origin; - tileInfo.lods = lods; - - callback({initExtent:initialExtent,fullExtent:fullExtent,tileInfo:tileInfo,resultObj:resultObj}); - }, - /** * Test for localStorage functionality * @returns {boolean} diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index 964e67b..3d0de7e 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -4,11 +4,17 @@ */ define([ "dojo/query", + "esri/geometry/Point", + "esri/geometry/Extent", + "esri/SpatialReference", + "esri/layers/TileInfo", + "esri/layers/LOD", "tiles/base64utils", ], - function(query,Base64Utils){ + function(query,Point,Extent,SpatialReference,TileInfo,LOD,Base64Utils){ "use strict"; var TilesCore = function(){ + this._storeTile= function(url,proxyPath,store,callback) // callback(success, msg) { url = url.split("?")[0]; @@ -41,6 +47,53 @@ define([ }; req.send(null); } + + this._parseGetTileInfo = function(data,callback){ + + var fixedResponse = data.replace(/\\'/g, "'"); + var resultObj = JSON.parse(fixedResponse); + var spatialRef = new SpatialReference({wkid:resultObj.spatialReference.wkid}); + + var lods = []; + + var lodsObj = JSON.parse(data,function(key,value){ + if(((typeof key == 'number') || (key % 1 == 0)) && (typeof value === "object")){ + var l = new LOD(); + l.level = value.level; + l.resolution = value.resolution; + l.scale = value.scale; + + if(value.hasOwnProperty("level")) lods.push(l); + return value; + } + else{ + return value; + } + }); + + var initialExtent = new Extent( + parseFloat(resultObj.initialExtent.xmin), + parseFloat(resultObj.initialExtent.ymin), + parseFloat(resultObj.initialExtent.xmax), + parseFloat(resultObj.initialExtent.ymax), + spatialRef + ); + + var fullExtent = new Extent( + parseFloat(resultObj.fullExtent.xmin), + parseFloat(resultObj.fullExtent.ymin), + parseFloat(resultObj.fullExtent.xmax), + parseFloat(resultObj.fullExtent.ymax), + spatialRef + ); + + var tileInfo = new TileInfo(resultObj.tileInfo); + var origin = new Point(tileInfo.origin.x,tileInfo.origin.y,spatialRef) + tileInfo.origin = origin; + tileInfo.lods = lods; + + callback({initExtent:initialExtent,fullExtent:fullExtent,tileInfo:tileInfo,resultObj:resultObj}); + } } return TilesCore; diff --git a/test/spec/offlineTilesEnablerLayerSpec.js b/test/spec/offlineTilesEnablerLayerSpec.js index 4933a94..16328c7 100644 --- a/test/spec/offlineTilesEnablerLayerSpec.js +++ b/test/spec/offlineTilesEnablerLayerSpec.js @@ -236,7 +236,7 @@ describe("offline enabler custom layer library", function() it("verifies ability to parse layer info",function(done){ g_basemapLayer._getTileInfoPrivate("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(result){ - g_basemapLayer.parseGetTileInfo(result,function(result){ + tilesCore._parseGetTileInfo(result,function(result){ expect(result).toEqual(jasmine.any(Object)); }) }) From d752f6ca8947b1abe6fcc5bc3889824d80c07b99 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 14 Aug 2014 09:43:52 -0600 Subject: [PATCH 05/86] added additional image type detection --- lib/tpk/TPKLayer.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index 877ad1c..ba3bf96 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -107,9 +107,21 @@ define([ case "JPEG": imgURL = "data:image/jpg;base64," + result; break; + case "PNG": + imgURL = "data:image/png;base64," + result; + break; case "PNG8": imgURL = "data:image/png;base64," + result; break; + case "PNG24": + imgURL = "data:image/png;base64," + result; + break; + case "PNG32": + imgURL = "data:image/png;base64," + result; + break; + default: + imgURL = "data:image/jpg;base64," + result; + } img.style.borderColor = "blue"; } From 3a94dceb78a30ad8e584e3403c333768abd6ddea Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 14 Aug 2014 10:01:37 -0600 Subject: [PATCH 06/86] console logging of errors now works correctly --- lib/tpk/TPKLayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index ba3bf96..a43b6f3 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -305,7 +305,7 @@ define([ var reader = new FileReader(); reader.token = data.token; reader.onerror = function (event) { - console.error(new Error("_unzipTileFiles Error: " + event.target.error.code).stack); + console.error("_unzipTileFiles Error: " + JSON.stringify(event.target.error)); that.emit(that.PARSING_ERROR, {msg: "Error parsing file: ", err: event.target.error}); } reader.addEventListener("loadend", function (evt) { From 58d4c0514ed383bfa4d45e123540f14b976b26d8 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 14 Aug 2014 10:14:16 -0600 Subject: [PATCH 07/86] removed unused code --- lib/tpk/TPKLayer.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index a43b6f3..dcd9681 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -330,8 +330,6 @@ define([ * @private */ _parseConfCdi: function(callback){ - var that = this._self; - var m_conf_i = this._inMemTilesObject[this.TILE_PATH + "CONF.CDI"]; var x2js = new X2JS(); From 7bd00cc37f69f0a019bc36570454de3182f56aa4 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 14 Aug 2014 10:16:05 -0600 Subject: [PATCH 08/86] added spatialReference to the Extent --- lib/tpk/TPKLayer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index dcd9681..59f3da5 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -340,9 +340,10 @@ define([ var ymin = parseFloat(envelopeInfo.YMin); var xmax = parseFloat(envelopeInfo.XMax); var ymax = parseFloat(envelopeInfo.YMax); + var sr = parseInt(envelopeInfo.SpatialReference.WKID); var initExtent = new Extent( - xmin,ymin,xmax,ymax + xmin,ymin,xmax,ymax, new SpatialReference({wkid:sr}) ); callback(initExtent); From 48b9129aacd62fe159dd90f45174fc1c0397a532 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 14 Aug 2014 13:21:05 -0600 Subject: [PATCH 09/86] more tweaks to error messages --- lib/tpk/TPKLayer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index 59f3da5..cf342ac 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -305,7 +305,7 @@ define([ var reader = new FileReader(); reader.token = data.token; reader.onerror = function (event) { - console.error("_unzipTileFiles Error: " + JSON.stringify(event.target.error)); + console.error("_unzipTileFiles Error: " + event.target.error.message); that.emit(that.PARSING_ERROR, {msg: "Error parsing file: ", err: event.target.error}); } reader.addEventListener("loadend", function (evt) { @@ -409,10 +409,11 @@ define([ this.store.retrieve(url, function(success, offlineTile){ if( success ) { - console.log("Tile found in indexedDB: " + url) + console.log("Tile found in storage: " + url) callback(offlineTile.img,tileId,url); } else { + console.log("Tile is not in storage: " + url) var snappedRow = Math.floor(row / 128) * 128; var snappedCol = Math.floor(col / 128) * 128; From 8846bfe986446864ee40dfacfcad41c089d8f496 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 14 Aug 2014 13:49:38 -0600 Subject: [PATCH 10/86] removed unused callback value --- lib/tpk/TPKLayer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index cf342ac..ae82b1f 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -62,7 +62,7 @@ define([ this._fileEntriesLength = files.length; this.emit(this.PROGRESS_EVENT,this.PROGRESS_START); - this._parseInMemFiles(files,function (buffer){ + this._parseInMemFiles(files,function (){ //Parse conf.xml and conf.cdi to get the required setup info this._parseConfCdi(function(initExtent){ this.initialExtent = (this.fullExtent = initExtent); @@ -237,7 +237,7 @@ define([ for(var i=0;i < inMemTilesLength;i++){ - var name = files[i].filename.toLocaleUpperCase(); + var name = files[i].filename.toLocaleUpperCase(); console.log("name: " + name) var index = name.indexOf("_ALLLAYERS",0); if(index != -1){ this.TILE_PATH = name.slice(0,index); @@ -286,7 +286,7 @@ define([ this._inMemTilesObject[name]= data.string; var size = this.ObjectSize(this._inMemTilesObject); if(size == this._fileEntriesLength - this._zeroLengthFileCounter - 1){ - callback(this._inMemTilesObject); + callback(); } }.bind(this)); }, @@ -315,7 +315,7 @@ define([ that._inMemTilesObject[name]= this.result; var size = that.ObjectSize(that._inMemTilesObject); if(size == that._fileEntriesLength - that._zeroLengthFileCounter - 1){ - callback(that._inMemTilesObject); + callback(); } } }); From dacf6c3cff023f5508d09edde2e5432592abd6af Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 11:31:57 -0600 Subject: [PATCH 11/86] switched to deferred promises in _parseInMemFiles() --- lib/tpk/TPKLayer.js | 47 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index ae82b1f..cf27c45 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -11,8 +11,9 @@ define([ "dojo/_base/declare","esri/geometry/Extent","dojo/query","esri/SpatialReference", "esri/layers/TileInfo","esri/layers/TiledMapServiceLayer","tiles/TilesStore","tiles/tilingScheme", - "tpk/zip","tpk/xml2json","tpk/autoCenterMap","dojo/Evented"], - function(declare,Extent,query,SpatialReference,TileInfo,TiledMapServiceLayer,TilesStore,TilingScheme,zip,X2JS,autoCenter,Evented){ + "tpk/zip","tpk/xml2json","tpk/autoCenterMap","dojo/Deferred","dojo/promise/all","dojo/Evented"], + function(declare,Extent,query,SpatialReference,TileInfo,TiledMapServiceLayer,TilesStore, + TilingScheme,zip,X2JS,autoCenter,Deferred,all,Evented){ return declare("esri.TPKLayer",[TiledMapServiceLayer,Evented],{ // @@ -232,12 +233,16 @@ define([ * @private */ _parseInMemFiles: function(files,callback){ + var inMemTilesLength = this._fileEntriesLength; this._zeroLengthFileCounter = 0; + var promises = []; for(var i=0;i < inMemTilesLength;i++){ - var name = files[i].filename.toLocaleUpperCase(); console.log("name: " + name) + var deferred = new Deferred(); + var name = files[i].filename.toLocaleUpperCase(); + var index = name.indexOf("_ALLLAYERS",0); if(index != -1){ this.TILE_PATH = name.slice(0,index); @@ -247,13 +252,33 @@ define([ var indexCDI = name.indexOf("CONF.CDI",0); var indexXML = name.indexOf("CONF.XML",0); + var indexBUNDLE = name.indexOf("BUNDLE",0); + var indexBUNDLX = name.indexOf("BUNDLX",0); + if(indexCDI != -1 || indexXML != -1){ - this._unzipConfFiles(files,i,callback); + this._unzipConfFiles(files,i,deferred,function(/* deferred */ d, /* token */ t){ console.log("CONF FILE") + d.resolve(t); + return d.promise; + }); + } + else if(indexBUNDLE != -1 || indexBUNDLX != -1){ + this._unzipTileFiles(files,i,deferred,function(/* deferred */ d, /* token */ t){ + d.resolve(t); + return d.promise; + }); } else{ - this._unzipTileFiles(files,i,callback); + deferred.resolve(i); } + promises.push(deferred); } + + all(promises).then( function(results) + { + callback && callback(results); + }); + + return promises; }, /** @@ -279,14 +304,14 @@ define([ * @param callback * @private */ - _unzipConfFiles: function(files,token,callback){ + _unzipConfFiles: function(files,token,deferred,callback){ files[token].getData(new zip.TextWriter(token),function(data){ this._inMemTilesIndex.push("blank"); var name = files[data.token].filename.toLocaleUpperCase(); this._inMemTilesObject[name]= data.string; var size = this.ObjectSize(this._inMemTilesObject); - if(size == this._fileEntriesLength - this._zeroLengthFileCounter - 1){ - callback(); + if(size > 0){ + callback(deferred,data.token); } }.bind(this)); }, @@ -298,7 +323,7 @@ define([ * @param callback * @private */ - _unzipTileFiles: function(files,token,callback){ + _unzipTileFiles: function(files,token,deferred,callback){ var that = this; files[token].getData(new zip.BlobWriter(token),function(data){ if(data.size != 0){ @@ -314,8 +339,8 @@ define([ var name = files[this.token].filename.toLocaleUpperCase(); that._inMemTilesObject[name]= this.result; var size = that.ObjectSize(that._inMemTilesObject); - if(size == that._fileEntriesLength - that._zeroLengthFileCounter - 1){ - callback(); + if(size > 0){ + callback(deferred,data.token); } } }); From e9eb35e25ce377ac40656c3de9f353b11df74157 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 11:45:52 -0600 Subject: [PATCH 12/86] fixed minor firefox bug --- samples/tpk-layer.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/samples/tpk-layer.html b/samples/tpk-layer.html index af15ab8..cf5659f 100644 --- a/samples/tpk-layer.html +++ b/samples/tpk-layer.html @@ -192,7 +192,15 @@ xhrRequest.responseType = "blob"; xhrRequest.onprogress = function(evt){ loading.style.visibility = "visible"; - var percent = (parseFloat(evt.loaded / evt.totalSize) * 100).toFixed(0); + var percent = 0; + + if(evt && evt.totalSize){ + percent = (parseFloat(evt.loaded / evt.totalSize) * 100).toFixed(0); + } + else if(evt && evt.total){ + percent = (parseFloat(evt.loaded / evt.total) * 100).toFixed(0); + } + getFileBtn.innerHTML = "Get file via url " + percent + "%"; console.log("Begin downloading remote tpk file...") } From 81d8db882373ba31a56270e7bd6e190365ffe5dc Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 11:54:52 -0600 Subject: [PATCH 13/86] updated TPKLayer how to use doc --- doc/howtousetpklibrary.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/howtousetpklibrary.md b/doc/howtousetpklibrary.md index cf275a8..045cad0 100644 --- a/doc/howtousetpklibrary.md +++ b/doc/howtousetpklibrary.md @@ -3,7 +3,7 @@ How to use the TPKLayer library ## `TPKLayer` Library -The `TPKLayer` Library allows you to display at TPK file as a map. +The `TPKLayer` Library allows you to display at TPK file as a map. **Step 1** Unzip the TPK file. This creates an array of Entry objects. Depending on your operating system you may have to rename the TPK file to .zip so that it becomes a recognized MIME type for the html input element. @@ -84,6 +84,15 @@ When you need to delete all tiles from the existing data use the following patte }) ``` + +**Can I use the TPKLayer with a tiled basemap?** + +Yes for ArcGIS API for JavaScript v3.x and ONLY if the TPKs Levels of Detail (LODs) match the tiled map services LODs exactly. + +The basemap (base tiled layer) defines the LODs that the map can display. Any other operational tiled layers on the map will not display if they don’t match the basemap’s LODs. Esri.Map doesn’t union LODs of all tiled layers on the map. + +For more information on creating TPKs go [here](http://resources.arcgis.com/en/help/main/10.1/index.html#//006600000457000000). + **Additional Considerations** There are a few things to keep in mind when working with TPK files and JavaScript. From 69ecbc1ce697ba1b4230a8439dbb0a91946dc5ac Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 13:31:51 -0600 Subject: [PATCH 14/86] refactored getTilePolygons to TilesCore --- lib/tiles/OfflineTilesEnablerLayer.js | 24 +--------- lib/tiles/TilesCore.js | 53 +++++++++++++++++++++-- lib/tiles/TilesStore.js | 2 +- lib/tiles/offlineTilesEnabler.js | 36 +-------------- test/spec/offlineTilesEnablerLayerSpec.js | 24 ++++++++++ test/spec/offlineTilesEnablerSpec.js | 24 ++++++++++ 6 files changed, 101 insertions(+), 62 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index 7c5a057..700adab 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -355,29 +355,7 @@ define([ */ getTilePolygons : function(callback) // callback(Polygon polygon) or callback(null, error) { - var store = this.offline.store; - var tilingScheme = new TilingScheme(this); - store.getAllTiles(function(url,img,err) - { - if(url && url.indexOf(layer.url) == 0) - { - var components = url.split("/"); - var level = parseInt(components[ components.length - 3],10); - var col = parseInt(components[ components.length - 2],10); - var row = parseInt(components[ components.length - 1],10); - var cellId = [row,col]; - var polygon = tilingScheme.getCellPolygonFromCellId(cellId, level); - //if( level == 15) - callback(polygon); - } - else - { - if(!url) - { - callback(null,err); - } - } - }); + this._tilesCore._getTilePolygons(this.offline.store,this.url,this,callback); }, /** diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index 3d0de7e..e213d79 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -10,8 +10,9 @@ define([ "esri/layers/TileInfo", "esri/layers/LOD", "tiles/base64utils", + "tiles/tilingScheme" ], - function(query,Point,Extent,SpatialReference,TileInfo,LOD,Base64Utils){ + function(query,Point,Extent,SpatialReference,TileInfo,LOD,Base64Utils,TilingScheme){ "use strict"; var TilesCore = function(){ @@ -46,7 +47,53 @@ define([ callback(false, e); }; req.send(null); - } + }; + + /** + * Gets polygons representing all cached cell ids within a particular + * zoom level and bounded by an extent. + * @param store local IndexedDB + * @param layerUrl the URL of tile layer + * @param context a reference to the layer + * @param callback callback(polygon, error) + */ + this._getTilePolygons = function(store,layerUrl,context,callback) // callback(Polygon polygon) or callback(null, error) + { + var components, level, col, row, cellId, polygon; + + var tilingScheme = new TilingScheme(context); + store.getAllTiles(function(url,img,err) + { + if(url && url.indexOf(layerUrl) == 0) + { + if(url.indexOf("_alllayers") != -1) + { + // V101/LAYERS/_alllayers/L01/R0C18C0B10 + components = url.split("/"); + level = parseInt(components[ components.length - 2].slice(1),10); + col = parseInt( components[ components.length -1].substring(1,5), 16); + row = parseInt( components[ components.length -1].substring(6,10), 16); + } + else + { + components = url.split("/"); + level = parseInt(components[ components.length - 3],10); + col = parseInt(components[ components.length - 2],10); + row = parseInt(components[ components.length - 1],10); + } + cellId = [row,col]; + polygon = tilingScheme.getCellPolygonFromCellId(cellId, level); + callback(polygon); + } + else + { + if(!url) + { + callback(null,err); + } + } + }); + }; this._parseGetTileInfo = function(data,callback){ @@ -94,7 +141,7 @@ define([ callback({initExtent:initialExtent,fullExtent:fullExtent,tileInfo:tileInfo,resultObj:resultObj}); } - } + }; return TilesCore; } diff --git a/lib/tiles/TilesStore.js b/lib/tiles/TilesStore.js index cedcbf5..44f7d28 100644 --- a/lib/tiles/TilesStore.js +++ b/lib/tiles/TilesStore.js @@ -154,7 +154,7 @@ define([],function() /** * Retrieve all tiles from indexeddb - * @param callback callbakck(url, img, err) + * @param callback callback(url, img, err) */ this.getAllTiles = function(callback) { diff --git a/lib/tiles/offlineTilesEnabler.js b/lib/tiles/offlineTilesEnabler.js index f5bcaff..21be3c7 100644 --- a/lib/tiles/offlineTilesEnabler.js +++ b/lib/tiles/offlineTilesEnabler.js @@ -257,41 +257,7 @@ define([ */ layer.getTilePolygons = function(callback) // callback(Polygon polygon) or callback(null, error) { - var components, level, col, row, cellId, polygon; - - var store = this.offline.store; - var tilingScheme = new TilingScheme(this); - store.getAllTiles(function(url,img,err) - { - if(url && url.indexOf(layer.url) == 0) - { - if(url.indexOf("_alllayers") != -1) - { - // V101/LAYERS/_alllayers/L01/R0C18C0B10 - components = url.split("/"); - level = parseInt(components[ components.length - 2].slice(1),10); - col = parseInt( components[ components.length -1].substring(1,5), 16); - row = parseInt( components[ components.length -1].substring(6,10), 16); - } - else - { - components = url.split("/"); - level = parseInt(components[ components.length - 3],10); - col = parseInt(components[ components.length - 2],10); - row = parseInt(components[ components.length - 1],10); - } - cellId = [row,col]; - polygon = tilingScheme.getCellPolygonFromCellId(cellId, level); - callback(polygon); - } - else - { - if(!url) - { - callback(null,err); - } - } - }); + layer._tilesCore._getTilePolygons(this.offline.store,layer.url,this,callback); }; /** diff --git a/test/spec/offlineTilesEnablerLayerSpec.js b/test/spec/offlineTilesEnablerLayerSpec.js index 16328c7..bbc6718 100644 --- a/test/spec/offlineTilesEnablerLayerSpec.js +++ b/test/spec/offlineTilesEnablerLayerSpec.js @@ -242,4 +242,28 @@ describe("offline enabler custom layer library", function() }) }); + async.it("get all tile polygons within extent",function(done){ + require(["dojo/Deferred","dojo/promise/all",],function(Deferred,all){ + + var promises = []; + + g_basemapLayer.getTilePolygons(function(result,err){ + + var deferred = new Deferred(); + if(result && result.type){ + console.log("Tile polygon: " + result); + expect(result.type).toEqual("polygon"); + } + deferred.resolve(result); + promises.push(deferred); + }) + + all(promises).then( function(results) + { + done(); + }); + + }) + }); + }); diff --git a/test/spec/offlineTilesEnablerSpec.js b/test/spec/offlineTilesEnablerSpec.js index 9f399be..4e22963 100644 --- a/test/spec/offlineTilesEnablerSpec.js +++ b/test/spec/offlineTilesEnablerSpec.js @@ -211,4 +211,28 @@ describe("offline enabler library", function() }) }); + async.it("get all tile polygons within extent",function(done){ + require(["dojo/Deferred","dojo/promise/all",],function(Deferred,all){ + + var promises = []; + + g_basemapLayer.getTilePolygons(function(result,err){ + + var deferred = new Deferred(); + if(result && result.type){ + console.log("Tile polygon: " + result); + expect(result.type).toEqual("polygon"); + } + deferred.resolve(result); + promises.push(deferred); + }) + + all(promises).then( function(results) + { + done(); + }); + + }) + }); + }); \ No newline at end of file From 0f834f9cfddaccfda3a4b96b9810473b45ebe74c Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 14:01:37 -0600 Subject: [PATCH 15/86] refactored loadFromFile() into TilesCore --- lib/tiles/OfflineTilesEnablerLayer.js | 53 +------------------- lib/tiles/TilesCore.js | 59 +++++++++++++++++++++++ lib/tiles/offlineTilesEnabler.js | 53 +------------------- test/spec/offlineTilesEnablerLayerSpec.js | 11 +++++ test/spec/offlineTilesEnablerSpec.js | 11 +++++ 5 files changed, 83 insertions(+), 104 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index 700adab..bd5be75 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -407,58 +407,7 @@ define([ loadFromFile : function(file, callback) // callback(success,msg) { console.log("reading",file); - - var store = this.offline.store; - var i; - - if (window.File && window.FileReader && window.FileList && window.Blob) - { - // Great success! All the File APIs are supported. - var reader = new FileReader(); - reader.onload = function(evt) - { - var csvContent = evt.target.result; - var tiles = csvContent.split("\r\n"); - var tileCount = 0; - var pair, tile; - - if(tiles[0] !== "url,img") - { - return callback(false, "File " + file.name + " doesn't contain tiles that can be loaded"); - } - - for(i=1; i Date: Fri, 15 Aug 2014 14:10:45 -0600 Subject: [PATCH 16/86] refactored estimateTileSize() into TilesCore --- lib/tiles/OfflineTilesEnablerLayer.js | 21 +---------------- lib/tiles/TilesCore.js | 34 ++++++++++++++++++++++++++- lib/tiles/offlineTilesEnabler.js | 21 +---------------- 3 files changed, 35 insertions(+), 41 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index bd5be75..1771c41 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -419,26 +419,7 @@ define([ */ estimateTileSize : function(callback) { - if(this._lastTileUrl) - { - var url = this.offline.proxyPath? this.offline.proxyPath + "?" + this._lastTileUrl : this._lastTileUrl; - request.get(url,{ - handleAs: "text/plain; charset=x-user-defined", - headers: { - "X-Requested-With": "" //bypasses a dojo xhr bug - }, - timeout: 2000 - }).then(function(response){ - var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(response)); - callback(img.length + url.length,null); - }, - function(err){ - callback(null,err); - }); - } - else{ - callback(NaN); - } + this._tilesCore._estimateTileSize(this._lastTileUrl,this.offline.proxyPath,callback); }, /** diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index 5920728..22f31df 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -4,6 +4,7 @@ */ define([ "dojo/query", + "dojo/request", "esri/geometry/Point", "esri/geometry/Extent", "esri/SpatialReference", @@ -12,7 +13,7 @@ define([ "tiles/base64utils", "tiles/tilingScheme" ], - function(query,Point,Extent,SpatialReference,TileInfo,LOD,Base64Utils,TilingScheme){ + function(query,request,Point,Extent,SpatialReference,TileInfo,LOD,Base64Utils,TilingScheme){ "use strict"; var TilesCore = function(){ @@ -49,6 +50,37 @@ define([ req.send(null); }; + /** + * Makes a request to a tile url and uses that as a basis for the + * the average tile size. + * Future Iterations could call multiple tiles and do an actual average. + * @param callback + * @returns {Number} Returns NaN if there was a problem retrieving the tile + */ + this._estimateTileSize = function(lastTileUrl,proxyPath,callback) + { + if(lastTileUrl) + { + var url = proxyPath? proxyPath + "?" + lastTileUrl : lastTileUrl; + request.get(url,{ + handleAs: "text/plain; charset=x-user-defined", + headers: { + "X-Requested-With": "" //bypasses a dojo xhr bug + }, + timeout: 2000 + }).then(function(response){ + var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(response)); + callback(img.length + url.length,null); + }, + function(err){ + callback(null,err); + }); + } + else{ + callback(NaN); + } + }; + /** * Loads a csv file into storage. * Format is "url,img\r\n somebase64image,http://esri.com" diff --git a/lib/tiles/offlineTilesEnabler.js b/lib/tiles/offlineTilesEnabler.js index 5389766..ceb9761 100644 --- a/lib/tiles/offlineTilesEnabler.js +++ b/lib/tiles/offlineTilesEnabler.js @@ -323,26 +323,7 @@ define([ */ layer.estimateTileSize = function(callback) { - if(layer._lastTileUrl) - { - var url = this.offline.proxyPath? this.offline.proxyPath + "?" + layer._lastTileUrl : layer._lastTileUrl; - request.get(url,{ - handleAs: "text/plain; charset=x-user-defined", - headers: { - "X-Requested-With": "" //bypasses a dojo xhr bug - }, - timeout: 2000 - }).then(function(response){ - var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(response)); - callback(img.length + url.length,null); - }, - function(err){ - callback(null,err); - }); - } - else{ - callback(NaN); - } + layer._tilesCore._estimateTileSize(this._lastTileUrl,this.offline.proxyPath,callback); }; /** From 32e9266e633956ffde866188d2c111575ddd08f2 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 14:21:53 -0600 Subject: [PATCH 17/86] refactored saveToFile() to TilesCore --- lib/tiles/OfflineTilesEnablerLayer.js | 33 +--------------- lib/tiles/TilesCore.js | 46 ++++++++++++++++++++++- lib/tiles/offlineTilesEnabler.js | 33 +--------------- test/spec/offlineTilesEnablerLayerSpec.js | 7 ++++ test/spec/offlineTilesEnablerSpec.js | 7 ++++ 5 files changed, 60 insertions(+), 66 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index 1771c41..db2afdd 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -365,38 +365,7 @@ define([ */ saveToFile : function(fileName, callback) // callback(success, msg) { - var store = this.offline.store; - var csv = []; - - csv.push("url,img"); - store.getAllTiles(function(url,img,evt) - { - if(evt==="end") - { - var blob = new Blob([ csv.join("\r\n") ], {type:"text/plain;charset=utf-8"}); - var saver = FileSaver.saveAs(blob, fileName); - - if( saver.readyState === saver.DONE ) - { - if( saver.error ) - { - return callback(false,"Error saving file " + fileName); - } - return callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); - } - saver.onerror = function() { - callback(false,"Error saving file " + fileName); - }; - saver.onwriteend = function() - { - callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); - }; - } - else - { - csv.push(url+","+img); - } - }); + this._tilesCore._saveToFile(fileName,this.offline.store,callback); }, /** diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index 22f31df..d26c945 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -11,9 +11,10 @@ define([ "esri/layers/TileInfo", "esri/layers/LOD", "tiles/base64utils", - "tiles/tilingScheme" + "tiles/tilingScheme", + "tiles/FileSaver" ], - function(query,request,Point,Extent,SpatialReference,TileInfo,LOD,Base64Utils,TilingScheme){ + function(query,request,Point,Extent,SpatialReference,TileInfo,LOD,Base64Utils,TilingScheme,FileSaver){ "use strict"; var TilesCore = function(){ @@ -50,6 +51,47 @@ define([ req.send(null); }; + /** + * Saves locally stored tiles to a csv + * @param fileName + * @param store + * @param callback + * @private + */ + this._saveToFile = function(fileName,store,callback){ + var csv = []; + + csv.push("url,img"); + store.getAllTiles(function(url,img,evt) + { + if(evt==="end") + { + var blob = new Blob([ csv.join("\r\n") ], {type:"text/plain;charset=utf-8"}); + var saver = FileSaver.saveAs(blob, fileName); + + if( saver.readyState === saver.DONE ) + { + if( saver.error ) + { + return callback(false,"Error saving file " + fileName); + } + return callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); + } + saver.onerror = function() { + callback(false,"Error saving file " + fileName); + }; + saver.onwriteend = function() + { + callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); + }; + } + else + { + csv.push(url+","+img); + } + }); + }; + /** * Makes a request to a tile url and uses that as a basis for the * the average tile size. diff --git a/lib/tiles/offlineTilesEnabler.js b/lib/tiles/offlineTilesEnabler.js index ceb9761..593d4cc 100644 --- a/lib/tiles/offlineTilesEnabler.js +++ b/lib/tiles/offlineTilesEnabler.js @@ -267,38 +267,7 @@ define([ */ layer.saveToFile = function(fileName, callback) // callback(success, msg) { - var store = this.offline.store; - var csv = []; - - csv.push("url,img"); - store.getAllTiles(function(url,img,evt) - { - if(evt==="end") - { - var blob = new Blob([ csv.join("\r\n") ], {type:"text/plain;charset=utf-8"}); - var saver = FileSaver.saveAs(blob, fileName); - - if( saver.readyState === saver.DONE ) - { - if( saver.error ) - { - return callback(false,"Error saving file " + fileName); - } - return callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); - } - saver.onerror = function() { - callback(false,"Error saving file " + fileName); - }; - saver.onwriteend = function() - { - callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); - }; - } - else - { - csv.push(url+","+img); - } - }); + layer._tilesCore._saveToFile(fileName,this.offline.store,callback); }; /** diff --git a/test/spec/offlineTilesEnablerLayerSpec.js b/test/spec/offlineTilesEnablerLayerSpec.js index 9480351..0ff1223 100644 --- a/test/spec/offlineTilesEnablerLayerSpec.js +++ b/test/spec/offlineTilesEnablerLayerSpec.js @@ -277,4 +277,11 @@ describe("offline enabler custom layer library", function() }) }); + async.it("save tiles to csv",function(done){ + g_basemapLayer.saveToFile("testSaveToCSV",function(success,result){ + expect(success).toBe(true); + done(); + }) + }); + }); diff --git a/test/spec/offlineTilesEnablerSpec.js b/test/spec/offlineTilesEnablerSpec.js index 6be290d..3094ba7 100644 --- a/test/spec/offlineTilesEnablerSpec.js +++ b/test/spec/offlineTilesEnablerSpec.js @@ -246,4 +246,11 @@ describe("offline enabler library", function() }) }); + async.it("save tiles to csv",function(done){ + g_basemapLayer.saveToFile("testSaveToCSV",function(success,result){ + expect(success).toBe(true); + done(); + }) + }); + }); \ No newline at end of file From 1b493cbe4675d75ad833c99cba7a777ded22cf50 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 14:46:29 -0600 Subject: [PATCH 18/86] refactored prepareForOffline() to TilesCore --- lib/tiles/OfflineTilesEnablerLayer.js | 28 ++++----------------------- lib/tiles/TilesCore.js | 23 ++++++++++++++++++++++ lib/tiles/offlineTilesEnabler.js | 28 ++++----------------------- 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index db2afdd..d658d47 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -266,30 +266,10 @@ define([ */ prepareForOffline : function(minLevel, maxLevel, extent, reportProgress) { - /* create list of tiles to store */ - var tilingScheme = new TilingScheme(this); - var cells = []; - var level; - - for(level=minLevel; level<=maxLevel; level++) - { - var level_cell_ids = tilingScheme.getAllCellIdsInExtent(extent,level); - - level_cell_ids.forEach(function(cell_id) - { - cells.push({ level: level, row: cell_id[1], col: cell_id[0]}); - }); - - // if the number of requested tiles is excessive, we just stop - if( cells.length > 5000 && level !== maxLevel) - { - console.log("enough is enough!"); - break; - } - } - - /* launch tile download */ - this._doNextTile(0, cells, reportProgress); + this._tilesCore._createCellsForOffline(this,minLevel,maxLevel,extent,function(cells){ + /* launch tile download */ + this._doNextTile(0, cells, reportProgress); + }.bind(this)); }, /** diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index d26c945..9554830 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -51,6 +51,29 @@ define([ req.send(null); }; + this._createCellsForOffline = function(context,minLevel,maxLevel,extent,callback){ + var tilingScheme = new TilingScheme(context); + var cells = []; + + for(var level=minLevel; level<=maxLevel; level++) + { + var level_cell_ids = tilingScheme.getAllCellIdsInExtent(extent,level); + + level_cell_ids.forEach(function(cell_id) + { + cells.push({ level: level, row: cell_id[1], col: cell_id[0]}); + }); + + // if the number of requested tiles is excessive, we just stop + if( cells.length > 5000 && level !== maxLevel) + { + console.log("enough is enough!"); + break; + } + } + callback(cells); + } + /** * Saves locally stored tiles to a csv * @param fileName diff --git a/lib/tiles/offlineTilesEnabler.js b/lib/tiles/offlineTilesEnabler.js index 593d4cc..aada740 100644 --- a/lib/tiles/offlineTilesEnabler.js +++ b/lib/tiles/offlineTilesEnabler.js @@ -168,30 +168,10 @@ define([ */ layer.prepareForOffline = function(minLevel, maxLevel, extent, reportProgress) { - /* create list of tiles to store */ - var tilingScheme = new TilingScheme(this); - var cells = []; - var level; - - for(level=minLevel; level<=maxLevel; level++) - { - var level_cell_ids = tilingScheme.getAllCellIdsInExtent(extent,level); - - level_cell_ids.forEach(function(cell_id) - { - cells.push({ level: level, row: cell_id[1], col: cell_id[0]}); - }); - - // if the number of requested tiles is excessive, we just stop - if( cells.length > 5000 && level !== maxLevel) - { - console.log("enough is enough!"); - break; - } - } - - /* launch tile download */ - this._doNextTile(0, cells, reportProgress); + layer._tilesCore._createCellsForOffline(this,minLevel,maxLevel,extent,function(cells){ + /* launch tile download */ + this._doNextTile(0, cells, reportProgress); + }.bind(this)); }; /** From 1649391f1369fa1c2101a645cbaf65d9dc2de614 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 15:51:09 -0600 Subject: [PATCH 19/86] refactored _getTilesUrl() to TilesCore --- lib/tiles/OfflineTilesEnablerLayer.js | 30 ++------------------------- lib/tiles/TilesCore.js | 30 +++++++++++++++++++++++++++ lib/tiles/offlineTilesEnabler.js | 28 ++----------------------- 3 files changed, 34 insertions(+), 54 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index d658d47..78a44a3 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -133,34 +133,8 @@ define([ /* temporary URL returned immediately, as we haven't retrieved the image from the indexeddb yet */ var tileid = "void:/"+level+"/"+row+"/"+col; - - this.offline.store.retrieve(url, function(success, offlineTile) - { console.log("TILE RETURN " + success + ", " + offlineTile) - /* when the .getTileUrl() callback is triggered we replace the temporary URL originally returned by the data:image url */ - // search for the img with src="void:"+level+"-"+row+"-"+col and replace with actual url - var img = query("img[src="+tileid+"]")[0]; - var imgURL; - - console.assert(img !== "undefined", "undefined image detected"); - - if( success ) - { - img.style.borderColor = "blue"; - console.log("found tile offline", url); - imgURL = "data:image/" + this._imageType +";base64," + offlineTile.img; - } - else - { - img.style.borderColor = "green"; - console.log("tile is not in the offline store", url); - imgURL = "data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="; - } - // when we return a nonexistent url to the image, the TiledMapServiceLayer::_tileErrorHandler() method - // 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; - return ""; /* this result goes nowhere, seriously */ - }.bind(this)); + var img = null; + this._tilesCore._getTiles(img,this._imageType,url,tileid,this.offline.store); return tileid; }, diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index 9554830..7b2f61a 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -18,6 +18,36 @@ define([ "use strict"; var TilesCore = function(){ + this._getTiles = function(image,imageType,url,tileid,store){ + store.retrieve(url, function(success, offlineTile) + { console.log("TILE RETURN " + success + ", " + offlineTile) + /* when the .getTileUrl() callback is triggered we replace the temporary URL originally returned by the data:image url */ + // search for the img with src="void:"+level+"-"+row+"-"+col and replace with actual url + image = query("img[src="+tileid+"]")[0]; + var imgURL; + + console.assert(image !== "undefined", "undefined image detected"); + + if( success ) + { + image.style.borderColor = "blue"; + console.log("found tile offline", url); + imgURL = "data:image/" + imageType +";base64," + offlineTile.img; + } + else + { + image.style.borderColor = "green"; + console.log("tile is not in the offline store", url); + imgURL = "data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="; + } + // when we return a nonexistent url to the image, the TiledMapServiceLayer::_tileErrorHandler() method + // sets img visibility to 'hidden', so we need to show the image back once we have put the data:image + image.style.visibility = "visible"; + image.src = imgURL; + return ""; /* this result goes nowhere, seriously */ + }); + }; + this._storeTile= function(url,proxyPath,store,callback) // callback(success, msg) { url = url.split("?")[0]; diff --git a/lib/tiles/offlineTilesEnabler.js b/lib/tiles/offlineTilesEnabler.js index aada740..cba052b 100644 --- a/lib/tiles/offlineTilesEnabler.js +++ b/lib/tiles/offlineTilesEnabler.js @@ -95,33 +95,9 @@ define([ /* temporary URL returned immediately, as we haven't retrieved the image from the indexeddb yet */ var tileid = "void:/"+level+"/"+row+"/"+col; - this.offline.store.retrieve(url, function(success, offlineTile) - { console.log("TILE RETURN " + success + ", " + offlineTile) - /* when the .get() callback is called we replace the temporary URL originally returned by the data:image url */ - // search for the img with src="void:"+level+"-"+row+"-"+col and replace with actual url - var img = query("img[src="+tileid+"]")[0]; - var imgURL; + var img = null;; - console.assert(img !== "undefined", "undefined image detected"); - - if( success ) - { - img.style.borderColor = "blue"; - console.log("found tile offline", url); - imgURL = "data:image/" + layer._imageType +";base64," + offlineTile.img; - } - else - { - img.style.borderColor = "green"; - console.log("tile is not in the offline store", url); - imgURL = "data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="; - } - // when we return a nonexistent url to the image, the TiledMapServiceLayer::_tileErrorHandler() method - // 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; - return ""; /* this result goes nowhere, seriously */ - }); + layer._tilesCore._getTiles(img,this._imageType,url,tileid,this.offline.store); return tileid; }; From 15afc2989ff2886c0f6a014009aedcd1ae490e15 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 15 Aug 2014 16:00:57 -0600 Subject: [PATCH 20/86] added code comments --- lib/tiles/TilesCore.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index 7b2f61a..c33dfd8 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -18,6 +18,15 @@ define([ "use strict"; var TilesCore = function(){ + /** + * Retrieves a tile from local store. + * @param image a holder for the image that is retrieved from storage. + * @param imageType + * @param url the url of the tile + * @param tileid a reference to the tile's unique level, row and column + * @param store + * @private + */ this._getTiles = function(image,imageType,url,tileid,store){ store.retrieve(url, function(success, offlineTile) { console.log("TILE RETURN " + success + ", " + offlineTile) @@ -48,6 +57,14 @@ define([ }); }; + /** + * Retrieves an image from a tile url and then stores it locally. + * @param url The image's url + * @param proxyPath + * @param store + * @param callback + * @private + */ this._storeTile= function(url,proxyPath,store,callback) // callback(success, msg) { url = url.split("?")[0]; @@ -81,6 +98,15 @@ define([ req.send(null); }; + /** + * Retrieves all the cells within a certain extent + * @param context Layer + * @param minLevel minimum zoom level + * @param maxLevel maximum zoom level + * @param extent Esri.Extent + * @param callback + * @private + */ this._createCellsForOffline = function(context,minLevel,maxLevel,extent,callback){ var tilingScheme = new TilingScheme(context); var cells = []; @@ -281,6 +307,12 @@ define([ }); }; + /** + * Gets all the important bits out of the map services description page + * @param data The http response via f=pjson + * @param callback callback({initExtent,fullExtent,tileInfo,resultObj}); + * @private + */ this._parseGetTileInfo = function(data,callback){ var fixedResponse = data.replace(/\\'/g, "'"); From a65c7bdc583c1b1a14f994a566e6d726f7acf0ba Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 20 Aug 2014 12:24:16 -0600 Subject: [PATCH 21/86] fixed console.log messages --- lib/tpk/TPKLayer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index cf27c45..8607981 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -190,7 +190,7 @@ define([ _validate: function(){ //Verify if basic functionality is supported by the browser if(!window.File && !window.FileReader && !window.Blob && !window.btoa && !window.DataView){ - console.error(new Error( "This library is not supported on your browser!").stack); + console.log("TPKLayer library is not supported by this browser"); this.emit(this.VALIDATION_EVENT,{msg:this.NO_SUPPORT_ERROR, err : null}); } else{ @@ -202,14 +202,14 @@ define([ { this.store.init(function(result){ if(result == false){ - console.error(new Error( "There was an error initializing the database.").stack); + console.log("There was an error initializing the TPKLayer database"); this.emit(this.DATABASE_ERROR_EVENT,{msg:this.DB_INIT_ERROR, err: null}); } else{ this.store.usedSpace(function(size,err){ var mb = this._bytes2MBs(size.sizeBytes); if(mb > this.MAX_DB_SIZE){ - console.error(new Error( "Database is full!").stack); + console.log("Database is full!"); this.emit(this.DATABASE_ERROR_EVENT,{msg:this.DB_FULL_ERROR,err : err}); } this.emit(this.VALIDATION_EVENT,{msg:this.DB_VALIDATED,err : null}) @@ -220,7 +220,7 @@ define([ } else { - console.error(new Error( "IndexedDB is not supported on your browser.").stack); + console.log("IndexedDB is not supported on your browser."); this.emit(this.VALIDATION_EVENT,{msg:this.NO_SUPPORT_ERROR, err : null}); } }, @@ -456,7 +456,7 @@ define([ that._buffer2Base64(bufferI,pointer,function(result){ if (that._isDBWriteable)that._storeTile(url, result, db,function(success,err){ if(err){ - console.error(new Error( "Error writing to database." + err).stack); + console.log("TPKLayer - Error writing to database." + err.message); that.emit(that.DATABASE_ERROR_EVENT,{msg:"Error writing to database. ", err : err}); } }); From be060930621d20e7049ed318eecdcc0a6d6a1492 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 20 Aug 2014 14:26:46 -0600 Subject: [PATCH 22/86] removed comments from jshintrc. Linter now works --- .jshintrc | 145 +++++++++++++++++++++++------------------------------- 1 file changed, 62 insertions(+), 83 deletions(-) diff --git a/.jshintrc b/.jshintrc index fdba1a1..68314df 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,97 +1,76 @@ { - // JSHint Configuration, esri jsapi - // Modified from [jshint web defaults][1]. - // Differences between the default and our file are noted - // Options that are commented out completely are uneccesary or problematic for our codebase - // [1] : https://github.com/jshint/jshint/blob/2.x/examples/.jshintrc - // See http://jshint.com/docs/ for more details - "maxerr" : 5000, // {int} Maximum error before stopping ** Get ALL the errors ** - // Enforcing - true = enforce this rule, false = don't enforce this rule - "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) - "camelcase" : false, // true: Identifiers must be in camelCase - "curly" : true, // true: Require {} for every new block or scope - "eqeqeq" : false, // true: Require triple equals (===) for comparison ** Just use triples with undefined, null, false, 0 and 1 ** - "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() ** Still needed until IE8 support is dropped ** - "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` ** Avoids confusion and minification errors ** - "latedef" : false, // true: Require variables/functions to be defined before being used - "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` ** Coding style enforcement ** - "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` - "noempty" : true, // true: Prohibit use of empty blocks - "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment) ** Coding style enforcement ** - "plusplus" : false, // true: Prohibit use of `++` & `--` - "quotmark" : true, // Quotation mark consistency: ** Use the same style. Doubles should be used it most cases ** - // false : do nothing (default) - // true : ensure whatever is used is consistent - // "single" : require single quotes - // "double" : require double quotes - "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) - "unused" : "vars", // true: Require all defined variables be used - "strict" : false, // true: Requires all functions run in ES5 Strict Mode ** Dojo style and existing codebase conflicts ** - "trailing" : false, // true: Prohibit trailing whitespaces - //"indent" : 4, // {int} Number of spaces to use for indentation - //"maxparams" : false, // {int} Max number of formal params allowed per function - //"maxdepth" : false, // {int} Max depth of nested blocks (within functions) - //"maxstatements" : false, // {int} Max number statements per function - //"maxcomplexity" : false, // {int} Max cyclomatic complexity per function - //"maxlen" : false, // {int} Max number of characters per line + "maxerr" : 5000, + "bitwise" : true, + "camelcase" : false, + "curly" : true, + "eqeqeq" : false, + "forin" : true, + "immed" : true, + "latedef" : false, + "newcap" : true, + "noarg" : true, + "noempty" : true, + "nonew" : true, + "plusplus" : false, + "quotmark" : true, - // Relaxing - false = continue to enforce this rule, true = don't enforce this rule (relax it) - "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) - "boss" : false, // true: Tolerate assignments where comparisons would be expected - "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. - "eqnull" : true, // true: Tolerate use of `== null` - "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) - "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) - "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) - // (ex: `for each`, multiple try/catch, function expression…) - "evil" : false, // true: Tolerate use of `eval` and `new Function()` - "expr" : false, // true: Tolerate `ExpressionStatement` as Programs - "funcscope" : true, // true: Tolerate defining variables inside control statements ** Other variable checks keep use from abusing this ** - "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') - "iterator" : false, // true: Tolerate using the `__iterator__` property - "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block - "laxbreak" : false, // true: Tolerate possibly unsafe line breakings - "laxcomma" : false, // true: Tolerate comma-first style coding - "loopfunc" : true, // true: Tolerate functions being defined in loops ** Almost required in some callback & promise style code ** - "multistr" : false, // true: Tolerate multi-line strings - "proto" : false, // true: Tolerate using the `__proto__` property - "scripturl" : true, // true: Tolerate script-targeted URLs ** If this is being used, there is probably a good reason ** - "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment - "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` - "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation - "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` - "validthis" : true, // true: Tolerate using this in a non-constructor function ** We don't run in `strict mode` & coding style conflicts ** - // Environments - "browser" : true, // Web Browser (window, document, etc) - "devel" : true, // Development/debugging (alert, confirm, etc) - "couch" : false, // CouchDB - "dojo" : false, // Dojo Toolkit ** Don't use global dojo objects. Use AMD style coding ** - "jquery" : false, // jQuery - "mootools" : false, // MooTools - "node" : false, // Node.js - "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) - "prototypejs" : false, // Prototype and Scriptaculous - "rhino" : false, // Rhino - "worker" : false, // Web Workers ** Make a jshint comment when this is `true` ** - "wsh" : false, // Windows Scripting Host - "yui" : false, // Yahoo User Interface - // Legacy ** According to jshint docs, these options are NOT to be used or relied on. Removing them. - //"nomen" : false, // true: Prohibit dangling `_` in variables - //"onevar" : false, // true: Allow only one `var` statement per function - //"passfail" : false, // true: Stop on first error - //"white" : false, // true: Check against strict whitespace and indentation rules - // Custom Globals - additional predefined global variables - // Using both `predef` and `globals` to support tools with older jshint parsers + "undef" : true, + "unused" : "vars", + "strict" : false, + "trailing" : false, + + + "asi" : false, + "boss" : false, + "debug" : false, + "eqnull" : true, + "es5" : false, + "esnext" : false, + "moz" : false, + + "evil" : false, + "expr" : false, + "funcscope" : true, + "globalstrict" : false, + "iterator" : false, + "lastsemic" : false, + "laxbreak" : false, + "laxcomma" : false, + "loopfunc" : true, + "multistr" : false, + "proto" : false, + "scripturl" : true, + "smarttabs" : false, + "shadow" : false, + "sub" : false, + "supernew" : false, + "validthis" : true, + + + "browser" : true, + "devel" : true, + "couch" : false, + "dojo" : false, + "jquery" : false, + "mootools" : false, + "node" : false, + "nonstandard" : false, + "prototypejs" : false, + "rhino" : false, + "worker" : false, + "wsh" : false, + "yui" : false, + "predef" : [ "define", "require" ], - "globals" : { // ** `false` = don't allow variable to be redefined locally + "globals" : { "define": false, "require": false } From da8e4990a511f69712367418a08c4e5b3f966a3f Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 20 Aug 2014 14:27:31 -0600 Subject: [PATCH 23/86] initial commit grunt and package.json --- Gruntfile.js | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 20 ++++++++++ 2 files changed, 130 insertions(+) create mode 100644 Gruntfile.js create mode 100644 package.json diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..c248392 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,110 @@ + + +module.exports = function(grunt) { + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + watch: { + js: { + files: [ + 'Gruntfile.js', + 'lib/*.js', + 'lib/edit/*.js', + 'lib/tiles/*.js', + 'lib/tpk/*.js' + ], + + tasks: ['concat', 'uglify'], + options: { + spawn: false + } + } + }, + jshint: { + options: { + jshintrc: '.jshintrc' + }, + all: { + src: [ + 'Gruntfile.js', + 'lib/*js', + 'lib/edit/*.js', + 'lib/tiles/*.js', + 'lib/tpk/*.js' + ] + } + }, + concat: { + options: { + separator: '\n', + banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>\n' + + '* Copyright (c) <%= grunt.template.today("yyyy") %> Environmental Systems Research Institute, Inc.\n' + + '* Apache License' + + '*/\n' + }, + full: { + src: [ + 'lib/*.js', + 'lib/edit/*.js', + 'lib/tiles/*.js', + 'lib/tpk/*.js' + ], + dest: 'dist/offline-editor-src.js' + }, + edit: { + src: [ + 'lib/*.js', + 'lib/edit/*.js' + ], + dest: 'dist/offline-editor-edit-src.js' + }, + tiles: { + src: [ + 'lib/*.js', + 'lib/tiles/*.js' + ], + dest: 'dist/offline-editor-tiles-src.js' + }, + tpk: { + src: [ + 'lib/tpk/TPKLayer.js', + 'lib/OfflineMaps.js', + 'lib/tpk/zip.js', + 'lib/tpk/autoCenterMap.js', + 'lib/tpk/inflate.js', + 'lib/tpk/xml2json.js' + ], + dest: 'dist/offline-editor-tpk-src.js' + } + }, + + uglify: { + options: { + compress: { + drop_console: true //remove console.log statements :) + }, + wrap: false +// mangle: { +// except: ['O'] +// } + }, + dist: { + files: { + 'dist/offline-editor.js': ['dist/offline-editor-src.js'], + 'dist/offline-editor-edit.js': ['dist/offline-editor-edit-src.js'], + 'dist/offline-editor-tiles.js': ['dist/offline-editor-tiles-src.js'], + 'dist/offline-editor-tpk.js': ['dist/offline-editor-tpk-src.js'] + } + } + } + }); + + // Load required modules + grunt.loadNpmTasks('grunt-contrib-concat'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-compress'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + + grunt.registerTask('build',['concat','uglify']); + grunt.registerTask('buildAll',['jshint','concat','uglify']); +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..2d1ec4b --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "offline-editor", + "version": "0.0.1", + "description": "Lighweight set of libraries for working offline with map tiles and ArcGIS feature services", + "author": "Andy Gup (http://blog.andygup.net)", + "license": "Apache 2", + "contributors": [ + "Javier Abadia " + ], + "readmeFilename": "README.md", + "dependencies": {}, + "devDependencies": { + "grunt": "~0.4.4", + "grunt-contrib-concat":"^0.3.0", + "grunt-contrib-uglify": "^0.2.0", + "grunt-contrib-compress": "^v0.10.0", + "grunt-contrib-watch": "^0.6.1", + "grunt-contrib-jshint": "^0.4.3" + } +} From 446df80a343266ff9d1528b01d5bcb65f130d84b Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 20 Aug 2014 14:47:43 -0600 Subject: [PATCH 24/86] initial commit TPKLayer. Works. --- lib/OfflineMaps.js | 14 + lib/tpk/TPKLayer.js | 24 +- lib/tpk/autoCenterMap.js | 208 +++++---- lib/tpk/inflate.js | 19 +- lib/tpk/xml2json.js | 940 +++++++++++++++++++-------------------- lib/tpk/zip.js | 141 +++--- samples/tpk-layer.html | 21 +- 7 files changed, 701 insertions(+), 666 deletions(-) create mode 100644 lib/OfflineMaps.js diff --git a/lib/OfflineMaps.js b/lib/OfflineMaps.js new file mode 100644 index 0000000..bebe27e --- /dev/null +++ b/lib/OfflineMaps.js @@ -0,0 +1,14 @@ +var O = {}; + +(function(){ + + O.esri = { + VERSION: '2.0', + Edit: {}, + Tiles: {}, + TPK: {}, + Utils: {} + } +}()) + +"use strict"; \ No newline at end of file diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index 8607981..2910f69 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -11,9 +11,9 @@ define([ "dojo/_base/declare","esri/geometry/Extent","dojo/query","esri/SpatialReference", "esri/layers/TileInfo","esri/layers/TiledMapServiceLayer","tiles/TilesStore","tiles/tilingScheme", - "tpk/zip","tpk/xml2json","tpk/autoCenterMap","dojo/Deferred","dojo/promise/all","dojo/Evented"], + "dojo/Deferred","dojo/promise/all","dojo/Evented"], function(declare,Extent,query,SpatialReference,TileInfo,TiledMapServiceLayer,TilesStore, - TilingScheme,zip,X2JS,autoCenter,Deferred,all,Evented){ + TilingScheme,Deferred,all,Evented){ return declare("esri.TPKLayer",[TiledMapServiceLayer,Evented],{ // @@ -95,7 +95,8 @@ define([ var tileid = "void:/" + level + "/" + row + "/" + col; if(this.map == null) this.map = this.getMap(); - if(this._autoCenter == null) this._autoCenter = new autoCenter(this.map,this.RECENTER_DELAY); + if(this._autoCenter == null) this._autoCenter = new O.esri.TPK.autoCenterMap(this.map,this.RECENTER_DELAY); + this._autoCenter.init(); this._getInMemTiles(url,layersDir, level, row, col,tileid,function (result,tileid,url) { var img = query("img[src=" + tileid + "]")[0]; @@ -104,21 +105,22 @@ define([ if (result) { console.log("found tile offline", url); + var png = "data:image/png;base64,"; switch(this.tileInfo.format) { case "JPEG": imgURL = "data:image/jpg;base64," + result; break; case "PNG": - imgURL = "data:image/png;base64," + result; + imgURL = png + result; break; case "PNG8": - imgURL = "data:image/png;base64," + result; + imgURL = png + result; break; case "PNG24": - imgURL = "data:image/png;base64," + result; + imgURL = png + result; break; case "PNG32": - imgURL = "data:image/png;base64," + result; + imgURL = png + result; break; default: imgURL = "data:image/jpg;base64," + result; @@ -305,7 +307,7 @@ define([ * @private */ _unzipConfFiles: function(files,token,deferred,callback){ - files[token].getData(new zip.TextWriter(token),function(data){ + files[token].getData(new O.esri.zip.TextWriter(token),function(data){ this._inMemTilesIndex.push("blank"); var name = files[data.token].filename.toLocaleUpperCase(); this._inMemTilesObject[name]= data.string; @@ -325,7 +327,7 @@ define([ */ _unzipTileFiles: function(files,token,deferred,callback){ var that = this; - files[token].getData(new zip.BlobWriter(token),function(data){ + files[token].getData(new O.esri.zip.BlobWriter(token),function(data){ if(data.size != 0){ var reader = new FileReader(); reader.token = data.token; @@ -357,7 +359,7 @@ define([ _parseConfCdi: function(callback){ var m_conf_i = this._inMemTilesObject[this.TILE_PATH + "CONF.CDI"]; - var x2js = new X2JS(); + var x2js = new O.esri.TPK.X2JS(); var jsonObj = x2js.xml_str2json( m_conf_i ); var envelopeInfo = jsonObj.EnvelopeN; @@ -382,7 +384,7 @@ define([ _parseConfXml:function(callback) { var m_conf = this._inMemTilesObject[this.TILE_PATH + "CONF.XML"]; - var x2js = new X2JS(); + var x2js = new O.esri.TPK.X2JS(); var jsonObj = x2js.xml_str2json(m_conf); var cacheInfo = jsonObj.CacheInfo; diff --git a/lib/tpk/autoCenterMap.js b/lib/tpk/autoCenterMap.js index d01f2c5..0c97560 100644 --- a/lib/tpk/autoCenterMap.js +++ b/lib/tpk/autoCenterMap.js @@ -1,105 +1,127 @@ -define(["dojo/_base/declare"],function(declare){ - return declare(null,{ +/** + * This library assists with autoCenter the map upon orientation change + * IMPORTANT: There are Esri dependencies in this library including + * esri.Geometry.Point, esri.SpatialReference and Esri.Map. + * The fact that these dependencies exist is implied that they were + * loaded via some other means and made globally available. + * Sometimes this happens by default, as is true in this case. + * @param map + * @param delay + */ +O.esri.TPK.autoCenterMap = function(/* Map */ map,/* int */ delay){ - constructor:function(/* Map */ map,/* int */ delay){ - this.map = map; - this._setPanListener(); - this._setZoomListener(); - this._setOrientationListener(delay); - var centerPt = map.extent.getCenter(); - this._setCenterPt(centerPt.x,centerPt.y,map.spatialReference.wkid); - }, + /** + * Activates the orientation listener and listens for native events. + */ + function _setOrientationListener(delay){ + var supportsOrientationChange = "onorientationchange" in window, + orientationEvent = supportsOrientationChange ? "orientationchange" : "resize"; - /** - * Activates the orientation listener and listens for native events. - */ - _setOrientationListener: function(delay){ - var supportsOrientationChange = "onorientationchange" in window, - orientationEvent = supportsOrientationChange ? "orientationchange" : "resize"; + window.addEventListener(orientationEvent, _debounceMap(function(){ + _centerMap(); + },delay)) + } - window.addEventListener(orientationEvent, function(evt){ - this._centerMap(this,delay); - }.bind(this), false); - }, + /** + * Center the map based on locations pulled from local storage + * @param context + * @param delay + * @private + */ + function _centerMap(){ + var locationStr = _getCenterPt().split(","); + var wkid = map.spatialReference.wkid; + var mapPt = null; - /** - * Center the map based on locations pulled from local storage - * @param context - * @param delay - * @private - */ - _centerMap: function(context,delay){ + if(wkid == 4326){ + mapPt = new esri.geometry.Point(locationStr[1],locationStr[0]); + } + else if(wkid = 102100){ + mapPt = new esri.geometry.Point(locationStr[0],locationStr[1], new esri.SpatialReference({ wkid: wkid })); + } + map.centerAt(mapPt); + } - setTimeout( - function(){ - var locationStr = context._getCenterPt().split(","); - var wkid = context.map.spatialReference.wkid; - var mapPt = null; + /** + * Minimize the number of times window readjustment fires a function + * http://davidwalsh.name/javascript-debounce-function + * @param func + * @param wait + * @param immediate + * @returns {Function} + */ + function _debounceMap(func, wait, immediate) { + var timeout; + return function() { + var context = this, args = arguments; + clearTimeout(timeout); + timeout = setTimeout(function() { + timeout = null; + if (!immediate) func.apply(context, args); + }, wait); + if (immediate && !timeout) func.apply(context, args); + }; + } - if(wkid == 4326){ - mapPt = new esri.geometry.Point(locationStr[1],locationStr[0]); - } - else if(wkid = 102100){ - mapPt = new esri.geometry.Point(locationStr[0],locationStr[1], new esri.SpatialReference({ wkid: wkid })); - } - context.map.centerAt(mapPt); - } - ,delay); - }, + /** + * Automatically sets new center point in local storage. + */ + function _setPanListener(){ + map.on("pan-end",function(){ + var center = map.extent.getCenter(); + _setCenterPt(center.x,center.y,map.spatialReference.wkid); + }) + } - /** - * Automatically sets new center point in local storage. - */ - _setPanListener: function(){ - this.map.on("pan-end",function(){ - var center = this.map.extent.getCenter(); - this._setCenterPt(center.x,center.y,this.map.spatialReference.wkid); - }.bind(this)) - }, + /** + * Automatically sets new center point and zoom level in + * local storage. + */ + function _setZoomListener(){ + map.on("zoom-end",function(){ + var center = map.extent.getCenter(); + _setCenterPt(center.x,center.y,map.spatialReference.wkid); + map.setZoom(map.getZoom()); + }.bind(self)) + } - /** - * Automatically sets new center point and zoom level in - * local storage. - */ - _setZoomListener: function(){ - this.map.on("zoom-end",function(){ - var center = this.map.extent.getCenter(); - this._setCenterPt(center.x,center.y,this.map.spatialReference.wkid); - this.map.setZoom(this.map.getZoom()); - }.bind(this)) - }, + /** + * Uses localStorage to save a location. + * @param lat + * @param lon + * @param spatialReference + */ + function _setCenterPt(lat,lon,spatialReference){ + localStorage.setItem("_centerPtX", lat); + localStorage.setItem("_centerPtY", lon); + localStorage.setItem("_spatialReference", spatialReference); + } - /** - * Uses localStorage to save a location. - * @param lat - * @param lon - * @param spatialReference - */ - _setCenterPt: function(lat,lon,spatialReference){ - localStorage.setItem("_centerPtX", lat); - localStorage.setItem("_centerPtY", lon); - localStorage.setItem("_spatialReference", spatialReference); - }, + /** + * Pulls a saved location from localStorage + * Requires that setCenterPt() has been set. + * @returns String x,y,spatialReference + */ + function _getCenterPt(){ + var value = null; - /** - * Pulls a saved location from localStorage - * Requires that setCenterPt() has been set. - * @returns String x,y,spatialReference - */ - _getCenterPt: function(){ - var value = null; - - try{ - value = localStorage.getItem("_centerPtX") + "," + localStorage.getItem("_centerPtY") + "," + - localStorage.getItem("_spatialReference"); - } - catch(err) - { - console.log("getCenterFromLocalStorage: " + err.message); - } - - return value; + try{ + value = localStorage.getItem("_centerPtX") + "," + localStorage.getItem("_centerPtY") + "," + + localStorage.getItem("_spatialReference"); + } + catch(err) + { + console.log("getCenterFromLocalStorage: " + err.message); } - }) -}) \ No newline at end of file + return value; + } + + this.init = function(){ + _setPanListener(); + _setZoomListener(); + _setOrientationListener(delay); + var centerPt = map.extent.getCenter(); + _setCenterPt(centerPt.x,centerPt.y,map.spatialReference.wkid); + } +} \ No newline at end of file diff --git a/lib/tpk/inflate.js b/lib/tpk/inflate.js index cc0e012..d5306e2 100755 --- a/lib/tpk/inflate.js +++ b/lib/tpk/inflate.js @@ -33,7 +33,7 @@ * and contributors of zlib. */ -(function(obj) { +O.esri.TPK.inflate = function(obj) { // Global var MAX_BITS = 15; @@ -2160,4 +2160,19 @@ }, false); } -})(this); +}; + +// @agup +// Convert the inflate library to a blobURL +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; diff --git a/lib/tpk/xml2json.js b/lib/tpk/xml2json.js index 0f57d1c..8919b7c 100644 --- a/lib/tpk/xml2json.js +++ b/lib/tpk/xml2json.js @@ -1,3 +1,4 @@ +//https://github.com/abdmob/x2js/blob/master/xml2json.js /* Copyright 2011-2013 Abdulla Abdurakhmanov Original sources are available at https://code.google.com/p/x2js/ @@ -14,544 +15,543 @@ See the License for the specific language governing permissions and limitations under the License. */ -define([],function() { - return function X2JS(config) { - 'use strict'; - var VERSION = "1.1.5"; +O.esri.TPK.X2JS = function(config){ + 'use strict'; - config = config || {}; - initConfigDefaults(); - initRequiredPolyfills(); + var VERSION = "1.1.5"; - function initConfigDefaults() { - if (config.escapeMode === undefined) { - config.escapeMode = true; + var config = config || {}; + initConfigDefaults(); + initRequiredPolyfills(); + + function initConfigDefaults() { + if (config.escapeMode === undefined) { + config.escapeMode = true; + } + config.attributePrefix = config.attributePrefix || "_"; + config.arrayAccessForm = config.arrayAccessForm || "none"; + config.emptyNodeForm = config.emptyNodeForm || "text"; + if (config.enableToStringFunc === undefined) { + config.enableToStringFunc = true; + } + config.arrayAccessFormPaths = config.arrayAccessFormPaths || []; + if (config.skipEmptyTextNodesForObj === undefined) { + config.skipEmptyTextNodesForObj = true; + } + if (config.stripWhitespaces === undefined) { + config.stripWhitespaces = true; + } + config.datetimeAccessFormPaths = config.datetimeAccessFormPaths || []; + } + + var DOMNodeTypes = { + ELEMENT_NODE: 1, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9 + }; + + function initRequiredPolyfills() { + function pad(number) { + var r = String(number); + if (r.length === 1) { + r = '0' + r; } - config.attributePrefix = config.attributePrefix || "_"; - config.arrayAccessForm = config.arrayAccessForm || "none"; - config.emptyNodeForm = config.emptyNodeForm || "text"; - if (config.enableToStringFunc === undefined) { - config.enableToStringFunc = true; - } - config.arrayAccessFormPaths = config.arrayAccessFormPaths || []; - if (config.skipEmptyTextNodesForObj === undefined) { - config.skipEmptyTextNodesForObj = true; - } - if (config.stripWhitespaces === undefined) { - config.stripWhitespaces = true; - } - config.datetimeAccessFormPaths = config.datetimeAccessFormPaths || []; + return r; } - var DOMNodeTypes = { - ELEMENT_NODE: 1, - TEXT_NODE: 3, - CDATA_SECTION_NODE: 4, - COMMENT_NODE: 8, - DOCUMENT_NODE: 9 - }; - - function initRequiredPolyfills() { - function pad(number) { - var r = String(number); - if (r.length === 1) { - r = '0' + r; - } - return r; - } - - // Hello IE8- - if (typeof String.prototype.trim !== 'function') { - String.prototype.trim = function () { - return this.replace(/^\s+|^\n+|(\s|\n)+$/g, ''); - } - } - if (typeof Date.prototype.toISOString !== 'function') { - // Implementation from http://stackoverflow.com/questions/2573521/how-do-i-output-an-iso-8601-formatted-string-in-javascript - Date.prototype.toISOString = function () { - return this.getUTCFullYear() - + '-' + pad(this.getUTCMonth() + 1) - + '-' + pad(this.getUTCDate()) - + 'T' + pad(this.getUTCHours()) - + ':' + pad(this.getUTCMinutes()) - + ':' + pad(this.getUTCSeconds()) - + '.' + String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5) - + 'Z'; - }; + // Hello IE8- + if (typeof String.prototype.trim !== 'function') { + String.prototype.trim = function () { + return this.replace(/^\s+|^\n+|(\s|\n)+$/g, ''); } } - - function getNodeLocalName(node) { - var nodeLocalName = node.localName; - if (nodeLocalName == null) // Yeah, this is IE!! - nodeLocalName = node.baseName; - if (nodeLocalName == null || nodeLocalName == "") // =="" is IE too - nodeLocalName = node.nodeName; - return nodeLocalName; + if (typeof Date.prototype.toISOString !== 'function') { + // Implementation from http://stackoverflow.com/questions/2573521/how-do-i-output-an-iso-8601-formatted-string-in-javascript + Date.prototype.toISOString = function () { + return this.getUTCFullYear() + + '-' + pad(this.getUTCMonth() + 1) + + '-' + pad(this.getUTCDate()) + + 'T' + pad(this.getUTCHours()) + + ':' + pad(this.getUTCMinutes()) + + ':' + pad(this.getUTCSeconds()) + + '.' + String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5) + + 'Z'; + }; } + } - function getNodePrefix(node) { - return node.prefix; - } + function getNodeLocalName(node) { + var nodeLocalName = node.localName; + if (nodeLocalName == null) // Yeah, this is IE!! + nodeLocalName = node.baseName; + if (nodeLocalName == null || nodeLocalName == "") // =="" is IE too + nodeLocalName = node.nodeName; + return nodeLocalName; + } - function escapeXmlChars(str) { - if (typeof(str) == "string") - return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/'); - else - return str; - } + function getNodePrefix(node) { + return node.prefix; + } - function unescapeXmlChars(str) { - return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(///g, '\/'); - } + function escapeXmlChars(str) { + if (typeof(str) == "string") + return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/'); + else + return str; + } - function toArrayAccessForm(obj, childName, path) { - switch (config.arrayAccessForm) { - case "property": - if (!(obj[childName] instanceof Array)) - obj[childName + "_asArray"] = [obj[childName]]; - else - obj[childName + "_asArray"] = obj[childName]; - break; - /*case "none": - break;*/ - } + function unescapeXmlChars(str) { + return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(///g, '\/'); + } - if (!(obj[childName] instanceof Array) && config.arrayAccessFormPaths.length > 0) { - var idx = 0; - for (; idx < config.arrayAccessFormPaths.length; idx++) { - var arrayPath = config.arrayAccessFormPaths[idx]; - if (typeof arrayPath === "string") { - if (arrayPath == path) - break; - } - else if (arrayPath instanceof RegExp) { - if (arrayPath.test(path)) - break; - } - else if (typeof arrayPath === "function") { - if (arrayPath(obj, childName, path)) - break; - } - } - if (idx != config.arrayAccessFormPaths.length) { - obj[childName] = [obj[childName]]; - } - } - } - - function fromXmlDateTime(prop) { - // Implementation based up on http://stackoverflow.com/questions/8178598/xml-datetime-to-javascript-date-object - // Improved to support full spec and optional parts - var bits = prop.split(/[-T:+Z]/g); - - var d = new Date(bits[0], bits[1] - 1, bits[2]); - var secondBits = bits[5].split("\."); - d.setHours(bits[3], bits[4], secondBits[0]); - if (secondBits.length > 1) - d.setMilliseconds(secondBits[1]); - - // Get supplied time zone offset in minutes - if (bits[6] && bits[7]) { - var offsetMinutes = bits[6] * 60 + Number(bits[7]); - var sign = /\d\d-\d\d:\d\d$/.test(prop) ? '-' : '+'; - - // Apply the sign - offsetMinutes = 0 + (sign == '-' ? -1 * offsetMinutes : offsetMinutes); - - // Apply offset and local timezone - d.setMinutes(d.getMinutes() - offsetMinutes - d.getTimezoneOffset()) - } - else if (prop.indexOf("Z", prop.length - 1) !== -1) { - d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds())); - } - - // d is now a local time equivalent to the supplied time - return d; - } - - function checkFromXmlDateTimePaths(value, childName, fullPath) { - if (config.datetimeAccessFormPaths.length > 0) { - var path = fullPath.split("\.#")[0]; - var idx = 0; - for (; idx < config.datetimeAccessFormPaths.length; idx++) { - var dtPath = config.datetimeAccessFormPaths[idx]; - if (typeof dtPath === "string") { - if (dtPath == path) - break; - } - else if (dtPath instanceof RegExp) { - if (dtPath.test(path)) - break; - } - else if (typeof dtPath === "function") { - if (dtPath(obj, childName, path)) - break; - } - } - if (idx != config.datetimeAccessFormPaths.length) { - return fromXmlDateTime(value); - } + function toArrayAccessForm(obj, childName, path) { + switch (config.arrayAccessForm) { + case "property": + if (!(obj[childName] instanceof Array)) + obj[childName + "_asArray"] = [obj[childName]]; else - return value; + obj[childName + "_asArray"] = obj[childName]; + break; + /*case "none": + break;*/ + } + + if (!(obj[childName] instanceof Array) && config.arrayAccessFormPaths.length > 0) { + var idx = 0; + for (; idx < config.arrayAccessFormPaths.length; idx++) { + var arrayPath = config.arrayAccessFormPaths[idx]; + if (typeof arrayPath === "string") { + if (arrayPath == path) + break; + } + else if (arrayPath instanceof RegExp) { + if (arrayPath.test(path)) + break; + } + else if (typeof arrayPath === "function") { + if (arrayPath(obj, childName, path)) + break; + } + } + if (idx != config.arrayAccessFormPaths.length) { + obj[childName] = [obj[childName]]; + } + } + } + + function fromXmlDateTime(prop) { + // Implementation based up on http://stackoverflow.com/questions/8178598/xml-datetime-to-javascript-date-object + // Improved to support full spec and optional parts + var bits = prop.split(/[-T:+Z]/g); + + var d = new Date(bits[0], bits[1] - 1, bits[2]); + var secondBits = bits[5].split("\."); + d.setHours(bits[3], bits[4], secondBits[0]); + if (secondBits.length > 1) + d.setMilliseconds(secondBits[1]); + + // Get supplied time zone offset in minutes + if (bits[6] && bits[7]) { + var offsetMinutes = bits[6] * 60 + Number(bits[7]); + var sign = /\d\d-\d\d:\d\d$/.test(prop) ? '-' : '+'; + + // Apply the sign + offsetMinutes = 0 + (sign == '-' ? -1 * offsetMinutes : offsetMinutes); + + // Apply offset and local timezone + d.setMinutes(d.getMinutes() - offsetMinutes - d.getTimezoneOffset()) + } + else if (prop.indexOf("Z", prop.length - 1) !== -1) { + d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds())); + } + + // d is now a local time equivalent to the supplied time + return d; + } + + function checkFromXmlDateTimePaths(value, childName, fullPath) { + if (config.datetimeAccessFormPaths.length > 0) { + var path = fullPath.split("\.#")[0]; + var idx = 0; + for (; idx < config.datetimeAccessFormPaths.length; idx++) { + var dtPath = config.datetimeAccessFormPaths[idx]; + if (typeof dtPath === "string") { + if (dtPath == path) + break; + } + else if (dtPath instanceof RegExp) { + if (dtPath.test(path)) + break; + } + else if (typeof dtPath === "function") { + if (dtPath(obj, childName, path)) + break; + } + } + if (idx != config.datetimeAccessFormPaths.length) { + return fromXmlDateTime(value); } else return value; } + else + return value; + } - function parseDOMChildren(node, path) { - if (node.nodeType == DOMNodeTypes.DOCUMENT_NODE) { - var result = new Object; - var nodeChildren = node.childNodes; - // Alternative for firstElementChild which is not supported in some environments - for (var cidx = 0; cidx < nodeChildren.length; cidx++) { - var child = nodeChildren.item(cidx); - if (child.nodeType == DOMNodeTypes.ELEMENT_NODE) { - var childName = getNodeLocalName(child); - result[childName] = parseDOMChildren(child, childName); - } - } - return result; - } - else if (node.nodeType == DOMNodeTypes.ELEMENT_NODE) { - var result = new Object; - result.__cnt = 0; - - var nodeChildren = node.childNodes; - - // Children nodes - for (var cidx = 0; cidx < nodeChildren.length; cidx++) { - var child = nodeChildren.item(cidx); // nodeChildren[cidx]; + function parseDOMChildren(node, path) { + if (node.nodeType == DOMNodeTypes.DOCUMENT_NODE) { + var result = new Object; + var nodeChildren = node.childNodes; + // Alternative for firstElementChild which is not supported in some environments + for (var cidx = 0; cidx < nodeChildren.length; cidx++) { + var child = nodeChildren.item(cidx); + if (child.nodeType == DOMNodeTypes.ELEMENT_NODE) { var childName = getNodeLocalName(child); + result[childName] = parseDOMChildren(child, childName); + } + } + return result; + } + else if (node.nodeType == DOMNodeTypes.ELEMENT_NODE) { + var result = new Object; + result.__cnt = 0; - if (child.nodeType != DOMNodeTypes.COMMENT_NODE) { - result.__cnt++; - if (result[childName] == null) { - result[childName] = parseDOMChildren(child, path + "." + childName); - toArrayAccessForm(result, childName, path + "." + childName); - } - else { - if (result[childName] != null) { - if (!(result[childName] instanceof Array)) { - result[childName] = [result[childName]]; - toArrayAccessForm(result, childName, path + "." + childName); - } + var nodeChildren = node.childNodes; + + // Children nodes + for (var cidx = 0; cidx < nodeChildren.length; cidx++) { + var child = nodeChildren.item(cidx); // nodeChildren[cidx]; + var childName = getNodeLocalName(child); + + if (child.nodeType != DOMNodeTypes.COMMENT_NODE) { + result.__cnt++; + if (result[childName] == null) { + result[childName] = parseDOMChildren(child, path + "." + childName); + toArrayAccessForm(result, childName, path + "." + childName); + } + else { + if (result[childName] != null) { + if (!(result[childName] instanceof Array)) { + result[childName] = [result[childName]]; + toArrayAccessForm(result, childName, path + "." + childName); } - (result[childName])[result[childName].length] = parseDOMChildren(child, path + "." + childName); } - } - } - - // Attributes - for (var aidx = 0; aidx < node.attributes.length; aidx++) { - var attr = node.attributes.item(aidx); // [aidx]; - result.__cnt++; - result[config.attributePrefix + attr.name] = attr.value; - } - - // Node namespace prefix - var nodePrefix = getNodePrefix(node); - if (nodePrefix != null && nodePrefix != "") { - result.__cnt++; - result.__prefix = nodePrefix; - } - - if (result["#text"] != null) { - result.__text = result["#text"]; - if (result.__text instanceof Array) { - result.__text = result.__text.join("\n"); - } - if (config.escapeMode) - result.__text = unescapeXmlChars(result.__text); - if (config.stripWhitespaces) - result.__text = result.__text.trim(); - delete result["#text"]; - if (config.arrayAccessForm == "property") - delete result["#text_asArray"]; - result.__text = checkFromXmlDateTimePaths(result.__text, childName, path + "." + childName); - } - if (result["#cdata-section"] != null) { - result.__cdata = result["#cdata-section"]; - delete result["#cdata-section"]; - if (config.arrayAccessForm == "property") - delete result["#cdata-section_asArray"]; - } - - if (result.__cnt == 1 && result.__text != null) { - result = result.__text; - } - else if (result.__cnt == 0 && config.emptyNodeForm == "text") { - result = ''; - } - else if (result.__cnt > 1 && result.__text != null && config.skipEmptyTextNodesForObj) { - if ((config.stripWhitespaces && result.__text == "") || (result.__text.trim() == "")) { - delete result.__text; - } - } - delete result.__cnt; - - if (config.enableToStringFunc && (result.__text != null || result.__cdata != null )) { - result.toString = function () { - return (this.__text != null ? this.__text : '') + ( this.__cdata != null ? this.__cdata : ''); - }; - } - - return result; - } - else if (node.nodeType == DOMNodeTypes.TEXT_NODE || node.nodeType == DOMNodeTypes.CDATA_SECTION_NODE) { - return node.nodeValue; - } - } - - function startTag(jsonObj, element, attrList, closed) { - var resultStr = "<" + ( (jsonObj != null && jsonObj.__prefix != null) ? (jsonObj.__prefix + ":") : "") + element; - if (attrList != null) { - for (var aidx = 0; aidx < attrList.length; aidx++) { - var attrName = attrList[aidx]; - var attrVal = jsonObj[attrName]; - if (config.escapeMode) - attrVal = escapeXmlChars(attrVal); - resultStr += " " + attrName.substr(config.attributePrefix.length) + "='" + attrVal + "'"; - } - } - if (!closed) - resultStr += ">"; - else - resultStr += "/>"; - return resultStr; - } - - function endTag(jsonObj, elementName) { - return ""; - } - - function endsWith(str, suffix) { - return str.indexOf(suffix, str.length - suffix.length) !== -1; - } - - function jsonXmlSpecialElem(jsonObj, jsonObjField) { - if ((config.arrayAccessForm == "property" && endsWith(jsonObjField.toString(), ("_asArray"))) - || jsonObjField.toString().indexOf(config.attributePrefix) == 0 - || jsonObjField.toString().indexOf("__") == 0 - || (jsonObj[jsonObjField] instanceof Function)) - return true; - else - return false; - } - - function jsonXmlElemCount(jsonObj) { - var elementsCnt = 0; - if (jsonObj instanceof Object) { - for (var it in jsonObj) { - if (jsonXmlSpecialElem(jsonObj, it)) - continue; - elementsCnt++; - } - } - return elementsCnt; - } - - function parseJSONAttributes(jsonObj) { - var attrList = []; - if (jsonObj instanceof Object) { - for (var ait in jsonObj) { - if (ait.toString().indexOf("__") == -1 && ait.toString().indexOf(config.attributePrefix) == 0) { - attrList.push(ait); + (result[childName])[result[childName].length] = parseDOMChildren(child, path + "." + childName); } } } - return attrList; - } - function parseJSONTextAttrs(jsonTxtObj) { - var result = ""; - - if (jsonTxtObj.__cdata != null) { - result += ""; + // Attributes + for (var aidx = 0; aidx < node.attributes.length; aidx++) { + var attr = node.attributes.item(aidx); // [aidx]; + result.__cnt++; + result[config.attributePrefix + attr.name] = attr.value; } - if (jsonTxtObj.__text != null) { + // Node namespace prefix + var nodePrefix = getNodePrefix(node); + if (nodePrefix != null && nodePrefix != "") { + result.__cnt++; + result.__prefix = nodePrefix; + } + + if (result["#text"] != null) { + result.__text = result["#text"]; + if (result.__text instanceof Array) { + result.__text = result.__text.join("\n"); + } if (config.escapeMode) - result += escapeXmlChars(jsonTxtObj.__text); - else - result += jsonTxtObj.__text; + result.__text = unescapeXmlChars(result.__text); + if (config.stripWhitespaces) + result.__text = result.__text.trim(); + delete result["#text"]; + if (config.arrayAccessForm == "property") + delete result["#text_asArray"]; + result.__text = checkFromXmlDateTimePaths(result.__text, childName, path + "." + childName); } - return result; - } - - function parseJSONTextObject(jsonTxtObj) { - var result = ""; - - if (jsonTxtObj instanceof Object) { - result += parseJSONTextAttrs(jsonTxtObj); - } - else if (jsonTxtObj != null) { - if (config.escapeMode) - result += escapeXmlChars(jsonTxtObj); - else - result += jsonTxtObj; + if (result["#cdata-section"] != null) { + result.__cdata = result["#cdata-section"]; + delete result["#cdata-section"]; + if (config.arrayAccessForm == "property") + delete result["#cdata-section_asArray"]; } - return result; - } - - function parseJSONArray(jsonArrRoot, jsonArrObj, attrList) { - var result = ""; - if (jsonArrRoot.length == 0) { - result += startTag(jsonArrRoot, jsonArrObj, attrList, true); + if (result.__cnt == 1 && result.__text != null) { + result = result.__text; } - else { - for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) { - result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false); - result += parseJSONObject(jsonArrRoot[arIdx]); - result += endTag(jsonArrRoot[arIdx], jsonArrObj); + else if (result.__cnt == 0 && config.emptyNodeForm == "text") { + result = ''; + } + else if (result.__cnt > 1 && result.__text != null && config.skipEmptyTextNodesForObj) { + if ((config.stripWhitespaces && result.__text == "") || (result.__text.trim() == "")) { + delete result.__text; } } + delete result.__cnt; + + if (config.enableToStringFunc && (result.__text != null || result.__cdata != null )) { + result.toString = function () { + return (this.__text != null ? this.__text : '') + ( this.__cdata != null ? this.__cdata : ''); + }; + } + return result; } + else if (node.nodeType == DOMNodeTypes.TEXT_NODE || node.nodeType == DOMNodeTypes.CDATA_SECTION_NODE) { + return node.nodeValue; + } + } - function parseJSONObject(jsonObj) { - var result = ""; + function startTag(jsonObj, element, attrList, closed) { + var resultStr = "<" + ( (jsonObj != null && jsonObj.__prefix != null) ? (jsonObj.__prefix + ":") : "") + element; + if (attrList != null) { + for (var aidx = 0; aidx < attrList.length; aidx++) { + var attrName = attrList[aidx]; + var attrVal = jsonObj[attrName]; + if (config.escapeMode) + attrVal = escapeXmlChars(attrVal); + resultStr += " " + attrName.substr(config.attributePrefix.length) + "='" + attrVal + "'"; + } + } + if (!closed) + resultStr += ">"; + else + resultStr += "/>"; + return resultStr; + } - var elementsCnt = jsonXmlElemCount(jsonObj); + function endTag(jsonObj, elementName) { + return ""; + } - if (elementsCnt > 0) { - for (var it in jsonObj) { + function endsWith(str, suffix) { + return str.indexOf(suffix, str.length - suffix.length) !== -1; + } - if (jsonXmlSpecialElem(jsonObj, it)) - continue; + function jsonXmlSpecialElem(jsonObj, jsonObjField) { + if ((config.arrayAccessForm == "property" && endsWith(jsonObjField.toString(), ("_asArray"))) + || jsonObjField.toString().indexOf(config.attributePrefix) == 0 + || jsonObjField.toString().indexOf("__") == 0 + || (jsonObj[jsonObjField] instanceof Function)) + return true; + else + return false; + } - var subObj = jsonObj[it]; + function jsonXmlElemCount(jsonObj) { + var elementsCnt = 0; + if (jsonObj instanceof Object) { + for (var it in jsonObj) { + if (jsonXmlSpecialElem(jsonObj, it)) + continue; + elementsCnt++; + } + } + return elementsCnt; + } - var attrList = parseJSONAttributes(subObj) + function parseJSONAttributes(jsonObj) { + var attrList = []; + if (jsonObj instanceof Object) { + for (var ait in jsonObj) { + if (ait.toString().indexOf("__") == -1 && ait.toString().indexOf(config.attributePrefix) == 0) { + attrList.push(ait); + } + } + } + return attrList; + } - if (subObj == null || subObj == undefined) { - result += startTag(subObj, it, attrList, true); + function parseJSONTextAttrs(jsonTxtObj) { + var result = ""; + + if (jsonTxtObj.__cdata != null) { + result += ""; + } + + if (jsonTxtObj.__text != null) { + if (config.escapeMode) + result += escapeXmlChars(jsonTxtObj.__text); + else + result += jsonTxtObj.__text; + } + return result; + } + + function parseJSONTextObject(jsonTxtObj) { + var result = ""; + + if (jsonTxtObj instanceof Object) { + result += parseJSONTextAttrs(jsonTxtObj); + } + else if (jsonTxtObj != null) { + if (config.escapeMode) + result += escapeXmlChars(jsonTxtObj); + else + result += jsonTxtObj; + } + + return result; + } + + function parseJSONArray(jsonArrRoot, jsonArrObj, attrList) { + var result = ""; + if (jsonArrRoot.length == 0) { + result += startTag(jsonArrRoot, jsonArrObj, attrList, true); + } + else { + for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) { + result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false); + result += parseJSONObject(jsonArrRoot[arIdx]); + result += endTag(jsonArrRoot[arIdx], jsonArrObj); + } + } + return result; + } + + function parseJSONObject(jsonObj) { + var result = ""; + + var elementsCnt = jsonXmlElemCount(jsonObj); + + if (elementsCnt > 0) { + for (var it in jsonObj) { + + if (jsonXmlSpecialElem(jsonObj, it)) + continue; + + var subObj = jsonObj[it]; + + var attrList = parseJSONAttributes(subObj) + + if (subObj == null || subObj == undefined) { + result += startTag(subObj, it, attrList, true); + } + else if (subObj instanceof Object) { + + if (subObj instanceof Array) { + result += parseJSONArray(subObj, it, attrList); } - else if (subObj instanceof Object) { - - if (subObj instanceof Array) { - result += parseJSONArray(subObj, it, attrList); - } - else if (subObj instanceof Date) { + else if (subObj instanceof Date) { + result += startTag(subObj, it, attrList, false); + result += subObj.toISOString(); + result += endTag(subObj, it); + } + else { + var subObjElementsCnt = jsonXmlElemCount(subObj); + if (subObjElementsCnt > 0 || subObj.__text != null || subObj.__cdata != null) { result += startTag(subObj, it, attrList, false); - result += subObj.toISOString(); + result += parseJSONObject(subObj); result += endTag(subObj, it); } else { - var subObjElementsCnt = jsonXmlElemCount(subObj); - if (subObjElementsCnt > 0 || subObj.__text != null || subObj.__cdata != null) { - result += startTag(subObj, it, attrList, false); - result += parseJSONObject(subObj); - result += endTag(subObj, it); - } - else { - result += startTag(subObj, it, attrList, true); - } + result += startTag(subObj, it, attrList, true); } } - else { - result += startTag(subObj, it, attrList, false); - result += parseJSONTextObject(subObj); - result += endTag(subObj, it); - } + } + else { + result += startTag(subObj, it, attrList, false); + result += parseJSONTextObject(subObj); + result += endTag(subObj, it); } } - result += parseJSONTextObject(jsonObj); - - return result; } + result += parseJSONTextObject(jsonObj); - this.parseXmlString = function (xmlDocStr) { - var isIEParser = window.ActiveXObject || "ActiveXObject" in window; - if (xmlDocStr === undefined) { - return null; - } - var xmlDoc; - if (window.DOMParser) { - var parser = new window.DOMParser(); - var parsererrorNS = null; - // IE9+ now is here - if (!isIEParser) { - try { - parsererrorNS = parser.parseFromString("INVALID", "text/xml").childNodes[0].namespaceURI; - } - catch (err) { - parsererrorNS = null; - } - } + return result; + } + + this.parseXmlString = function (xmlDocStr) { + var isIEParser = window.ActiveXObject || "ActiveXObject" in window; + if (xmlDocStr === undefined) { + return null; + } + var xmlDoc; + if (window.DOMParser) { + var parser = new window.DOMParser(); + var parsererrorNS = null; + // IE9+ now is here + if (!isIEParser) { try { - xmlDoc = parser.parseFromString(xmlDocStr, "text/xml"); - if (parsererrorNS != null && xmlDoc.getElementsByTagNameNS(parsererrorNS, "parsererror").length > 0) { - //throw new Error('Error parsing XML: '+xmlDocStr); - xmlDoc = null; - } + parsererrorNS = parser.parseFromString("INVALID", "text/xml").childNodes[0].namespaceURI; } catch (err) { + parsererrorNS = null; + } + } + try { + xmlDoc = parser.parseFromString(xmlDocStr, "text/xml"); + if (parsererrorNS != null && xmlDoc.getElementsByTagNameNS(parsererrorNS, "parsererror").length > 0) { + //throw new Error('Error parsing XML: '+xmlDocStr); xmlDoc = null; } } - else { - // IE :( - if (xmlDocStr.indexOf("") + 2); - } - xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); - xmlDoc.async = "false"; - xmlDoc.loadXML(xmlDocStr); + catch (err) { + xmlDoc = null; } - return xmlDoc; - }; - - this.asArray = function (prop) { - if (prop instanceof Array) - return prop; - else - return [prop]; - }; - - this.toXmlDateTime = function (dt) { - if (dt instanceof Date) - return dt.toISOString(); - else if (typeof(dt) === 'number') - return new Date(dt).toISOString(); - else - return null; - }; - - this.asDateTime = function (prop) { - if (typeof(prop) == "string") { - return fromXmlDateTime(prop); + } + else { + // IE :( + if (xmlDocStr.indexOf("") + 2); } - else - return prop; - }; + xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(xmlDocStr); + } + return xmlDoc; + }; - this.xml2json = function (xmlDoc) { - return parseDOMChildren(xmlDoc); - }; + this.asArray = function (prop) { + if (prop instanceof Array) + return prop; + else + return [prop]; + }; - this.xml_str2json = function (xmlDocStr) { - var xmlDoc = this.parseXmlString(xmlDocStr); - if (xmlDoc != null) - return this.xml2json(xmlDoc); - else - return null; - }; + this.toXmlDateTime = function (dt) { + if (dt instanceof Date) + return dt.toISOString(); + else if (typeof(dt) === 'number') + return new Date(dt).toISOString(); + else + return null; + }; - this.json2xml_str = function (jsonObj) { - return parseJSONObject(jsonObj); - }; + this.asDateTime = function (prop) { + if (typeof(prop) == "string") { + return fromXmlDateTime(prop); + } + else + return prop; + }; - this.json2xml = function (jsonObj) { - var xmlDocStr = this.json2xml_str(jsonObj); - return this.parseXmlString(xmlDocStr); - }; + this.xml2json = function (xmlDoc) { + return parseDOMChildren(xmlDoc); + }; - this.getVersion = function () { - return VERSION; - }; + this.xml_str2json = function(xmlDocStr) { + var xmlDoc = this.parseXmlString(xmlDocStr); + if (xmlDoc != null) + return this.xml2json(xmlDoc); + else + return null; + }; - } -}) + this.json2xml_str = function (jsonObj) { + return parseJSONObject(jsonObj); + }; + + this.json2xml = function (jsonObj) { + var xmlDocStr = this.json2xml_str(jsonObj); + return this.parseXmlString(xmlDocStr); + }; + + this.getVersion = function () { + return VERSION; + }; + +} \ No newline at end of file diff --git a/lib/tpk/zip.js b/lib/tpk/zip.js index c0a450d..cf6bb91 100755 --- a/lib/tpk/zip.js +++ b/lib/tpk/zip.js @@ -1,3 +1,4 @@ +//https://github.com/gildas-lormeau/zip.js/blob/master/WebContent/zip.js /* Copyright (c) 2013 Gildas Lormeau. All rights reserved. @@ -25,8 +26,8 @@ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -define([],function() { - var obj = this; + +(function(obj) { var ERR_BAD_FORMAT = "File format is not recognized."; var ERR_ENCRYPTED = "File contains encrypted entry."; @@ -38,7 +39,7 @@ define([],function() { var ERR_DUPLICATED_NAME = "File already exists."; var CHUNK_SIZE = 512 * 1024; - var INFLATE_JS = "inflate.js"; + var INFLATE_JS = ""; //left blank intentionally! Modified by @agup var DEFLATE_JS = "deflate.js"; var TEXT_PLAIN = "text/plain"; @@ -53,17 +54,16 @@ define([],function() { function Crc32() { var crc = -1, that = this; - that.append = function (data) { + that.append = function(data) { var offset, table = that.table; for (offset = 0; offset < data.length; offset++) crc = (crc >>> 8) ^ table[(crc ^ data[offset]) & 0xFF]; }; - that.get = function () { + that.get = function() { return ~crc; }; } - - Crc32.prototype.table = (function () { + Crc32.prototype.table = (function() { var i, j, t, table = []; for (i = 0; i < 256; i++) { t = i; @@ -95,9 +95,9 @@ define([],function() { if (bytes) dataArray.set(bytes, 0); return { - buffer: dataBuffer, - array: dataArray, - view: new DataView(dataBuffer) + buffer : dataBuffer, + array : dataArray, + view : new DataView(dataBuffer) }; } @@ -110,10 +110,10 @@ define([],function() { function init(callback, onerror) { var blob = new Blob([ text ], { - type: TEXT_PLAIN + type : TEXT_PLAIN }); blobReader = new BlobReader(blob); - blobReader.init(function () { + blobReader.init(function() { that.size = blobReader.size; callback(); }, onerror); @@ -127,7 +127,6 @@ define([],function() { that.init = init; that.readUint8Array = readUint8Array; } - TextReader.prototype = new Reader(); TextReader.prototype.constructor = TextReader; @@ -158,7 +157,6 @@ define([],function() { that.init = init; that.readUint8Array = readUint8Array; } - Data64URIReader.prototype = new Reader(); Data64URIReader.prototype.constructor = Data64URIReader; @@ -172,7 +170,7 @@ define([],function() { function readUint8Array(index, length, callback, onerror) { var reader = new FileReader(); - reader.onload = function (e) { + reader.onload = function(e) { callback(new Uint8Array(e.target.result)); }; reader.onerror = onerror; @@ -183,7 +181,6 @@ define([],function() { that.init = init; that.readUint8Array = readUint8Array; } - BlobReader.prototype = new Reader(); BlobReader.prototype.constructor = BlobReader; @@ -191,8 +188,7 @@ define([],function() { function Writer() { } - - Writer.prototype.getData = function (callback) { + Writer.prototype.getData = function(callback) { callback(this.data); }; @@ -228,7 +224,6 @@ define([],function() { that.writeUint8Array = writeUint8Array; that.getData = getData; } - TextWriter.prototype = new Writer(); TextWriter.prototype.constructor = TextWriter; @@ -262,7 +257,6 @@ define([],function() { that.writeUint8Array = writeUint8Array; that.getData = getData; } - Data64URIWriter.prototype = new Writer(); Data64URIWriter.prototype.constructor = Data64URIWriter; @@ -293,7 +287,6 @@ define([],function() { that.writeUint8Array = writeUint8Array; that.getData = getData; } - BlobWriter.prototype = new Writer(); BlobWriter.prototype.constructor = BlobWriter; @@ -312,7 +305,7 @@ define([],function() { if (message.onappend) { outputSize += data.length; - writer.writeUint8Array(data, function () { + writer.writeUint8Array(data, function() { onappend(false, data); step(); }, onwriteerror); @@ -320,7 +313,7 @@ define([],function() { if (message.onflush) if (data) { outputSize += data.length; - writer.writeUint8Array(data, function () { + writer.writeUint8Array(data, function() { onappend(false, data); onflush(); }, onwriteerror); @@ -333,10 +326,10 @@ define([],function() { function step() { index = chunkIndex * CHUNK_SIZE; if (index < size) - reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function (array) { + reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(array) { worker.postMessage({ - append: true, - data: array + append : true, + data : array }); chunkIndex++; if (onprogress) @@ -345,7 +338,7 @@ define([],function() { }, onreaderror); else worker.postMessage({ - flush: true + flush : true }); } @@ -361,14 +354,14 @@ define([],function() { var outputData; index = chunkIndex * CHUNK_SIZE; if (index < size) - reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function (inputData) { - var outputData = process.append(inputData, function () { + reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(inputData) { + var outputData = process.append(inputData, function() { if (onprogress) onprogress(offset + index, size); }); outputSize += outputData.length; onappend(true, inputData); - writer.writeUint8Array(outputData, function () { + writer.writeUint8Array(outputData, function() { onappend(false, outputData); chunkIndex++; setTimeout(step, 1); @@ -380,7 +373,7 @@ define([],function() { outputData = process.flush(); if (outputData) { outputSize += outputData.length; - writer.writeUint8Array(outputData, function () { + writer.writeUint8Array(outputData, function() { onappend(false, outputData); onend(outputSize); }, onwriteerror); @@ -405,6 +398,7 @@ define([],function() { } if (obj.zip.useWebWorkers) { + worker = new Worker(obj.zip.workerScriptsPath + INFLATE_JS); launchWorkerProcess(worker, reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror); } else @@ -433,8 +427,8 @@ define([],function() { worker = new Worker(obj.zip.workerScriptsPath + DEFLATE_JS); worker.addEventListener(MESSAGE_EVENT, onmessage, false); worker.postMessage({ - init: true, - level: level + init : true, + level : level }); } else launchProcess(new obj.zip.Deflater(), reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror); @@ -447,12 +441,12 @@ define([],function() { function step() { var index = chunkIndex * CHUNK_SIZE; if (index < size) - reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function (array) { + reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(array) { if (computeCrc32) crc32.append(array); if (onprogress) onprogress(index, size, array); - writer.writeUint8Array(array, function () { + writer.writeUint8Array(array, function() { chunkIndex++; step(); }, onwriteerror); @@ -533,7 +527,7 @@ define([],function() { function Entry() { } - Entry.prototype.getData = function (writer, onend, onprogress, checkCrc32) { + Entry.prototype.getData = function(writer, onend, onprogress, checkCrc32) { var that = this, worker; function terminate(callback, param) { @@ -554,7 +548,7 @@ define([],function() { if (checkCrc32 && !testCrc32(crc32)) onreaderror(); else - writer.getData(function (data) { + writer.getData(function(data) { terminate(onend, data); }); } @@ -567,7 +561,7 @@ define([],function() { terminate(onerror, ERR_WRITE_DATA); } - reader.readUint8Array(that.offset, 30, function (bytes) { + reader.readUint8Array(that.offset, 30, function(bytes) { var data = getDataHelper(bytes.length, bytes), dataOffset; if (data.view.getUint32(0) != 0x504b0304) { onerror(ERR_BAD_FORMAT); @@ -575,7 +569,7 @@ define([],function() { } readCommonHeader(that, data, 4, false, onerror); dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength; - writer.init(function () { + writer.init(function() { if (that.compressionMethod === 0) copy(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror); else @@ -585,30 +579,30 @@ define([],function() { }; function seekEOCDR(offset, entriesCallback) { - reader.readUint8Array(reader.size - offset, offset, function (bytes) { + reader.readUint8Array(reader.size - offset, offset, function(bytes) { var dataView = getDataHelper(bytes.length, bytes).view; if (dataView.getUint32(0) != 0x504b0506) { seekEOCDR(offset + 1, entriesCallback); } else { entriesCallback(dataView); } - }, function () { + }, function() { onerror(ERR_READ); }); } return { - getEntries: function (callback) { + getEntries : function(callback) { if (reader.size < 22) { onerror(ERR_BAD_FORMAT); return; } // look for End of central directory record - seekEOCDR(22, function (dataView) { + seekEOCDR(22, function(dataView) { var datalength, fileslength; datalength = dataView.getUint32(16, true); fileslength = dataView.getUint16(8, true); - reader.readUint8Array(datalength, reader.size - datalength, function (bytes) { + reader.readUint8Array(datalength, reader.size - datalength, function(bytes) { var i, index = 0, entries = [], entry, filename, comment, data = getDataHelper(bytes.length, bytes); for (i = 0; i < fileslength; i++) { entry = new Entry(); @@ -631,12 +625,12 @@ define([],function() { index += 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength; } callback(entries); - }, function () { + }, function() { onerror(ERR_READ); }); }); }, - close: function (callback) { + close : function(callback) { if (callback) callback(); } @@ -676,7 +670,7 @@ define([],function() { } return { - add: function (name, reader, onend, onprogress, options) { + add : function(name, reader, onend, onprogress, options) { var header, filename, date; function writeHeader(callback) { @@ -684,11 +678,11 @@ define([],function() { date = options.lastModDate || new Date(); header = getDataHelper(26); files[name] = { - headerArray: header.array, - directory: options.directory, - filename: filename, - offset: datalength, - comment: getBytes(encodeUTF8(options.comment || "")) + headerArray : header.array, + directory : options.directory, + filename : filename, + offset : datalength, + comment : getBytes(encodeUTF8(options.comment || "")) }; header.view.setUint32(0, 0x14000808); if (options.version) @@ -720,7 +714,7 @@ define([],function() { footer.view.setUint32(12, reader.size, true); header.view.setUint32(18, reader.size, true); } - writer.writeUint8Array(footer.array, function () { + writer.writeUint8Array(footer.array, function() { datalength += 16; terminate(onend); }, onwriteerror); @@ -737,7 +731,7 @@ define([],function() { } filename = getBytes(encodeUTF8(name)); filenames.push(name); - writeHeader(function () { + writeHeader(function() { if (reader) if (dontDeflate || options.level === 0) copy(reader, writer, 0, reader.size, true, writeFooter, onprogress, onreaderror, onwriteerror); @@ -753,7 +747,7 @@ define([],function() { else writeFile(); }, - close: function (callback) { + close : function(callback) { var data, length = 0, index = 0, indexFilename, file; for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) { file = files[filenames[indexFilename]]; @@ -778,8 +772,8 @@ define([],function() { data.view.setUint16(index + 10, filenames.length, true); data.view.setUint32(index + 12, length, true); data.view.setUint32(index + 16, datalength, true); - writer.writeUint8Array(data.array, function () { - terminate(function () { + writer.writeUint8Array(data.array, function() { + terminate(function() { writer.getData(callback); }); }, onwriteerror); @@ -787,28 +781,27 @@ define([],function() { }; } - return obj.zip = { - Reader: Reader, - Writer: Writer, - BlobReader: BlobReader, - Data64URIReader: Data64URIReader, - TextReader: TextReader, - BlobWriter: BlobWriter, - Data64URIWriter: Data64URIWriter, - TextWriter: TextWriter, - createReader: function (reader, callback, onerror) { - reader.init(function () { + obj.zip = { + Reader : Reader, + Writer : Writer, + BlobReader : BlobReader, + Data64URIReader : Data64URIReader, + TextReader : TextReader, + BlobWriter : BlobWriter, + Data64URIWriter : Data64URIWriter, + TextWriter : TextWriter, + createReader : function(reader, callback, onerror) { + reader.init(function() { callback(createZipReader(reader, onerror)); }, onerror); }, - createWriter: function (writer, callback, onerror, dontDeflate) { - writer.init(function () { + createWriter : function(writer, callback, onerror, dontDeflate) { + writer.init(function() { callback(createZipWriter(writer, onerror, dontDeflate)); }, onerror); }, - workerScriptsPath: "", - useWebWorkers: true + workerScriptsPath : "", + useWebWorkers : true }; - -}); +}(O.esri)); \ No newline at end of file diff --git a/samples/tpk-layer.html b/samples/tpk-layer.html index cf5659f..de0b7d9 100644 --- a/samples/tpk-layer.html +++ b/samples/tpk-layer.html @@ -123,8 +123,8 @@ - + - - - + + + @@ -45,26 +45,18 @@ var tilesEntries = null; var tpkLayer = null; - require(["esri/map", - "esri/layers/GraphicsLayer", "esri/graphic", "esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", - "esri/SpatialReference","esri/geometry", - "edit/attachmentsStore", - "dojo/dom", "dojo/on", - "tpk/TPKLayer","tpk/zip", + require([ + "../dist/offline-editor-tpk.js", "dojo/domReady!"], - function(Map, - GraphicsLayer, Graphic, SimpleFillSymbol, SimpleMarkerSymbol, SimpleLineSymbol, - SpatialReference, geometry, - AttachmentsStore, - dom, on, TPKLayer,zip) + function() { var jasmineEnv; loading = dojo.byId("loader-gif"); getFileBtn = dojo.byId("url-btn"); //IMPORTANT! - zip.workerScriptsPath = locationPath + "/../lib/tpk/"; //tell zip.js where to find it's associated scripts +// zip.workerScriptsPath = locationPath + "/../lib/tpk/"; //tell zip.js where to find it's associated scripts - tpkLayer = new TPKLayer(); + tpkLayer = new esri.TPKLayer(); initChooseLocalFile(); @@ -84,7 +76,7 @@ jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; - jasmineEnv.defaultTimeoutInterval = 10000; // 1 sec + jasmineEnv.defaultTimeoutInterval = 3000; // 1 sec var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); diff --git a/test/spec/tpkLayerSpec.js b/test/spec/tpkLayerSpec.js index 987549f..9addc71 100644 --- a/test/spec/tpkLayerSpec.js +++ b/test/spec/tpkLayerSpec.js @@ -26,7 +26,7 @@ describe("TPKLayer module", function(){ async.it("Unzip TPK file", function(done){ var blob = FILE; - zip.createReader(new zip.BlobReader(blob), function (zipReader) { + O.esri.zip.createReader(new O.esri.zip.BlobReader(blob), function (zipReader) { zipReader.getEntries(function (entries) { tilesEntries = entries; @@ -43,10 +43,11 @@ describe("TPKLayer module", function(){ }) async.it("Parse file entry", function(done){ + var obj = {}; tpkLayer._fileEntriesLength = 2; - tpkLayer._unzipConfFiles(tilesEntries,1,function(evt){ - var objectSize = tpkLayer.ObjectSize(evt); - expect(objectSize).toEqual(1); + tpkLayer._unzipConfFiles(tilesEntries,1,obj,function(deferred,token){ + expect(token).toEqual(1); + expect(deferred).toEqual(obj); done(); }) }) @@ -65,8 +66,10 @@ describe("TPKLayer module", function(){ var indexCDI = name.indexOf("CONF.CDI",0); var indexXML = name.indexOf("CONF.XML",0); if(indexCDI == -1 || indexXML == -1){ - tpkLayer._unzipTileFiles(tilesEntries,i,function(result){ - expect(result).toEqual(jasmine.any(Object)); + var obj = {}; + tpkLayer._unzipTileFiles(tilesEntries,i,obj,function(deferred,token){ + expect(token).toEqual(jasmine.any(Number)); + expect(deferred).toEqual(obj); done(); },tpkLayer._self); } @@ -90,8 +93,9 @@ describe("TPKLayer module", function(){ var indexCDI = name.indexOf("CONF.CDI",0); if(indexCDI != -1){ - tpkLayer._unzipConfFiles(tilesEntries,i,function(result){ - expect(result).toEqual(jasmine.any(Object)); + var obj = {}; + tpkLayer._unzipConfFiles(tilesEntries,i,obj,function(deferred,token){ + expect(deferred).toEqual(obj); tpkLayer._parseConfCdi(function(extent){ expect(extent.type).toEqual("extent"); done(); @@ -118,8 +122,9 @@ describe("TPKLayer module", function(){ var indexXML = name.indexOf("CONF.XML",0); if(indexXML != -1){ - tpkLayer._unzipConfFiles(tilesEntries,i,function(result){ - expect(result).toEqual(jasmine.any(Object)); + var obj = {}; + tpkLayer._unzipConfFiles(tilesEntries,i,obj,function(deferred,token){ + expect(deferred).toEqual(obj); tpkLayer._parseConfXml(function(result){ expect(result).toEqual(jasmine.any(Object)); expect(result.lods.length).toBeGreaterThan(0); @@ -255,9 +260,9 @@ describe("TPKLayer module", function(){ tpkLayer._storeTile(url,imgURL,db,function(success,err){ expect(success).toBeTruthy(); - tpkLayer._getInMemTiles("test",null,null,null,null,null,function(img,id,url){ + tpkLayer._getInMemTiles("test",null,null,null,null,"101",function(img,id,url){ expect(img).toBe(imgURL); - expect(id).toBeNull(); + expect(id).toBe("101"); expect(url).toBe("test"); done(); }) From f4f42d7fb70b965962bcc58871ffce39a0e59525 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 20 Aug 2014 16:36:58 -0600 Subject: [PATCH 30/86] renamed OfflineMaps to OfflineMapsNS --- Gruntfile.js | 12 +----------- lib/{OfflineMaps.js => OfflineMapsNS.js} | 0 2 files changed, 1 insertion(+), 11 deletions(-) rename lib/{OfflineMaps.js => OfflineMapsNS.js} (100%) diff --git a/Gruntfile.js b/Gruntfile.js index c54ece6..5e0f6a5 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -41,15 +41,6 @@ module.exports = function(grunt) { '* Apache License' + '*/\n' }, - full: { - src: [ - 'lib/*.js', - 'lib/edit/*.js', - 'lib/tiles/*.js', - 'lib/tpk/*.js' - ], - dest: 'dist/offline-editor-src.js' - }, edit: { src: [ 'lib/*.js', @@ -67,7 +58,7 @@ module.exports = function(grunt) { tpk: { src: [ 'lib/tpk/TPKLayer.js', - 'lib/OfflineMaps.js', + 'lib/OfflineMapsNS.js', 'lib/tiles/TilesStore.js', 'lib/tpk/zip.js', 'lib/tpk/autoCenterMap.js', @@ -90,7 +81,6 @@ module.exports = function(grunt) { }, dist: { files: { - 'dist/offline-editor.js': ['dist/offline-editor-src.js'], 'dist/offline-editor-edit.js': ['dist/offline-editor-edit-src.js'], 'dist/offline-editor-tiles.js': ['dist/offline-editor-tiles-src.js'], 'dist/offline-editor-tpk.js': ['dist/offline-editor-tpk-src.js'] diff --git a/lib/OfflineMaps.js b/lib/OfflineMapsNS.js similarity index 100% rename from lib/OfflineMaps.js rename to lib/OfflineMapsNS.js From 1c3b92cf01ce2c23841086357aa2af770a0c5de7 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 21 Aug 2014 16:00:43 -0600 Subject: [PATCH 31/86] initial refactor for offlineTilesEnabler --- Gruntfile.js | 28 +- lib/tiles/FileSaver.js | 18 +- lib/tiles/OfflineTilesEnablerLayer.js | 10 +- lib/tiles/TilesCore.js | 672 +++++++++++------------ lib/tiles/TilesStore.js | 488 ++++++++-------- lib/tiles/base64utils.js | 140 ++--- lib/tiles/offlineTilesEnabler.js | 27 +- lib/tiles/tilingScheme.js | 92 ++-- samples/appcache-tiles.html | 8 +- samples/tiles-indexed-db.html | 12 +- test/SpecRunner.offlineTilesEnabler.html | 6 +- 11 files changed, 748 insertions(+), 753 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 5e0f6a5..aeb3ced 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -48,12 +48,31 @@ module.exports = function(grunt) { ], dest: 'dist/offline-editor-edit-src.js' }, - tiles: { + /* Tiles basic is for use with WebMaps. Cannot be reloaded or restarted while offline */ + tilesBasic: { src: [ 'lib/*.js', - 'lib/tiles/*.js' + 'lib/tiles/base64utils.js', + 'lib/tiles/FileSaver.js', + 'lib/tiles/offlineTilesEnabler.js', + 'lib/tiles/TilesCore.js', + 'lib/tiles/TilesStore.js', + 'lib/tiles/tilingScheme.js' ], - dest: 'dist/offline-editor-tiles-src.js' + dest: 'dist/offline-editor-tiles-basic-src.js' + }, + /* Tiles advanced is for use with tiled map services. Works with reload or restart while offline */ + tilesAdvanced: { + src: [ + 'lib/*.js', + 'lib/tiles/base64utils.js', + 'lib/tiles/FileSaver.js', + 'lib/tiles/offlineTilesEnablerLayer.js', + 'lib/tiles/TilesCore.js', + 'lib/tiles/TilesStore.js', + 'lib/tiles/tilingScheme.js' + ], + dest: 'dist/offline-editor-tiles-advanced-src.js' }, tpk: { src: [ @@ -82,7 +101,8 @@ module.exports = function(grunt) { dist: { files: { 'dist/offline-editor-edit.js': ['dist/offline-editor-edit-src.js'], - 'dist/offline-editor-tiles.js': ['dist/offline-editor-tiles-src.js'], + 'dist/offline-editor-tiles-basic-min.js': ['dist/offline-editor-tiles-basic-src.js'], + 'dist/offline-editor-tiles-advanced-min.js': ['dist/offline-editor-tiles-advanced-src.js'], 'dist/offline-editor-tpk.js': ['dist/offline-editor-tpk-src.js'] } } diff --git a/lib/tiles/FileSaver.js b/lib/tiles/FileSaver.js index adc5527..553cc91 100644 --- a/lib/tiles/FileSaver.js +++ b/lib/tiles/FileSaver.js @@ -13,11 +13,11 @@ /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ -define([],function() -{ -var saveAs = saveAs - || (typeof navigator !== 'undefined' && navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) - || (function(view) { + +O.esri.Tiles.saveAs = +// IE 10 support, see Eli Grey's original source +// || (typeof navigator !== 'undefined' && navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) + function(view) { "use strict"; var doc = view.document @@ -226,13 +226,11 @@ var saveAs = saveAs view.addEventListener("unload", process_deletion_queue, false); return saveAs; -}(this.self || this.window || this.content)); + +}(this.self || this.window || this.content); // `self` is undefined in Firefox for Android content script context // while `this` is nsIContentFrameMessageManager // with an attribute `content` that corresponds to the window //if (typeof module !== 'undefined') module.exports = saveAs; - return { - saveAs: saveAs - } -}); + diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index 78a44a3..aa510d0 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -2,22 +2,16 @@ define([ "dojo/query", "dojo/request", "dojo/_base/declare", - "tiles/TilesCore", - "tiles/base64utils", - "tiles/TilesStore", - "tiles/tilingScheme", - "tiles/FileSaver", "esri/layers/LOD", "esri/geometry/Point", "esri/geometry/Extent", "esri/layers/TileInfo", "esri/SpatialReference", "esri/layers/TiledMapServiceLayer" -], function(query, request, declare,TilesCore,Base64Utils,TilesStore,TilingScheme, - FileSaver,LOD,Point,Extent,TileInfo,SpatialReference,TiledMapServerLayer) +], function(query, request, declare,LOD,Point,Extent,TileInfo,SpatialReference,TiledMapServerLayer) { "use strict"; - return declare("OfflineTileEnablerLayer",[TiledMapServerLayer],{ + return declare("esri.OfflineTileEnablerLayer",[TiledMapServerLayer],{ tileInfo: null, _imageType: "", diff --git a/lib/tiles/TilesCore.js b/lib/tiles/TilesCore.js index c33dfd8..5f57009 100644 --- a/lib/tiles/TilesCore.js +++ b/lib/tiles/TilesCore.js @@ -2,365 +2,353 @@ * This library contains common core code between offlineTilesEnabler.js * and OfflineTilesEnablerLayer.js */ -define([ - "dojo/query", - "dojo/request", - "esri/geometry/Point", - "esri/geometry/Extent", - "esri/SpatialReference", - "esri/layers/TileInfo", - "esri/layers/LOD", - "tiles/base64utils", - "tiles/tilingScheme", - "tiles/FileSaver" - ], - function(query,request,Point,Extent,SpatialReference,TileInfo,LOD,Base64Utils,TilingScheme,FileSaver){ - "use strict"; - var TilesCore = function(){ - /** - * Retrieves a tile from local store. - * @param image a holder for the image that is retrieved from storage. - * @param imageType - * @param url the url of the tile - * @param tileid a reference to the tile's unique level, row and column - * @param store - * @private - */ - this._getTiles = function(image,imageType,url,tileid,store){ - store.retrieve(url, function(success, offlineTile) - { console.log("TILE RETURN " + success + ", " + offlineTile) - /* when the .getTileUrl() callback is triggered we replace the temporary URL originally returned by the data:image url */ - // search for the img with src="void:"+level+"-"+row+"-"+col and replace with actual url - image = query("img[src="+tileid+"]")[0]; - var imgURL; +O.esri.Tiles.TilesCore = function(){ - console.assert(image !== "undefined", "undefined image detected"); + /** + * Retrieves a tile from local store. + * @param image a holder for the image that is retrieved from storage. + * @param imageType + * @param url the url of the tile + * @param tileid a reference to the tile's unique level, row and column + * @param store + * @private + */ + this._getTiles = function(image,imageType,url,tileid,store,query){ + store.retrieve(url, function(success, offlineTile) + { console.log("TILE RETURN " + success + ", " + offlineTile) + /* when the .getTileUrl() callback is triggered we replace the temporary URL originally returned by the data:image url */ + // search for the img with src="void:"+level+"-"+row+"-"+col and replace with actual url + image = query("img[src="+tileid+"]")[0]; + var imgURL; - if( success ) - { - image.style.borderColor = "blue"; - console.log("found tile offline", url); - imgURL = "data:image/" + imageType +";base64," + offlineTile.img; - } - else - { - image.style.borderColor = "green"; - console.log("tile is not in the offline store", url); - imgURL = "data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="; - } - // when we return a nonexistent url to the image, the TiledMapServiceLayer::_tileErrorHandler() method - // sets img visibility to 'hidden', so we need to show the image back once we have put the data:image - image.style.visibility = "visible"; - image.src = imgURL; - return ""; /* this result goes nowhere, seriously */ - }); - }; + console.assert(image !== "undefined", "undefined image detected"); - /** - * Retrieves an image from a tile url and then stores it locally. - * @param url The image's url - * @param proxyPath - * @param store - * @param callback - * @private - */ - this._storeTile= function(url,proxyPath,store,callback) // callback(success, msg) + if( success ) { - url = url.split("?")[0]; - - /* download the tile */ - var imgurl = proxyPath ? proxyPath + "?" + url : url; - var req = new XMLHttpRequest(); - req.open("GET", imgurl, true); - req.overrideMimeType("text/plain; charset=x-user-defined"); // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM%2FXMLHttpRequest%2FUsing_XMLHttpRequest#Handling_binary_data - - req.onload = function () { - if (req.status === 200 && req.responseText !== "") { - var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(this.responseText)); - - var tile = { - url: url, - img: img - }; - - store.store(tile, callback); - } - else { - console.log("xhr failed for", imgurl); - callback(false, req.status + " " + req.statusText + ": " + req.response + " when downloading " + imgurl); - } - }; - req.onerror = function (e) { - console.log("xhr failed for", imgurl); - callback(false, e); - }; - req.send(null); - }; - - /** - * Retrieves all the cells within a certain extent - * @param context Layer - * @param minLevel minimum zoom level - * @param maxLevel maximum zoom level - * @param extent Esri.Extent - * @param callback - * @private - */ - this._createCellsForOffline = function(context,minLevel,maxLevel,extent,callback){ - var tilingScheme = new TilingScheme(context); - var cells = []; - - for(var level=minLevel; level<=maxLevel; level++) - { - var level_cell_ids = tilingScheme.getAllCellIdsInExtent(extent,level); - - level_cell_ids.forEach(function(cell_id) - { - cells.push({ level: level, row: cell_id[1], col: cell_id[0]}); - }); - - // if the number of requested tiles is excessive, we just stop - if( cells.length > 5000 && level !== maxLevel) - { - console.log("enough is enough!"); - break; - } - } - callback(cells); + image.style.borderColor = "blue"; + console.log("found tile offline", url); + imgURL = "data:image/" + imageType +";base64," + offlineTile.img; } - - /** - * Saves locally stored tiles to a csv - * @param fileName - * @param store - * @param callback - * @private - */ - this._saveToFile = function(fileName,store,callback){ - var csv = []; - - csv.push("url,img"); - store.getAllTiles(function(url,img,evt) - { - if(evt==="end") - { - var blob = new Blob([ csv.join("\r\n") ], {type:"text/plain;charset=utf-8"}); - var saver = FileSaver.saveAs(blob, fileName); - - if( saver.readyState === saver.DONE ) - { - if( saver.error ) - { - return callback(false,"Error saving file " + fileName); - } - return callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); - } - saver.onerror = function() { - callback(false,"Error saving file " + fileName); - }; - saver.onwriteend = function() - { - callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); - }; - } - else - { - csv.push(url+","+img); - } - }); - }; - - /** - * Makes a request to a tile url and uses that as a basis for the - * the average tile size. - * Future Iterations could call multiple tiles and do an actual average. - * @param callback - * @returns {Number} Returns NaN if there was a problem retrieving the tile - */ - this._estimateTileSize = function(lastTileUrl,proxyPath,callback) + else { - if(lastTileUrl) + image.style.borderColor = "green"; + console.log("tile is not in the offline store", url); + imgURL = "data:image/png;base64,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pmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg=="; + } + // when we return a nonexistent url to the image, the TiledMapServiceLayer::_tileErrorHandler() method + // sets img visibility to 'hidden', so we need to show the image back once we have put the data:image + image.style.visibility = "visible"; + image.src = imgURL; + return ""; /* this result goes nowhere, seriously */ + }); + }; + + /** + * Retrieves an image from a tile url and then stores it locally. + * @param url The image's url + * @param proxyPath + * @param store + * @param callback + * @private + */ + this._storeTile= function(url,proxyPath,store,callback) // callback(success, msg) + { + url = url.split("?")[0]; + + /* download the tile */ + var imgurl = proxyPath ? proxyPath + "?" + url : url; + var req = new XMLHttpRequest(); + req.open("GET", imgurl, true); + req.overrideMimeType("text/plain; charset=x-user-defined"); // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM%2FXMLHttpRequest%2FUsing_XMLHttpRequest#Handling_binary_data + + req.onload = function () { + if (req.status === 200 && req.responseText !== "") { + var img = O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(this.responseText)); + + var tile = { + url: url, + img: img + }; + + store.store(tile, callback); + } + else { + console.log("xhr failed for", imgurl); + callback(false, req.status + " " + req.statusText + ": " + req.response + " when downloading " + imgurl); + } + }; + req.onerror = function (e) { + console.log("xhr failed for", imgurl); + callback(false, e); + }; + req.send(null); + }; + + /** + * Retrieves all the cells within a certain extent + * @param context Layer + * @param minLevel minimum zoom level + * @param maxLevel maximum zoom level + * @param extent Esri.Extent + * @param callback + * @private + */ + this._createCellsForOffline = function(context,minLevel,maxLevel,extent,callback){ + var tilingScheme = new O.esri.Tiles.TilingScheme(context); + var cells = []; + + for(var level=minLevel; level<=maxLevel; level++) + { + var level_cell_ids = tilingScheme.getAllCellIdsInExtent(extent,level); + + level_cell_ids.forEach(function(cell_id) + { + cells.push({ level: level, row: cell_id[1], col: cell_id[0]}); + }); + + // if the number of requested tiles is excessive, we just stop + if( cells.length > 5000 && level !== maxLevel) + { + console.log("enough is enough!"); + break; + } + } + callback(cells); + } + + /** + * Saves locally stored tiles to a csv + * @param fileName + * @param store + * @param callback + * @private + */ + this._saveToFile = function(fileName,store,callback){ + var csv = []; + + csv.push("url,img"); + store.getAllTiles(function(url,img,evt) + { + if(evt==="end") + { + var blob = new Blob([ csv.join("\r\n") ], {type:"text/plain;charset=utf-8"}); + var saver = O.esri.Tiles.saveAs(blob, fileName); + + if( saver.readyState === saver.DONE ) { - var url = proxyPath? proxyPath + "?" + lastTileUrl : lastTileUrl; - request.get(url,{ - handleAs: "text/plain; charset=x-user-defined", - headers: { - "X-Requested-With": "" //bypasses a dojo xhr bug - }, - timeout: 2000 - }).then(function(response){ - var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(response)); - callback(img.length + url.length,null); - }, - function(err){ - callback(null,err); - }); + if( saver.error ) + { + return callback(false,"Error saving file " + fileName); + } + return callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); } - else{ - callback(NaN); + saver.onerror = function() { + callback(false,"Error saving file " + fileName); + }; + saver.onwriteend = function() + { + callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName); + }; + } + else + { + csv.push(url+","+img); + } + }); + }; + + /** + * Makes a request to a tile url and uses that as a basis for the + * the average tile size. + * Future Iterations could call multiple tiles and do an actual average. + * @param request "dojo/request" + * @param lastTileUrl FQDN of a tile location + * @param proxyPath your local proxy + * @param callback + * @returns {Number} Returns NaN if there was a problem retrieving the tile + */ + this._estimateTileSize = function(request,lastTileUrl,proxyPath,callback) + { + if(lastTileUrl) + { + var url = proxyPath? proxyPath + "?" + lastTileUrl : lastTileUrl; + request.get(url,{ + handleAs: "text/plain; charset=x-user-defined", + headers: { + "X-Requested-With": "" //bypasses a dojo xhr bug + }, + timeout: 2000 + }).then(function(response){ + var img = O.esri.Tiles.Base64Utils.wordToBase64(O.esri.Tiles.Base64Utils.stringToWord(response)); + callback(img.length + url.length,null); + }, + function(err){ + callback(null,err); + }); + } + else{ + callback(NaN); + } + }; + + /** + * Loads a csv file into storage. + * Format is "url,img\r\n somebase64image,http://esri.com" + * @param file + * @param store + * @param callback + * @private + */ + this._loadFromFile = function(file,store,callback){ + if (window.File && window.FileReader && window.FileList && window.Blob) + { + // Great success! All the File APIs are supported. + var reader = new FileReader(); + reader.onload = function(evt) + { + var csvContent = evt.target.result; + var tiles = csvContent.split("\r\n"); + var tileCount = 0; + var pair, tile; + + if(tiles[0] !== "url,img") + { + return callback(false, "File " + file.name + " doesn't contain tiles that can be loaded"); + } + + for(var i=1; i>16)+(b>>16)+(l>>16); - return (m<<16)|(l&0xFFFF); // word - }; +O.esri.Tiles.Base64Utils.addWords=function(/* word */a, /* word */b){ + // summary: + // add a pair of words together with rollover + var l=(a&0xFFFF)+(b&0xFFFF); + var m=(a>>16)+(b>>16)+(l>>16); + return (m<<16)|(l&0xFFFF); // word +}; - // word-based conversion method, for efficiency sake; - // most digests operate on words, and this should be faster - // than the encoding version (which works on bytes). - var chrsz=8; // 16 for Unicode - var mask=(1<>5]|=(s.charCodeAt(i/chrsz)&mask)<<(i%32); - } - return wa; // word[] - }; + // word-based conversion method, for efficiency sake; + // most digests operate on words, and this should be faster + // than the encoding version (which works on bytes). + var chrsz=8; // 16 for Unicode + var mask=(1<>5]>>>(i%32))&mask)); - } - return s.join(""); // string - }; - Base64Utils.wordToHex=function(/* word[] */wa){ - // summary: - // convert an array of words to a hex tab - var h="0123456789abcdef", s=[]; - for(var i=0, l=wa.length*4; i>2]>>((i%4)*8+4))&0xF)+h.charAt((wa[i>>2]>>((i%4)*8))&0xF)); - } - return s.join(""); // string - }; - Base64Utils.wordToBase64=function(/* word[] */wa){ - // summary: - // convert an array of words to base64 encoding, should be more efficient - // than using dojox.encoding.base64 - var p="=", tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", s=[]; - for(var i=0, l=wa.length*4; i>2]>>8*(i%4))&0xFF)<<16)|(((wa[i+1>>2]>>8*((i+1)%4))&0xFF)<<8)|((wa[i+2>>2]>>8*((i+2)%4))&0xFF); - for(var j=0; j<4; j++){ - if(i*8+j*6>wa.length*32){ - s.push(p); - } else { - s.push(tab.charAt((t>>6*(3-j))&0x3F)); - } - } - } - return s.join(""); // string - }; + var wa=[]; + for(var i=0, l=s.length*chrsz; i>5]|=(s.charCodeAt(i/chrsz)&mask)<<(i%32); + } + return wa; // word[] +}; + +O.esri.Tiles.Base64Utils.wordToString=function(/* word[] */wa){ + // summary: + // convert an array of words to a string + + // word-based conversion method, for efficiency sake; + // most digests operate on words, and this should be faster + // than the encoding version (which works on bytes). + var chrsz=8; // 16 for Unicode + var mask=(1<>5]>>>(i%32))&mask)); + } + return s.join(""); // string +}; + +O.esri.Tiles.Base64Utils.wordToHex=function(/* word[] */wa){ + // summary: + // convert an array of words to a hex tab + var h="0123456789abcdef", s=[]; + for(var i=0, l=wa.length*4; i>2]>>((i%4)*8+4))&0xF)+h.charAt((wa[i>>2]>>((i%4)*8))&0xF)); + } + return s.join(""); // string +}; + +O.esri.Tiles.Base64Utils.wordToBase64=function(/* word[] */wa){ + // summary: + // convert an array of words to base64 encoding, should be more efficient + // than using dojox.encoding.base64 + var p="=", tab="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", s=[]; + for(var i=0, l=wa.length*4; i>2]>>8*(i%4))&0xFF)<<16)|(((wa[i+1>>2]>>8*((i+1)%4))&0xFF)<<8)|((wa[i+2>>2]>>8*((i+2)%4))&0xFF); + for(var j=0; j<4; j++){ + if(i*8+j*6>wa.length*32){ + s.push(p); + } else { + s.push(tab.charAt((t>>6*(3-j))&0x3F)); + } + } + } + return s.join(""); // string +}; - return Base64Utils; -}); diff --git a/lib/tiles/offlineTilesEnabler.js b/lib/tiles/offlineTilesEnabler.js index cba052b..c2407f8 100644 --- a/lib/tiles/offlineTilesEnabler.js +++ b/lib/tiles/offlineTilesEnabler.js @@ -1,16 +1,11 @@ define([ "dojo/query", "dojo/request", - "dojo/_base/declare", - "tiles/TilesCore", - "tiles/base64utils", - "tiles/TilesStore", - "tiles/tilingScheme", - "tiles/FileSaver" - ], function(query, request, declare,TilesCore,Base64Utils,TilesStore,TilingScheme,FileSaver) + "dojo/_base/declare" + ], function(query, request, declare) { "use strict"; - return declare([],{ + return declare("esri.OfflineTilesEnabler",[],{ /** * Utility method to get the basemap layer reference * @param map @@ -35,7 +30,7 @@ define([ { console.log("extending layer", layer.url); - layer._tilesCore = new TilesCore(); + layer._tilesCore = new O.esri.Tiles.TilesCore(); layer._lastTileUrl = ""; layer._imageType = ""; @@ -52,7 +47,7 @@ define([ layer.offline = { online: isOnline, - store: new TilesStore(), + store: new O.esri.Tiles.TilesStore(), proxyPath: "../lib/resource-proxy/proxy.php" }; @@ -95,9 +90,9 @@ define([ /* temporary URL returned immediately, as we haven't retrieved the image from the indexeddb yet */ var tileid = "void:/"+level+"/"+row+"/"+col; - var img = null;; + var img = null; - layer._tilesCore._getTiles(img,this._imageType,url,tileid,this.offline.store); + layer._tilesCore._getTiles(img,this._imageType,url,tileid,this.offline.store,query); return tileid; }; @@ -123,7 +118,7 @@ define([ */ layer.getLevelEstimation = function(extent, level, tileSize) { - var tilingScheme = new TilingScheme(this); + var tilingScheme = new O.esri.Tiles.TilingScheme(this); var cellIds = tilingScheme.getAllCellIdsInExtent(extent,level); var levelEstimation = { @@ -213,7 +208,7 @@ define([ */ layer.getTilePolygons = function(callback) // callback(Polygon polygon) or callback(null, error) { - layer._tilesCore._getTilePolygons(this.offline.store,layer.url,this,callback); + layer._tilesCore._getTilePolygons(Polygon,this.offline.store,layer.url,this,callback); }; /** @@ -248,7 +243,7 @@ define([ */ layer.estimateTileSize = function(callback) { - layer._tilesCore._estimateTileSize(this._lastTileUrl,this.offline.proxyPath,callback); + layer._tilesCore._estimateTileSize(request,this._lastTileUrl,this.offline.proxyPath,callback); }; /** @@ -268,7 +263,7 @@ define([ * @returns Array */ layer.getTileUrlsByExtent = function(extent,level){ - var tilingScheme = new TilingScheme(layer); + var tilingScheme = new O.esri.Tiles.TilingScheme(layer); var level_cell_ids = tilingScheme.getAllCellIdsInExtent(extent,level); var cells = []; diff --git a/lib/tiles/tilingScheme.js b/lib/tiles/tilingScheme.js index bcfb56f..b4888a1 100644 --- a/lib/tiles/tilingScheme.js +++ b/lib/tiles/tilingScheme.js @@ -1,60 +1,54 @@ -define([ - "esri/geometry/Polygon" - ], function (Polygon) { - "use strict"; - var TilingScheme = function (layer) { - this.tileInfo = layer.tileInfo; - }; +O.esri.Tiles.TilingScheme = function (layer) { + this.tileInfo = layer.tileInfo; +}; - TilingScheme.prototype = { - getCellIdFromXy: function (x, y, level) { - var col = Math.floor((x - this.tileInfo.origin.x) / (this.tileInfo.cols * this.tileInfo.lods[level].resolution)); - var row = Math.floor((this.tileInfo.origin.y - y) / (this.tileInfo.rows * this.tileInfo.lods[level].resolution)); - return [col, row]; - }, +O.esri.Tiles.TilingScheme.prototype = { + getCellIdFromXy: function (x, y, level) { + var col = Math.floor((x - this.tileInfo.origin.x) / (this.tileInfo.cols * this.tileInfo.lods[level].resolution)); + var row = Math.floor((this.tileInfo.origin.y - y) / (this.tileInfo.rows * this.tileInfo.lods[level].resolution)); + return [col, row]; + }, - getCellPolygonFromCellId: function (cellId, level) { - var col1 = cellId[0]; - var row1 = cellId[1]; - var col2 = col1 + 1; - var row2 = row1 + 1; + getCellPolygonFromCellId: function (Polygon,cellId, level) { + var col1 = cellId[0]; + var row1 = cellId[1]; + var col2 = col1 + 1; + var row2 = row1 + 1; - var x1 = this.tileInfo.origin.x + (col1 * this.tileInfo.cols * this.tileInfo.lods[level].resolution); - var y1 = this.tileInfo.origin.y - (row1 * this.tileInfo.rows * this.tileInfo.lods[level].resolution); - var x2 = this.tileInfo.origin.x + (col2 * this.tileInfo.cols * this.tileInfo.lods[level].resolution); - var y2 = this.tileInfo.origin.y - (row2 * this.tileInfo.rows * this.tileInfo.lods[level].resolution); + var x1 = this.tileInfo.origin.x + (col1 * this.tileInfo.cols * this.tileInfo.lods[level].resolution); + var y1 = this.tileInfo.origin.y - (row1 * this.tileInfo.rows * this.tileInfo.lods[level].resolution); + var x2 = this.tileInfo.origin.x + (col2 * this.tileInfo.cols * this.tileInfo.lods[level].resolution); + var y2 = this.tileInfo.origin.y - (row2 * this.tileInfo.rows * this.tileInfo.lods[level].resolution); - var polygon = new Polygon(this.tileInfo.spatialReference); - polygon.addRing([ - [x1, y1], // clockwise - [x2, y1], - [x2, y2], - [x1, y2], - [x1, y1] - ]); - return polygon; - }, + var polygon = new Polygon(this.tileInfo.spatialReference); + polygon.addRing([ + [x1, y1], // clockwise + [x2, y1], + [x2, y2], + [x1, y2], + [x1, y1] + ]); + return polygon; + }, - getAllCellIdsInExtent: function (extent, gridLevel) { - var cellId0 = this.getCellIdFromXy(extent.xmin, extent.ymin, gridLevel); - var cellId1 = this.getCellIdFromXy(extent.xmax, extent.ymax, gridLevel); + getAllCellIdsInExtent: function (extent, gridLevel) { + var cellId0 = this.getCellIdFromXy(extent.xmin, extent.ymin, gridLevel); + var cellId1 = this.getCellIdFromXy(extent.xmax, extent.ymax, gridLevel); - var i, j; - var i0 = Math.max(Math.min(cellId0[0], cellId1[0]), this.tileInfo.lods[gridLevel].startTileCol); - var i1 = Math.min(Math.max(cellId0[0], cellId1[0]), this.tileInfo.lods[gridLevel].endTileCol); - var j0 = Math.max(Math.min(cellId0[1], cellId1[1]), this.tileInfo.lods[gridLevel].startTileRow); - var j1 = Math.min(Math.max(cellId0[1], cellId1[1]), this.tileInfo.lods[gridLevel].endTileRow); + var i, j; + var i0 = Math.max(Math.min(cellId0[0], cellId1[0]), this.tileInfo.lods[gridLevel].startTileCol); + var i1 = Math.min(Math.max(cellId0[0], cellId1[0]), this.tileInfo.lods[gridLevel].endTileCol); + var j0 = Math.max(Math.min(cellId0[1], cellId1[1]), this.tileInfo.lods[gridLevel].startTileRow); + var j1 = Math.min(Math.max(cellId0[1], cellId1[1]), this.tileInfo.lods[gridLevel].endTileRow); - var cellIds = []; + var cellIds = []; - for (i = i0; i <= i1; i++) { - for (j = j0; j <= j1; j++) { - cellIds.push([i, j]); - } + for (i = i0; i <= i1; i++) { + for (j = j0; j <= j1; j++) { + cellIds.push([i, j]); } - return cellIds; } - }; + return cellIds; + } +}; - return TilingScheme; -}); \ No newline at end of file diff --git a/samples/appcache-tiles.html b/samples/appcache-tiles.html index e0829cc..5fbfd79 100644 --- a/samples/appcache-tiles.html +++ b/samples/appcache-tiles.html @@ -1,5 +1,5 @@ - + @@ -110,8 +110,8 @@ ask if you want to reload the application. var map; -require(["esri/map","utils/appCacheManager","tiles/OfflineTilesEnablerLayer","dojo/on","dojo/domReady!"], - function(Map,AppCacheManager,OfflineTileEnablerLayer,on) { +require(["esri/map","utils/appCacheManager","dojo/on","../dist/offline-editor-tiles-basic-src.js","dojo/domReady!"], + function(Map,AppCacheManager,on) { var tileLayer = null; @@ -153,7 +153,7 @@ require(["esri/map","utils/appCacheManager","tiles/OfflineTilesEnablerLayer","do sliderStyle: "small" }); - tileLayer = new OfflineTileEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ + tileLayer = esri.OfflineTileEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ console.log("Offline tile lib is enabled. Application state is: " + Offline.state); },_isOnline); diff --git a/samples/tiles-indexed-db.html b/samples/tiles-indexed-db.html index d65e5ba..194f748 100644 --- a/samples/tiles-indexed-db.html +++ b/samples/tiles-indexed-db.html @@ -231,9 +231,7 @@ - - - + + + @@ -32,17 +32,17 @@ require(["esri/map", "esri/layers/GraphicsLayer", "esri/graphic", "esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", "esri/SpatialReference","esri/geometry", - "edit/attachmentsStore", "dojo/dom", "dojo/on", "dojo/query", - "dojo/dom-construct", "dojo/domReady!"], + "dojo/dom-construct", + "../dist/offline-editor-edit-src.js", + "dojo/domReady!"], function(Map, GraphicsLayer, Graphic, SimpleFillSymbol, SimpleMarkerSymbol, SimpleLineSymbol, - SpatialReference, geometry, - AttachmentsStore, + SpatialReference, geometry, dom, on, query, domConstruct) { - g_attachmentsStore = new AttachmentsStore(); + g_attachmentsStore = new O.esri.Edit.AttachmentsStore(); g_inputNode = dom.byId('theFile'); test(); diff --git a/test/SpecRunner.editsStore.html b/test/SpecRunner.editsStore.html index 1aeb6c9..ccad102 100644 --- a/test/SpecRunner.editsStore.html +++ b/test/SpecRunner.editsStore.html @@ -17,9 +17,9 @@ } - - - + + + @@ -33,14 +33,14 @@ require(["esri/map", "esri/layers/GraphicsLayer", "esri/graphic", "esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", "esri/SpatialReference","esri/geometry", - "edit/editsStore", "dojo/dom", "dojo/on", "dojo/query", - "dojo/dom-construct", "dojo/domReady!"], + "dojo/dom-construct", + "../dist/offline-editor-edit-src.js", + "dojo/domReady!"], function(Map, GraphicsLayer, Graphic, SimpleFillSymbol, SimpleMarkerSymbol, SimpleLineSymbol, - SpatialReference, geometry, - editsStore, - dom, on, query, + SpatialReference, geometry, + dom, on, query, domConstruct) { /* @@ -53,7 +53,7 @@ g_map.on('load', test); */ - g_editsStore = editsStore; + g_editsStore = new O.esri.Edit.EditStore(Graphic); test(); function initTestData() diff --git a/test/SpecRunner.offlineAttachments.html b/test/SpecRunner.offlineAttachments.html index a8bb387..c7e4be3 100644 --- a/test/SpecRunner.offlineAttachments.html +++ b/test/SpecRunner.offlineAttachments.html @@ -20,9 +20,9 @@ window.proxyPath = "../lib/resource-proxy/proxy.php"; - - - + + + @@ -40,19 +40,19 @@ "esri/layers/GraphicsLayer", "esri/graphic", "esri/layers/FeatureLayer", "esri/geometry", "esri/request", "dojo/dom", "dojo/on", "dojo/query", - "edit/offlineFeaturesManager", "edit/editsStore", - "dojo/dom-construct", "dojo/domReady!"], + "dojo/dom-construct", + "../dist/offline-editor-edit-src.js", + "dojo/domReady!"], function(Map, GraphicsLayer, Graphic, FeatureLayer, geometry, esriRequest, dom, on, query, - OfflineFeaturesManager,editsStore, domConstruct) { g_modules.esriRequest = esriRequest; g_modules.Graphic = Graphic; - g_offlineFeaturesManager = new OfflineFeaturesManager(); - g_editsStore = editsStore; + g_offlineFeaturesManager = new esri.OfflineFeaturesManager(); + g_editsStore = new O.esri.Edit.EditStore(); esriConfig.defaults.io.proxyUrl = window.proxyPath; @@ -105,7 +105,7 @@ { var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; - jasmineEnv.defaultTimeoutInterval = 10000; // 10 sec + jasmineEnv.defaultTimeoutInterval = 5000; // 10 sec var htmlReporter = new jasmine.HtmlReporter(); jasmineEnv.addReporter(htmlReporter); diff --git a/test/SpecRunner.offlineFeaturesManager.html b/test/SpecRunner.offlineFeaturesManager.html index e7815d4..fbd8210 100644 --- a/test/SpecRunner.offlineFeaturesManager.html +++ b/test/SpecRunner.offlineFeaturesManager.html @@ -19,9 +19,9 @@ } - - - + + + diff --git a/test/SpecRunner.offlineTilesEnabler.html b/test/SpecRunner.offlineTilesEnabler.html index fe6836e..88a854b 100755 --- a/test/SpecRunner.offlineTilesEnabler.html +++ b/test/SpecRunner.offlineTilesEnabler.html @@ -16,9 +16,9 @@ } - - - + + + diff --git a/test/SpecRunner.offlineTilesEnablerLayer.html b/test/SpecRunner.offlineTilesEnablerLayer.html index 3882316..b20085a 100644 --- a/test/SpecRunner.offlineTilesEnablerLayer.html +++ b/test/SpecRunner.offlineTilesEnablerLayer.html @@ -16,9 +16,9 @@ } - - - + + + From 5d87be2ddbd9a028754679519bde5892e38acf2b Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 3 Sep 2014 16:32:34 -0600 Subject: [PATCH 37/86] updated library reference for attachmentsStore --- lib/edit/offlineFeaturesManager.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index 8ca537f..bfe2fe6 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -1,7 +1,5 @@ define([ - "edit/attachmentsStore", - "dojo/Evented", "dojo/_base/Deferred", "dojo/promise/all", @@ -17,7 +15,7 @@ define([ "esri/symbols/SimpleLineSymbol", "esri/symbols/SimpleFillSymbol", "esri/urlUtils"], - function(AttachmentsStore, + function( Evented,Deferred,all,declare,array,domAttr,domStyle,query, esriConfig,GraphicsLayer,Graphic,SimpleMarkerSymbol,SimpleLineSymbol,SimpleFillSymbol,urlUtils) { @@ -59,7 +57,7 @@ define([ try { - this.attachmentsStore = new AttachmentsStore(); + this.attachmentsStore = new O.esri.Edit.AttachmentsStore(); if( /*false &&*/ this.attachmentsStore.isSupported() ) { @@ -102,7 +100,7 @@ define([ } return true; } - alert("The File APIs are not fully supported in this browser."); + console.log("The File APIs are not fully supported in this browser."); return false; }, From c6f2bdc9b800eeef9f65b8e8cf94b2ba9eff47df Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 3 Sep 2014 16:33:02 -0600 Subject: [PATCH 38/86] test spec bug fixes --- test/spec/editsStoreSpec.js | 8 +++++++- test/spec/offlineAttachmentsSpec.js | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/test/spec/editsStoreSpec.js b/test/spec/editsStoreSpec.js index e171f41..6e72ef3 100644 --- a/test/spec/editsStoreSpec.js +++ b/test/spec/editsStoreSpec.js @@ -330,7 +330,8 @@ describe("Public Interface", function() { it("exhaust localStorage capacity", function() { - console.log("this will take some time"); + window.localStorage.clear(); + console.log("this will take some time"); // clean everything before for( var key in window.localStorage ) @@ -349,6 +350,8 @@ describe("Public Interface", function() } // first, fill localStorage up to max capacity + var error = null; + try { var index = 0; @@ -367,8 +370,11 @@ describe("Public Interface", function() catch(err) { console.log(err); + error = err; } + expect(error).not.toBe(null); + // now, try to push one edit var result = g_editsStore.pushEdit(g_editsStore.ADD, 20, g_test.polygonFeature); expect(result.success).toBeFalsy(); diff --git a/test/spec/offlineAttachmentsSpec.js b/test/spec/offlineAttachmentsSpec.js index 0319af9..e3ab922 100644 --- a/test/spec/offlineAttachmentsSpec.js +++ b/test/spec/offlineAttachmentsSpec.js @@ -58,7 +58,7 @@ describe("Attachments", function() clearFeatureLayer( g_featureLayers[3], function(success,response) { expect(success).toBeTruthy(); - var listener = g_featureLayers[3].on('update-end', function(){ listener.remove(); completedOne();}) + var listener = g_featureLayers[3].on('update-end', function(){ listener.remove(); }) g_featureLayers[3].refresh(); done(); }); From 1845d70c7e415d7ec7326025cb34daa65cec8e06 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 3 Sep 2014 17:02:37 -0600 Subject: [PATCH 39/86] updated readme --- README.md | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index c5eba8a..e914a08 100644 --- a/README.md +++ b/README.md @@ -3,23 +3,19 @@ offline-editor-js A prototype JavaScript toolkit for using the ArcGIS API for JavaScript offline. It offers both lightweight editing and tile management capabilities while offline or intermittently offline. It's still a work-in-progress so if you have suggestions open an issue or if you want to make a pull request we welcome your proposed modifications. -*IMPORTANT:* If you want a full, robust offline solution then you should be using our ArcGIS Runtime SDKs for .NET, WPF, Java, iOS, Android and Qt. +*IMPORTANT:* If you want a fully integrated robust offline solution then you should be using our ArcGIS Runtime SDKs for .NET, WPF, Java, iOS, Android and Qt. This repo contains the following libraries: -- `/edit`: handles vector features and stores adds, updates and deletes while offline. Resync's edits with server once connection is reestablished - * `offlineFeaturesManager` - Extends and overrides a feature layer. - * `editsStore` - Provides static helper methods for working with the offline data store. - * `attachmentsStore` - Provides limited support for attachments. -- `/tiles`: stores portions of tiled maps client-side and uses the cached tiles when device is offline - * `offlineTilesEnabler` Extends and overrides a tiled map service from ArcGIS Online or for partial offline use. - * `OfflineTilesEnablerLayer` Extends any Esri tiled basemap service for a web app that has a requirement for browser reload and/or restart. This library should be used in conjunction with an application cache coding pattern. -- `/tpk`: lets you work with TPK files. - * `TPKLayer` - parses a TPK file and displays it as a tiled map layer. -- `/utils`: contains various helper libraries. +- `/dist`: + * `offline-edit-min.js` - stores adds, updates and deletes of features as well as limited attachment support while offline. Resync's edits with server once connection is reestablished. + * `offline-tiles-basic-min.js` stores portions of tiled maps client-side and uses the cached tiles when device is offline or partial offline. Use this library with ArcGIS Online Web maps. + * `offline-tiles-advanced-min.js` Extends any Esri tiled basemap service for a web app that has a requirement for offline browser reload and/or restart. This library should be used in conjunction with an HTML5 application cache coding pattern. + * `offline-tpk-min.js` - parses a TPK file and displays it as a tiled map layer. +- `/utils`: contains various helper library modules. - `/samples`: sample apps to show how to use different aspects of the offline library capabilities. -#Workflows Supported (v1) +#Workflows Supported The following workflow is currently supported for both both features and tiles: 1) Load web application while online. @@ -37,22 +33,22 @@ __Attachment Support__: Attachments are supported with some limitations. See doc #API Doc -##offlineFeaturesManager -Extends and overrides a feature layer. This library allows you to extend esri.layers.FeatureLayer objects with offline capability and manage the resync process. +##Offline Editing of Geographic Features +Extends and overrides an ArcGIS Feature Layer. This library allows you to extend esri.layers.FeatureLayer with offline capabilities and to manage the resync process. -* __Click [here](doc/offlinefeaturesmanager.md) to see the full API doc for `offlineFeaturesManager`__ +* __Click [here](doc/offlinefeaturesmanager.md) to see the full API doc for `offline-edit-min.js`__ -##offlineTilesEnabler +##Offline Mapping Tiles Extends and overrides a tiled map service. Provides the ability to customize the extent used to cut the tiles. See the detailed description of basemap.prepareForOffline() in the "How To Use" section to learn different options. -* __Click [here](doc/offlinetilesenabler.md) to see the full API doc for `offlineTilesEnabler`__ +* __Click [here](doc/offlinetilesenabler.md) to see the full API doc for `offline-tiles-basic-min.js and offline-tiles-advanced-min.js`__ ##TPKLayer -Extends TileMapServiceLayer. You can display TPK files with this library. TPK's are binary tile package files. Go [here](http://resources.arcgis.com/en/help/main/10.1/index.html#//00170000017w000000) for more information on how to create a TPK file. +You can display TPK files with this library. TPK's are binary tile package files. Extends TileMapServiceLayer. Go [here](http://resources.arcgis.com/en/help/main/10.1/index.html#//00170000017w000000) for more information on how to create a TPK file. -* __Click [here](doc/tpklayer.md) to see the full API doc for `TPKLayer`__ +* __Click [here](doc/tpklayer.md) to see the full API doc for `offline-tpk-min.js`__ #How to use @@ -69,12 +65,17 @@ Extends TileMapServiceLayer. You can display TPK files with this library. TPK's 3. Run `git submodule init` and `git submodule update` 4. Try out the apps in the `/samples` folder. +If you need to build the libraries: + +1. From the root directory run `npm install` +2. Run `Grunt build`. If there are no errors, the libraries will be output to `\dist` + ##Samples * `appcache-features.html` - shows how to work with the application manifest, tiles and features. * `appcache-tiles.html` - shows how to work with the application manifest and map tiles. * `attachments-editor.html` - demonstrates how to work with this library using feature attachments. -* ~~`military-offline.html`~~ - renamed `draw-pointlinepoly-offline.html` shows working with points, lines and polygons locally. +* `draw-pointlinepoly-offline.html` shows working with points, lines and polygons locally. * `tpklayer.html` - shows how to work with TPK files. * `tiles-indexed-db.html` - shows how to work with storing tiles locally. * `Gruntfile.js` - a node.js app and its associated `package.json` file to help with creating an application manifest file. From 3919a31d375cb3a314941a715e042110a36c800e Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 4 Sep 2014 16:44:28 -0600 Subject: [PATCH 40/86] update how-to docs --- README.md | 12 +++---- doc/attachments.md | 10 +++--- doc/howtouseeditlibrary.md | 10 +++--- doc/howtousetiles.md | 67 +++++++++-------------------------- doc/howtousetpklibrary.md | 24 +++++++++---- doc/offlinefeaturesmanager.md | 8 ++--- doc/offlinetilesenabler.md | 16 ++++----- doc/tpklayer.md | 2 +- 8 files changed, 63 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index e914a08..94485f6 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,12 @@ A prototype JavaScript toolkit for using the ArcGIS API for JavaScript offline. This repo contains the following libraries: - `/dist`: - * `offline-edit-min.js` - stores adds, updates and deletes of features as well as limited attachment support while offline. Resync's edits with server once connection is reestablished. - * `offline-tiles-basic-min.js` stores portions of tiled maps client-side and uses the cached tiles when device is offline or partial offline. Use this library with ArcGIS Online Web maps. - * `offline-tiles-advanced-min.js` Extends any Esri tiled basemap service for a web app that has a requirement for offline browser reload and/or restart. This library should be used in conjunction with an HTML5 application cache coding pattern. - * `offline-tpk-min.js` - parses a TPK file and displays it as a tiled map layer. + * `offline-edit-min.js` - _(replaces v1.x of OfflineFeaturesManager.js)_ stores adds, updates and deletes of features as well as limited attachment support while offline. Resync's edits with server once connection is reestablished. + * `offline-tiles-basic-min.js` - _(replaces v1.x of offlineTilesEnabler.js)_ stores portions of tiled maps client-side and uses the cached tiles when device is offline or partial offline. Use this library with ArcGIS Online Web maps. + * `offline-tiles-advanced-min.js` - _(replaces v1.x of OfflineTilesEnablerLayer.js)_ Extends any ArcGIS Tiled Map Service that has a requirement for offline browser reload and/or restart. This library should be used in conjunction with an HTML5 application cache coding pattern. + * `offline-tpk-min.js` - _(replaces v1.x of TPKLayer.js)_ parses a TPK file and displays it as a tiled map layer. - `/utils`: contains various helper library modules. -- `/samples`: sample apps to show how to use different aspects of the offline library capabilities. +- `/samples`: samples that show how to use the different offline libraries capabilities. #Workflows Supported The following workflow is currently supported for both both features and tiles: @@ -68,7 +68,7 @@ You can display TPK files with this library. TPK's are binary tile package files If you need to build the libraries: 1. From the root directory run `npm install` -2. Run `Grunt build`. If there are no errors, the libraries will be output to `\dist` +2. Run `Grunt build`. If there are no errors, the minimized _(min)_ and source _(src)_ versions of the libraries will be output to `\dist` ##Samples diff --git a/doc/attachments.md b/doc/attachments.md index d254df3..2948b4a 100644 --- a/doc/attachments.md +++ b/doc/attachments.md @@ -1,5 +1,5 @@ #Attachment Support -The __offlineFeaturesManager__ has support for attachments in offline mode. See [attachments-editor.html](../samples/attachments-editor.html) sample. +The __offline-edit-min.js__ has support for attachments in offline mode. See [attachments-editor.html](../samples/attachments-editor.html) sample. ##What you can do: While your application is in `OFFLINE` mode, you can: @@ -11,20 +11,20 @@ While your application is in `OFFLINE` mode, you can: * when the app goes to `ONLINE` mode, all attachments are sent back to the server and removed from local browser storage ##How you do that: -You can either use the FeatureLayer API directly or use the built-in [AttachmentEditor](https://developers.arcgis.com/javascript/jsapi/attachmenteditor-amd.html) widget that support feature attachment editing. Both approaches work well, and the code you write works the same either if you are on `ONLINE` or `OFFLINE` modes. +You can either use the ArcGIS FeatureLayer API _(esri.layers.FeatureLayer)_ directly or use the built-in [AttachmentEditor](https://developers.arcgis.com/javascript/jsapi/attachmenteditor-amd.html) widget that support feature attachment editing. Both approaches work well, and the code you write works the same either if you are on `ONLINE` or `OFFLINE` modes. The only differences in your code are: -* create an offlineFeaturesManager and enable attachment support: +* create an offlineFeaturesManager enabled for attachment support: - var offlineFeaturesManager = new OfflineFeaturesManager(); + var offlineFeaturesManager = new esri.OfflineFeaturesManager(); offlineFeaturesManager.initAttachments(); * extend your featureLayers with offline editing functionality: offlineFeaturesManager.extend(featureLayer, function(success) { - console.log("layer extended", success? "successfully" : "without success"); + console.log("layer extended", success? "success" : "failed"); }); ###Using the FeatureLayer API diff --git a/doc/howtouseeditlibrary.md b/doc/howtouseeditlibrary.md index ccc6ef1..2284888 100644 --- a/doc/howtouseeditlibrary.md +++ b/doc/howtouseeditlibrary.md @@ -5,16 +5,16 @@ How to use the edit library The `edit` library allows a developer to extend a feature layer with offline editing support. -**Step 1** Include `offline.min.js`, `tiles/offlineTilesEnabler` and `tiles/editsStore` in your app. +**Step 1** Include `offline.min.js`, `offline-tiles-basic-min.js` and `offline-edit-min.js` in your app. The pattern for including the tiles and edit library is called generic script injection. ```html - - -``` - -**Step 2** Include the `tiles/offlineTilesEnabler` library in your app. +**Step 1** Include the `offline-tiles-basic-min.js` library in your app. ```js require([ "esri/map", - "tiles/offlineTilesEnabler"], - function(Map,OfflineTilesEnabler) + "..dist/offline-tiles-basic-min.js"], + function(Map) { ... }); ``` -**Step 3** Once your map is created (either using new Map() or using esriUtils.createMap(webmapid,...), you extend the basemap layer with the offline functionality +**Step 2** Once your map is created (either using _new Map()_ or using _esriUtils.createMap(webmapid,...)_, you extend the basemap layer with the offline functionality ```js var basemapLayer = map.getLayer( map.layerIds[0] ); - var offlineTilesEnabler = new OfflineTilesEnabler(); + var offlineTilesEnabler = new esri.OfflineTilesEnabler(); offlineTilesEnabler.extend(basemapLayer, function(success) { if(success) { @@ -53,7 +36,7 @@ Approach #1 is best for partial offline use cases and it uses the `offlineTilesE } }); ``` -**Step 4** Use the new offline methods on the layer to prepare for offline mode while still online: +**Step 3** Use the new offline methods on the layer to prepare for offline mode while still online: ####basemap.getLevelEstimation(extent, level, tileSize) Returns an object that contains the number of tiles that would need to be downloaded for the specified extent and zoom level, and the estimated byte size of such tiles. This method is useful to give the user an indication of the required time and space before launching the actual download operation: @@ -116,6 +99,7 @@ It calculates the number of tiles that are stored in the indexed db database and ``` ####basemap.getTilePolygons(callback) It calculates the geographic boundary of each of the tiles stored in the indexed db. This method calls the callback once for each tile, passing an esri/geometry/Polygon that can be added to a GraphicsLayer. This method is useful to show graphically which tiles are stored in the local database, like this: + ```js graphics = new GraphicsLayer(); map.addLayer( graphics ); @@ -130,44 +114,27 @@ It calculates the geographic boundary of each of the tiles stored in the indexed } ``` -## Approach #2 - Custom TileLayer +## Approach #2 - Tiled Map Services -This approach is best if you have requirements for restarting or reloading your application while offline. For this approach use the `OfflineTilesEnablerLayer.js` library. This library extends TileMapServiceLayer and you can use it with any Esri tiled basemap layer. +This approach is best if you have requirements for restarting or reloading your browser application while offline. For this approach use the `offline-tiles-advanced-min.js` library. This library extends TileMapServiceLayer and you can use it with any Esri tiled basemap layer. - -**Step 1** Configure paths for dojo loader to find the tiles and vendor modules (you need to set paths relative to the location of your html document), before loading ArcGIS JavaScript API - -```html - - - -``` - -**Step 2** Include the `tiles/offlineTilesEnabler` library in your app. +**Step 1** Include the `offline-tiles-advanced-min.js` library in your app. ```js require([ "esri/map", - "tiles/OfflineTilesEnablerLayer"], - function(Map,OfflineTilesEnablerLayer) + "..dist/offline-tiles-advanced-min.js"], + function(Map) { ... }); ``` -**Step 3** Create a new instance of `OfflineTilesEnablerLayer`. Note, when you instantiate the `Map` leave off the `basemap` property because we are adding a customer tile layer as our basemap. `OfflineTilesEnablerLayer` has three properties in the constructor. The first is the REST endpoint of the basemap you want to use, the second is the callback and the last is an optional parameter to preset the layer as online or offline. This will help with with drawing tiles correctly during offline restarts or reloads. +**Step 2** Create a new instance of `OfflineTilesEnablerLayer`. Note, when you instantiate the `Map` leave off the `basemap` property because we are adding a customer tile layer as our basemap. `OfflineTilesEnablerLayer` has three properties in the constructor. The first is the REST endpoint of the basemap you want to use, the second is the callback and the last is an optional parameter to preset the layer as online or offline. This will help with with drawing tiles correctly during offline restarts or reloads. ```js - tileLayer = new OfflineTilesEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ + + tileLayer = new esri.OfflineTilesEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ console.log("Tile Layer Loaded."); },_isOnline); @@ -182,7 +149,7 @@ This approach is best if you have requirements for restarting or reloading your ``` -All map events will continue to work normally. Although some methods that are typically available will now have to be accessed through the OfflineTilesEnablerLayer such as `getLevel()`, `getMaxZoom()`, and `getMinZoom()`. +All map events will continue to work normally. Although some methods that are typically available will now have to be accessed through OfflineTilesEnablerLayer such as `getLevel()`, `getMaxZoom()`, and `getMinZoom()`. To get the current extent you will need to monitor the `zoom-end` and `pan-end` events like this: diff --git a/doc/howtousetpklibrary.md b/doc/howtousetpklibrary.md index 045cad0..62f287f 100644 --- a/doc/howtousetpklibrary.md +++ b/doc/howtousetpklibrary.md @@ -5,13 +5,23 @@ How to use the TPKLayer library The `TPKLayer` Library allows you to display at TPK file as a map. -**Step 1** Unzip the TPK file. This creates an array of Entry objects. Depending on your operating system you may have to rename the TPK file to .zip so that it becomes a recognized MIME type for the html input element. +**Step 1** Include the `offline-tpk-min.js` library in your app. + +```js + require([ + "esri/map", + "..dist/offline-tpk-min.js"], + function(Map) + { + ... + }); +``` + +**Step 2** Unzip the TPK file. This creates an array of Entry objects. Depending on your operating system you may have to rename the TPK file to .zip so that it becomes a recognized MIME type for the html input element. ```js - //IMPORTANT: Tell zip.js where to find its associated scripts - zip.workerScriptsPath = locationPath + "/../lib/tpk/"; - zip.createReader(new zip.BlobReader(blob), function (zipReader) { + O.esri.zip.createReader(new O.esri.zip.BlobReader(blob), function (zipReader) { zipReader.getEntries(function (entries) { initMap(entries); zipReader.close(function(evt){ @@ -24,12 +34,12 @@ The `TPKLayer` Library allows you to display at TPK file as a map. ``` -**Step 2** Create a new instance of TPKLayer and pass the array of Entry objects from the zipReader into the `extend()` method's constructor. Then add the layer to the map. As soon as this code executes the layer will start parsing the TPK file. +**Step 3** Create a new instance of TPKLayer and pass the array of Entry objects from the zipReader into the `extend()` method's constructor. Then add the layer to the map. As soon as this code executes the layer will start parsing the TPK file. ```js - tpkLayer = new TPKLayer(); + tpkLayer = new esri.TPKLayer(); //Listen for progress events to provide UX feedback tpkLayer.on("progress", function (evt) { @@ -87,7 +97,7 @@ When you need to delete all tiles from the existing data use the following patte **Can I use the TPKLayer with a tiled basemap?** -Yes for ArcGIS API for JavaScript v3.x and ONLY if the TPKs Levels of Detail (LODs) match the tiled map services LODs exactly. +Yes for ArcGIS API for JavaScript v3.8+ and ONLY if the TPKs Levels of Detail (LODs) match the tiled map services LODs exactly. The basemap (base tiled layer) defines the LODs that the map can display. Any other operational tiled layers on the map will not display if they don’t match the basemap’s LODs. Esri.Map doesn’t union LODs of all tiled layers on the map. diff --git a/doc/offlinefeaturesmanager.md b/doc/offlinefeaturesmanager.md index f565c2f..b911ea3 100644 --- a/doc/offlinefeaturesmanager.md +++ b/doc/offlinefeaturesmanager.md @@ -1,14 +1,14 @@ -API offlineFeaturesManager +API OfflineFeaturesManager ================================== -##offlineFeaturesManager +##esri.OfflineFeaturesManager Extends and overrides a feature layer. This library allows you to extend esri.layers.FeatureLayer objects with offline capability and manage the resync process. ###Constructor Constructor | Description --- | --- -`OfflineFeaturesManager()` | Creates an instance of the offlineFeaturesManager class. This library allows you to extend FeatureLayer objects with offline editing capabilities and manage the online/offline resynchronization process. +`esri.OfflineFeaturesManager()` | Creates an instance of the offlineFeaturesManager class. This library allows you to extend FeatureLayer objects with offline editing capabilities and manage the online/offline resynchronization process. ###ENUMs The manager can be in one of these three states (see `getOnlineStatus()` method): @@ -55,7 +55,7 @@ Methods | Returns | Description ##editsStore -Provides a number of public static methods that are used by `offlineFeaturesManager` lib. They provide a low-level storage mechanism using indexedDb browser functions. These methods don't require a `new` statement or a constructor. After the module has been included in your application you can access these methods directly for example: `editsStore.getEditsStoreSizeBytes();`. +Provides a number of public static methods that are used by `OfflineFeaturesManager` lib. They provide a low-level storage mechanism using indexedDb browser functions. These methods don't require a `new` statement or a constructor. After the module has been included in your application you can access these methods directly for example: `O.esri.Edit.EditStore.getEditsStoreSizeBytes();`. ###Public Methods Methods | Returns | Description diff --git a/doc/offlinetilesenabler.md b/doc/offlinetilesenabler.md index e417cc3..cb71c21 100644 --- a/doc/offlinetilesenabler.md +++ b/doc/offlinetilesenabler.md @@ -1,19 +1,19 @@ -API Doc for offlineTilesEnabler +API Doc for OfflineTilesEnabler =============================== -There are two different libraries for taking tiles offline: offlineTilesEnabler.js and OfflineTilesEnablerLayer.js. The first one, offlineTilesEnabler.js, is for use with ArcGIS.com web maps and partial offline scenario. You won't be able to restart or reload your app when using this library. +There are two different libraries for taking tiles offline: `offline-tiles-basic-min.js` and `offline-tiles-advanced-min.js`. The basic library is for use with ArcGIS.com web maps and partial/intermittently offline use cases. You won't be able to restart or reload your app when using this library offline. -If you have a requirement to allow restarting or reloading then you should use OfflineTilesEnablerLayer.js. The OfflineTilesEnablerLayer.js library lets you create a custom basemap layer that extends TiledMapServiceLayer. To view the docs for this library scroll down on this page. +If you have a requirement for restarting or reloading the app while offline then you should use the advanced library. The `offline-tiles-advanced-min.js` library lets you create a custom basemap layer that extends TiledMapServiceLayer. To view the docs for this library scroll down on this page. -##offlineTilesEnabler +##esri.OfflineTilesEnabler Extends and overrides a tiled map service. For use with ArcGIS.com maps or partial-offline situations that don't require a browser restart or reload. -Provides the ability to customize the extent used to cut the tiles. See the detailed description of basemap.prepareForOffline() in the "How To Use" section below to learn different options. +Provides the ability to customize the extent used to cut the tiles. See the detailed description of `basemap.prepareForOffline()` in the "How To Use" section below to learn different options. ###Constructor Constructor | Description --- | --- -`OfflineTilesEnabler()` | Creates an instance of the offlineTilesEnabler class. This library allows you to extend an ArcGISTiledMapServiceLayer with partial offline capability as well as manage the online/offline resynchronization process. +`esri.OfflineTilesEnabler()` | Creates an instance of the offlineTilesEnabler class. This library allows you to extend an ArcGISTiledMapServiceLayer with partial offline capability as well as manage the online/offline resynchronization process. ###Methods @@ -41,12 +41,12 @@ Property | Description ##OfflineTilesEnablerLayer -Extends and overrides a tiled map service. This library creates a custom tiled map layer. It can be used in situations where a browser restart or reload is required. +Extends and overrides a tiled map service. This library can be used in situations where an offline browser restart or reload is required. ###Constructor Constructor | Description --- | --- -`OfflineTilesEnablerLayer(url,callback,state)` | Creates an instance of the offlineTilesEnabler class. This library allows you to extend an ArcGISTiledMapServiceLayer with offline capability as well as manage the online/offline resynchronization process. Any Esri basemap REST endpoint should work. The state property is a boolean for specifying if the application is intializing the layer online (true) or offline (false). When you first load the map you should set this property to `true`. +`esri.OfflineTilesEnablerLayer(url,callback,state)` | Creates an instance of the offlineTilesEnabler class. This library allows you to extend an ArcGISTiledMapServiceLayer with offline capability as well as manage the online/offline resynchronization process. Any Esri basemap REST endpoint should work. The state property is a boolean for specifying if the application is intializing the layer online (true) or offline (false). When you first load the map you should set this property to `true`. ###Methods diff --git a/doc/tpklayer.md b/doc/tpklayer.md index 355a479..af0d72d 100644 --- a/doc/tpklayer.md +++ b/doc/tpklayer.md @@ -9,7 +9,7 @@ Extends a tiled map service and provides the ability to display tiles from a .tp Constructor | Description --- | --- -`TPKLayer()` | Creates an instance of the TPKLayer class. This library allows you to extend a TiledMapServiceLayer for the purpose of displaying a TPK file as a map. +`esri.TPKLayer()` | Creates an instance of the TPKLayer class. This library allows you to extend a TiledMapServiceLayer for the purpose of displaying a TPK file as a map. ###Methods Methods | Returns | Description From 6ee58bce806981717d86df861c8c85f87f483070 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 5 Sep 2014 15:58:04 -0600 Subject: [PATCH 41/86] fixes namespace issues. Allows for simultaneous multiple libraries --- lib/edit/OfflineEditNS.js | 14 ++++++++++++++ lib/tiles/OfflineTilesNS.js | 16 ++++++++++++++++ lib/tpk/OfflineTpkNS.js | 14 ++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 lib/edit/OfflineEditNS.js create mode 100644 lib/tiles/OfflineTilesNS.js create mode 100644 lib/tpk/OfflineTpkNS.js diff --git a/lib/edit/OfflineEditNS.js b/lib/edit/OfflineEditNS.js new file mode 100644 index 0000000..b8dcb69 --- /dev/null +++ b/lib/edit/OfflineEditNS.js @@ -0,0 +1,14 @@ +(function(){ + + if(typeof O != "undefined"){ + O.esri.Edit = {} + } + else{ + O = {}; + O.esri = { + VERSION: '2.0', + Edit: {} + } + } + +}()) diff --git a/lib/tiles/OfflineTilesNS.js b/lib/tiles/OfflineTilesNS.js new file mode 100644 index 0000000..6fc4c9a --- /dev/null +++ b/lib/tiles/OfflineTilesNS.js @@ -0,0 +1,16 @@ +(function(){ + + if(typeof O != "undefined"){ + O.esri.Tiles = {} + } + else{ + O = {}; + O.esri = { + VERSION: '2.0', + Tiles: {} + } + } + +}()) + +"use strict"; \ No newline at end of file diff --git a/lib/tpk/OfflineTpkNS.js b/lib/tpk/OfflineTpkNS.js new file mode 100644 index 0000000..1324084 --- /dev/null +++ b/lib/tpk/OfflineTpkNS.js @@ -0,0 +1,14 @@ +(function(){ + + if(typeof O != "undefined"){ + O.esri.TPK = {} + } + else{ + O = {}; + O.esri = { + VERSION: '2.0', + TPK: {} + } + } + +}()) From 5d8132fc307e5545a7c43c5061834130db9bb346 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 5 Sep 2014 15:58:59 -0600 Subject: [PATCH 42/86] updates to Grunt files --- Gruntfile.js | 17 ++++++++++------- samples/Gruntfile.js | 8 +++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 55274ea..0b6e532 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -41,9 +41,10 @@ module.exports = function(grunt) { '* Apache License' + '*/\n' }, + /* All feature editing capabilities: adds, updates and deletes */ edit: { src: [ - 'lib/*.js', + 'lib/edit/OfflineEditNS.js', 'lib/edit/offlineFeaturesManager.js', 'lib/edit/editsStore.js', 'lib/edit/attachmentsStore.js' @@ -53,7 +54,7 @@ module.exports = function(grunt) { /* Tiles basic is for use with WebMaps. Cannot be reloaded or restarted while offline */ tilesBasic: { src: [ - 'lib/*.js', + 'lib/tiles/OfflineTilesNS.js', 'lib/tiles/base64utils.js', 'lib/tiles/FileSaver.js', 'lib/tiles/offlineTilesEnabler.js', @@ -66,20 +67,21 @@ module.exports = function(grunt) { /* Tiles advanced is for use with tiled map services. Works with reload or restart while offline */ tilesAdvanced: { src: [ - 'lib/*.js', + 'lib/tiles/OfflineTilesNS.js', 'lib/tiles/base64utils.js', 'lib/tiles/FileSaver.js', - 'lib/tiles/offlineTilesEnablerLayer.js', 'lib/tiles/TilesCore.js', 'lib/tiles/TilesStore.js', - 'lib/tiles/tilingScheme.js' + 'lib/tiles/tilingScheme.js', + 'lib/tiles/offlineTilesEnablerLayer.js' ], dest: 'dist/offline-editor-tiles-advanced-src.js' }, + /* TPKLayer - for working directly with tile packages (.tpk files) */ tpk: { src: [ 'lib/tpk/TPKLayer.js', - 'lib/OfflineMapsNS.js', + 'lib/tpk/OfflineTpkNS.js', 'lib/tiles/TilesStore.js', 'lib/tpk/zip.js', 'lib/tpk/autoCenterMap.js', @@ -119,5 +121,6 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.registerTask('build',['concat','uglify']); - grunt.registerTask('buildAll',['jshint','concat','uglify']); + // JSHint not currently working. Needs alot of cleanup. +// grunt.registerTask('buildAll',['jshint','concat','uglify']); } \ No newline at end of file diff --git a/samples/Gruntfile.js b/samples/Gruntfile.js index 6d4865c..50f7b5b 100644 --- a/samples/Gruntfile.js +++ b/samples/Gruntfile.js @@ -59,16 +59,18 @@ module.exports = function(grunt) { verbose: true, timestamp: true }, + /* Include all library files that you need here! */ src: [ "../samples/images/*.png", "../samples/css/*.css", "../vendor/IndexedDBShim/dist/*.js", "../vendor/offline/offline.min.js", - "../lib/tiles/*.js", "../lib/tiles/*.png", "../lib/tiles/*.psd", - "../lib/edit/*.js", - "../utils/*.js" + "../utils/*.js", + "../dist/offline-editor-edit-src.js", + "../dist/offline-editor-tiles-advanced-src.js", + "../dist/offline-editor-tiles-basic-src.js" /* "images/*", "css/*.css" From c221c709a368eeca0e12d2afabd37d3d570df8b4 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 5 Sep 2014 15:59:13 -0600 Subject: [PATCH 43/86] fixes proxy path bug --- lib/tiles/OfflineTilesEnablerLayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index 63e58ba..d5830b1 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -422,7 +422,7 @@ define([ */ _getTileInfoPrivate: function(url, callback){ var req = new XMLHttpRequest(); - var url = this.offline.proxyPath? this.offline.proxyPath + "?" + url + "?f=pjson" : url + "?f=pjson"; + var url = this.offline.proxyPath != null? this.offline.proxyPath + "?" + url + "?f=pjson" : url + "?f=pjson"; req.open("GET", url, true); req.onload = function() { From a0b989e5140a19f307a84d25ceca3d978cc1e0da Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Fri, 5 Sep 2014 16:20:42 -0600 Subject: [PATCH 44/86] minor update to namespaces --- lib/edit/OfflineEditNS.js | 6 ++++++ lib/tiles/OfflineTilesNS.js | 4 ++++ lib/tpk/OfflineTpkNS.js | 9 ++++++++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/edit/OfflineEditNS.js b/lib/edit/OfflineEditNS.js index b8dcb69..29703bf 100644 --- a/lib/edit/OfflineEditNS.js +++ b/lib/edit/OfflineEditNS.js @@ -1,3 +1,7 @@ +/** + * Creates a namespace for the non-AMD libraries in this directory + */ + (function(){ if(typeof O != "undefined"){ @@ -12,3 +16,5 @@ } }()) + +"use strict"; diff --git a/lib/tiles/OfflineTilesNS.js b/lib/tiles/OfflineTilesNS.js index 6fc4c9a..41e0c9b 100644 --- a/lib/tiles/OfflineTilesNS.js +++ b/lib/tiles/OfflineTilesNS.js @@ -1,3 +1,7 @@ +/** + * Creates a namespace for the non-AMD libraries in this directory + */ + (function(){ if(typeof O != "undefined"){ diff --git a/lib/tpk/OfflineTpkNS.js b/lib/tpk/OfflineTpkNS.js index 1324084..72f7b75 100644 --- a/lib/tpk/OfflineTpkNS.js +++ b/lib/tpk/OfflineTpkNS.js @@ -1,3 +1,7 @@ +/** + * Creates a namespace for the non-AMD libraries in this directory + */ + (function(){ if(typeof O != "undefined"){ @@ -7,8 +11,11 @@ O = {}; O.esri = { VERSION: '2.0', - TPK: {} + TPK: {}, + Tiles: {} } } }()) + +"use strict"; From f3a69f6ae50f58976fa5f435b1aef61725305420 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 11:41:13 -0600 Subject: [PATCH 45/86] updated namespace for consistency --- lib/edit/offlineFeaturesManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index bfe2fe6..7d9c5b9 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -20,7 +20,7 @@ define([ esriConfig,GraphicsLayer,Graphic,SimpleMarkerSymbol,SimpleLineSymbol,SimpleFillSymbol,urlUtils) { "use strict"; - return declare("esri.OfflineFeaturesManager",[Evented], + return declare("O.esri.Edit.OfflineFeaturesManager",[Evented], { _onlineStatus: "online", _featureLayers: {}, From ae3ac63622e6c74568b4680b64fc90599d686df9 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 11:41:43 -0600 Subject: [PATCH 46/86] fixed minor bug --- lib/edit/offlineFeaturesManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/edit/offlineFeaturesManager.js b/lib/edit/offlineFeaturesManager.js index 7d9c5b9..644da7c 100644 --- a/lib/edit/offlineFeaturesManager.js +++ b/lib/edit/offlineFeaturesManager.js @@ -585,7 +585,7 @@ define([ getReadableEdit: function(edit) { var layer = this._featureLayers[ edit.layer ]; - var graphic = self._editStore._deserialize(edit.graphic); + var graphic = this._editStore._deserialize(edit.graphic); var readableGraphic = graphic.geometry.type; var layerId = edit.layer.substring(edit.layer.lastIndexOf("/")+1); if(layer) From 523a439f8bab64ee77d1600905e3183b29aef41e Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 12:49:50 -0600 Subject: [PATCH 47/86] update OfflineFeaturesManager namespace --- doc/howtouseeditlibrary.md | 16 +++++++++------- doc/offlinefeaturesmanager.md | 15 +++++++++++---- test/SpecRunner.offlineFeaturesManager.html | 4 ++-- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/doc/howtouseeditlibrary.md b/doc/howtouseeditlibrary.md index 2284888..0688f91 100644 --- a/doc/howtouseeditlibrary.md +++ b/doc/howtouseeditlibrary.md @@ -3,9 +3,9 @@ How to use the edit library ##`edit` library -The `edit` library allows a developer to extend a feature layer with offline editing support. +The `edit` library allows a developer to extend a feature layer with offline editing support. You can combine this functionality with offline tiles. -**Step 1** Include `offline.min.js`, `offline-tiles-basic-min.js` and `offline-edit-min.js` in your app. The pattern for including the tiles and edit library is called generic script injection. +**Step 1** Include `offline.min.js`, `offline-tiles-basic-min.js` and `offline-edit-min.js` in your app. The pattern for how we include the tiles and edit library within the _require_ statement is called generic script injection. ```html @@ -22,7 +22,7 @@ The `edit` library allows a developer to extend a feature layer with offline edi **Step 2** Once your map is created (either using new Map() or using esriUtils.createMap(webmapid,...), you create a new OfflineFeaturesManager instance and starting assigning events listeners to tie the library into your user interface: ```js - var offlineFeaturesManager = new esri.OfflineFeaturesManager(); + var offlineFeaturesManager = new O.esri.Edit.OfflineFeaturesManager(); offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_ENQUEUED, updateStatus); offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT, updateStatus); offlineFeaturesManager.on(offlineFeaturesManager.events.ALL_EDITS_SENT, updateStatus); @@ -116,12 +116,14 @@ Within your application you can manually check online status and then update you ``` -####editsStore.hasPendingEdits() -You can check if there are any edits pending. If there are then iterate `editsStore._retrieveEditsQueue()` and then convert the edits to a readable format via `offlineFeaturesManager.getReadableEdit(edit)`. +####editStore.hasPendingEdits() +You can check if there are any edits pending by using the EditStore library. If there are edits then you can iterate `editsStore._retrieveEditsQueue()` and convert the edits to a readable format via `offlineFeaturesManager.getReadableEdit(edit)`. + ```js - if( editsStore.hasPendingEdits()) + var editStore = new O.esri.Edit.EditStore(Graphic); + if( editStore.hasPendingEdits()) { - var edits = editsStore._retrieveEditsQueue(); + var edits = editStore._retrieveEditsQueue(); edits.forEach(function(edit) { var readableEdit = offlineFeaturesManager.getReadableEdit(edit); diff --git a/doc/offlinefeaturesmanager.md b/doc/offlinefeaturesmanager.md index b911ea3..c37b256 100644 --- a/doc/offlinefeaturesmanager.md +++ b/doc/offlinefeaturesmanager.md @@ -1,14 +1,14 @@ API OfflineFeaturesManager ================================== -##esri.OfflineFeaturesManager +##O.esri.Edit.OfflineFeaturesManager Extends and overrides a feature layer. This library allows you to extend esri.layers.FeatureLayer objects with offline capability and manage the resync process. ###Constructor Constructor | Description --- | --- -`esri.OfflineFeaturesManager()` | Creates an instance of the offlineFeaturesManager class. This library allows you to extend FeatureLayer objects with offline editing capabilities and manage the online/offline resynchronization process. +`O.esri.Edit.OfflineFeaturesManager()` | Creates an instance of the offlineFeaturesManager class. This library allows you to extend FeatureLayer objects with offline editing capabilities and manage the online/offline resynchronization process. ###ENUMs The manager can be in one of these three states (see `getOnlineStatus()` method): @@ -33,6 +33,7 @@ Methods | Returns | Description Application code can subscribe to offlineFeaturesManager events to be notified of different conditions. ```js + offlineFeaturesManager.on( offlineFeaturesManager.events.EDITS_SENT, function(edits) @@ -53,15 +54,21 @@ Methods | Returns | Description --- | --- | --- `applyEdits(` `adds, updates, deletes,` `callback, errback)` | `deferred`| applyEdits() method is replaced by this library. It's behaviour depends upon online state of the manager. You need to pass the same arguments as to the original applyEdits() method and it returns a deferred object, that will be resolved in the same way as the original, as well as the callbacks will be called under the same conditions. This method looks the same as the original to calling code, the only difference is internal. -##editsStore +##O.esri.Edit.EditStore -Provides a number of public static methods that are used by `OfflineFeaturesManager` lib. They provide a low-level storage mechanism using indexedDb browser functions. These methods don't require a `new` statement or a constructor. After the module has been included in your application you can access these methods directly for example: `O.esri.Edit.EditStore.getEditsStoreSizeBytes();`. +Provides a number of public methods that are used by `OfflineFeaturesManager` library. They provide a low-level storage mechanism using indexedDb browser functions. Instiantiate this library using a `new` statement. + +###Constructor +Constructor | Description +--- | --- +`O.esri.Edit.EditStore(Graphic)` | Creates an instance of the EditStore class. This library is responsible for managing the storage, reading, writing, serialization, deserialization of geometric features. Importing `Graphic` _("esri/graphic")_ allows the library to provide more abstraction when returning serialized data. ###Public Methods Methods | Returns | Description --- | --- | --- `isSupported()` | boolean | Determines if local storage is available. If it is not available then the storage cache will not work. It's a best practice to verify this before attempting to write to the local cache. `hasPendingEdits()` | boolean | Determines if there are any queued edits in the local cache. +`resetEditsQueue()` | nothing | Empties the edits queue and replaces it with an empty string. `pendingEditsCount()` | int | The total number of edits that are queued in the local cache. `getEditsStoreSizeBytes()` | Number | Returns the total size of all pending edits in bytes. `getLocalStorageSizeBytes()` | Number | Returns the total size in bytes of all items for local storage cached using the current domain name. diff --git a/test/SpecRunner.offlineFeaturesManager.html b/test/SpecRunner.offlineFeaturesManager.html index fbd8210..6a3eeb9 100644 --- a/test/SpecRunner.offlineFeaturesManager.html +++ b/test/SpecRunner.offlineFeaturesManager.html @@ -39,7 +39,7 @@ "esri/layers/FeatureLayer", "esri/geometry", "esri/request", "dojo/dom", "dojo/on", "dojo/query", "dojo/dom-construct", - "../dist/offline-editor-edit-src.js", + "../dist/offline-edit-src.js", "dojo/domReady!"], function(Map, GraphicsLayer, Graphic, @@ -50,7 +50,7 @@ { g_modules.esriRequest = esriRequest; g_modules.Graphic = Graphic; - g_offlineFeaturesManager = new esri.OfflineFeaturesManager(); + g_offlineFeaturesManager = new O.esri.Edit.OfflineFeaturesManager(); g_editsStore = new O.esri.Edit.EditStore(); g_map = new Map("map", { From e036dbe02cfea7c1e8e29d38348f8ad534a6c3bd Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:00:56 -0600 Subject: [PATCH 48/86] updates retrieveEditsQueue from private to public --- doc/howtouseeditlibrary.md | 4 ++-- doc/offlinefeaturesmanager.md | 1 + lib/edit/editsStore.js | 18 +++++++++--------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/howtouseeditlibrary.md b/doc/howtouseeditlibrary.md index 0688f91..fe6c62c 100644 --- a/doc/howtouseeditlibrary.md +++ b/doc/howtouseeditlibrary.md @@ -117,13 +117,13 @@ Within your application you can manually check online status and then update you ``` ####editStore.hasPendingEdits() -You can check if there are any edits pending by using the EditStore library. If there are edits then you can iterate `editsStore._retrieveEditsQueue()` and convert the edits to a readable format via `offlineFeaturesManager.getReadableEdit(edit)`. +You can check if there are any edits pending by using the EditStore library. If there are edits then you can iterate `editsStore.retrieveEditsQueue()` and convert the edits to a readable format via `offlineFeaturesManager.getReadableEdit(edit)`. ```js var editStore = new O.esri.Edit.EditStore(Graphic); if( editStore.hasPendingEdits()) { - var edits = editStore._retrieveEditsQueue(); + var edits = editStore.retrieveEditsQueue(); edits.forEach(function(edit) { var readableEdit = offlineFeaturesManager.getReadableEdit(edit); diff --git a/doc/offlinefeaturesmanager.md b/doc/offlinefeaturesmanager.md index c37b256..417fd61 100644 --- a/doc/offlinefeaturesmanager.md +++ b/doc/offlinefeaturesmanager.md @@ -69,6 +69,7 @@ Methods | Returns | Description `isSupported()` | boolean | Determines if local storage is available. If it is not available then the storage cache will not work. It's a best practice to verify this before attempting to write to the local cache. `hasPendingEdits()` | boolean | Determines if there are any queued edits in the local cache. `resetEditsQueue()` | nothing | Empties the edits queue and replaces it with an empty string. +`retrieveEditsQueue()` | Array | returns an array of all pending edits. `pendingEditsCount()` | int | The total number of edits that are queued in the local cache. `getEditsStoreSizeBytes()` | Number | Returns the total size of all pending edits in bytes. `getLocalStorageSizeBytes()` | Number | Returns the total size in bytes of all items for local storage cached using the current domain name. diff --git a/lib/edit/editsStore.js b/lib/edit/editsStore.js index 6652a74..5a23c6a 100644 --- a/lib/edit/editsStore.js +++ b/lib/edit/editsStore.js @@ -38,7 +38,7 @@ O.esri.Edit.EditStore = function(Graphic){ graphic: this._serialize(graphic) }; - var edits = this._retrieveEditsQueue(); + var edits = this.retrieveEditsQueue(); edits.push(edit); var success = this._storeEditsQueue(edits); return { success: success, error: success? undefined : {code: 1000, description:this.ERROR_LOCALSTORAGE_FULL} }; @@ -46,7 +46,7 @@ O.esri.Edit.EditStore = function(Graphic){ this.peekFirstEdit = function() { - var edits = this._retrieveEditsQueue(); + var edits = this.retrieveEditsQueue(); var firstEdit; if( edits ) @@ -60,7 +60,7 @@ O.esri.Edit.EditStore = function(Graphic){ this.popFirstEdit = function() { - var edits = this._retrieveEditsQueue(); + var edits = this.retrieveEditsQueue(); var firstEdit; if( edits ) @@ -97,6 +97,12 @@ O.esri.Edit.EditStore = function(Graphic){ window.localStorage.setItem(EDITS_QUEUE_KEY, ""); }; + this.retrieveEditsQueue = function() + { + var storedValue = window.localStorage.getItem(EDITS_QUEUE_KEY) || ""; + return this._unpackArrayOfEdits(storedValue); + }; + this.getEditsStoreSizeBytes = function() { var editsQueueValue = window.localStorage.getItem(EDITS_QUEUE_KEY); @@ -147,12 +153,6 @@ O.esri.Edit.EditStore = function(Graphic){ return graphic; }; - this._retrieveEditsQueue = function() - { - var storedValue = window.localStorage.getItem(EDITS_QUEUE_KEY) || ""; - return this._unpackArrayOfEdits(storedValue); - }; - this._storeEditsQueue = function(edits) { try From 707c185ca6b7be0e9fdb5a6a9b1fd09f5f97b481 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:05:47 -0600 Subject: [PATCH 49/86] updates test for retrieveEditsQueue --- test/spec/offlineFeaturesManagerSpec.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/spec/offlineFeaturesManagerSpec.js b/test/spec/offlineFeaturesManagerSpec.js index 5927cdd..2bc062c 100644 --- a/test/spec/offlineFeaturesManagerSpec.js +++ b/test/spec/offlineFeaturesManagerSpec.js @@ -555,6 +555,10 @@ describe("Offline Editing", function() expect(getObjectIds(g_featureLayers[0].graphics)).toEqual(getObjectIds([g1,g2,g4,g5,g6])); expect(g_featureLayers[0].graphics.length).toBe(5); expect(g_editsStore.pendingEditsCount()).toBe(16); + + var queue = g_editsStore.retrieveEditsQueue(); + expect(queue.length).toBe(16); + countFeatures(g_featureLayers[0], function(success,result) { expect(success).toBeTruthy(); @@ -644,6 +648,10 @@ describe("Offline Editing", function() } } expect(g_editsStore.pendingEditsCount()).toBe(0); + + var queue = g_editsStore.retrieveEditsQueue(); + expect(queue.length).toBe(0); + // how to get the final id of g4 and g6 ? //expect(getObjectIds(g_featureLayers[0].graphics)).toEqual(getObjectIds([g1,g2,g4,g6])); // all of them are positive From 62963c2f692b64dd1ae35ae3a13d2e3c7b7d523c Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:09:16 -0600 Subject: [PATCH 50/86] update TPKLayer namespace --- lib/tpk/TPKLayer.js | 2 +- test/SpecRunner.TPKLayer.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tpk/TPKLayer.js b/lib/tpk/TPKLayer.js index c177c1a..8177cb4 100644 --- a/lib/tpk/TPKLayer.js +++ b/lib/tpk/TPKLayer.js @@ -14,7 +14,7 @@ define([ "dojo/Deferred","dojo/promise/all","dojo/Evented"], function(declare,Extent,query,SpatialReference,TileInfo,TiledMapServiceLayer, Deferred,all,Evented){ - return declare("esri.TPKLayer",[TiledMapServiceLayer,Evented],{ + return declare("O.esri.TPK.TPKLayer",[TiledMapServiceLayer,Evented],{ // // Public Properties diff --git a/test/SpecRunner.TPKLayer.html b/test/SpecRunner.TPKLayer.html index 30c70a8..5c3f03f 100644 --- a/test/SpecRunner.TPKLayer.html +++ b/test/SpecRunner.TPKLayer.html @@ -46,7 +46,7 @@ var tpkLayer = null; require([ - "../dist/offline-editor-tpk.js", + "../dist/offline-tpk-src.js", "dojo/domReady!"], function() { @@ -56,7 +56,7 @@ //IMPORTANT! // zip.workerScriptsPath = locationPath + "/../lib/tpk/"; //tell zip.js where to find it's associated scripts - tpkLayer = new esri.TPKLayer(); + tpkLayer = new O.esri.TPK.TPKLayer(); initChooseLocalFile(); From 6f21bb8bcb77a16046968ec67c16cfea2c4a055f Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:35:33 -0600 Subject: [PATCH 51/86] update to TPK doc, namespace for zip.js --- doc/tpklayer.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/tpklayer.md b/doc/tpklayer.md index af0d72d..4ab340d 100644 --- a/doc/tpklayer.md +++ b/doc/tpklayer.md @@ -46,3 +46,12 @@ Methods | Returns | Description --- | --- | --- `getTileUrl(level, row, col)` | String | Use the url's level, row and column to retrieve tiles as requested by the ArcGIS API for JavaScript. If a tile is in the local database it is returned. If it is not then the library parsing the TPK file for the appropriate tile image. If `isDBWriteable()` is set to true (default), then an image retrieved from the TPK will be written to the database. Tile retrieval times from images stored in the database are significantly faster than pulling images from the TPK. +###O.esri.zip + +Integrates zip.js into the TPKLayer library. Here is a short listing, for a completing listing of zip.js functionality go [here](http://gildas-lormeau.github.io/zip.js/). + +Methods | Returns | Description +--- | --- | --- +`createReader(reader, callback[, onerror])` | {ZipReader} | Create a ZipReader object. A ZipReader object helps to read the zipped content. +`BlobReader(blob)` | Binary contents of Blob | Use this as the reader property in the createReader constructor. + From 80ed5f00418fec0c60c7447120fe1ffde5e1b178 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:51:25 -0600 Subject: [PATCH 52/86] update namespace offlineTilesEnabler --- lib/tiles/offlineTilesEnabler.js | 2 +- test/SpecRunner.offlineTilesEnabler.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/tiles/offlineTilesEnabler.js b/lib/tiles/offlineTilesEnabler.js index 9a23af4..4a3303c 100644 --- a/lib/tiles/offlineTilesEnabler.js +++ b/lib/tiles/offlineTilesEnabler.js @@ -6,7 +6,7 @@ define([ ], function(query, request, Polygon,declare) { "use strict"; - return declare("esri.OfflineTilesEnabler",[],{ + return declare("O.esri.Tiles.OfflineTilesEnabler",[],{ /** * Utility method to get the basemap layer reference * @param map diff --git a/test/SpecRunner.offlineTilesEnabler.html b/test/SpecRunner.offlineTilesEnabler.html index 88a854b..b3f187b 100755 --- a/test/SpecRunner.offlineTilesEnabler.html +++ b/test/SpecRunner.offlineTilesEnabler.html @@ -37,7 +37,7 @@ "esri/urlUtils", "esri/geometry/webMercatorUtils", "tiles/offlineTilesEnabler", "dojo/dom-construct", - "../dist/offline-editor-tiles-basic-src.js", + "../dist/offline-tiles-basic-src.js", "dojo/domReady!"], function(Map, GraphicsLayer, Graphic, SimpleFillSymbol, @@ -59,7 +59,7 @@ function test() { g_basemapLayer = g_map.getLayer( g_map.layerIds[0] ); - g_offlineTilesEnabler = new OfflineTilesEnabler(); + g_offlineTilesEnabler = new O.esri.Tiles.OfflineTilesEnabler(); var jasmineEnv = jasmine.getEnv(); jasmineEnv.updateInterval = 1000; From 5d01364393359587347a3655c1f4234a9e5fa39c Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:51:52 -0600 Subject: [PATCH 53/86] update namespace offlineTilesEnablerLayer --- lib/tiles/OfflineTilesEnablerLayer.js | 2 +- test/SpecRunner.offlineTilesEnablerLayer.html | 10 ++-------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/tiles/OfflineTilesEnablerLayer.js b/lib/tiles/OfflineTilesEnablerLayer.js index d5830b1..5a39e08 100644 --- a/lib/tiles/OfflineTilesEnablerLayer.js +++ b/lib/tiles/OfflineTilesEnablerLayer.js @@ -12,7 +12,7 @@ define([ ], function(query, request, declare,LOD,Point,Extent,TileInfo,SpatialReference,Polygon,TiledMapServerLayer) { "use strict"; - return declare("esri.OfflineTileEnablerLayer",[TiledMapServerLayer],{ + return declare("O.esri.Tiles.OfflineTileEnablerLayer",[TiledMapServerLayer],{ tileInfo: null, _imageType: "", diff --git a/test/SpecRunner.offlineTilesEnablerLayer.html b/test/SpecRunner.offlineTilesEnablerLayer.html index b20085a..943b516 100644 --- a/test/SpecRunner.offlineTilesEnablerLayer.html +++ b/test/SpecRunner.offlineTilesEnablerLayer.html @@ -10,12 +10,6 @@ - - @@ -40,7 +34,7 @@ "esri/layers/TileInfo", "esri/SpatialReference", "esri/geometry/Polygon", - "../dist/offline-editor-tiles-advanced-src.js", + "../dist/offline-tiles-advanced-src.js", "dojo/dom-construct", "dojo/domReady!"], function(Map, GraphicsLayer, Graphic, SimpleFillSymbol, @@ -52,7 +46,7 @@ domConstruct) { - g_basemapLayer = new esri.OfflineTileEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ + g_basemapLayer = new O.esri.Tiles.OfflineTileEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ console.log("Tile Layer Loaded."); },true); From e20dbf6f17ff7a3fdfcd9c97251f4dd5575781cb Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:52:30 -0600 Subject: [PATCH 54/86] update grunt files for names/namespaces --- Gruntfile.js | 18 +++++++++--------- samples/Gruntfile.js | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 0b6e532..5ee01b6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -49,7 +49,7 @@ module.exports = function(grunt) { 'lib/edit/editsStore.js', 'lib/edit/attachmentsStore.js' ], - dest: 'dist/offline-editor-edit-src.js' + dest: 'dist/offline-edit-src.js' }, /* Tiles basic is for use with WebMaps. Cannot be reloaded or restarted while offline */ tilesBasic: { @@ -62,7 +62,7 @@ module.exports = function(grunt) { 'lib/tiles/TilesStore.js', 'lib/tiles/tilingScheme.js' ], - dest: 'dist/offline-editor-tiles-basic-src.js' + dest: 'dist/offline-tiles-basic-src.js' }, /* Tiles advanced is for use with tiled map services. Works with reload or restart while offline */ tilesAdvanced: { @@ -75,20 +75,20 @@ module.exports = function(grunt) { 'lib/tiles/tilingScheme.js', 'lib/tiles/offlineTilesEnablerLayer.js' ], - dest: 'dist/offline-editor-tiles-advanced-src.js' + dest: 'dist/offline-tiles-advanced-src.js' }, /* TPKLayer - for working directly with tile packages (.tpk files) */ tpk: { src: [ - 'lib/tpk/TPKLayer.js', 'lib/tpk/OfflineTpkNS.js', + 'lib/tpk/TPKLayer.js', 'lib/tiles/TilesStore.js', 'lib/tpk/zip.js', 'lib/tpk/autoCenterMap.js', 'lib/tpk/inflate.js', 'lib/tpk/xml2json.js' ], - dest: 'dist/offline-editor-tpk-src.js' + dest: 'dist/offline-tpk-src.js' } }, @@ -104,10 +104,10 @@ module.exports = function(grunt) { }, dist: { files: { - 'dist/offline-editor-edit.js': ['dist/offline-editor-edit-src.js'], - 'dist/offline-editor-tiles-basic-min.js': ['dist/offline-editor-tiles-basic-src.js'], - 'dist/offline-editor-tiles-advanced-min.js': ['dist/offline-editor-tiles-advanced-src.js'], - 'dist/offline-editor-tpk.js': ['dist/offline-editor-tpk-src.js'] + 'dist/offline-edit-min.js': ['dist/offline-edit-src.js'], + 'dist/offline-tiles-basic-min.js': ['dist/offline-tiles-basic-src.js'], + 'dist/offline-tiles-advanced-min.js': ['dist/offline-tiles-advanced-src.js'], + 'dist/offline-tpk-min.js': ['dist/offline-tpk-src.js'] } } } diff --git a/samples/Gruntfile.js b/samples/Gruntfile.js index 50f7b5b..2622387 100644 --- a/samples/Gruntfile.js +++ b/samples/Gruntfile.js @@ -29,7 +29,7 @@ module.exports = function(grunt) { "<%= pkg.optimizedApiURL %>/dojo/nls/dojo_en-us.js", "<%= pkg.optimizedApiURL %>/dojo/selector/acme.js", "#", - "<%= pkg.arcGISBaseURL %>/js/esri/dijit/images/popup-sprite.png", + "#<%= pkg.arcGISBaseURL %>/js/esri/dijit/images/popup-sprite.png", "<%= pkg.arcGISBaseURL %>/js/esri/dijit/images/attribute_inspector_sprite.png", "<%= pkg.arcGISBaseURL %>/js/dojo/dojox/gfx/svg.js", "<%= pkg.arcGISBaseURL %>/js/dojo/dojo/resources/blank.gif", @@ -68,9 +68,9 @@ module.exports = function(grunt) { "../lib/tiles/*.png", "../lib/tiles/*.psd", "../utils/*.js", - "../dist/offline-editor-edit-src.js", - "../dist/offline-editor-tiles-advanced-src.js", - "../dist/offline-editor-tiles-basic-src.js" + "../dist/offline-edit-src.js", + "../dist/offline-tiles-advanced-src.js", + "../dist/offline-tiles-basic-src.js" /* "images/*", "css/*.css" From cf905064f6bd286bf0ef003f984d22ef6ede188d Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:52:53 -0600 Subject: [PATCH 55/86] clarification of breaking changes --- CHANGELOG.md | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d60273..2e82fc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,20 +1,52 @@ # offline-editor-js - Changelog -## Version 2 - +## Version 2.0 - + +Version 2.0 involves many changes. Please read the following carefully when upgrading from v1.x to v2.0. + Breaking Changes: -- Provides a single, concatenated and minified library files in /dist for edit, tiles and tpk. This single library pattern offers a performance boost by providing a significantly smaller size and one file to load. +- Provides a single, concatenated and minified library files in /dist for edit, tiles and tpk. This single library pattern offers a HTTP request/response performance boost by providing libraries of significantly smaller size and only one file to retrieve as compared to making multiple HTTP requests. +- Added a new namespacing for use with all non-AMD JavaScript libraries in the repo. - Internally refactored all libraries. - Single, concatenated source library files also available in /dist. - Updated all samples to ArcGIS API for JavaScript v3.10. -- Concatentation and minification available via a Grunt task. +- Concatentation and minification of libraries via a Grunt task. - All tests updated. -- All samples updated to use single library file pattern. -- Added a universal namespace via OfflineMapsNS.js for use with all pure JavaScript libraries in the repo. -- Deprecated restartOfflineFeaturesManager.js -- Renamed various internal libraries. +- All samples updated. - Consolidated duplicate functionality between offlineTilesEnabler.js and OfflineTilesEnablerLayer.js into TilesCore.js. The coding pattern is a bit ackward to access the shared functionality, but the upside is that duplicate functionality only needs to be maintained in a single library. +Overview of the new namespace pattern: + + * `O.esri.Edit` references all offline edit libraries + * `O.esri.Tiles` references all offline tile libraries + * `O.esri.TPK` references all TPK libraries + * `O.esri.zip` a wrapper around Zip.js + +Breaking name changes for the following libraries: + + * `offline-edit-min.js` - replaces _OfflineFeaturesManager.js_ + * `offline-tiles-basic-min.js` - _replaces offlineTilesEnabler.js_ + * `offline-tiles-advanced-min.js` - replaces _OfflineTilesEnablerLayer.js_ + * `offline-tpk-min.js` - replaces _TPKLayer.js_ + +Breaking name changes for the following Classes: + + * `O.esri.Edit.OfflineFeaturesManager()` replaces _OfflineFeaturesManager()_ + * `O.esri.Tiles.OfflineTilesEnabler()` replaces _OfflineTilesEnabler()_ + * `O.esri.Tiles.OfflineTilesEnablerLayer()` replaces _OfflineTilesEnablerLayer()_ + * `O.esri.TPK.TPKLayer()` replaces _TPKLayer()_ + * `O.esri.Edit.EditStore()` replaces _editsStore()_ + * `O.esri.zip` replaces the module _"tpk/zip"_ + +Breaking changes for the following methods: + + * `O.esri.Edits.EditStore().retrieveEditsQueue()` replaces the formerly private method _editsStore()._retrieveEditsQueue()_ + +Deprecations: + +- Deprecated _restartOfflineFeaturesManager.js_. This functionality has been integrated directly into `OfflineFeaturesManager.js` + ## Version 1.x - Various - Multiple, non-versioned updates From 3d816384b6ae08a2057320d05aa16eb15b0056bf Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Mon, 8 Sep 2014 13:53:33 -0600 Subject: [PATCH 56/86] update samples --- samples/appcache-features.appcache | 37 ++++++++++-------------- samples/appcache-features.html | 35 +++++++++++++---------- samples/appcache-tiles.appcache | 38 +++++++++++++------------ samples/appcache-tiles.html | 24 +++++++++------- samples/attachments-editor.html | 27 +++++++++--------- samples/draw-pointlinepoly-offline.html | 37 ++++++++++++------------ samples/package.json | 6 ++-- samples/service-inspector.html | 2 +- samples/tiles-indexed-db.html | 14 ++++----- samples/tpk-layer.html | 4 +-- 10 files changed, 114 insertions(+), 110 deletions(-) diff --git a/samples/appcache-features.appcache b/samples/appcache-features.appcache index 64547ed..a13b9fd 100644 --- a/samples/appcache-features.appcache +++ b/samples/appcache-features.appcache @@ -1,6 +1,6 @@ CACHE MANIFEST # This manifest was generated by grunt-manifest HTML5 Cache Manifest Generator -# Time: Tue Jun 17 2014 11:23:22 GMT-0600 (MDT) +# Time: Mon Sep 08 2014 11:50:44 GMT-0600 (MDT) CACHE: # manifest-generator, version: 0.0.1 @@ -13,23 +13,23 @@ http://js.arcgis.com/o/agup_hack4co/appcacheFeatures/dojo/dojo.js http://js.arcgis.com/o/agup_hack4co/appcacheFeatures/dojo/nls/dojo_en-us.js http://js.arcgis.com/o/agup_hack4co/appcacheFeatures/dojo/selector/acme.js # -http://js.arcgis.com/3.9/js/esri/dijit/images/popup-sprite.png -http://js.arcgis.com/3.9/js/esri/dijit/images/attribute_inspector_sprite.png -http://js.arcgis.com/3.9/js/dojo/dojox/gfx/svg.js -http://js.arcgis.com/3.9/js/dojo/dojo/resources/blank.gif -http://js.arcgis.com/3.9/js/esri/dijit/images/ajax-loader.gif -http://js.arcgis.com/3.9/js/esri/images/map/logo-sm.png -http://js.arcgis.com/3.9/js/esri/images/map/logo-med.png -http://js.arcgis.com/3.9/js/esri/css/esri.css -http://js.arcgis.com/3.9/js/dojo/dijit/themes/claro/claro.css -http://js.arcgis.com/3.9/js/esri/nls/jsapi_en-us.js +#http://js.arcgis.com/3.10/js/esri/dijit/images/popup-sprite.png +http://js.arcgis.com/3.10/js/esri/dijit/images/attribute_inspector_sprite.png +http://js.arcgis.com/3.10/js/dojo/dojox/gfx/svg.js +http://js.arcgis.com/3.10/js/dojo/dojo/resources/blank.gif +http://js.arcgis.com/3.10/js/esri/dijit/images/ajax-loader.gif +http://js.arcgis.com/3.10/js/esri/images/map/logo-sm.png +http://js.arcgis.com/3.10/js/esri/images/map/logo-med.png +http://js.arcgis.com/3.10/js/esri/css/esri.css +http://js.arcgis.com/3.10/js/dojo/dijit/themes/claro/claro.css +http://js.arcgis.com/3.10/js/esri/nls/jsapi_en-us.js # //services.arcgisonline.com/ArcGIS/rest/info?f=json //static.arcgis.com/attribution/World_Topo_Map?f=json //services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer?f=json&callback=dojo.io.script.jsonp_dojoIoScript1._jsonpCallback # # required for web maps -http://js.arcgis.com/3.9/js/esri/dijit/images/ajax-loader.gif +http://js.arcgis.com/3.10/js/esri/dijit/images/ajax-loader.gif # # required local html # /xyz/style.css @@ -40,20 +40,13 @@ http://js.arcgis.com/3.9/js/esri/dijit/images/ajax-loader.gif ../vendor/IndexedDBShim/dist/IndexedDBShim.js ../vendor/IndexedDBShim/dist/IndexedDBShim.min.js ../vendor/offline/offline.min.js -../lib/tiles/FileSaver.js -../lib/tiles/OfflineTilesEnablerLayer.js -../lib/tiles/TilesStore.js -../lib/tiles/base64utils.js -../lib/tiles/offlineTilesEnabler.js -../lib/tiles/tilingScheme.js ../lib/tiles/notile.png ../lib/tiles/notile.psd -../lib/edit/attachmentsStore.js -../lib/edit/editsStore.js -../lib/edit/offlineFeaturesManager.js -../lib/edit/restartOfflineFeaturesManager.js ../utils/appCacheManager.js ../utils/debouncer.js +../dist/offline-edit-src.js +../dist/offline-tiles-advanced-src.js +../dist/offline-tiles-basic-src.js NETWORK: * diff --git a/samples/appcache-features.html b/samples/appcache-features.html index 2929d4e..6cf186a 100644 --- a/samples/appcache-features.html +++ b/samples/appcache-features.html @@ -174,15 +174,17 @@
- + @@ -110,7 +110,7 @@ ask if you want to reload the application. var map; -require(["esri/map","utils/appCacheManager","dojo/on","../dist/offline-editor-tiles-basic-src.js","dojo/domReady!"], +require(["esri/map","utils/appCacheManager","dojo/on","../dist/offline-tiles-advanced-src.js","dojo/domReady!"], function(Map,AppCacheManager,on) { var tileLayer = null; @@ -132,6 +132,8 @@ require(["esri/map","utils/appCacheManager","dojo/on","../dist/offline-editor-ti Offline.check(); Offline.on('up down', updateState ); + initAppCacheManager(); + /** * There have been a few bugs in the offline detection library (offline.min.js) * This is a utility check to 100% validate if the application is online or @@ -141,7 +143,14 @@ require(["esri/map","utils/appCacheManager","dojo/on","../dist/offline-editor-ti result == true ? _isOnline = true : _isOnline = false; startMap(); - }) + }); + + function initAppCacheManager(){ + appCacheManager = new AppCacheManager(true,true); + appCacheManager.on(appCacheManager.CACHE_EVENT,cacheEventHandler); + appCacheManager.on(appCacheManager.CACHE_ERROR,cacheErrorHandler); + appCacheManager.on(appCacheManager.CACHE_LOADED,cacheLoaderHandler); + } function startMap(){ //Make sure map shows up after a browser refresh @@ -153,7 +162,7 @@ require(["esri/map","utils/appCacheManager","dojo/on","../dist/offline-editor-ti sliderStyle: "small" }); - tileLayer = esri.OfflineTileEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ + tileLayer = O.esri.Tiles.OfflineTileEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){ console.log("Offline tile lib is enabled. Application state is: " + Offline.state); },_isOnline); @@ -191,11 +200,6 @@ require(["esri/map","utils/appCacheManager","dojo/on","../dist/offline-editor-ti console.log("Zoom level = " + tileLayer.getLevel()) }); - appCacheManager = new AppCacheManager(true,true); - appCacheManager.on(appCacheManager.CACHE_EVENT,cacheEventHandler); - appCacheManager.on(appCacheManager.CACHE_ERROR,cacheErrorHandler); - appCacheManager.on(appCacheManager.CACHE_LOADED,cacheLoaderHandler); - btnGetTiles = document.getElementById("btn-get-tiles"); btnOnlineOffline = document.getElementById("btn-online-offline"); diff --git a/samples/attachments-editor.html b/samples/attachments-editor.html index 04a6580..b2d8bb2 100644 --- a/samples/attachments-editor.html +++ b/samples/attachments-editor.html @@ -44,13 +44,12 @@ @@ -82,14 +81,16 @@ "dojo/parser", "dojo/dom", "dojo/dom-class", "dojo/dom-construct", - "edit/offlineFeaturesManager", - "edit/editsStore", "dojo/on", - "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dojo/domReady!" + "dijit/layout/BorderContainer", "dijit/layout/ContentPane", + + "../dist/offline-edit-src.js", + + "dojo/domReady!" ], function( Map,FeatureLayer,AttachmentEditor,esriConfig, - parser,dom,domClass,domConstruct,OfflineFeaturesManager,editsStore,on + parser,dom,domClass,domConstruct,on ) { parser.parse(); @@ -103,7 +104,7 @@ //esriConfig.defaults.io.proxyUrl = "../lib/proxy.php"; esriConfig.defaults.io.proxyUrl = window.proxyPath; - var offlineFeaturesManager = new OfflineFeaturesManager(); + var offlineFeaturesManager = new O.esri.Edit.OfflineFeaturesManager(); offlineFeaturesManager.initAttachments(function(success) { attachmentsInited = success; @@ -138,7 +139,7 @@ var attachmentEditor = new AttachmentEditor({}, dom.byId("content")); attachmentEditor.startup(); - featureLayer.on("click", function(evt) + featureLayer.on("click", function(evt) { var event = evt; var objectId = evt.graphic.attributes[featureLayer.objectIdField]; diff --git a/samples/draw-pointlinepoly-offline.html b/samples/draw-pointlinepoly-offline.html index 8804371..f1ab8c2 100644 --- a/samples/draw-pointlinepoly-offline.html +++ b/samples/draw-pointlinepoly-offline.html @@ -67,9 +67,7 @@ - + - + @@ -19,6 +19,17 @@ The `edit` library allows a developer to extend a feature layer with offline edi ... }); ``` + +Also, if you have other AMD libraries in your project and you want to refer to offline-editor-js within a `define` statement you can use the following pattern for importing the library. Note you can leave off the `.js` from the module identifier, for example: + +```js + + define(["..dist/offline-edit-min"],function(){ + ... + }) + +``` + **Step 2** Once your map is created (either using new Map() or using esriUtils.createMap(webmapid,...), you create a new OfflineFeaturesManager instance and starting assigning events listeners to tie the library into your user interface: ```js diff --git a/doc/migratefromv1tov2.md b/doc/migratefromv1tov2.md new file mode 100644 index 0000000..1738db6 --- /dev/null +++ b/doc/migratefromv1tov2.md @@ -0,0 +1,55 @@ +Migrating from v1 to v2 +======================= + +This doc is to provide pointers for migrating from offline-editor-js v1 to v2. Migration should be fairly straightforward as you are simply going to be changing library names and method namespaces. Check the [CHANGELOG](CHANGELOG.md) doc for specifics as well as any deprecations. + + +##Importing the libraries + +In your main html application you can use generic script injection to import the offline-editor-js libraries into your project. + +```html + diff --git a/samples/appcache-tiles.html b/samples/appcache-tiles.html index b11bef2..3f392c4 100644 --- a/samples/appcache-tiles.html +++ b/samples/appcache-tiles.html @@ -70,7 +70,6 @@ ask if you want to reload the application. var locationPath = location.pathname.replace(/\/[^/]+$/, ""); var dojoConfig = { paths: { - tiles: locationPath + "/../lib/tiles", vendor: locationPath + "/../vendor", utils: locationPath + "/../utils" } diff --git a/samples/attachments-editor.html b/samples/attachments-editor.html index 6e16a65..74d496d 100644 --- a/samples/attachments-editor.html +++ b/samples/attachments-editor.html @@ -44,12 +44,6 @@ diff --git a/samples/draw-pointlinepoly-offline.html b/samples/draw-pointlinepoly-offline.html index df1e599..5d70afd 100644 --- a/samples/draw-pointlinepoly-offline.html +++ b/samples/draw-pointlinepoly-offline.html @@ -65,12 +65,6 @@ From 5b0f4f2bc5eee410f0bb389d300146cb03839c3b Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 17 Sep 2014 12:06:56 -0600 Subject: [PATCH 76/86] remove old index.html --- index.html | 65 ------------------------------------------------------ 1 file changed, 65 deletions(-) delete mode 100644 index.html diff --git a/index.html b/index.html deleted file mode 100644 index 1078c6c..0000000 --- a/index.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - Offline Editing and Mapping with the ArcGIS API for JavaScript - - - - - - -Fork me on GitHub -

ArcGIS Offline Library for JavaScript

-

This repo contains a set of libraries for using the ArcGIS API for JavaScript offline. You can now edit and map while offline. It's still a work-in-progress - so if you have suggestions open an issue or if you want to make a pull request we welcome your proposed modifications.

- -

Why edit offline with JavaScript?

-

Many "in the field" use cases for mapping apps require that workers go into areas with limited, intermittent or no cellular network connectivity. -Using this library will allow you to re-use your JavaScript skills for building web applications that work in these use cases.

-

What types of features will it work with?

-

The library currently works with esri.geometry.Geometry.Point, Polygon and Polyline.

- - - - - - - - -
- \ No newline at end of file From 5fd969e392b81e1cb577048a6a480de2734cf421 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 17 Sep 2014 12:07:16 -0600 Subject: [PATCH 77/86] stub new index.html --- demo/index.html | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 demo/index.html diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..71ce692 --- /dev/null +++ b/demo/index.html @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file From 6c4b406d0ee12471bd72f3cb6a25881b286bceff Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 17 Sep 2014 12:48:08 -0600 Subject: [PATCH 78/86] stub style.css --- demo/css/style.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 demo/css/style.css diff --git a/demo/css/style.css b/demo/css/style.css new file mode 100644 index 0000000..d93fc64 --- /dev/null +++ b/demo/css/style.css @@ -0,0 +1,12 @@ +body { + padding-top: 50px; + padding-bottom: 20px; +} +.navbar-brand-title{ + float: left; + padding: 15px; + height: 50px; + line-height: 20px; + font-size: large; + color: #ffffff; +} \ No newline at end of file From 549c895be0192a9285036b47f80cd27bda013e56 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Wed, 17 Sep 2014 12:48:21 -0600 Subject: [PATCH 79/86] basic outline for index.html --- demo/index.html | 77 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/demo/index.html b/demo/index.html index 71ce692..c1735ce 100644 --- a/demo/index.html +++ b/demo/index.html @@ -1,10 +1,79 @@ - - - - + + + + + + + Offline-editor-js + + + + + + + + + + + + + + + +
+
+

Offline-editor-js

+

Examples of how to build offline mapping applications for the web.

+

Learn more »

+
+
+ +
+ +
+
+

Editing

+

Build web mapping and geospatial applications that use the ArcGIS JavaScript API and allow you to temporarily store adds, updates and deletes while the device is offline.

+
+
+

Tiles

+

Store tile images from tiled map services locally so that they can continue to be used when your application goes offline.

+
+
+

TPK

+

Add .tpk files (binary tile packages) directly to your app whether it is online or offline. TPKs can be used by themselves or alongside tiled map services.

+
+
+ +
+
+ + + + + + \ No newline at end of file From c2436fc1b4e557428695f30dd3a23bf8f35b7fc8 Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 18 Sep 2014 14:19:04 -0600 Subject: [PATCH 80/86] tweak navbar and thumbnails style.css --- demo/css/style.css | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/demo/css/style.css b/demo/css/style.css index d93fc64..211fc10 100644 --- a/demo/css/style.css +++ b/demo/css/style.css @@ -2,11 +2,18 @@ body { padding-top: 50px; padding-bottom: 20px; } -.navbar-brand-title{ +.thumbnail { + width: 200px; +} +.navbar-brand-title { float: left; padding: 15px; height: 50px; line-height: 20px; font-size: large; color: #ffffff; +} +.navbar-brand-title:hover { + color: lightgray; + text-decoration: none; } \ No newline at end of file From be7aa52608ce4c7fef39fb341fd3dc3c535e9eef Mon Sep 17 00:00:00 2001 From: Andy Gup Date: Thu, 18 Sep 2014 14:19:48 -0600 Subject: [PATCH 81/86] sample apps screenshot thumbnails --- demo/images/appcachefeatures-demo-thumb.png | Bin 0 -> 50154 bytes demo/images/appcachetiles-demo-thumb.png | Bin 0 -> 19453 bytes demo/images/attachments-demo-thumb.png | Bin 0 -> 66754 bytes demo/images/cop-demo-thumb.png | Bin 0 -> 45054 bytes demo/images/favicon.ico | Bin 0 -> 4055 bytes demo/images/tiles-demo-thumb.png | Bin 0 -> 46505 bytes demo/images/tpk-demo-thumb.png | Bin 0 -> 36050 bytes 7 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 demo/images/appcachefeatures-demo-thumb.png create mode 100644 demo/images/appcachetiles-demo-thumb.png create mode 100644 demo/images/attachments-demo-thumb.png create mode 100644 demo/images/cop-demo-thumb.png create mode 100644 demo/images/favicon.ico create mode 100644 demo/images/tiles-demo-thumb.png create mode 100644 demo/images/tpk-demo-thumb.png diff --git a/demo/images/appcachefeatures-demo-thumb.png b/demo/images/appcachefeatures-demo-thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..654c200516ce2d90c755f1cca8b1cf6dcea76ea4 GIT binary patch literal 50154 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%0Aww@p!>Jk{enbbc7d90!Lt!Q(bF?RKnVT5s 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 LNkvXXu0mjf5?B73 literal 0 HcmV?d00001 diff --git a/demo/images/appcachetiles-demo-thumb.png b/demo/images/appcachetiles-demo-thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..b9c8cf50733e6e6a7be6ef40fcc0e0f114686254 GIT binary patch literal 19453 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 N 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 literal 0 HcmV?d00001 diff --git a/demo/images/favicon.ico b/demo/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4697bdbbd897cc5e8c079e11e88c0502f5917852 GIT binary patch literal 4055 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 literal 0 HcmV?d00001 diff --git a/demo/images/tpk-demo-thumb.png b/demo/images/tpk-demo-thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..1050b03ca9dc1f28f7d3588ce5d4fae11aa13a13 GIT binary patch literal 36050 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 Date: Thu, 18 Sep 2014 14:20:02 -0600 Subject: [PATCH 82/86] added examples section --- demo/index.html | 64 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/demo/index.html b/demo/index.html index c1735ce..143c72f 100644 --- a/demo/index.html +++ b/demo/index.html @@ -4,6 +4,7 @@ + Offline-editor-js @@ -44,8 +45,8 @@

@@ -54,20 +55,73 @@

Editing

-

Build web mapping and geospatial applications that use the ArcGIS JavaScript API and allow you to temporarily store adds, updates and deletes while the device is offline.

+

Build web mapping and geospatial applications that use the ArcGIS JavaScript API and allow you to temporarily store adds, updates and deletes of geographic features while the device is offline.

Tiles

-

Store tile images from tiled map services locally so that they can continue to be used when your application goes offline.

+

Stores tile images from tiled map services locally. Once tiles are stored your mapping app can seemlessly transition between online and offline modes.

TPK

-

Add .tpk files (binary tile packages) directly to your app whether it is online or offline. TPKs can be used by themselves or alongside tiled map services.

+

Add .tpk files (binary tile packages) directly to your app whether it is online or offline. Use TPKs as the stand-alone mapping source file or alongside tiled map services.


+
+
+

Examples

+

Use the following applications as building blocks.

+
+
+ + + +

Tiles Demo

+

Gives you an overview of the functionality related to storing and managing multiple layers of tiles.

+
+
+ + + +

COP Demo

+

Common Operating Picture app lets you add, update and delete geographic features while offline.

+
+
+ + + +

Attachments Demo

+

Demonstrates working with geographic feature attachments such as images and associating them with a feature.

+
+
+
+
+ + + +

TPK Demo

+

Shows how to integrate .tpk files into your mapping app.

+
+
+ + + +

AppCache Features and Tiles Demo

+

This mobile feature editing demo is configured to maintain your edits and tiles after offline browser reloads and restarts.

+
+
+ + + +

AppCache Tiles Demo

+

A bare bones app that demos maintaining the map tiles after offline browser reloads and restarts.

+
+
+
+
+

Offline-editor-js

-

Examples of how to build offline mapping applications for the web.

-

Learn more »

+

Examples of how to build lightweight offline mapping applications for the web.

+