2014-06-10 14:16:13 -06:00

477 lines
20 KiB
HTML

<!DOCTYPE html>
<!--<html>-->
<html manifest="appcache-features.appcache">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--The viewport meta tag is used to improve the presentation and behavior of the samples
on iOS devices-->
<meta name="viewport" content="initial-scale=1, maximum-scale=1,user-scalable=no">
<title>AppCache Tiles and Features</title>
<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">
<style>
html, body, #map {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
body {
background-color: #FFF;
overflow: hidden;
font-family: "Trebuchet MS";
}
#map {
z-index: 1;
position: absolute;
top: 50px;
left: 0;
}
#button-div1{
position: relative;
z-index: 2;
background: #000000;
color: #ffffff;
width: 100%;
height: 50px;
}
#right-div{
height: 50px;
position: relative; float: right;
}
#img-offline-indicator{
/*padding: 8px;*/
position: relative; float: right;
position: relative; float: right;
}
#pending-edits{
padding: 8px;
position: relative; float: right;
}
.basic-btn{
background-color: #000000;
border-color: #ffffff 1px;
color: #ffffff;
padding: 8px;
position: relative; float: left;
}
</style>
<script src="../vendor/offline/offline.min.js"></script>
<script>
Offline.options = {
checks: {
image: {
url: function() {
return 'http://esri.github.io/offline-editor-js/tiny-image.png?_=' + (Math.floor(Math.random() * 1000000000));
}
},
active: 'image'
}
}
var locationPath = location.pathname.replace(/\/[^/]+$/, "");
var dojoConfig = {
paths: {
edit: locationPath + "/../lib/edit",
vendor: locationPath + "/../vendor",
utils: locationPath + "/../utils",
tiles: locationPath + "/../lib/tiles"
}
}
</script>
<!-- Required when using the offline-editor-js library with Safari -->
<script src="../vendor/IndexedDBShim/dist/IndexedDBShim.js"></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>-->
</head>
<body>
<div id="button-div1">
<button class="basic-btn" id="btn-get-tiles">1. Download Tiles</button>
<button class="basic-btn" data-dojo-type="dijit/form/ToggleButton" id="btn-online-offline">2. Go Offline</button>
<div id="right-div">
<img id="img-offline-indicator" src="../samples/images/blue-pin.png"/>
<div id="pending-edits">Pending edits: 0</div>
</div>
</div>
<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",
"edit/offlineFeaturesManager", "edit/editsStore",
"dojo/on",
"dojo/domReady!"],
function(Map,FeatureLayer,OfflineTilesEnabler,restartOfflineFeaturesMgr,AppCacheManager,
SimpleRenderer,SimpleMarkerSymbol,Color,OfflineFeaturesManager,editsStore,on) {
initAppCacheManager();
var _isOnline = true;
var defaultSymbol;
// Variables for editing handling
var busStopFeatureLayer;
var offlineFeaturesManager;
var pendingEdits = document.getElementById("pending-edits");
var imgOfflineIndicator = document.getElementById("img-offline-indicator");
// Variables for handling tiles
var tileLayer;
var _currentExtent = null;
var _wantToCancel;
var globalState = {};
var btnGetTiles = document.getElementById("btn-get-tiles");
// Important settings for determining which tile layers gets stored for offline use.
var minZoomAdjust = -1, maxZoomAdjust = 1, resetZoom = 18;
var EXTENT_BUFFER = 0; //buffers the map extent in meters
// Misc.
var appCacheManager;
var btnOnlineOffline = document.getElementById("btn-online-offline");
// Symbols and images
var redPinPath = "../samples/images/red-pin.png";
var redPinImage = new Image().src = redPinPath;
var bluePinPath = "../samples/images/blue-pin.png";
// Modify symbol size based on screen size.
// Bigger screens get smaller symbols. Smaller screens get larger symbols.
if (document.documentElement.clientHeight > 768 || document.documentElement.clientWidth > 1024) {
defaultSymbol= new SimpleMarkerSymbol().setStyle(
SimpleMarkerSymbol.STYLE_DIAMOND).setColor(
new Color([255,0,0,0.5])).setSize(20); // scripts
}
else{
defaultSymbol= new SimpleMarkerSymbol().setStyle(
SimpleMarkerSymbol.STYLE_DIAMOND).setColor(
new Color([255,0,0,0.5])).setSize(35);
}
/**
* 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
* offline prior to launching any map functionality.
*/
verifyOnline(function(result){ console.log("VERIFY ONLINE " + result)
result == true ? _isOnline = true : _isOnline = false;
startMap();
})
function startMap(){
//Make sure map shows up after a browser refresh
Offline.check();
Offline.state === 'up' ? resetZoom = 18 : resetZoom = 17;
tileLayer = new OfflineTilesEnabler("http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer",function(evt){
console.log("Tile Layer Loaded.");
},_isOnline);
var map = new Map("map",{
center: [-104.98,39.74], // long, lat
zoom: 8,
sliderStyle: "small"
});
map.addLayer(tileLayer);
busStopFeatureLayer = new FeatureLayer("http://services.arcgis.com/IZtlGBUe4KTzLOl4/arcgis/rest/services/BPX_RTD_BusStops2/FeatureServer/0", {
mode: FeatureLayer.MODE_SNAPSHOT,
outFields: ["OBJECTID","BSID","ROUTES","STOPNAME"]
});
//Set the graphics to red boxes to make it easy to click on them
//on a mobile device.
busStopFeatureLayer.setRenderer(new SimpleRenderer(defaultSymbol));
busStopFeatureLayer.on("update-end",function(evt){
on(btnGetTiles,"click",downloadTiles);
initOfflineFeaturesMgr();
var features = evt.target.graphics;
// Convert existing feature graphics into JSON.
// 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){
if(typeof(Storage) !== "undefined") {
localStorage.offlineLayerDef = layerDef;
localStorage.offlineFeature = features;
console.log("Done pushing layerDef and features to localStorage.")
} else {
alert("The offline library is not supported on this browser.")
}
})
});
map.on("zoom-end",function(evt){
_currentExtent = evt.extent;
});
map.on("pan-end",function(evt){
_currentExtent = evt.extent;
});
map.on("load",function(evt){
_currentExtent = evt.map.extent;
updateOfflineUsage();
if(_isOnline == false){
var featureLayer = JSON.parse(localStorage.offlineLayerDef);
var featuresArray = JSON.parse(localStorage.offlineFeature);
var geometryType = "esriGeometryPoint";
restartOfflineFeaturesMgr.getFeatureDefinition(featureLayer,featuresArray,geometryType,function(featureDef){
busStopsFeatureLayer = new FeatureLayer(featureDef,{
mode: FeatureLayer.MODE_SNAPSHOT,
outFields: ["OBJECTID","BSID","ROUTES","STOPNAME"]
});
// Set the graphics to red boxes to make it easy to click on them
// on a mobile device.
busStopsFeatureLayer.setRenderer(new SimpleRenderer(defaultSymbol));
var mapListen = map.on("update-end",function(evt){
console.log("Feature has been added back to the map while offline.")
mapListen.remove();
})
map.addLayer(busStopsFeatureLayer);
// offlineFeaturesManager.extend(busStopsFeatureLayer);
// setFeatureLayerClickHandler()
});
}
});
map.addLayer(busStopFeatureLayer);
}
/**
* **********************************************
* FEATURELAYER MANAGEMENT CODE GOES BELOW HERE
* **********************************************
*/
function initOfflineFeaturesMgr(){
offlineFeaturesManager = new OfflineFeaturesManager();
offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_ENQUEUED, updateStatus);
offlineFeaturesManager.on(offlineFeaturesManager.events.EDITS_SENT, updateStatus);
offlineFeaturesManager.on(offlineFeaturesManager.events.ALL_EDITS_SENT, updateStatus);
offlineFeaturesManager.extend(busStopFeatureLayer);
console.log("offlineFeaturesManager initialized.");
Offline.check();
Offline.on('up', goOnline);
Offline.on('down', goOffline);
}
function updateStatus(){
if( editsStore.hasPendingEdits())
{
var edits = editsStore._retrieveEditsQueue();
pendingEdits.innerHTML = "Pending edits: " + edits.length;
}
else
{
pendingEdits.innerHTML = "Pending edits: 0";
}
}
/**
* ************************************
* TILE MANAGEMENT CODE GOES BELOW HERE
* ************************************
*/
/**
* Manually starts the process to download and store tiles
* in the local database
*/
function downloadTiles(){
tileLayer.deleteAllTiles(function(success,err){
if(success == false){
alert("There was a problem deleting the tile cache");
}
else{
console.log("success deleting tile cache");
if( globalState.downloadState == 'downloading')
{
console.log("cancel!");
_wantToCancel = true;
btnGetTiles.innerHTML = "cancelling..";
}
else
{
var zoom = getMinMaxZoom();
var extent = tileLayer.getExtentBuffer(EXTENT_BUFFER,_currentExtent);
_wantToCancel = false;
tileLayer.prepareForOffline(zoom.min, zoom.max, extent, reportProgress.bind(this));
globalState.downloadState = 'downloading';
}
}
}.bind(this))
}
/**
* Reports the process while downloading tiles.
*/
function reportProgress(progress)
{
console.log("downloading tiles...");
if(progress.hasOwnProperty("countNow")){
var percent = Math.floor(progress.countNow / progress.countMax * 100);
btnGetTiles.innerHTML = 'Saving to phone ' + percent + "% - Tap to Cancel";
}
if( progress.finishedDownloading )
{
btnGetTiles.innerHTML = "Saving to phone 100% - Tap to Cancel";
updateOfflineUsage();
if( progress.cancelRequested )
{
globalState.downloadState = 'cancelled';
alert("Tile download was cancelled");
}
else
{
globalState.downloadState = 'downloaded';
alert("Tile download complete");
}
btnGetTiles.innerHTML = '1. Download Tiles';
}
return _wantToCancel; //determines if a cancel request has been issued
}
/**
* Utility function to validate min and max zoom settings of the map
*/
function getMinMaxZoom(){
var zoom = {};
var min = tileLayer.getLevel() + minZoomAdjust;
var max = tileLayer.getLevel() + maxZoomAdjust;
var mMaxZoom;
tileLayer.getMaxZoom(function(result){
mMaxZoom = result;
});
var mMinZoom;
tileLayer.getMinZoom(function(result){
mMinZoom = result;
});
zoom.max = Math.min(mMaxZoom, max); //prevent errors by setting the tile layer floor
zoom.min = Math.max(mMinZoom, min); //prevent errors by setting the tile layer ceiling
return zoom;
}
/**
* ************************************
* MISC. CODE GOES BELOW HERE
* ************************************
*/
function goOnline(){
console.log("Going online...");
offlineFeaturesManager.goOnline(function(success,error){
if(error === undefined){
btnOnlineOffline.innerHTML = "2. Go Offline";
imgOfflineIndicator.src = bluePinPath;
console.log("offlineFeatureManager is online.");
}
else{
alert("There was a problem syncing offline edits: " + JSON.stringify(error));
}
});
updateOfflineUsage();
if(typeof baseMapLayer != "undefined") baseMapLayer.goOnline();
}
function goOffline(){
console.log("Going offline...");
btnOnlineOffline.innerHTML = "2. Go Online";
imgOfflineIndicator.src = redPinPath;
offlineFeaturesManager.goOffline();
if(typeof tileLayer != "undefined") tileLayer.goOffline();
}
/**
* For internal use - detecting amount of storage used and number of tiles stored.
*/
function updateOfflineUsage()
{
tileLayer.offline.store.usedSpace(function(result,err){
if(result != null){
console.log( "DB Tile count: " + result.tileCount + ", DB Bytes: " + result.sizeBytes);
}
else{
console.log("DB Tile count: " + count + ", DB Bytes: Error");
}
})
}
function initAppCacheManager(){
appCacheManager = new AppCacheManager(true,true);
appCacheManager.on(appCacheManager.CACHE_EVENT,cacheEventHandler);
appCacheManager.on(appCacheManager.CACHE_ERROR,cacheErrorHandler);
appCacheManager.on(appCacheManager.CACHE_LOADED,cacheLoadedHandler);
}
function cacheLoadedHandler(evt){
if(evt == appCacheManager.CACHE_LOADED) console.log("Application cache successfully loaded!")
}
function cacheEventHandler(evt){
if(evt.hasOwnProperty("total")) console.log("CACHE EVENT loaded #:" + evt.loaded + ", out of " + evt.total);
}
function cacheErrorHandler(evt){
console.log("CACHE ERROR: " + JSON.stringify(evt));
alert("There was a problem loading the cache. Try reloading the app. ")
}
/**
* Attempts an http request to verify if app is online or offline.
* Use this in conjunction with the offline checker library: offline.min.js
* @param callback
*/
function verifyOnline(callback){
var req = new XMLHttpRequest();
req.open("GET", "images/blue-pin.png?" + (Math.floor(Math.random() * 1000000000)), true);
req.onload = function()
{
if( req.status === 200 && req.responseText !== "")
{
callback(true);
}
else
{
console.log("verifyOffline failed");
callback(false);
}
};
req.onerror = function(e)
{
console.log("verifyOffline failed: " + e);
callback(false);
};
req.send(null);
}
});
</script>
</body>
</html>