Merge pull request #240 from andygup/gh-pages

Update gh-pages
This commit is contained in:
Andy 2014-09-18 16:13:50 -06:00
commit a114cc2ab3
75 changed files with 11882 additions and 2375 deletions

145
.jshintrc
View File

@ -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
}

69
CHANGELOG.md Normal file
View File

@ -0,0 +1,69 @@
# offline-editor-js - Changelog
## Version 2.0.3 - Sep 15, 2014
- Closes issue #234 - Minor cleanup of samples. Removed unused dojoConfig pathnames.
## Version 2.0.2 - Sep 9, 2014
- Documentation update only.
- More minor doc updates.
- Minor README update to include link to new migration tips doc.
## Version 2.0.1 - Sep 8, 2014
- Minor updates to documentation
- Removed VERSION property from namespace (for now)
- Deleted OfflineMapsNS.js - not used (left over from v2 testing)
## Version 2.0 - Sep 8, 2014
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 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 of libraries via a Grunt task.
- All tests updated.
- 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
## Version 1 - Sep 19. 2013
- Initial commit.

126
Gruntfile.js Normal file
View File

@ -0,0 +1,126 @@
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'
},
/* All feature editing capabilities: adds, updates and deletes */
edit: {
src: [
'lib/edit/offlineFeaturesManager.js',
'lib/edit/OfflineEditNS.js',
'lib/edit/editsStore.js',
'lib/edit/attachmentsStore.js'
],
dest: 'dist/offline-edit-src.js'
},
/* Tiles basic is for use with WebMaps. Cannot be reloaded or restarted while offline */
tilesBasic: {
src: [
'lib/tiles/offlineTilesEnabler.js',
'lib/tiles/OfflineTilesNS.js',
'lib/tiles/base64utils.js',
'lib/tiles/FileSaver.js',
'lib/tiles/TilesCore.js',
'lib/tiles/TilesStore.js',
'lib/tiles/tilingScheme.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: {
src: [
'lib/tiles/offlineTilesEnablerLayer.js',
'lib/tiles/OfflineTilesNS.js',
'lib/tiles/base64utils.js',
'lib/tiles/FileSaver.js',
'lib/tiles/TilesCore.js',
'lib/tiles/TilesStore.js',
'lib/tiles/tilingScheme.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/tiles/TilesStore.js',
'lib/tpk/zip.js',
'lib/tpk/autoCenterMap.js',
'lib/tpk/inflate.js',
'lib/tpk/xml2json.js'
],
dest: 'dist/offline-tpk-src.js'
}
},
uglify: {
options: {
compress: {
drop_console: true //remove console.log statements :)
},
wrap: false
// mangle: {
// except: ['O']
// }
},
dist: {
files: {
'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']
}
}
}
});
// 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']);
// JSHint not currently working. Needs alot of cleanup.
// grunt.registerTask('buildAll',['jshint','concat','uglify']);
}

View File

@ -1,25 +1,23 @@
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.
Offline-editor-js is a set of JavaScript libraries for using the ArcGIS API for JavaScript offline. It offers both lightweight editing and tile management capabilities while offline or intermittently offline. It's 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.
Online samples are available here: [http://esri.github.io/offline-editor-js/demo/](http://esri.github.io/offline-editor-js/demo/)
*IMPORTANT:* If you need 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.
- `/samples`: sample apps to show how to use different aspects of the offline library capabilities.
- `/dist`:
* `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)_ caches map tiles for partial offline use cases. Use this library with ArcGIS Online Web maps as well as with tiled map services. This repo will not work with browser restarts or reloads while offline.
* `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. These modules are all AMD compliant.
- `/samples`: samples that show how to use the different offline libraries 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,24 +35,24 @@ __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
##How to use
* [Learn more about using the `tile` library](doc/howtousetiles.md)
* [Learn more about using the `edit` library](doc/howtouseeditlibrary.md)
@ -62,6 +60,10 @@ Extends TileMapServiceLayer. You can display TPK files with this library. TPK's
* [Learn more about using an application cache with this library](doc/howtouseappcache.md)
## Migrating from v1 to v2
If you are migrating your v1 code to v2 then go [here](doc/migratefromv1tov2.md) for some pointers.
##Setup Instructions
1. [Fork and clone the repo.](https://help.github.com/articles/fork-a-repo)
@ -69,12 +71,18 @@ 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.
##Build Instructions
1. From the root directory run `npm install`
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
* `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.
* `appcache-features.html` - shows how to work with the application manifest, tiles and features. This sample works with browser reloads and restarts.
* `appcache-tiles.html` - shows how to work with the application manifest and map tiles. This sample works with browser reloads and restarts.
* `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.
@ -83,6 +91,7 @@ Extends TileMapServiceLayer. You can display TPK files with this library. TPK's
##Dependencies
* ArcGIS API for JavaScript (v3.8+)
* Node.js required for building the source
* NOTE: browser limitations and technical dependencies. The offline capabilities in this toolkit depend on certain HTML5 capabilities being present in the browser. Go [here](doc/dependencies.md) for a detailed breakdown of the information.
* We offer browser support for Chrome and Safari only, at this time. Some of the capabilities in the repository will not work on Internet Explorer. We continue to evaluate IE's capabilities as new releases become available to try and identify a point where we might be able to support it.
@ -91,6 +100,7 @@ Extends TileMapServiceLayer. You can display TPK files with this library. TPK's
* [offline.js](https://github.com/hubspot/offline) - it allows detection of the online/offline condition and provides events to hook callbacks on when this condition changes
* [IndexedDBShim](https://github.com/axemclion/IndexedDBShim) - polyfill to simulate indexedDB functionality in browsers/platforms where it is not supported (notably desktop Safari and iOS Safari)
- IMPORTANT: There are known [issues](https://github.com/axemclion/IndexedDBShim/issues/115) with IndexedDBShim on Safari. For Safari, the storage error workaround is to switch from using /dist/IndexedDBShim.min.js to just using IndexedDBShim.js and then search for and modify the line that defines the value for `DEFAULT_DB_SIZE`. Set this to more appropriate size that will meet all your storage needs, for example: ```var DEFAULT_DB_SIZE = 24 * 1024 * 1024```
- IMPORTANT: Coming in Safari 8 is built-in supported for IndexedDB.
* [jasmine.async](https://github.com/derickbailey/jasmine.async.git) - library to help implementing tests of async functionality (used in tests)
* Non sub-module based libraries

19
demo/css/style.css Normal file
View File

@ -0,0 +1,19 @@
body {
padding-top: 50px;
padding-bottom: 20px;
}
.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;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
demo/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

185
demo/index.html Normal file
View File

@ -0,0 +1,185 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--<link rel="shortcut icon" href="../images/favicon.ico">-->
<title>Offline-editor-js</title>
<!-- Bootstrap core CSS -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="css/style.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body>
<a class="hidden-xs" href="https://github.com/Esri/offline-editor-js">
<img style="position: absolute; top: 50; right: 0; border: 0; width:150px;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_green_007200.png" alt="Fork me on GitHub">
</a>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand-title" href="#">Offline-editor-js</a>
</div>
</div>
</div>
<!-- Main jumbotron for a primary marketing message or call to action -->
<div class="jumbotron">
<div class="container">
<h1>Offline-editor-js</h1>
<p>Examples of how to build lightweight offline mapping applications for the web.</p>
<!--<p><a class="btn btn-primary btn-lg" role="button">Samples &raquo;</a></p>-->
</div>
</div>
<div class="container">
<!-- Example row of columns -->
<div class="row">
<div class="col-md-4">
<h2>Editing</h2>
<p>Build web mapping and geospatial applications that use the <a href="http://developers.arcgis.com/en/documentation" target="_blank">ArcGIS JavaScript API</a> and allow you to temporarily store adds, updates and deletes of geographic features while the device is offline.</p>
</div>
<div class="col-md-4">
<h2>Tiles</h2>
<p>Stores tile images from tiled map services locally. Once tiles are stored your mapping app can seemlessly transition between online and offline modes.</p>
</div>
<div class="col-md-4">
<h2>TPK</h2>
<p>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.</p>
</div>
</div>
<hr>
</div> <!-- /container -->
<div class="container">
<div class="bs-docs-section">
<h1 id="examples" class="page-header">Examples</h1>
<p class="lead">Use the following applications as building blocks. </p>
<div class="row bs-examples">
<div class="col-xs-6 col-md-4">
<a class="thumbnail" href="../samples/tiles-indexed-db.html">
<img src="images/tiles-demo-thumb.png">
</a>
<h3>Tiles Only</h3>
<p>Gives you an overview of the functionality related to storing and managing multiple layers of tiles.</p>
</div>
<div class="col-xs-6 col-md-4">
<a class="thumbnail" href="../samples/draw-pointlinepoly-offline.html">
<img src="images/cop-demo-thumb.png">
</a>
<h3>Feature Editing</h3>
<p>This app shows how add, update and delete geographic features while offline and auto-resync when internet is reestablished. </p>
</div>
<div class="col-xs-6 col-md-4">
<a class="thumbnail" href="../samples/attachments-editor.html">
<img src="images/attachments-demo-thumb.png">
</a>
<h3>Attachments</h3>
<p>Demonstrates working with geographic feature attachments such as images and associating them with a feature while offline.</p>
</div>
</div>
<div class="row bs-examples">
<div class="col-xs-6 col-md-4">
<a class="thumbnail" href="../samples/tpk-layer.html">
<img src="images/tpk-demo-thumb.png">
</a>
<h3>TPK Only</h3>
<p>Shows how to integrate <a href="http://resources.arcgis.com/en/help/main/10.1/index.html#//006600000457000000">.tpk files</a> into your mapping app. Tile packages are a set of tile images from a map compressed into a single binary file.</p>
</div>
<div class="col-xs-6 col-md-4">
<a class="thumbnail" href="../samples/appcache-features.html">
<img src="images/appcachefeatures-demo-thumb.png">
</a>
<h3>AppCache Features and Tiles</h3>
<p>This mobile feature editing demo is configured to maintain your edits and tiles after offline browser reloads and restarts.</p>
</div>
<div class="col-xs-6 col-md-4">
<a class="thumbnail" href="../samples/appcache-tiles.html">
<img src="images/appcachetiles-demo-thumb.png">
</a>
<h3>AppCache Tiles Only</h3>
<p>A bare bones app that demos maintaining the map tiles after offline browser reloads and restarts.</p>
</div>
</div>
</div>
<div class="bs-docs-section">
<h1 id="examples" class="page-header">Supported Browsers</h1>
<p class="lead">The offline libraries are supported on the <strong>latest versions</strong> of the following browsers and operating systems. At this time, we don't offer any IE support.<br>
</p>
<table class="table table-bordered table-striped">
<thead>
<tr>
<td></td>
<th>Chrome</th>
<th>Firefox</th>
<th>Internet Explorer</th>
<th>Opera</th>
<th>Safari</th>
</tr>
</thead>
<tbody>
<tr>
<th>Android</th>
<td class="text-success"><span class="glyphicon glyphicon-ok"></span> <span class="sr-only">Supported</span></td>
<td class="text-success"><span class="glyphicon glyphicon-ok"></span> <span class="sr-only">Supported</span></td>
<td class="text-muted" rowspan="3" style="vertical-align: middle;">N/A</td>
<td class="text-danger"><span class="glyphicon glyphicon-remove"></span> <span class="sr-only">Not Supported</span></td>
<td class="text-muted">N/A</td>
</tr>
<tr>
<th>iOS</th>
<td class="text-success"><span class="glyphicon glyphicon-ok"></span> <span class="sr-only">Supported</span></td>
<td class="text-muted">N/A</td>
<td class="text-danger"><span class="glyphicon glyphicon-remove"></span> <span class="sr-only">Not Supported</span></td>
<td class="text-success"><span class="glyphicon glyphicon-ok">*</span> <span class="sr-only">Supported</span></td>
</tr>
<tr>
<th>Mac OS X</th>
<td class="text-success"><span class="glyphicon glyphicon-ok"></span> <span class="sr-only">Supported</span></td>
<td class="text-success"><span class="glyphicon glyphicon-ok"></span> <span class="sr-only">Supported</span></td>
<td class="text-danger"><span class="glyphicon glyphicon-remove"></span> <span class="sr-only">Not Supported</span></td>
<td class="text-success"><span class="glyphicon glyphicon-ok">*</span> <span class="sr-only">Supported</span></td>
</tr>
<tr>
<th>Windows</th>
<td class="text-success"><span class="glyphicon glyphicon-ok"></span> <span class="sr-only">Supported</span></td>
<td class="text-success"><span class="glyphicon glyphicon-ok"></span> <span class="sr-only">Supported</span></td>
<td class="text-danger"><span class="glyphicon glyphicon-remove"></span> <span class="sr-only">Not Supported</span></td>
<td class="text-danger"><span class="glyphicon glyphicon-remove"></span> <span class="sr-only">Not Supported</span></td>
<td class="text-danger"><span class="glyphicon glyphicon-remove"></span> <span class="sr-only">Not Supported</span></td>
</tr>
</tbody>
</table>
<span class="text-success"><span class="glyphicon glyphicon-ok">*</span></span> - means that <a href="https://github.com/axemclion/IndexedDBShim"> IndexedDBShim.js</a> may be required on this browser/platform. More info available in the <a href="https://github.com/Esri/offline-editor-js"> README </a> under Dependencies.
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
</body>
</html>

1
dist/offline-edit-min.js vendored Normal file

File diff suppressed because one or more lines are too long

1610
dist/offline-edit-src.js vendored Normal file

File diff suppressed because it is too large Load Diff

1
dist/offline-tiles-advanced-min.js vendored Normal file

File diff suppressed because one or more lines are too long

1455
dist/offline-tiles-advanced-src.js vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/offline-tiles-basic-min.js vendored Normal file

File diff suppressed because one or more lines are too long

1318
dist/offline-tiles-basic-src.js vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/offline-tpk-min.js vendored Normal file

File diff suppressed because one or more lines are too long

4622
dist/offline-tpk-src.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -3,26 +3,37 @@ 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`, `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. `ofline.mins.js` is another 3rd party library for detecting if the browser is online or offline. The pattern for how we include the tiles and edit library within the `require` statement is called generic script injection.
```html
<script src="../vendor/offline/offline.min.js"></script>
<script>
require([
"esri/map",
"edit/offlineFeaturesManager",
"edit/editsStore",
function(Map,OfflineFeaturesManager,editsStore)
"..dist/offline-tiles-basic-min.js",
"..dist/offline-edit-min.js",
function(Map)
{
...
});
```
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
var offlineFeaturesManager = new 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 +127,15 @@ 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())
// Be sure to import the module "esri/graphic" in your require statement
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);

View File

@ -7,43 +7,27 @@ The `tiles` library allows a developer to extend a tiled layer with offline supp
There are two approaches to using this set of libraries. The first approach is if you are using an ArcGIS.com Web Map, and the second approach is if you need to be able to restart or reload your application offline.
## Approach 1 - ArcGIS.com Map
Approach #1 is best for partial offline use cases and it uses the `offlineTilesEnabler.js` library. This approach will not allow you to reload or restart the application.
Approach #1 is best for partial offline use cases and it uses the `offline-tiles-basic-min.js` library. This approach will not allow you to reload or restart the application while offline.
**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
<script>
// configure paths BEFORE loading arcgis or dojo libs
var locationPath = location.pathname.replace(/\/[^/]+$/, "");
var dojoConfig = {
paths: {
tiles: locationPath + "/../../lib/tiles",
vendor: locationPath + "/../../vendor"
}
}
</script>
<script src="//js.arcgis.com/3.8compact"></script>
```
**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 O.esri.Tiles.OfflineTilesEnabler();
offlineTilesEnabler.extend(basemapLayer, function(success)
{
if(success) {
@ -53,7 +37,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 +100,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,47 +115,31 @@ 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
<script>
// configure paths BEFORE loading arcgis or dojo libs
var locationPath = location.pathname.replace(/\/[^/]+$/, "");
var dojoConfig = {
paths: {
tiles: locationPath + "/../../lib/tiles",
vendor: locationPath + "/../../vendor"
}
}
</script>
<script src="//js.arcgis.com/3.8compact"></script>
```
**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 O.esri.Tiles.OfflineTilesEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){
console.log("Tile Layer Loaded.");
},_isOnline);
// NOTE: When instantiating the Map, do not specify the basemap property!
var map = new Map("map",{
center: [-104.98,39.74], // long, lat
zoom: 8,
@ -182,7 +151,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:

View File

@ -3,15 +3,25 @@ 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.
**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 O.esri.TPK.TPKLayer();
//Listen for progress events to provide UX feedback
tpkLayer.on("progress", function (evt) {
@ -84,6 +94,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.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 dont match the basemaps LODs. Esri.Map doesnt 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.

58
doc/migratefromv1tov2.md Normal file
View File

@ -0,0 +1,58 @@
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 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. Don't create any aliases for the offline-editor-js libraries within the function statement and add them to the end of the module array, but before domReady. As you can see in the example below, the only alias is for `Map`.
```html
<script>
require([
"esri/map",
"..dist/offline-tiles-basic-min.js",
"..dist/offline-edit-min.js",
"dojo/domReady!"
function(Map)
{
...
});
```
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, and again don't include aliases in the function statement for the offline-editor-js libraries:
```js
define(["..dist/offline-edit-min"],function(){
...
})
```
## Referencing the libraries by namespace
Once the libraries are imported, you can reference the functionality via the following namespace pattern. Check out the `\doc` directory for API specific info.
* `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
## Removing the v1 dojoConfig pathnames
In v2 you can remove the old pathname configurations. In the example below I've commented them out for demonstration purposes. However, I recommend you carefully remove any of the old paths because they simply won't work anymore and could potentially cause errors and headaches for you when you go to update your v1 code to v2.
```js
//var locationPath = location.pathname.replace(/\/[^/]+$/, "");
//var dojoConfig = {
// paths: {
// edit: locationPath + "/../lib/edit",
// tiles: locationPath + "/../lib/tiles",
// }
//}
```

View File

@ -1,14 +1,14 @@
API offlineFeaturesManager
API OfflineFeaturesManager
==================================
##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
--- | ---
`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,22 @@ 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: `editsStore.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.
`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.

View File

@ -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
##O.esri.Tiles.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.
`O.esri.Tiles.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
@ -40,13 +40,13 @@ 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.
##O.esri.Tiles.OfflineTilesEnablerLayer
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`.
`O.esri.Tiles.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

View File

@ -1,7 +1,7 @@
API Doc for TPKLayer
====================
##TPKLayer
##O.esri.TPK.TPKLayer
Extends a tiled map service and provides the ability to display tiles from a .tpk (ArcGIS Tile Page).
@ -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.
`O.esri.TPK.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
@ -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.

View File

@ -1,76 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Offline Editing and Mapping with the ArcGIS API for JavaScript</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style>
body {
font-family: "Helvetica Neue", Arial, Helvetica, sans-serif;
}
h1 {
background-color: #0078c5;
color: white;
padding: 1em;
margin-bottom:1em;
border-radius: 10px;
}
pre code {
padding: 10px 15px;
border: 1px solid #ccc;
background: white;
color: #444;
/* margin: 1em 0 0;*/
font-size: 12px;
font-family: "Consolas", "Menlo", "Lucida Console", "Courier New", monospace;
box-shadow: 0 0 15px #ddd;
-moz-box-shadow: 0 0 15px #ddd;
-webkit-box-shadow: 0 0 15px #ddd;
display: block;
border-radius: 5px;
}
.banner {
background-color: #0078c5;
color: white;
padding: 1em;
margin-bottom:1em;
border-radius: 10px;
}
</style>
</head>
<body>
</body>
<a href="https://github.com/Esri/offline-editor-js"><img style="position: absolute; top: 0; right: 0; border: 0;"
src="https://s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png" alt="Fork me on GitHub"></a>
<h1 class="title">ArcGIS Offline Library for JavaScript</h1>
<p>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.</p>
<p>This repo contains two libraries: </p>
<ul>
<li><b>/edit</b>: handles vector features and stores adds, updates and deletes while offline. Resync's edits with server once connection is reestablished</li>
<ul>
<li><b>offlineFeaturesManager</b> - Extends and overrides a feature layer. </li>
<li><b>editsStore</b> - Provides static helper methods for working with the offline data store.</li>
</ul>
<li><b>/tiles</b>: stores portions of tiled maps client-side and uses the cached tiles when device is offline</li>
<ul>
<li><b>offlineTilesEnabler</b> - Extends and overrides a tiled map service.</li>
</ul>
</ul>
<h3>Why edit offline with JavaScript?</h3>
<p>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.</p>
<h3>What types of features will it work with?</h3>
<p>The library currently works with esri.geometry.Geometry.Point, Polygon and Polyline.</p>
<!--<pre><code>-->
<!--//Instantiate the OfflineStore library and pass it a reference to the map.-->
<!--offlineStore = new OfflineStore(map);-->
<!--</code></pre>-->
<br>
</html>

16
lib/edit/OfflineEditNS.js Normal file
View File

@ -0,0 +1,16 @@
/**
* Creates a namespace for the non-AMD libraries in this directory
*/
if(typeof O != "undefined"){
O.esri.Edit = {}
}
else{
O = {};
O.esri = {
Edit: {}
}
}
"use strict";

View File

@ -1,8 +1,7 @@
/*global IDBKeyRange,indexedDB */
define([], function()
{
"use strict";
var AttachmentsStore = function()
O.esri.Edit.AttachmentsStore = function()
{
this._db = null;
@ -364,5 +363,4 @@ define([], function()
}.bind(this);
};
};
return AttachmentsStore;
});

View File

@ -1,217 +1,214 @@
define(["esri/graphic"], function(Graphic)
{
"use strict";
O.esri.Edit.EditStore = function(Graphic){
/* private consts */
var EDITS_QUEUE_KEY = "esriEditsQueue";
var SEPARATOR = "|@|";
return {
//
// public interface
//
//
// public interface
//
// enum
// enum
ADD: "add",
UPDATE: "update",
DELETE:"delete",
this.ADD = "add";
this.UPDATE = "update";
this.DELETE = "delete";
// ERROR_DUPLICATE_EDIT: "Attempt to insert duplicated edit",
ERROR_LOCALSTORAGE_FULL: "LocalStorage capacity exceeded",
// ERROR_DUPLICATE_EDIT: "Attempt to insert duplicated edit",
this.ERROR_LOCALSTORAGE_FULL = "LocalStorage capacity exceeded";
isSupported: function()
{
// http://stackoverflow.com/questions/11214404/how-to-detect-if-browser-supports-html5-local-storage
var mod = "esriLocalStorageTest";
try {
window.localStorage.setItem(mod, mod);
window.localStorage.removeItem(mod);
return true;
} catch(e) {
return false;
this.isSupported = function()
{
// http://stackoverflow.com/questions/11214404/how-to-detect-if-browser-supports-html5-local-storage
var mod = "esriLocalStorageTest";
try {
window.localStorage.setItem(mod, mod);
window.localStorage.removeItem(mod);
return true;
} catch(e) {
return false;
}
};
this.pushEdit = function(operation,layer,graphic)
{
var edit = {
operation: operation,
layer: layer,
graphic: this._serialize(graphic)
};
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} };
};
this.peekFirstEdit = function()
{
var edits = this.retrieveEditsQueue();
var firstEdit;
if( edits )
{
firstEdit = edits[0];
firstEdit.graphic = this._deserialize(firstEdit.graphic);
return firstEdit;
}
return null;
};
this.popFirstEdit = function()
{
var edits = this.retrieveEditsQueue();
var firstEdit;
if( edits )
{
firstEdit = edits.shift();
this._storeEditsQueue(edits);
firstEdit.graphic = this._deserialize(firstEdit.graphic);
return firstEdit;
}
return null;
};
this.hasPendingEdits = function()
{
var storedValue = window.localStorage.getItem(EDITS_QUEUE_KEY) || "";
return ( storedValue !== "" );
};
this.pendingEditsCount = function()
{
var storedValue = window.localStorage.getItem(EDITS_QUEUE_KEY) || "";
if( storedValue === "" )
{
return 0; // fast easy case
}
var editsArray = this._unpackArrayOfEdits(storedValue);
return editsArray.length;
};
this.resetEditsQueue = function()
{
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);
return (editsQueueValue? EDITS_QUEUE_KEY.length + editsQueueValue.length : 0);
}
this.getLocalStorageSizeBytes = function()
{
var bytes = 0,
key, value;
for(key in window.localStorage )
{
if( window.localStorage.hasOwnProperty(key))
{
value = window.localStorage.getItem(key);
bytes += key.length + value.length;
}
},
}
return bytes;
};
pushEdit: function(operation,layer,graphic)
{
var edit = {
operation: operation,
layer: layer,
graphic: this._serialize(graphic)
};
//
// internal methods
//
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} };
},
//
// graphic serialization/deserialization
//
this._serialize = function(graphic)
{
// keep only attributes and geometry, that are the values that get sent to the server by applyEdits()
// see http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Apply_Edits_Feature_Service_Layer/02r3000000r6000000/
// use graphic's built-in serializing method
var json = graphic.toJson();
var jsonClean =
{
attributes: json.attributes,
geometry: json.geometry
};
return JSON.stringify(jsonClean);
};
peekFirstEdit: function()
{
var edits = this._retrieveEditsQueue();
var firstEdit;
this._deserialize = function(json)
{
var graphic = new Graphic(JSON.parse(json));
return graphic;
};
if( edits )
{
firstEdit = edits[0];
firstEdit.graphic = this._deserialize(firstEdit.graphic);
return firstEdit;
}
return null;
},
this._storeEditsQueue = function(edits)
{
try
{
var serializedEdits = this._packArrayOfEdits(edits);
window.localStorage.setItem(EDITS_QUEUE_KEY, serializedEdits);
return true;
}
catch(err)
{
return false;
}
};
popFirstEdit: function()
{
var edits = this._retrieveEditsQueue();
var firstEdit;
this._packArrayOfEdits = function(edits)
{
var serializedEdits = [];
edits.forEach(function(edit)
{
serializedEdits.push( JSON.stringify(edit) );
});
return serializedEdits.join(SEPARATOR);
};
if( edits )
{
firstEdit = edits.shift();
this._storeEditsQueue(edits);
firstEdit.graphic = this._deserialize(firstEdit.graphic);
return firstEdit;
}
return null;
},
this._unpackArrayOfEdits = function(serializedEdits)
{
if( !serializedEdits )
{
return [];
}
hasPendingEdits: function()
{
var storedValue = window.localStorage.getItem(EDITS_QUEUE_KEY) || "";
return ( storedValue !== "" );
},
var edits = [];
serializedEdits.split(SEPARATOR).forEach( function(serializedEdit)
{
edits.push( JSON.parse(serializedEdit) );
});
pendingEditsCount: function()
{
var storedValue = window.localStorage.getItem(EDITS_QUEUE_KEY) || "";
return edits;
};
if( storedValue === "" )
{
return 0; // fast easy case
}
this._isEditDuplicated = function(newEdit,edits)
{
var i,
edit;
var editsArray = this._unpackArrayOfEdits(storedValue);
return editsArray.length;
},
for(i=0; i<edits.length; i++)
{
edit = edits[i];
if( edit.operation === newEdit.operation &&
edit.layer === newEdit.layer &&
edit.graphic === newEdit.graphic )
{
return true;
}
}
return false;
}
};
resetEditsQueue: function()
{
window.localStorage.setItem(EDITS_QUEUE_KEY, "");
},
getEditsStoreSizeBytes: function()
{
var editsQueueValue = window.localStorage.getItem(EDITS_QUEUE_KEY);
return (editsQueueValue? EDITS_QUEUE_KEY.length + editsQueueValue.length : 0);
},
getLocalStorageSizeBytes: function()
{
var bytes = 0,
key, value;
for(key in window.localStorage )
{
if( window.localStorage.hasOwnProperty(key))
{
value = window.localStorage.getItem(key);
bytes += key.length + value.length;
}
}
return bytes;
},
//
// internal methods
//
//
// graphic serialization/deserialization
//
_serialize: function(graphic)
{
// keep only attributes and geometry, that are the values that get sent to the server by applyEdits()
// see http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#/Apply_Edits_Feature_Service_Layer/02r3000000r6000000/
// use graphic's built-in serializing method
var json = graphic.toJson();
var jsonClean =
{
attributes: json.attributes,
geometry: json.geometry
};
return JSON.stringify(jsonClean);
},
_deserialize: function(json)
{
var graphic = new Graphic(JSON.parse(json));
return graphic;
},
_retrieveEditsQueue: function()
{
var storedValue = window.localStorage.getItem(EDITS_QUEUE_KEY) || "";
return this._unpackArrayOfEdits(storedValue);
},
_storeEditsQueue: function(edits)
{
try
{
var serializedEdits = this._packArrayOfEdits(edits);
window.localStorage.setItem(EDITS_QUEUE_KEY, serializedEdits);
return true;
}
catch(err)
{
return false;
}
},
_packArrayOfEdits: function(edits)
{
var serializedEdits = [];
edits.forEach(function(edit)
{
serializedEdits.push( JSON.stringify(edit) );
});
return serializedEdits.join(SEPARATOR);
},
_unpackArrayOfEdits: function(serializedEdits)
{
if( !serializedEdits )
{
return [];
}
var edits = [];
serializedEdits.split(SEPARATOR).forEach( function(serializedEdit)
{
edits.push( JSON.parse(serializedEdit) );
});
return edits;
},
_isEditDuplicated: function(newEdit,edits)
{
var i,
edit;
for(i=0; i<edits.length; i++)
{
edit = edits[i];
if( edit.operation === newEdit.operation &&
edit.layer === newEdit.layer &&
edit.graphic === newEdit.graphic )
{
return true;
}
}
return false;
}
};
});

View File

@ -1,8 +1,5 @@
define([
"edit/editsStore",
"edit/attachmentsStore",
"dojo/Evented",
"dojo/_base/Deferred",
"dojo/promise/all",
@ -18,15 +15,16 @@ define([
"esri/symbols/SimpleLineSymbol",
"esri/symbols/SimpleFillSymbol",
"esri/urlUtils"],
function(editsStore, AttachmentsStore,
function(
Evented,Deferred,all,declare,array,domAttr,domStyle,query,
esriConfig,GraphicsLayer,Graphic,SimpleMarkerSymbol,SimpleLineSymbol,SimpleFillSymbol,urlUtils)
{
"use strict";
return declare([Evented],
return declare("O.esri.Edit.OfflineFeaturesManager",[Evented],
{
_onlineStatus: "online",
_featureLayers: {},
_editStore: new O.esri.Edit.EditStore(Graphic),
ONLINE: "online", // all edits will directly go to the server
OFFLINE: "offline", // edits will be enqueued
@ -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;
},
@ -342,13 +340,13 @@ define([
{
var objectId = this._getNextTempId();
addEdit.attributes[ this.objectIdField ] = objectId;
var result = editsStore.pushEdit(editsStore.ADD, this.url, addEdit);
var result = self._editStore.pushEdit(self._editStore.ADD, this.url, addEdit);
results.addResults.push({ success:result.success, error: result.error, objectId: objectId});
if(result.success)
{
var phantomAdd = new Graphic(
addEdit.geometry,
self._getPhantomSymbol(addEdit.geometry, editsStore.ADD),
self._getPhantomSymbol(addEdit.geometry, self._editStore.ADD),
{
objectId: objectId
});
@ -362,14 +360,14 @@ define([
updates.forEach(function(updateEdit)
{
var objectId = updateEdit.attributes[ this.objectIdField ];
var result = editsStore.pushEdit(editsStore.UPDATE, this.url, updateEdit);
var result = self._editStore.pushEdit(self._editStore.UPDATE, this.url, updateEdit);
results.updateResults.push({success:result.success, error: result.error, objectId: objectId});
updatesMap[ objectId ] = updateEdit;
if(result.success)
{
var phantomUpdate = new Graphic(
updateEdit.geometry,
self._getPhantomSymbol(updateEdit.geometry, editsStore.UPDATE),
self._getPhantomSymbol(updateEdit.geometry, self._editStore.UPDATE),
{
objectId: objectId
});
@ -383,13 +381,13 @@ define([
deletes.forEach(function(deleteEdit)
{
var objectId = deleteEdit.attributes[ this.objectIdField ];
var result = editsStore.pushEdit(editsStore.DELETE, this.url, deleteEdit);
var result = self._editStore.pushEdit(self._editStore.DELETE, this.url, deleteEdit);
results.deleteResults.push({success:result.success, error: result.error, objectId: objectId});
if(result.success)
{
var phantomDelete = new Graphic(
deleteEdit.geometry,
self._getPhantomSymbol(deleteEdit.geometry, editsStore.DELETE),
self._getPhantomSymbol(deleteEdit.geometry, self._editStore.DELETE),
{
objectId: objectId
});
@ -416,6 +414,54 @@ define([
return deferred;
}; // layer.applyEdits()
/**
* Converts an array of graphics/features into JSON
* @param features
* @param updateEndEvent
* @param callback
*/
layer.convertGraphicLayerToJSON = function(features,updateEndEvent,callback){
var layerDefinition = {};
layerDefinition.objectIdFieldName = updateEndEvent.target.objectIdField;
layerDefinition.globalIdFieldName = updateEndEvent.target.globalIdField;
layerDefinition.geometryType = updateEndEvent.target.geometryType;
layerDefinition.spatialReference = updateEndEvent.target.spatialReference;
layerDefinition.fields = updateEndEvent.target.fields;
var length = features.length;
var jsonArray = [];
for(var i=0; i < length; i++){
var jsonGraphic = features[i].toJson();
jsonArray.push(jsonGraphic);
if(i == (length - 1)) {
var featureJSON = JSON.stringify(jsonArray);
var layerDefJSON = JSON.stringify(layerDefinition);
callback(featureJSON,layerDefJSON);
break;
}
}
}
/**
* Create a featureDefinition
* @param featureLayer
* @param featuresArr
* @param geometryType
* @param callback
*/
layer.getFeatureDefinition = function(/* Object */ featureLayer,/* Array */ featuresArr,/* String */ geometryType,callback){
var featureDefinition = {
"layerDefinition":featureLayer,
"featureSet":{
"features": featuresArr,
"geometryType": geometryType
}
}
callback(featureDefinition);
}
/* internal methods */
@ -539,7 +585,7 @@ define([
getReadableEdit: function(edit)
{
var layer = this._featureLayers[ edit.layer ];
var graphic = editsStore._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)
@ -565,19 +611,19 @@ define([
var width = 1.5;
this._phantomSymbols['point'] = [];
this._phantomSymbols['point'][editsStore.ADD] = new SimpleMarkerSymbol({
this._phantomSymbols['point'][this._editStore.ADD] = new SimpleMarkerSymbol({
"type": "esriSMS", "style": "esriSMSCross",
"xoffset": 10, "yoffset": 10,
"color": [255,255,255,0], "size": 15,
"outline": { "color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid" }
});
this._phantomSymbols['point'][editsStore.UPDATE] = new SimpleMarkerSymbol({
this._phantomSymbols['point'][this._editStore.UPDATE] = new SimpleMarkerSymbol({
"type": "esriSMS", "style": "esriSMSCircle",
"xoffset": 0, "yoffset": 0,
"color": [255,255,255,0], "size": 15,
"outline": { "color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid" }
});
this._phantomSymbols['point'][editsStore.DELETE] = new SimpleMarkerSymbol({
this._phantomSymbols['point'][this._editStore.DELETE] = new SimpleMarkerSymbol({
"type": "esriSMS", "style": "esriSMSX",
"xoffset": 0, "yoffset": 0,
"color": [255,255,255,0], "size": 15,
@ -586,33 +632,33 @@ define([
this._phantomSymbols['multipoint'] = null;
this._phantomSymbols['polyline'] = [];
this._phantomSymbols['polyline'][editsStore.ADD] = new SimpleLineSymbol({
this._phantomSymbols['polyline'][this._editStore.ADD] = new SimpleLineSymbol({
"type": "esriSLS", "style": "esriSLSSolid",
"color": color,"width": width
});
this._phantomSymbols['polyline'][editsStore.UPDATE] = new SimpleLineSymbol({
this._phantomSymbols['polyline'][this._editStore.UPDATE] = new SimpleLineSymbol({
"type": "esriSLS", "style": "esriSLSSolid",
"color": color,"width": width
});
this._phantomSymbols['polyline'][editsStore.DELETE] = new SimpleLineSymbol({
this._phantomSymbols['polyline'][this._editStore.DELETE] = new SimpleLineSymbol({
"type": "esriSLS", "style": "esriSLSSolid",
"color": color,"width": width
});
this._phantomSymbols['polygon'] = [];
this._phantomSymbols['polygon'][editsStore.ADD] = new SimpleFillSymbol({
this._phantomSymbols['polygon'][this._editStore.ADD] = new SimpleFillSymbol({
"type": "esriSFS",
"style": "esriSFSSolid",
"color": [255,255,255,0],
"outline": { "type": "esriSLS", "style": "esriSLSSolid", "color": color, "width": width }
});
this._phantomSymbols['polygon'][editsStore.UPDATE] = new SimpleFillSymbol({
this._phantomSymbols['polygon'][this._editStore.UPDATE] = new SimpleFillSymbol({
"type": "esriSFS",
"style": "esriSFSSolid",
"color": [255,255,255,0],
"outline": { "type": "esriSLS", "style": "esriSLSDash", "color": color, "width": width }
});
this._phantomSymbols['polygon'][editsStore.DELETE] = new SimpleFillSymbol({
this._phantomSymbols['polygon'][this._editStore.DELETE] = new SimpleFillSymbol({
"type": "esriSFS",
"style": "esriSFSSolid",
"color": [255,255,255,0],
@ -746,15 +792,15 @@ define([
_optimizeEditsQueue: function()
{
var optimizedEdits = {},
editCount = editsStore.pendingEditsCount(),
editCount = this._editStore.pendingEditsCount(),
optimizedCount = 0;
var edit, layer;
var layerEdits, objectId;
while( editsStore.hasPendingEdits() )
while( this._editStore.hasPendingEdits() )
{
edit = editsStore.popFirstEdit();
edit = this._editStore.popFirstEdit();
layer = this._featureLayers[ edit.layer ];
if( ! (edit.layer in optimizedEdits) )
@ -776,13 +822,13 @@ define([
// we already have seen one edit for this same feature... we can merge the two edits in a single operation
switch( edit.operation )
{
case editsStore.ADD:
case this._editStore.ADD:
/* impossible!! */
throw("can't add the same feature twice!");
case editsStore.UPDATE:
case this._editStore.UPDATE:
layerEdits[ objectId ].graphic = edit.graphic;
break;
case editsStore.DELETE:
case this._editStore.DELETE:
if(objectId < 0)
{
delete layerEdits[ objectId ];
@ -790,7 +836,7 @@ define([
}
else
{
layerEdits[objectId].operation = editsStore.DELETE;
layerEdits[objectId].operation = this._editStore.DELETE;
}
break;
}
@ -807,7 +853,7 @@ define([
_replayStoredEdits: function(callback)
{
if( editsStore.hasPendingEdits() )
if( this._editStore.hasPendingEdits() )
{
//
// flatten the queue into unique edits for each feature, grouped by FeatureLayer
@ -867,7 +913,7 @@ define([
edit = layerEdits[objectId];
switch(edit.operation)
{
case editsStore.ADD:
case this._editStore.ADD:
for(i=0; i<layer.graphics.length; i++)
{
g = layer.graphics[i];
@ -881,10 +927,10 @@ define([
delete edit.graphic.attributes[ layer.objectIdField ];
adds.push(edit.graphic);
break;
case editsStore.UPDATE:
case this._editStore.UPDATE:
updates.push(edit.graphic);
break;
case editsStore.DELETE:
case this._editStore.DELETE:
deletes.push(edit.graphic);
break;
}

View File

@ -1,58 +0,0 @@
/**
* Helper library for handling features during browser restarts or reloads.
*/
define(["esri/graphic"], function(Graphic) {
"use strict";
return {
/**
* Converts an array of graphics/features into JSON
* @param features
* @param updateEndEvent
* @param callback
*/
convertGraphicLayerToJSON: function(features,updateEndEvent,callback){
var layerDefinition = {};
layerDefinition.objectIdFieldName = updateEndEvent.target.objectIdField;
layerDefinition.globalIdFieldName = updateEndEvent.target.globalIdField;
layerDefinition.geometryType = updateEndEvent.target.geometryType;
layerDefinition.spatialReference = updateEndEvent.target.spatialReference;
layerDefinition.fields = updateEndEvent.target.fields;
var length = features.length;
var jsonArray = [];
for(var i=0; i < length; i++){
var jsonGraphic = features[i].toJson();
jsonArray.push(jsonGraphic);
if(i == (length - 1)) {
var featureJSON = JSON.stringify(jsonArray);
var layerDefJSON = JSON.stringify(layerDefinition);
callback(featureJSON,layerDefJSON);
break;
}
}
},
/**
* Create a featureDefinition
* @param featureLayer
* @param featuresArr
* @param geometryType
* @param callback
*/
getFeatureDefinition: function(/* Object */ featureLayer,/* Array */ featuresArr,/* String */ geometryType,callback){
var featureDefinition = {
"layerDefinition":featureLayer,
"featureSet":{
"features": featuresArr,
"geometryType": geometryType
}
}
callback(featureDefinition);
}
}
})

View File

@ -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
}
});

View File

@ -1,3 +1,5 @@
License for FileSaver.js
This software is licensed under the MIT/X11 license.
MIT/X11 license

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,15 @@
/**
* Creates a namespace for the non-AMD libraries in this directory
*/
if(typeof O != "undefined"){
O.esri.Tiles = {}
}
else{
O = {};
O.esri = {
Tiles: {}
}
}
"use strict";

356
lib/tiles/TilesCore.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -6,259 +6,255 @@
* Author: Andy Gup (@agup)
* Contributor: Javier Abadia (@javierabadia)
*/
define([],function()
{
"use strict";
var TilesStore = function()
O.esri.Tiles.TilesStore = function(){
/**
* Internal reference to the local database
* @type {null}
* @private
*/
this._db = null;
var DB_NAME = "offline_tile_store";
/**
* Determines if indexedDB is supported
* @returns {boolean}
*/
this.isSupported = function(){
if(!window.indexedDB && !window.openDatabase){
return false;
}
return true;
};
/**
* Adds an object to the database
* @param urlDataPair
* @param callback callback(boolean, err)
*/
this.store = function(urlDataPair,callback)
{
/**
* Internal reference to the local database
* @type {null}
* @private
*/
this._db = null;
var DB_NAME = "offline_tile_store";
/**
* Determines if indexedDB is supported
* @returns {boolean}
*/
this.isSupported = function(){
if(!window.indexedDB && !window.openDatabase){
return false;
}
return true;
};
/**
* Adds an object to the database
* @param urlDataPair
* @param callback callback(boolean, err)
*/
this.store = function(urlDataPair,callback)
try
{
try
var transaction = this._db.transaction(["tilepath"],"readwrite");
transaction.oncomplete = function()
{
var transaction = this._db.transaction(["tilepath"],"readwrite");
callback(true);
};
transaction.oncomplete = function()
{
callback(true);
};
transaction.onerror = function(event)
{
callback(false,event.target.error.message);
};
var objectStore = transaction.objectStore("tilepath");
var request = objectStore.put(urlDataPair);
request.onsuccess = function()
{
//console.log("item added to db " + event.target.result);
};
}
catch(err)
transaction.onerror = function(event)
{
console.log("TilesStore: " + err.stack);
callback(false, err.stack);
}
};
callback(false,event.target.error.message);
};
/**
* Retrieve a record.
* @param url
* @param callback
*/
this.retrieve = function(/* String */ url,callback)
var objectStore = transaction.objectStore("tilepath");
var request = objectStore.put(urlDataPair);
request.onsuccess = function()
{
//console.log("item added to db " + event.target.result);
};
}
catch(err)
{
if(this._db !== null)
{
var objectStore = this._db.transaction(["tilepath"]).objectStore("tilepath");
var request = objectStore.get(url);
request.onsuccess = function(event)
{
var result = event.target.result;
if(result == undefined)
{
callback(false,"not found");
}
else
{
callback(true,result);
}
};
request.onerror = function(err)
{
console.log(err);
callback(false, err);
};
}
};
console.log("TilesStore: " + err.stack);
callback(false, err.stack);
}
};
/**
* Deletes entire database
* @param callback callback(boolean, err)
*/
this.deleteAll = function(callback)
/**
* Retrieve a record.
* @param url
* @param callback
*/
this.retrieve = function(/* String */ url,callback)
{
if(this._db !== null)
{
if(this._db !== null)
{
var request = this._db.transaction(["tilepath"],"readwrite")
.objectStore("tilepath")
.clear();
request.onsuccess = function()
{
callback(true);
};
request.onerror = function(err)
{
callback(false, err);
};
}
else
{
callback(false,null);
}
};
/**
* Delete an individual entry
* @param url
* @param callback callback(boolean, err)
*/
this.delete = function(/* String */ url,callback)
{
if(this._db !== null)
{
var request = this._db.transaction(["tilepath"],"readwrite")
.objectStore("tilepath")
.delete(url);
request.onsuccess = function()
{
callback(true);
};
request.onerror = function(err)
{
callback(false, err);
};
}
else
{
callback(false,null);
}
};
/**
* Retrieve all tiles from indexeddb
* @param callback callbakck(url, img, err)
*/
this.getAllTiles = function(callback)
{
if(this._db !== null){
var transaction = this._db.transaction(["tilepath"])
.objectStore("tilepath")
.openCursor();
transaction.onsuccess = function(event)
{
var cursor = event.target.result;
if(cursor){
var url = cursor.value.url;
var img = cursor.value.img;
callback(url,img,null);
cursor.continue();
}
else
{
callback(null, null, "end");
}
}.bind(this);
transaction.onerror = function(err)
{
callback(null, null, err);
};
}
else
{
callback(null, null, "no db");
}
};
/**
* Provides the size of database in bytes
* @param callback callback(size, null) or callback(null, error)
*/
this.usedSpace = function(callback){
if(this._db !== null){
var usage = { sizeBytes: 0, tileCount: 0 };
var transaction = this._db.transaction(["tilepath"])
.objectStore("tilepath")
.openCursor();
transaction.onsuccess = function(event){
var cursor = event.target.result;
if(cursor){
var storedObject = cursor.value;
var json = JSON.stringify(storedObject);
usage.sizeBytes += this._stringBytes(json);
usage.tileCount += 1;
cursor.continue();
}
else
{
callback(usage,null);
}
}.bind(this);
transaction.onerror = function(err)
{
callback(null, err);
};
}
else
{
callback(null,null);
}
};
this._stringBytes = function(str) {
return str.length /**2*/ ;
};
this.init = function(callback)
{
var request = indexedDB.open(DB_NAME, 4);
callback = callback || function(success) { console.log("TilesStore::init() success:", success); }.bind(this);
request.onerror = function(event)
{
console.log("indexedDB error: " + event.target.errorCode);
callback(false,event.target.errorCode);
}.bind(this);
request.onupgradeneeded = function(event)
{
var db = event.target.result;
if( db.objectStoreNames.contains("tilepath"))
{
db.deleteObjectStore("tilepath");
}
db.createObjectStore("tilepath", { keyPath: "url" });
}.bind(this);
var objectStore = this._db.transaction(["tilepath"]).objectStore("tilepath");
var request = objectStore.get(url);
request.onsuccess = function(event)
{
this._db = event.target.result;
console.log("database opened successfully");
callback(true);
}.bind(this);
};
var result = event.target.result;
if(result == undefined)
{
callback(false,"not found");
}
else
{
callback(true,result);
}
};
request.onerror = function(err)
{
console.log(err);
callback(false, err);
};
}
};
return TilesStore;
});
/**
* Deletes entire database
* @param callback callback(boolean, err)
*/
this.deleteAll = function(callback)
{
if(this._db !== null)
{
var request = this._db.transaction(["tilepath"],"readwrite")
.objectStore("tilepath")
.clear();
request.onsuccess = function()
{
callback(true);
};
request.onerror = function(err)
{
callback(false, err);
};
}
else
{
callback(false,null);
}
};
/**
* Delete an individual entry
* @param url
* @param callback callback(boolean, err)
*/
this.delete = function(/* String */ url,callback)
{
if(this._db !== null)
{
var request = this._db.transaction(["tilepath"],"readwrite")
.objectStore("tilepath")
.delete(url);
request.onsuccess = function()
{
callback(true);
};
request.onerror = function(err)
{
callback(false, err);
};
}
else
{
callback(false,null);
}
};
/**
* Retrieve all tiles from indexeddb
* @param callback callback(url, img, err)
*/
this.getAllTiles = function(callback)
{
if(this._db !== null){
var transaction = this._db.transaction(["tilepath"])
.objectStore("tilepath")
.openCursor();
transaction.onsuccess = function(event)
{
var cursor = event.target.result;
if(cursor){
var url = cursor.value.url;
var img = cursor.value.img;
callback(url,img,null);
cursor.continue();
}
else
{
callback(null, null, "end");
}
}.bind(this);
transaction.onerror = function(err)
{
callback(null, null, err);
};
}
else
{
callback(null, null, "no db");
}
};
/**
* Provides the size of database in bytes
* @param callback callback(size, null) or callback(null, error)
*/
this.usedSpace = function(callback){
if(this._db !== null){
var usage = { sizeBytes: 0, tileCount: 0 };
var transaction = this._db.transaction(["tilepath"])
.objectStore("tilepath")
.openCursor();
transaction.onsuccess = function(event){
var cursor = event.target.result;
if(cursor){
var storedObject = cursor.value;
var json = JSON.stringify(storedObject);
usage.sizeBytes += this._stringBytes(json);
usage.tileCount += 1;
cursor.continue();
}
else
{
callback(usage,null);
}
}.bind(this);
transaction.onerror = function(err)
{
callback(null, err);
};
}
else
{
callback(null,null);
}
};
this._stringBytes = function(str) {
return str.length /**2*/ ;
};
this.init = function(callback)
{
var request = indexedDB.open(DB_NAME, 4);
callback = callback || function(success) { console.log("TilesStore::init() success:", success); }.bind(this);
request.onerror = function(event)
{
console.log("indexedDB error: " + event.target.errorCode);
callback(false,event.target.errorCode);
}.bind(this);
request.onupgradeneeded = function(event)
{
var db = event.target.result;
if( db.objectStoreNames.contains("tilepath"))
{
db.deleteObjectStore("tilepath");
}
db.createObjectStore("tilepath", { keyPath: "url" });
}.bind(this);
request.onsuccess = function(event)
{
this._db = event.target.result;
console.log("database opened successfully");
callback(true);
}.bind(this);
};
};

View File

@ -1,74 +1,80 @@
define([],function()
{
"use strict";
var Base64Utils={};
Base64Utils.outputTypes={
// summary:
// Enumeration for input and output encodings.
Base64:0, Hex:1, String:2, Raw:3
};
O.esri.Tiles.Base64Utils={};
O.esri.Tiles.Base64Utils.outputTypes={
// summary:
// Enumeration for input and output encodings.
Base64:0, Hex:1, String:2, Raw:3
};
// word-based addition
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
};
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<<chrsz)-1;
O.esri.Tiles.Base64Utils.stringToWord=function(/* string */s){
// summary:
// convert a string to a word array
Base64Utils.stringToWord=function(/* string */s){
// summary:
// convert a string to a word array
var wa=[];
for(var i=0, l=s.length*chrsz; i<l; i+=chrsz){
wa[i>>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<<chrsz)-1;
Base64Utils.wordToString=function(/* word[] */wa){
// summary:
// convert an array of words to a string
var s=[];
for(var i=0, l=wa.length*32; i<l; i+=chrsz){
s.push(String.fromCharCode((wa[i>>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<l; i++){
s.push(h.charAt((wa[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<l; i+=3){
var t=(((wa[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<l; i+=chrsz){
wa[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<<chrsz)-1;
var s=[];
for(var i=0, l=wa.length*32; i<l; i+=chrsz){
s.push(String.fromCharCode((wa[i>>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<l; i++){
s.push(h.charAt((wa[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<l; i+=3){
var t=(((wa[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;
});

File diff suppressed because one or more lines are too long

View File

@ -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;
});

17
lib/tpk/OfflineTpkNS.js Normal file
View File

@ -0,0 +1,17 @@
/**
* Creates a namespace for the non-AMD libraries in this directory
*/
if(typeof O != "undefined"){
O.esri.TPK = {}
}
else{
O = {};
O.esri = {
TPK: {},
Tiles: {}
}
}
"use strict";

View File

@ -10,10 +10,11 @@
*/
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){
return declare("esri.TPKLayer",[TiledMapServiceLayer,Evented],{
"esri/layers/TileInfo","esri/layers/TiledMapServiceLayer",
"dojo/Deferred","dojo/promise/all","dojo/Evented"],
function(declare,Extent,query,SpatialReference,TileInfo,TiledMapServiceLayer,
Deferred,all,Evented){
return declare("O.esri.TPK.TPKLayer",[TiledMapServiceLayer,Evented],{
//
// Public Properties
@ -54,7 +55,7 @@ define([
this._self = this;
this._inMemTilesIndex = [];
this._inMemTilesObject = {};
this.store = new TilesStore();
this.store = new O.esri.Tiles.TilesStore();
this._validate();
},
@ -62,7 +63,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);
@ -94,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];
@ -103,13 +105,26 @@ 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 "PNG8":
imgURL = "data:image/png;base64," + result;
case "PNG":
imgURL = png + result;
break;
case "PNG8":
imgURL = png + result;
break;
case "PNG24":
imgURL = png + result;
break;
case "PNG32":
imgURL = png + result;
break;
default:
imgURL = "data:image/jpg;base64," + result;
}
img.style.borderColor = "blue";
}
@ -177,7 +192,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{
@ -189,14 +204,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})
@ -207,7 +222,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});
}
},
@ -220,12 +235,16 @@ define([
* @private
*/
_parseInMemFiles: function(files,callback){
var inMemTilesLength = this._fileEntriesLength;
this._zeroLengthFileCounter = 0;
var promises = [];
for(var i=0;i < inMemTilesLength;i++){
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);
@ -235,13 +254,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;
},
/**
@ -267,14 +306,14 @@ define([
* @param callback
* @private
*/
_unzipConfFiles: function(files,token,callback){
files[token].getData(new zip.TextWriter(token),function(data){
_unzipConfFiles: function(files,token,deferred,callback){
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;
var size = this.ObjectSize(this._inMemTilesObject);
if(size == this._fileEntriesLength - this._zeroLengthFileCounter - 1){
callback(this._inMemTilesObject);
if(size > 0){
callback(deferred,data.token);
}
}.bind(this));
},
@ -286,14 +325,14 @@ 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){
files[token].getData(new O.esri.zip.BlobWriter(token),function(data){
if(data.size != 0){
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: " + event.target.error.message);
that.emit(that.PARSING_ERROR, {msg: "Error parsing file: ", err: event.target.error});
}
reader.addEventListener("loadend", function (evt) {
@ -302,8 +341,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(that._inMemTilesObject);
if(size > 0){
callback(deferred,data.token);
}
}
});
@ -318,11 +357,9 @@ define([
* @private
*/
_parseConfCdi: function(callback){
var that = this._self;
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;
@ -330,9 +367,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);
@ -346,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;
@ -398,10 +436,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;
@ -419,7 +458,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});
}
});

View File

@ -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);
}
})
})
return value;
}
this.init = function(){
_setPanListener();
_setZoomListener();
_setOrientationListener(delay);
var centerPt = map.extent.getCenter();
_setCenterPt(centerPt.x,centerPt.y,map.spatialReference.wkid);
}
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -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));

20
package.json Normal file
View File

@ -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 <agup@esri.com> (http://blog.andygup.net)",
"license": "Apache 2",
"contributors": [
"Javier Abadia <javier.abadia@esri.es>"
],
"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"
}
}

View File

@ -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",
@ -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-edit-src.js",
"../dist/offline-tiles-advanced-src.js",
"../dist/offline-tiles-basic-src.js"
/*
"images/*",
"css/*.css"

View File

@ -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:
*

View File

@ -109,10 +109,8 @@
var locationPath = location.pathname.replace(/\/[^/]+$/, "");
var dojoConfig = {
paths: {
edit: locationPath + "/../lib/edit",
vendor: locationPath + "/../vendor",
utils: locationPath + "/../utils",
tiles: locationPath + "/../lib/tiles"
utils: locationPath + "/../utils"
}
}
</script>
@ -174,15 +172,17 @@
<div id="map"></div>
<script>
require(["esri/map","esri/layers/FeatureLayer","tiles/OfflineTilesEnablerLayer",
"edit/restartOfflineFeaturesManager", "utils/appCacheManager",
"esri/renderers/SimpleRenderer","esri/symbols/SimpleMarkerSymbol","esri/Color","esri/tasks/query",
"edit/offlineFeaturesManager", "edit/editsStore",
"dojo/on",
"dojo/domReady!"],
function(Map,FeatureLayer,OfflineTilesEnablerLayer,restartOfflineFeaturesMgr,AppCacheManager,
require(["esri/map","esri/layers/FeatureLayer",
"utils/appCacheManager",
"esri/renderers/SimpleRenderer","esri/symbols/SimpleMarkerSymbol","esri/Color","esri/tasks/query",
"dojo/on",
"esri/graphic",
"../dist/offline-tiles-advanced-min.js",
"../dist/offline-edit-min.js",
"dojo/domReady!"],
function(Map,FeatureLayer,AppCacheManager,
SimpleRenderer,SimpleMarkerSymbol,Color,Query,
OfflineFeaturesManager,editsStore,on) {
on,Graphic) {
initAppCacheManager();
@ -239,6 +239,8 @@
new Color([255,0,0,0.5])).setSize(35);
}
var editsStore = new O.esri.Edit.EditStore(Graphic);
/**
* 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
@ -255,7 +257,7 @@
Offline.check();
Offline.state === 'up' ? resetZoom = 18 : resetZoom = 17;
tileLayer = new OfflineTilesEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){
tileLayer = new O.esri.Tiles.OfflineTileEnablerLayer("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){
console.log("Tile Layer Loaded.");
},_isOnline);
@ -293,7 +295,7 @@
// These are then stored in localStorage.
// If you want you can store multiple feature layers.
// Just be aware of the localStorage limitations.
restartOfflineFeaturesMgr.convertGraphicLayerToJSON(features,evt,function(features,layerDef){
busStopFeatureLayer.convertGraphicLayerToJSON(features,evt,function(features,layerDef){
if(typeof(Storage) !== "undefined") {
localStorage.offlineLayerDef = layerDef;
@ -324,7 +326,9 @@
var featuresArray = JSON.parse(localStorage.offlineFeature);
var geometryType = "esriGeometryPoint";
restartOfflineFeaturesMgr.getFeatureDefinition(featureLayer,featuresArray,geometryType,function(featureDef){
initOfflineFeaturesMgr();
busStopFeatureLayer.getFeatureDefinition(featureLayer,featuresArray,geometryType,function(featureDef){
busStopFeatureLayer = new FeatureLayer(featureDef,{
mode: FeatureLayer.MODE_SNAPSHOT,
@ -339,7 +343,6 @@
on(btnGetTiles,"click",downloadTiles);
on(btnOnlineOffline, 'click', goOnlineOffline);
initOfflineFeaturesMgr();
setFeatureLayerClickHandler();
setModalPopupClickListeners();
mapListen.remove();
@ -359,7 +362,7 @@
*/
function initOfflineFeaturesMgr(){
offlineFeaturesManager = new OfflineFeaturesManager();
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);
@ -375,7 +378,7 @@
function updateStatus(){
if( editsStore.hasPendingEdits())
{
var edits = editsStore._retrieveEditsQueue();
var edits = editsStore.retrieveEditsQueue();
pendingEdits.innerHTML = "Pending edits: " + edits.length;
}
else
@ -558,7 +561,7 @@
});
updateOfflineUsage();
if(typeof baseMapLayer != "undefined") baseMapLayer.goOnline();
if(typeof tileLayer != "undefined") tileLayer.goOnline();
}
function goOffline(){
@ -635,7 +638,7 @@
};
req.onerror = function(e)
{
console.log("verifyOffline failed: " + e);
console.log("appcache-features.html verifyOnline failed: " + e);
callback(false);
};
req.send(null);

View File

@ -1,6 +1,6 @@
CACHE MANIFEST
# This manifest was generated by grunt-manifest HTML5 Cache Manifest Generator
# Time: Wed Jun 04 2014 17:49:41 GMT-0600 (MDT)
# Time: Mon Sep 08 2014 13:44:30 GMT-0600 (MDT)
CACHE:
# manifest-generator, version: 0.0.1
@ -9,42 +9,44 @@ CACHE:
appcache-tiles.html
#
# ArcGIS API for JavaScript files
http://js.arcgis.com/o/agup_hack4co/appcache2/dojo/dojo.js
http://js.arcgis.com/o/agup_hack4co/appcache2/dojo/nls/dojo_en-us.js
http://js.arcgis.com/o/agup_hack4co/appcache2/dojo/selector/acme.js
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/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/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
# /img/1.png
../samples/images/blue-pin.png
../samples/images/red-pin.png
../samples/css/modular-popup.css
../vendor/IndexedDBShim/dist/IndexedDBShim.js
../vendor/IndexedDBShim/dist/IndexedDBShim.min.js
../vendor/offline/offline.min.js
../lib/tiles/FileSaver.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
../utils/appCacheManager.js
../utils/debouncer.js
../dist/offline-edit-src.js
../dist/offline-tiles-advanced-src.js
../dist/offline-tiles-basic-src.js
NETWORK:
*

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html manifest="appcache-tiles.appcache">
<html manifest="appcache-tiles.appcache">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
@ -70,14 +70,13 @@ 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"
}
}
</script>
<!-- This is a custom build of the ArcGIS API for JavaScript using the new Web Optimizer Tool -->
<script src="http://js.arcgis.com/o/agup_hack4co/appcache2/dojo/dojo.js" data-dojo-config="async: true"></script>
<script src="http://js.arcgis.com/o/agup_hack4co/appcacheFeatures/dojo/dojo.js" data-dojo-config="async: true"></script>
<!-- Use this tag below if you are hosting your ArcGIS API for JavaScript files locally -->
<!--<script src="libs/dojo/dojo/dojo.js" data-dojo-config="async: true"></script>-->
<script src="../vendor/IndexedDBShim/dist/IndexedDBShim.min.js"></script>
@ -110,8 +109,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-tiles-advanced-min.js","dojo/domReady!"],
function(Map,AppCacheManager,on) {
var tileLayer = null;
@ -132,6 +131,8 @@ require(["esri/map","utils/appCacheManager","tiles/OfflineTilesEnablerLayer","do
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 +142,14 @@ require(["esri/map","utils/appCacheManager","tiles/OfflineTilesEnablerLayer","do
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 +161,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 = 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 +199,6 @@ require(["esri/map","utils/appCacheManager","tiles/OfflineTilesEnablerLayer","do
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");

View File

@ -44,13 +44,6 @@
</style>
<script>
var locationPath = location.pathname.replace(/\/[^/]+$/, "");
var dojoConfig = {
paths: {
edit: locationPath + "/../lib/edit",
vendor: locationPath + "/../vendor"
}
}
window.proxyPath = "../lib/resource-proxy/proxy.php";
</script>
@ -82,14 +75,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-min.js",
"dojo/domReady!"
], function(
Map,FeatureLayer,AttachmentEditor,esriConfig,
parser,dom,domClass,domConstruct,OfflineFeaturesManager,editsStore,on
parser,dom,domClass,domConstruct,on
)
{
parser.parse();
@ -103,7 +98,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 +133,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];

View File

@ -65,14 +65,6 @@
</style>
<script>
var locationPath = location.pathname.replace(/\/[^/]+$/, "");
var dojoConfig = {
paths: {
edit: locationPath + "/../lib/edit",
tiles: locationPath + "/../lib/tiles",
vendor: locationPath + "/../vendor"
}
}
window.proxyPath = "../lib/resource-proxy/proxy.php";
</script>
@ -102,10 +94,7 @@
"esri/layers/ArcGISTiledMapServiceLayer",
"esri/layers/FeatureLayer",
"edit/offlineFeaturesManager",
"edit/editsStore",
"tiles/offlineTilesEnabler",
"esri/symbols/SimpleMarkerSymbol",
"esri/symbols/SimpleLineSymbol",
@ -119,20 +108,24 @@
"dojo/_base/array", "dojo/parser", "dojo/keys",
"dojo/dom", "dojo/on", "dojo/dom-construct", "dojo/dom-class", "esri/domUtils",
"dijit/layout/BorderContainer", "dijit/layout/ContentPane",
"esri/graphic",
"dijit/layout/BorderContainer", "dijit/layout/ContentPane",
"../dist/offline-tiles-basic-min.js",
"../dist/offline-edit-src.js",
"dojo/domReady!"
], function(
Map, GeometryService, Edit,
ArcGISTiledMapServiceLayer, FeatureLayer,
OfflineFeaturesManager, editsStore, OfflineTilesEnabler,
ArcGISTiledMapServiceLayer, FeatureLayer,
SimpleMarkerSymbol, SimpleLineSymbol,
Editor, TemplatePicker,
esriConfig, jsapiBundle,
arrayUtils, parser, keys,
dom, on, domConstruct, domClass, domUtils
dom, on, domConstruct, domClass, domUtils,Graphic
) {
var editor;
var basemapLayer;
var editStore = new O.esri.Edit.EditStore(Graphic); // include esri/graphic reference for serialization/deserialization
parser.parse();
dojo.fadeOut({node:'loader-cloak', onEnd: function(n){n.style.display="none"}}).play();
@ -143,7 +136,7 @@
on(dom.byId('go-offline-btn'),'click', goOffline);
on(dom.byId('go-online-btn'),'click', goOnline);
on(dom.byId('refresh-feature-layers-btn'),'click', refreshFeatureLayers);
var offlineFeaturesManager = new 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);
@ -186,7 +179,7 @@
function initEditor(evt)
{
try {
var offlineTilesEnabler = new OfflineTilesEnabler();
var offlineTilesEnabler = new O.esri.Tiles.OfflineTilesEnabler();
basemapLayer = map.getLayer( map.layerIds[0] );
offlineTilesEnabler.extend(basemapLayer);
@ -292,10 +285,10 @@
{
return Math.floor(bytes / 1024 / 1024 * 100)/100 + " MBs";
}
var localStorageSizeBytes = editsStore.getLocalStorageSizeBytes();
var editsStoreSizeBytes = editsStore.getEditsStoreSizeBytes();
var localStorageSizeBytes = editStore.getLocalStorageSizeBytes();
var editStoreSizeBytes = editStore.getEditsStoreSizeBytes();
var info = "Used " + formatMb(localStorageSizeBytes) + " (" + formatMb(editsStoreSizeBytes) + ")";
var info = "Used " + formatMb(localStorageSizeBytes) + " (" + formatMb(editStoreSizeBytes) + ")";
dom.byId('storageInfo').innerHTML = info;
}
@ -313,9 +306,9 @@
{
var li;
domConstruct.empty('pendingEdits');
if( editsStore.hasPendingEdits())
if( editStore.hasPendingEdits())
{
var edits = editsStore._retrieveEditsQueue();
var edits = editStore.retrieveEditsQueue();
edits.forEach(function(edit)
{
var readableEdit = offlineFeaturesManager.getReadableEdit(edit);
@ -332,7 +325,7 @@
function clearPendingEdits()
{
editsStore.resetEditsQueue();
editStore.resetEditsQueue();
updateStatus();
}

View File

@ -5,10 +5,10 @@
"https://www.npmjs.org/doc/cli/npm-init.html"
],
"name": "manifest-generator",
"manifestName": "appcache-features.appcache",
"appHomePage": "appcache-features.html",
"manifestName": "appcache-tiles.appcache",
"appHomePage": "appcache-tiles.html",
"optimizedApiURL": "http://js.arcgis.com/o/agup_hack4co/appcacheFeatures",
"arcGISBaseURL": "http://js.arcgis.com/3.9",
"arcGISBaseURL": "http://js.arcgis.com/3.10",
"version": "0.0.1",
"private": true,
"description": "manifest generator project",

View File

@ -237,7 +237,7 @@
function refreshLocalStorageSummary()
{
domConstruct.empty('local-storage-summary-table-body');
var edits = editsStore._retrieveEditsQueue();
var edits = editsStore.retrieveEditsQueue();
edits.forEach(function(edit)
{
var components = edit.layer.split('/');

View File

@ -9,7 +9,7 @@
<link rel="shortcut icon" href="http://esri.github.io/bootstrap-map-js/doc/images/favicon.ico">
<link rel="stylesheet" href="//js.arcgis.com/3.7/js/esri/css/esri.css">
<link rel="stylesheet" href="//js.arcgis.com/3.10/js/esri/css/esri.css">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" >
<link rel="stylesheet" href="http://esri.github.io/bootstrap-map-js/src/css/bootstrapmap.css">
@ -231,9 +231,7 @@
<script>
var locationPath = location.pathname.replace(/\/[^/]+$/, "");
var dojoConfig = {
paths: {
tiles: locationPath + "/../lib/tiles",
vendor: locationPath + "/../vendor",
paths: {
utils: locationPath + "/../utils",
bootstrapmap: "//esri.github.io/bootstrap-map-js/src/js/bootstrapmap"
}
@ -242,9 +240,9 @@
</script>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="//js.arcgis.com/3.8compact"></script>
<script src="//js.arcgis.com/3.10compact"></script>
<script src="../vendor/IndexedDBShim/dist/IndexedDBShim.min.js"></script>
<script src="../vendor/offline/offline.min.js"></script>
<!-- For detecting if app is online or offline --><script src="../vendor/offline/offline.min.js"></script>
<script>
Offline.options = {
checks: {
@ -272,16 +270,16 @@ require(
"esri/layers/GraphicsLayer", "esri/graphic", "esri/symbols/SimpleFillSymbol",
"esri/dijit/Scalebar", "esri/arcgis/utils", "esri/geometry",
"esri/urlUtils", "esri/geometry/webMercatorUtils",
"tiles/offlineTilesEnabler","utils/debouncer",
"utils/debouncer",
"dojo/on", "dojo/query",
"dojo/dom","dojo/dom-construct","dojo/dom-class","dojo/dom-style",
"dojo/_base/window",
"bootstrapmap", "dojo/domReady!"],
"bootstrapmap","../dist/offline-tiles-basic-min.js", "dojo/domReady!"],
function(Map,
GraphicsLayer, Graphic, SimpleFillSymbol,
Scalebar, esriUtils, geometry,
urlUtils, webMercatorUtils,
OfflineTilesEnabler,debouncer,
debouncer,
on, query,
dom,domConstruct,domClass,domStyle,
win,
@ -293,7 +291,7 @@ require(
var stateNode;
initOfflineDetector();
var offlineTilesEnabler = new OfflineTilesEnabler();
var offlineTilesEnabler = new O.esri.Tiles.OfflineTilesEnabler();
// Load web map when page loads
var urlObject = urlUtils.urlToObject(window.location.href);
@ -522,8 +520,8 @@ require(
{
console.log('updating');
var minLevel = dojo.byId('minLevel').value;
var maxLevel = dojo.byId('maxLevel').value;
var minLevel = parseInt(dojo.byId('minLevel').value);
var maxLevel = parseInt(dojo.byId('maxLevel').value);
var totalEstimation = { tileCount:0, sizeBytes:0 }

View File

@ -4,7 +4,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no"/>
<title>TPKLayer</title>
<link rel="stylesheet" href="http://js.arcgis.com/3.9/js/esri/css/esri.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/esri/css/esri.css">
<style>
html, body, #map {
height: 100%;
@ -111,20 +111,11 @@
<div id="map"></div>
<script>
var locationPath = location.pathname.replace(/\/[^/]+$/, "");
var dojoConfig = {
paths: {
tpk: locationPath + "/../lib/tpk",
tiles: locationPath + "/../lib/tiles"
}
}
</script>
<script src="../vendor/IndexedDBShim/dist/IndexedDBShim.min.js"></script>
<script src="http://js.arcgis.com/3.9/"></script>
<script src="http://js.arcgis.com/3.10/"></script>
<script>
require(["esri/map","tpk/TPKLayer","tpk/zip","dojo/on","dojo/_base/window","dojo/domReady!"],
function(Map,TPKLayer,zip,on,win) {
require(["esri/map","dojo/on","dojo/_base/window","../dist/offline-tpk-min.js","dojo/domReady!"],
function(Map,on,win) {
var map;
var fileInput,tpkLayer, urlInputBtn;
@ -139,10 +130,7 @@
*/
function zipParser(blob){
//IMPORTANT!
zip.workerScriptsPath = locationPath + "/../lib/tpk/"; //tell zip.js where to find it's associated scripts
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){
@ -158,7 +146,7 @@
* Initialize the Map and the TPKLayer
*/
function initMap(entries){
tpkLayer = new TPKLayer();
tpkLayer = new O.esri.TPK.TPKLayer();
tpkLayer.on("progress", function (evt) {
evt == "start" ? loading.style.visibility = "visible" : loading.style.visibility = "hidden";
})

View File

@ -31,9 +31,9 @@
}
</script>
<link rel="stylesheet" href="http://js.arcgis.com/3.9/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.9/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.9/"></script>
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.10/"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/tpkLayerSpec.js"></script>
@ -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-tpk-min.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 O.esri.TPK.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);

View File

@ -17,9 +17,9 @@
}
</script>
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.8/"></script>
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.10/"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/attachmentsStoreSpec.js"></script>
@ -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-edit-min.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();

View File

@ -17,9 +17,9 @@
}
</script>
<link rel="stylesheet" href="http://js.arcgis.com/3.6/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.6/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.6/"></script>
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.10/"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/editsStoreSpec.js"></script>
@ -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-edit-min.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()

View File

@ -20,9 +20,9 @@
window.proxyPath = "../lib/resource-proxy/proxy.php";
</script>
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.8/"></script>
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.10/"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/offlineAttachmentsSpec.js"></script>
@ -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-edit-min.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 O.esri.Edit.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);

View File

@ -19,9 +19,9 @@
}
</script>
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.8/"></script>
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.10/"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/offlineFeaturesManagerSpec.js"></script>
@ -38,8 +38,9 @@
"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-edit-min.js",
"dojo/domReady!"],
function(Map,
GraphicsLayer, Graphic,
FeatureLayer, geometry, esriRequest,
@ -49,8 +50,8 @@
{
g_modules.esriRequest = esriRequest;
g_modules.Graphic = Graphic;
g_offlineFeaturesManager = new OfflineFeaturesManager();
g_editsStore = editsStore;
g_offlineFeaturesManager = new O.esri.Edit.OfflineFeaturesManager();
g_editsStore = new O.esri.Edit.EditStore();
g_map = new Map("map", {
basemap: "satellite",
@ -94,7 +95,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);

View File

@ -16,9 +16,9 @@
}
</script>
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.8/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.8/"></script>
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.10/"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/offlineTilesEnablerSpec.js"></script>
@ -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",
@ -35,7 +36,9 @@
"dojo/dom", "dojo/on", "dojo/query",
"esri/urlUtils", "esri/geometry/webMercatorUtils",
"tiles/offlineTilesEnabler",
"dojo/dom-construct", "dojo/domReady!"],
"dojo/dom-construct",
"../dist/offline-tiles-basic-min.js",
"dojo/domReady!"],
function(Map,
GraphicsLayer, Graphic, SimpleFillSymbol,
Scalebar, esriUtils, geometry,
@ -56,11 +59,11 @@
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;
jasmineEnv.defaultTimeoutInterval = 10000; // 10 sec
jasmineEnv.defaultTimeoutInterval = 5000; // 15 sec
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);

View File

@ -10,15 +10,9 @@
<script type="text/javascript" src="../vendor/jasmine-1.3.1/jasmine-html.js"></script>
<script type="text/javascript" src="../vendor/jasmine.async/lib/jasmine.async.js"></script>
<script>
var dojoConfig = {
paths: { tiles: location.pathname.replace(/\/[^/]+$/, "") + "../../lib/tiles" }
}
</script>
<link rel="stylesheet" href="http://js.arcgis.com/3.9/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.9/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.9/"></script>
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/dojo/dijit/themes/claro/claro.css">
<link rel="stylesheet" href="http://js.arcgis.com/3.10/js/esri/css/esri.css">
<script src="http://js.arcgis.com/3.10/"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec/offlineTilesEnablerLayerSpec.js"></script>
@ -27,23 +21,32 @@
var g_map;
var g_basemapLayer;
var tilesCore;
require(["esri/map",
"esri/layers/GraphicsLayer", "esri/graphic", "esri/symbols/SimpleFillSymbol",
"esri/dijit/Scalebar", "esri/arcgis/utils", "esri/geometry",
"dojo/dom", "dojo/on", "dojo/query",
"esri/urlUtils", "esri/geometry/webMercatorUtils",
"tiles/OfflineTilesEnablerLayer",
"esri/layers/LOD",
"esri/geometry/Point",
"esri/geometry/Extent",
"esri/layers/TileInfo",
"esri/SpatialReference",
"esri/geometry/Polygon",
"../dist/offline-tiles-advanced-min.js",
"dojo/dom-construct", "dojo/domReady!"],
function(Map,
GraphicsLayer, Graphic, SimpleFillSymbol,
Scalebar, esriUtils, geometry,
dom, on, query,
urlUtils, webMercatorUtils,
LOD,Point,Extent,TileInfo,SpatialReference,Polygon,
OfflineTilesEnablerLayer,
domConstruct)
{
g_basemapLayer = new OfflineTilesEnablerLayer("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);
@ -57,11 +60,13 @@
g_map.addLayer(g_basemapLayer);
tilesCore = new O.esri.Tiles.TilesCore();
function test()
{
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
jasmineEnv.defaultTimeoutInterval = 20000; // 20 sec
jasmineEnv.defaultTimeoutInterval = 5000; // 20 sec
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);

View File

@ -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();

View File

@ -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();
});
@ -452,7 +452,7 @@ describe("Attachments", function()
});
});
async.it("go Online", function(done)
it("go Online", function(done)
{
expect(g_featureLayers[3].graphics.length).toBe(2);
@ -492,28 +492,28 @@ describe("Attachments", function()
{
expect(success).toBeTruthy();
expect(result.count).toBe(2);
done();
// done();
});
});
expect(g_offlineFeaturesManager.getOnlineStatus()).toBe(g_offlineFeaturesManager.RECONNECTING);
});
async.it("no edits pending", function(done)
it("no edits pending", function(done)
{
expect(g_editsStore.pendingEditsCount()).toBe(0);
done();
// done();
});
async.it("no attachments pending", function(done)
it("no attachments pending", function(done)
{
g_offlineFeaturesManager.attachmentsStore.getUsage(function(usage)
{
expect(usage.attachmentCount).toBe(0);
done();
// done();
});
});
async.it("query attachments info - online - 1", function(done)
it("query attachments info - online - 1", function(done)
{
g_featureLayers[3].queryAttachmentInfos(g1_online.attributes.objectid,
function(attachmentsInfo)
@ -521,16 +521,16 @@ describe("Attachments", function()
expect(attachmentsInfo.length).toBe(1);
expect(attachmentsInfo[0].objectId).toBe(g1_online.attributes.objectid);
expect(attachmentsInfo[0].id).toBeGreaterThan(0);
done();
// done();
},
function(err)
{
expect(true).toBeFalsy();
done();
// done();
});
});
async.it("query attachments info - online - 2", function(done)
it("query attachments info - online - 2", function(done)
{
g_featureLayers[3].queryAttachmentInfos(g2_offline.attributes.objectid,
function(attachmentsInfo)
@ -538,12 +538,12 @@ describe("Attachments", function()
expect(attachmentsInfo.length).toBe(1);
expect(attachmentsInfo[0].objectId).toBe(g2_offline.attributes.objectid);
expect(attachmentsInfo[0].id).toBeGreaterThan(0);
done();
// done();
},
function(err)
{
expect(true).toBeFalsy();
done();
// done();
});
});
});

View File

@ -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

View File

@ -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)
@ -220,19 +226,77 @@ describe("offline enabler custom layer library", function()
})
});
it("verifies ability to retrieve layer info",function(done){
async.it("verifies ability to retrieve layer info",function(done){
g_basemapLayer._getTileInfoPrivate("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(result){
var fixedResponse = result.replace(/\\'/g, "'");
var resultObj = JSON.parse(fixedResponse);
expect(resultObj).toEqual(jasmine.any(Object));
done();
})
});
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){
expect(result).toEqual(jasmine.any(Object));
async.it("verifies ability to parse layer info",function(done){
require(["esri/layers/LOD",
"esri/geometry/Point",
"esri/geometry/Extent",
"esri/layers/TileInfo",
"esri/SpatialReference",
"esri/geometry/Polygon"],function(LOD,Point,Extent,TileInfo,SpatialReference){
g_basemapLayer._getTileInfoPrivate("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(result){
tilesCore._parseGetTileInfo(SpatialReference,LOD,Extent,TileInfo,Point,result,function(result){
expect(result.resultObj).toEqual(jasmine.any(Object));
expect(result.initExtent.type).toEqual("extent");
expect(result.fullExtent.type).toEqual("extent");
expect(result.tileInfo.format).toEqual("JPEG");
done();
})
})
})
});
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();
});
})
});
async.it("load csv from file",function(done){
var csv = ["url,img\r\nhttp://esri.com,base64image_goes_here"];
var blob = new Blob(csv, {type : 'text/csv'});
blob.name = "test1";
g_basemapLayer.loadFromFile(blob,function(success,result){
expect(success).toBe(true);
expect(result).toEqual("1 tiles loaded from test1");
done();
})
});
async.it("save tiles to csv",function(done){
g_basemapLayer.saveToFile("testSaveToCSV",function(success,result){
expect(success).toBe(true);
done();
})
});

View File

@ -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)
{
@ -206,4 +211,46 @@ 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();
});
})
});
async.it("load csv from file",function(done){
var csv = ["url,img\r\nhttp://esri.com,base64image_goes_here"];
var blob = new Blob(csv, {type : 'text/csv'});
blob.name = "test1";
g_basemapLayer.loadFromFile(blob,function(success,result){
expect(success).toBe(true);
expect(result).toEqual("1 tiles loaded from test1");
done();
})
});
async.it("save tiles to csv",function(done){
g_basemapLayer.saveToFile("testSaveToCSV",function(success,result){
expect(success).toBe(true);
done();
})
});
});

View File

@ -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();
})