mirror of
https://github.com/Esri/offline-editor-js.git
synced 2025-12-15 15:20:05 +00:00
Fix package version to work with current NPM. Required before npm install would run. Update changelog and increment npm package version.
4662 lines
155 KiB
JavaScript
4662 lines
155 KiB
JavaScript
/*! offline-editor-js - v2.3.1 - 2014-10-14
|
|
* Copyright (c) 2014 Environmental Systems Research Institute, Inc.
|
|
* Apache License*/
|
|
/**
|
|
* Library for reading an ArcGIS Tile Package (.tpk) file and displaying the tiles
|
|
* as a map that can be used both online and offline.
|
|
*
|
|
* Note: you may have to rename your .tpk file to use .zip in order for it to be recognized.
|
|
*
|
|
* Author: Andy Gup
|
|
* Credits: Mansour Raad for his ArcGIS API for Flex TPKLayer,
|
|
* and Jon leighton for his super fast ArrayBuffer to Base64 convert.
|
|
*/
|
|
define([
|
|
"dojo/_base/declare","esri/geometry/Extent","dojo/query","esri/SpatialReference",
|
|
"esri/layers/TileInfo","esri/layers/TiledMapServiceLayer",
|
|
"dojo/Deferred","dojo/promise/all","dojo/Evented"],
|
|
function(declare,Extent,query,SpatialReference,TileInfo,TiledMapServiceLayer,
|
|
Deferred,all,Evented){
|
|
return declare("O.esri.TPK.TPKLayer",[TiledMapServiceLayer,Evented],{
|
|
|
|
//
|
|
// Public Properties
|
|
//
|
|
map: null,
|
|
store: null, // Reference to the local database store and hooks to it's functionality
|
|
MAX_DB_SIZE: 75, // Recommended maximum size in MBs
|
|
TILE_PATH:"", // The absolute path to the root of bundle/bundleX files e.g. V101/YOSEMITE_MAP/
|
|
RECENTER_DELAY: 350, // Millisecond delay before attempting to recent map after an orientation change
|
|
PARSING_ERROR: 'parsingError', // An error was encountered while parsing a TPK file.
|
|
DB_INIT_ERROR: "dbInitError", // An error occurred while initializing the database.
|
|
DB_FULL_ERROR: "dbFullError", // No space left in the database.
|
|
NO_SUPPORT_ERROR: "libNotSupportedError", // Error indicating this library is not supported in a particular browser.
|
|
PROGRESS_START: "start",
|
|
PROGRESS_END: "end",
|
|
WINDOW_VALIDATED: "windowValidated", // All window functionality checks have passed
|
|
DB_VALIDATED: "dbValidated", // All database functionality checks have passed
|
|
|
|
//
|
|
// Events
|
|
//
|
|
DATABASE_ERROR_EVENT: "databaseErrorEvent", // An error thrown by the database.
|
|
VALIDATION_EVENT: "validationEvent", // Library validation checks.
|
|
PROGRESS_EVENT: "progress", // Event dispatched while parsing a bundle file.
|
|
|
|
//
|
|
// Private properties
|
|
//
|
|
_maxDBSize: 75, // User configurable maximum size in MBs.
|
|
_isDBWriteable: true, // Manually allow or stop writing to the database.
|
|
_isDBValid: false, // Does the browser support IndexedDB or IndexedDBShim
|
|
_autoCenter: null, // Auto center the map
|
|
_fileEntriesLength: 0, // Number of files in zip
|
|
_inMemTilesObject: null, // Stores unzipped files from tpk
|
|
_inMemTilesObjectLength: 0,
|
|
_zeroLengthFileCounter: 0, // For counting the number of zero length files in the tpk (e.g. directories)
|
|
|
|
constructor:function(){
|
|
this._self = this;
|
|
this._inMemTilesIndex = [];
|
|
this._inMemTilesObject = {};
|
|
this.store = new O.esri.Tiles.TilesStore();
|
|
this._validate();
|
|
},
|
|
|
|
extend: function(files){
|
|
this._fileEntriesLength = files.length;
|
|
this.emit(this.PROGRESS_EVENT,this.PROGRESS_START);
|
|
|
|
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);
|
|
this._parseConfXml(function(result){
|
|
this.tileInfo = new TileInfo(result);
|
|
this.spatialReference = new SpatialReference({wkid:this.tileInfo.spatialReference.wkid});
|
|
this.loaded = true;
|
|
this.onLoad(this);
|
|
this.emit(this.PROGRESS_EVENT,this.PROGRESS_END);
|
|
}.bind(this._self));
|
|
}.bind(this._self));
|
|
}.bind(this._self));
|
|
},
|
|
|
|
/**
|
|
* Overrides getTileUrl method
|
|
* @param level
|
|
* @param row
|
|
* @param col
|
|
* @returns {string}
|
|
*/
|
|
getTileUrl:function(level,row,col){
|
|
this.emit(this.PROGRESS_EVENT,this.PROGRESS_START);
|
|
var layersDir = this._self.TILE_PATH + "_alllayers";
|
|
var url = this._getCacheFilePath(layersDir,level,row,col);
|
|
|
|
if(this._inMemTilesObject != {}) {
|
|
/* temporary URL returned immediately, as we haven't retrieved the image from the indexeddb yet */
|
|
var tileid = "void:/" + level + "/" + row + "/" + col;
|
|
|
|
if(this.map == null) this.map = this.getMap();
|
|
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];
|
|
if (typeof img == "undefined")img = new Image(); //create a blank place holder for undefined images
|
|
var imgURL;
|
|
|
|
if (result) {
|
|
console.log("found tile offline", url);
|
|
var png = "data:image/png;base64,";
|
|
switch(this.tileInfo.format) {
|
|
case "JPEG":
|
|
imgURL = "data:image/jpg;base64," + result;
|
|
break;
|
|
case "PNG":
|
|
imgURL = 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";
|
|
}
|
|
else {
|
|
img.style.borderColor = "green";
|
|
console.log("tile is not in the offline store", url);
|
|
imgURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAABQdJREFUeNrs2yFv6mocwOH/ualYRUVJRrKKCRATCCZqJ/mOfKQJBGaiYkcguoSJigoQTc4VN222Mdhu7l0ysudJjqFAD13669u37a/lcvkngB8piYhYLBa2BPxAf9kEIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgAIACAAAACAAgACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAgAIAAAAIACAAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIACAAgAAAAgAIAPxsiU3wfbRtG1mWnVzedV3kef7q9a7rYrvdxm63i4iILMtiNBpFkiQfftdnZFkWbdtGRAzr7j+fZdnR9Xy0jiRJTv5eBOBHqaoqsiyLm5ubo8ubponFYjG8Vtd1VFV1sKMlSRI3NzdRFMXJ7/qMsixjtVpFRAzr7j9fluVBkD67jjzPoyxLf3gBoLfZbGI8Hh/dqV6q6zoeHh4iSZKYTCYxGo0iImK73Q7Luq6L6+vrg88WRfFqHfv9Puq6jjRN4+rq6tV7Ly4u/tNvKori3e9I09QfXAB4a71ex93d3ckhfNd1UVXVcIR+OZTO8zyKooj7+/uoqiouLy8Pdra3I4OmaaKu67i4uIjpdPq//p63seH7MAn4DXVdF+v1+sOjf390f+88Osuy4ci/2WxsVATgXEwmk2ia5uSOu91uIyJiPB4ffU+/rJ/AA6cAZ2A6ncbz83NUVRV5nr97hO8n104Nrftln53s+ypVVR2czpj8MwLghPl8HkmSDBN556xt22ia5tU/jAA4IU3TmE6nUVVVVFUVs9nsbH/LqUuFGAFwxPX1deR5HnVdD+f8LwPx0fl9f2OQy20IwJm6vb0dTgX2+/3wej8vcCoA/VDb3XYIwLmeoyVJzGaz6LpuOKJHRFxeXkbEP5cDj+mX9e8FAThD4/H44HJfURSRpmk0TROPj48Hn3l4eIimaSJN06O3A4NJwDMxm82ibdtXo4D5fB6r1Sp+//4dz8/Pw5H+6ekpdrtdJEkS8/n8S/9f713ie3vaceo9x557QAB451Sgfyin34HKshweunk5HzAej2MymXz5+f9nbjJyI9L39Wu5XP55+XQZ39uxR4Z3u90wSXjqEV0wAjhjx47oaZq63Me/ZhIQBAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAAbAJQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAQAAAAQAEABAAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEABAAAABAAQAEAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEABAAQAEAAAAEAvqe/BwCeKjUweoA8pQAAAABJRU5ErkJggg==";
|
|
}
|
|
// when we return a nonexistent url to the image, the TiledMapServiceLayer::_tileErrorHandler() method
|
|
// sets img visibility to 'hidden', so we need to show the image back once we have put the data:image
|
|
img.style.visibility = "visible";
|
|
img.src = imgURL;
|
|
console.log("URL length " + imgURL.length + ", image: " + imgURL);
|
|
this.emit(this.PROGRESS_EVENT,this.PROGRESS_END);
|
|
return "";
|
|
/* this result goes nowhere, seriously */
|
|
}.bind(this._self))
|
|
|
|
return tileid;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Optional. Set the maximum database size. Recommended maximum for mobile devices is 100MBs.
|
|
* Making the database too large can result in browser crashes and slow performance.
|
|
* TPKs can contain a lot of data!
|
|
* @param size
|
|
*/
|
|
setMaxDBSize: function(size){
|
|
//Make sure the entry is an integer.
|
|
var testRegex = /^\d+$/;
|
|
if(testRegex.test(size) && size <= this.MAX_DB_SIZE){
|
|
this._maxDBSize = size;
|
|
}
|
|
else{
|
|
console.log("setMaxDBSize Error: invalid entry. Integers only and less than " + this.MAX_DB_SIZE + "MBs");
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns the size of the tiles database.
|
|
* @param callback {size , error}. Note, size is in bytes.
|
|
*/
|
|
getDBSize: function(callback){
|
|
this.store.usedSpace(function(size,err){
|
|
callback(size,err);
|
|
}.bind(this))
|
|
},
|
|
|
|
/**
|
|
* Sets whether or not tiles can be written to the database. This function
|
|
* can help you manage the size of the tiles database.
|
|
* Use this in conjunction with getDBSize() on a map pan or zoom event listener.
|
|
* @param value
|
|
*/
|
|
setDBWriteable: function(/* Boolean */ value){
|
|
this._isDBWriteable = value;
|
|
},
|
|
|
|
/**
|
|
* Validates whether or not the browser supports this library
|
|
* @returns {boolean}
|
|
*/
|
|
isDBValid: function(){
|
|
this._validate();
|
|
return this._isDBValid;
|
|
},
|
|
|
|
/**
|
|
* Reads a tile into tile database. Works with offlineTilesEnabler.js and OfflineTilesEnablerLayer.js
|
|
* saveToFile() functionality.
|
|
* IMPORTANT! The tile must confirm to an object using the pattern shown in _storeTile().
|
|
* @param file
|
|
* @param callback callback( boolean, error)
|
|
*/
|
|
loadFromURL: function (tile, callback) // callback(success,msg)
|
|
{
|
|
if (this.isDBValid()) {
|
|
this.store.store(tile, function (success,err) {
|
|
//Check the result
|
|
if (success) {
|
|
console.log("loadFromURL() success.");
|
|
callback(true, "");
|
|
}
|
|
else {
|
|
console.log("loadFromURL() Failed.");
|
|
callback(false, err);
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
callback(false, "not supported");
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Runs specific validation tasks. Reserved for future use.
|
|
* Currently only throws console errors. Does not stop execution of the library!
|
|
* @private
|
|
*/
|
|
_validate: function(){
|
|
//Verify if basic functionality is supported by the browser
|
|
if(!window.File && !window.FileReader && !window.Blob && !window.btoa && !window.DataView){
|
|
console.log("TPKLayer library is not supported by this browser");
|
|
this.emit(this.VALIDATION_EVENT,{msg:this.NO_SUPPORT_ERROR, err : null});
|
|
}
|
|
else{
|
|
this.emit(this.VALIDATION_EVENT,{msg:this.WINDOW_VALIDATED, err : null});
|
|
}
|
|
|
|
//Verify if IndexedDB is supported and initializes properly
|
|
if( /*false &&*/ this.store.isSupported() )
|
|
{
|
|
this.store.init(function(result){
|
|
if(result == false){
|
|
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.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})
|
|
console.log("DB size: " + mb + " MBs, Tile count: " + size.tileCount + ", Error: " + err);
|
|
this._isDBValid = true;
|
|
}.bind(this))
|
|
}
|
|
}.bind(this));
|
|
}
|
|
else
|
|
{
|
|
console.log("IndexedDB is not supported on your browser.");
|
|
this.emit(this.VALIDATION_EVENT,{msg:this.NO_SUPPORT_ERROR, err : null});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Function for pulling out individual files from the .tpk/zip and storing
|
|
* them in memory.
|
|
* @param files
|
|
* @param callback
|
|
* @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);
|
|
}
|
|
|
|
if(files[i].compressedSize == 0) this._zeroLengthFileCounter++;
|
|
|
|
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,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{
|
|
deferred.resolve(i);
|
|
}
|
|
promises.push(deferred);
|
|
}
|
|
|
|
all(promises).then( function(results)
|
|
{
|
|
callback && callback(results);
|
|
});
|
|
|
|
return promises;
|
|
},
|
|
|
|
/**
|
|
* Calculate the size of an Object based on whether or not the item is enumerable.
|
|
* Native Objects don't have a built in size property.
|
|
* More info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
|
|
* @param obj
|
|
* @returns {number}
|
|
* @constructor
|
|
*/
|
|
ObjectSize: function(obj) {
|
|
var size = 0, key;
|
|
for (key in obj) {
|
|
if (obj.hasOwnProperty(key)) size++;
|
|
}
|
|
return size;
|
|
},
|
|
|
|
/**
|
|
* Retrieve XML config files
|
|
* @param files
|
|
* @param token
|
|
* @param callback
|
|
* @private
|
|
*/
|
|
_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 > 0){
|
|
callback(deferred,data.token);
|
|
}
|
|
}.bind(this));
|
|
},
|
|
|
|
/**
|
|
* Retrieve binary tile files as ArrayBuffers
|
|
* @param files
|
|
* @param token
|
|
* @param callback
|
|
* @private
|
|
*/
|
|
_unzipTileFiles: function(files,token,deferred,callback){
|
|
var that = this;
|
|
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("_unzipTileFiles Error: " + event.target.error.message);
|
|
that.emit(that.PARSING_ERROR, {msg: "Error parsing file: ", err: event.target.error});
|
|
}
|
|
reader.addEventListener("loadend", function (evt) {
|
|
if(this.token != undefined){
|
|
that._inMemTilesIndex.push("blank");
|
|
var name = files[this.token].filename.toLocaleUpperCase();
|
|
that._inMemTilesObject[name]= this.result;
|
|
var size = that.ObjectSize(that._inMemTilesObject);
|
|
if(size > 0){
|
|
callback(deferred,data.token);
|
|
}
|
|
}
|
|
});
|
|
reader.readAsArrayBuffer(data); //open bundleX
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Parse conf.cdi
|
|
* @param callback
|
|
* @private
|
|
*/
|
|
_parseConfCdi: function(callback){
|
|
var m_conf_i = this._inMemTilesObject[this.TILE_PATH + "CONF.CDI"];
|
|
|
|
var x2js = new O.esri.TPK.X2JS();
|
|
|
|
var jsonObj = x2js.xml_str2json( m_conf_i );
|
|
var envelopeInfo = jsonObj.EnvelopeN;
|
|
var xmin = parseFloat(envelopeInfo.XMin);
|
|
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, new SpatialReference({wkid:sr})
|
|
);
|
|
|
|
callback(initExtent);
|
|
},
|
|
|
|
/**
|
|
* Parse conf.xml
|
|
* @param callback
|
|
* @private
|
|
*/
|
|
_parseConfXml:function(callback) {
|
|
var m_conf = this._inMemTilesObject[this.TILE_PATH + "CONF.XML"];
|
|
|
|
var x2js = new O.esri.TPK.X2JS();
|
|
|
|
var jsonObj = x2js.xml_str2json(m_conf);
|
|
var cacheInfo = jsonObj.CacheInfo;
|
|
var tileInfo = {};
|
|
tileInfo.rows = parseInt(cacheInfo.TileCacheInfo.TileRows);
|
|
tileInfo.cols = parseInt(cacheInfo.TileCacheInfo.TileCols);
|
|
tileInfo.dpi = parseInt(cacheInfo.TileCacheInfo.DPI);
|
|
tileInfo.format = cacheInfo.TileImageInfo.CacheTileFormat;
|
|
tileInfo.compressionQuality = parseInt(cacheInfo.TileImageInfo.CompressionQuality);
|
|
tileInfo.origin = {
|
|
x: parseInt(cacheInfo.TileCacheInfo.TileOrigin.X),
|
|
y: parseInt(cacheInfo.TileCacheInfo.TileOrigin.Y)
|
|
}
|
|
tileInfo.spatialReference = {
|
|
"wkid": parseInt(cacheInfo.TileCacheInfo.SpatialReference.WKID)
|
|
}
|
|
|
|
var lods = cacheInfo.TileCacheInfo.LODInfos.LODInfo;
|
|
var finalLods = [];
|
|
for (var i = 0; i < lods.length; i++) {
|
|
finalLods.push({
|
|
"level": parseFloat(lods[i].LevelID),
|
|
"resolution": parseFloat(lods[i].Resolution),
|
|
"scale": parseFloat(lods[i].Scale)})
|
|
}
|
|
|
|
tileInfo.lods = finalLods;
|
|
callback(tileInfo);
|
|
},
|
|
|
|
/**
|
|
* Parses the in-memory tile cache and returns a base64 tile image
|
|
* @param layersDir
|
|
* @param level
|
|
* @param row
|
|
* @param col
|
|
* @param tileCount - number of tiles in the Extent
|
|
* @param tiledId - id of the associated tile being passed
|
|
* @param callback
|
|
* @private
|
|
*/
|
|
_getInMemTiles: function(url,layersDir,level,row,col,tileId,callback){
|
|
|
|
var that = this._self;
|
|
var db = this.store;
|
|
|
|
//First check in the database if the tile exists.
|
|
//If not then we store the tile in the database later.
|
|
this.store.retrieve(url, function(success, offlineTile){
|
|
if( success )
|
|
{
|
|
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;
|
|
|
|
var path = this._getCacheFilePath(layersDir, level, snappedRow, snappedCol).toLocaleUpperCase();
|
|
|
|
var offset;
|
|
var bundleIndex = path + ".BUNDLE";
|
|
var bufferI = this._inMemTilesObject[bundleIndex];
|
|
var bufferX = this._inMemTilesObject[path + ".BUNDLX"];
|
|
|
|
if(bufferI != undefined || bufferX != undefined) {
|
|
offset = this._getOffset(level, row, col, snappedRow, snappedCol);
|
|
var pointer = that._getPointer(bufferX, offset);
|
|
|
|
that._buffer2Base64(bufferI,pointer,function(result){
|
|
if (that._isDBWriteable)that._storeTile(url, result, db,function(success,err){
|
|
if(err){
|
|
console.log("TPKLayer - Error writing to database." + err.message);
|
|
that.emit(that.DATABASE_ERROR_EVENT,{msg:"Error writing to database. ", err : err});
|
|
}
|
|
});
|
|
callback(result,tileId, url);
|
|
}.bind(that));
|
|
}
|
|
else{
|
|
console.log("_getInMemTiles Error: Invalid values");
|
|
callback(null,tileId,url);
|
|
}
|
|
}
|
|
}.bind(that))
|
|
},
|
|
|
|
/**
|
|
* Stores a tile in the local database.
|
|
* @param url
|
|
* @param base64Str
|
|
* @param db
|
|
* @param callback
|
|
* @private
|
|
*/
|
|
_storeTile: function(url,base64Str,db,callback){
|
|
var tile = {
|
|
url: url,
|
|
img: base64Str
|
|
};
|
|
|
|
db.store(tile,function(success,err){
|
|
callback(success,err);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Returns a pointer for reading a BUNDLE binary file as based on the given offset.
|
|
* @param buffer
|
|
* @param offset
|
|
* @returns {Uint8}
|
|
* @private
|
|
*/
|
|
_getPointer: function(/* ArrayBuffer */ buffer,offset){
|
|
var snip = buffer.slice(offset);
|
|
var dv = new DataView(snip,0,5);
|
|
|
|
var nume1 = dv.getUint8(0,true);
|
|
var nume2 = dv.getUint8(1,true);
|
|
var nume3 = dv.getUint8(2,true);
|
|
var nume4 = dv.getUint8(3,true);
|
|
var nume5 = dv.getUint8(4,true);
|
|
|
|
var value = nume5;
|
|
value = value * 256 + nume4;
|
|
value = value * 256 + nume3;
|
|
value = value * 256 + nume2;
|
|
value = value * 256 + nume1;
|
|
|
|
return value;
|
|
},
|
|
|
|
/**
|
|
* Convert an ArrayBuffer to base64. My testing shows this to be
|
|
* much faster than combining Blobs and btoa().
|
|
* ALL CREDITS: https://gist.github.com/jonleighton/958841
|
|
* NO licensing listed at the gist repo.
|
|
* @param arrayBuffer
|
|
* @returns {string}
|
|
* @private
|
|
*/
|
|
_base64ArrayBuffer: function(arrayBuffer) {
|
|
var base64 = ''
|
|
var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
|
|
var bytes = new Uint8Array(arrayBuffer)
|
|
var byteLength = bytes.byteLength
|
|
var byteRemainder = byteLength % 3
|
|
var mainLength = byteLength - byteRemainder
|
|
|
|
var a, b, c, d
|
|
var chunk
|
|
|
|
// Main loop deals with bytes in chunks of 3
|
|
for (var i = 0; i < mainLength; i = i + 3) {
|
|
// Combine the three bytes into a single integer
|
|
chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
|
|
|
|
// Use bitmasks to extract 6-bit segments from the triplet
|
|
a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
|
|
b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
|
|
c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
|
|
d = chunk & 63 // 63 = 2^6 - 1
|
|
|
|
// Convert the raw binary segments to the appropriate ASCII encoding
|
|
base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
|
|
}
|
|
|
|
// Deal with the remaining bytes and padding
|
|
if (byteRemainder == 1) {
|
|
chunk = bytes[mainLength]
|
|
|
|
a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
|
|
|
|
// Set the 4 least significant bits to zero
|
|
b = (chunk & 3) << 4 // 3 = 2^2 - 1
|
|
|
|
base64 += encodings[a] + encodings[b] + '=='
|
|
} else if (byteRemainder == 2) {
|
|
chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
|
|
|
|
a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
|
|
b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
|
|
|
|
// Set the 2 least significant bits to zero
|
|
c = (chunk & 15) << 2 // 15 = 2^4 - 1
|
|
|
|
base64 += encodings[a] + encodings[b] + encodings[c] + '='
|
|
}
|
|
|
|
return base64
|
|
},
|
|
|
|
/**
|
|
* Given a ArrayBuffer and a position it will return a Base64 tile image
|
|
* @param arrayBuffer
|
|
* @param position
|
|
* @returns {string}
|
|
* @private
|
|
*/
|
|
_buffer2Base64: function(/* ArrayBuffer */arrayBuffer,/* int */ position,callback){
|
|
var view = new DataView(arrayBuffer,position);
|
|
var chunk = view.getInt32(0,true);
|
|
var buffer = view.buffer.slice(position + 4,position + 4 + chunk);
|
|
var string = this._base64ArrayBuffer(buffer);
|
|
callback(string);
|
|
},
|
|
|
|
/**
|
|
* Converts an integer to hex
|
|
* @param value
|
|
* @returns {string}
|
|
* @private
|
|
*/
|
|
_int2HexString: function(/* int */ value){
|
|
var text = value.toString(16).toUpperCase();
|
|
if (text.length === 1)
|
|
{
|
|
return "000" + text;
|
|
}
|
|
if (text.length === 2)
|
|
{
|
|
return "00" + text;
|
|
}
|
|
if (text.length === 3)
|
|
{
|
|
return "0" + text;
|
|
}
|
|
return text.substr(0, text.length);
|
|
},
|
|
|
|
/**
|
|
* Determines where to start reading a BUNDLEX binary file
|
|
* @param level
|
|
* @param row
|
|
* @param col
|
|
* @param startRow
|
|
* @param startCol
|
|
* @returns {number}
|
|
* @private
|
|
*/
|
|
_getOffset: function(/* int */level, /* number */row,/* number */col, /* number */startRow, /* number */ startCol){
|
|
var recordNumber = 128 * (col - startCol) + (row - startRow);
|
|
return 16 + recordNumber * 5;
|
|
},
|
|
|
|
/**
|
|
* Returns a hexadecimal representation of a cache file path
|
|
* @param layerDir
|
|
* @param level
|
|
* @param row
|
|
* @param col
|
|
* @returns {string}
|
|
* @private
|
|
*/
|
|
_getCacheFilePath: function(/* String */ layerDir, /* int */level, /* int */row, /* int */ col){
|
|
var arr = [];
|
|
|
|
arr.push(layerDir);
|
|
arr.push("/");
|
|
arr.push("L");
|
|
arr.push(level < 10 ? "0" + level : level);
|
|
arr.push("/");
|
|
arr.push("R");
|
|
arr.push(this._int2HexString(row));
|
|
arr.push("C");
|
|
arr.push(this._int2HexString(col));
|
|
|
|
return arr.join("");
|
|
},
|
|
|
|
/**
|
|
* Returns database size in MBs.
|
|
* @returns {string}
|
|
* @private
|
|
*/
|
|
_bytes2MBs: function(bytes){
|
|
return (bytes >>> 20 ) + '.' + ( bytes & (2*0x3FF ) )
|
|
}
|
|
})
|
|
}
|
|
)
|
|
/**
|
|
* 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";
|
|
|
|
|
|
/*global indexedDB */
|
|
/**
|
|
* Library for handling the storing of map tiles in IndexedDB.
|
|
*
|
|
* Author: Andy Gup (@agup)
|
|
* Contributor: Javier Abadia (@javierabadia)
|
|
*/
|
|
|
|
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)
|
|
{
|
|
try
|
|
{
|
|
var transaction = this._db.transaction(["tilepath"],"readwrite");
|
|
|
|
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)
|
|
{
|
|
console.log("TilesStore: " + err.stack);
|
|
callback(false, err.stack);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Retrieve a record.
|
|
* @param url
|
|
* @param callback
|
|
*/
|
|
this.retrieve = function(/* String */ url,callback)
|
|
{
|
|
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);
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 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);
|
|
};
|
|
};
|
|
|
|
|
|
//https://github.com/gildas-lormeau/zip.js/blob/master/WebContent/zip.js
|
|
/*
|
|
Copyright (c) 2013 Gildas Lormeau. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
3. The names of the authors may not be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
(function(obj) {
|
|
|
|
var ERR_BAD_FORMAT = "File format is not recognized.";
|
|
var ERR_ENCRYPTED = "File contains encrypted entry.";
|
|
var ERR_ZIP64 = "File is using Zip64 (4gb+ file size).";
|
|
var ERR_READ = "Error while reading zip file.";
|
|
var ERR_WRITE = "Error while writing zip file.";
|
|
var ERR_WRITE_DATA = "Error while writing file data.";
|
|
var ERR_READ_DATA = "Error while reading file data.";
|
|
var ERR_DUPLICATED_NAME = "File already exists.";
|
|
var CHUNK_SIZE = 512 * 1024;
|
|
|
|
var INFLATE_JS = ""; //left blank intentionally! Modified by @agup
|
|
var DEFLATE_JS = "deflate.js";
|
|
|
|
var TEXT_PLAIN = "text/plain";
|
|
|
|
var MESSAGE_EVENT = "message";
|
|
|
|
var appendABViewSupported;
|
|
try {
|
|
appendABViewSupported = new Blob([ new DataView(new ArrayBuffer(0)) ]).size === 0;
|
|
} catch (e) {
|
|
}
|
|
|
|
function Crc32() {
|
|
var crc = -1, that = this;
|
|
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() {
|
|
return ~crc;
|
|
};
|
|
}
|
|
Crc32.prototype.table = (function() {
|
|
var i, j, t, table = [];
|
|
for (i = 0; i < 256; i++) {
|
|
t = i;
|
|
for (j = 0; j < 8; j++)
|
|
if (t & 1)
|
|
t = (t >>> 1) ^ 0xEDB88320;
|
|
else
|
|
t = t >>> 1;
|
|
table[i] = t;
|
|
}
|
|
return table;
|
|
})();
|
|
|
|
function blobSlice(blob, index, length) {
|
|
if (blob.slice)
|
|
return blob.slice(index, index + length);
|
|
else if (blob.webkitSlice)
|
|
return blob.webkitSlice(index, index + length);
|
|
else if (blob.mozSlice)
|
|
return blob.mozSlice(index, index + length);
|
|
else if (blob.msSlice)
|
|
return blob.msSlice(index, index + length);
|
|
}
|
|
|
|
function getDataHelper(byteLength, bytes) {
|
|
var dataBuffer, dataArray;
|
|
dataBuffer = new ArrayBuffer(byteLength);
|
|
dataArray = new Uint8Array(dataBuffer);
|
|
if (bytes)
|
|
dataArray.set(bytes, 0);
|
|
return {
|
|
buffer : dataBuffer,
|
|
array : dataArray,
|
|
view : new DataView(dataBuffer)
|
|
};
|
|
}
|
|
|
|
// Readers
|
|
function Reader() {
|
|
}
|
|
|
|
function TextReader(text) {
|
|
var that = this, blobReader;
|
|
|
|
function init(callback, onerror) {
|
|
var blob = new Blob([ text ], {
|
|
type : TEXT_PLAIN
|
|
});
|
|
blobReader = new BlobReader(blob);
|
|
blobReader.init(function() {
|
|
that.size = blobReader.size;
|
|
callback();
|
|
}, onerror);
|
|
}
|
|
|
|
function readUint8Array(index, length, callback, onerror) {
|
|
blobReader.readUint8Array(index, length, callback, onerror);
|
|
}
|
|
|
|
that.size = 0;
|
|
that.init = init;
|
|
that.readUint8Array = readUint8Array;
|
|
}
|
|
TextReader.prototype = new Reader();
|
|
TextReader.prototype.constructor = TextReader;
|
|
|
|
function Data64URIReader(dataURI) {
|
|
var that = this, dataStart;
|
|
|
|
function init(callback) {
|
|
var dataEnd = dataURI.length;
|
|
while (dataURI.charAt(dataEnd - 1) == "=")
|
|
dataEnd--;
|
|
dataStart = dataURI.indexOf(",") + 1;
|
|
that.size = Math.floor((dataEnd - dataStart) * 0.75);
|
|
callback();
|
|
}
|
|
|
|
function readUint8Array(index, length, callback) {
|
|
var i, data = getDataHelper(length);
|
|
var start = Math.floor(index / 3) * 4;
|
|
var end = Math.ceil((index + length) / 3) * 4;
|
|
var bytes = obj.atob(dataURI.substring(start + dataStart, end + dataStart));
|
|
var delta = index - Math.floor(start / 4) * 3;
|
|
for (i = delta; i < delta + length; i++)
|
|
data.array[i - delta] = bytes.charCodeAt(i);
|
|
callback(data.array);
|
|
}
|
|
|
|
that.size = 0;
|
|
that.init = init;
|
|
that.readUint8Array = readUint8Array;
|
|
}
|
|
Data64URIReader.prototype = new Reader();
|
|
Data64URIReader.prototype.constructor = Data64URIReader;
|
|
|
|
function BlobReader(blob) {
|
|
var that = this;
|
|
|
|
function init(callback) {
|
|
this.size = blob.size;
|
|
callback();
|
|
}
|
|
|
|
function readUint8Array(index, length, callback, onerror) {
|
|
var reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
callback(new Uint8Array(e.target.result));
|
|
};
|
|
reader.onerror = onerror;
|
|
reader.readAsArrayBuffer(blobSlice(blob, index, length));
|
|
}
|
|
|
|
that.size = 0;
|
|
that.init = init;
|
|
that.readUint8Array = readUint8Array;
|
|
}
|
|
BlobReader.prototype = new Reader();
|
|
BlobReader.prototype.constructor = BlobReader;
|
|
|
|
// Writers
|
|
|
|
function Writer() {
|
|
}
|
|
Writer.prototype.getData = function(callback) {
|
|
callback(this.data);
|
|
};
|
|
|
|
//Added by Andy G. Tracking token
|
|
function TextWriter(token,encoding) {
|
|
var that = this, blob;
|
|
|
|
function init(callback) {
|
|
blob = new Blob([], {
|
|
type: TEXT_PLAIN
|
|
});
|
|
callback();
|
|
}
|
|
|
|
function writeUint8Array(array, callback) {
|
|
blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], {
|
|
type: TEXT_PLAIN
|
|
});
|
|
callback();
|
|
}
|
|
|
|
function getData(callback, onerror) {
|
|
var reader = new FileReader();
|
|
reader.onload = function (e) {
|
|
var obj = {string: e.target.result,token:token};
|
|
callback(obj);
|
|
};
|
|
reader.onerror = onerror;
|
|
reader.readAsText(blob, encoding);
|
|
}
|
|
|
|
that.init = init;
|
|
that.writeUint8Array = writeUint8Array;
|
|
that.getData = getData;
|
|
}
|
|
TextWriter.prototype = new Writer();
|
|
TextWriter.prototype.constructor = TextWriter;
|
|
|
|
function Data64URIWriter(contentType) {
|
|
var that = this, data = "", pending = "";
|
|
|
|
function init(callback) {
|
|
data += "data:" + (contentType || "") + ";base64,";
|
|
callback();
|
|
}
|
|
|
|
function writeUint8Array(array, callback) {
|
|
var i, delta = pending.length, dataString = pending;
|
|
pending = "";
|
|
for (i = 0; i < (Math.floor((delta + array.length) / 3) * 3) - delta; i++)
|
|
dataString += String.fromCharCode(array[i]);
|
|
for (; i < array.length; i++)
|
|
pending += String.fromCharCode(array[i]);
|
|
if (dataString.length > 2)
|
|
data += obj.btoa(dataString);
|
|
else
|
|
pending = dataString;
|
|
callback();
|
|
}
|
|
|
|
function getData(callback) {
|
|
callback(data + obj.btoa(pending));
|
|
}
|
|
|
|
that.init = init;
|
|
that.writeUint8Array = writeUint8Array;
|
|
that.getData = getData;
|
|
}
|
|
Data64URIWriter.prototype = new Writer();
|
|
Data64URIWriter.prototype.constructor = Data64URIWriter;
|
|
|
|
//Added by Andy G. Tracking token
|
|
function BlobWriter(token,contentType) {
|
|
var blob, that = this ;
|
|
|
|
function init(callback) {
|
|
blob = new Blob([], {
|
|
type: contentType
|
|
});
|
|
callback();
|
|
}
|
|
|
|
function writeUint8Array(array, callback) {
|
|
blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], {
|
|
type: contentType
|
|
});
|
|
blob.token = token;
|
|
callback();
|
|
}
|
|
|
|
function getData(callback) {
|
|
callback(blob);
|
|
}
|
|
|
|
that.init = init;
|
|
that.writeUint8Array = writeUint8Array;
|
|
that.getData = getData;
|
|
}
|
|
BlobWriter.prototype = new Writer();
|
|
BlobWriter.prototype.constructor = BlobWriter;
|
|
|
|
// inflate/deflate core functions
|
|
|
|
function launchWorkerProcess(worker, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) {
|
|
var chunkIndex = 0, index, outputSize;
|
|
|
|
function onflush() {
|
|
worker.removeEventListener(MESSAGE_EVENT, onmessage, false);
|
|
onend(outputSize);
|
|
}
|
|
|
|
function onmessage(event) {
|
|
var message = event.data, data = message.data;
|
|
|
|
if (message.onappend) {
|
|
outputSize += data.length;
|
|
writer.writeUint8Array(data, function() {
|
|
onappend(false, data);
|
|
step();
|
|
}, onwriteerror);
|
|
}
|
|
if (message.onflush)
|
|
if (data) {
|
|
outputSize += data.length;
|
|
writer.writeUint8Array(data, function() {
|
|
onappend(false, data);
|
|
onflush();
|
|
}, onwriteerror);
|
|
} else
|
|
onflush();
|
|
if (message.progress && onprogress)
|
|
onprogress(index + message.current, size);
|
|
}
|
|
|
|
function step() {
|
|
index = chunkIndex * CHUNK_SIZE;
|
|
if (index < size)
|
|
reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(array) {
|
|
worker.postMessage({
|
|
append : true,
|
|
data : array
|
|
});
|
|
chunkIndex++;
|
|
if (onprogress)
|
|
onprogress(index, size);
|
|
onappend(true, array);
|
|
}, onreaderror);
|
|
else
|
|
worker.postMessage({
|
|
flush : true
|
|
});
|
|
}
|
|
|
|
outputSize = 0;
|
|
worker.addEventListener(MESSAGE_EVENT, onmessage, false);
|
|
step();
|
|
}
|
|
|
|
function launchProcess(process, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) {
|
|
var chunkIndex = 0, index, outputSize = 0;
|
|
|
|
function step() {
|
|
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() {
|
|
if (onprogress)
|
|
onprogress(offset + index, size);
|
|
});
|
|
outputSize += outputData.length;
|
|
onappend(true, inputData);
|
|
writer.writeUint8Array(outputData, function() {
|
|
onappend(false, outputData);
|
|
chunkIndex++;
|
|
setTimeout(step, 1);
|
|
}, onwriteerror);
|
|
if (onprogress)
|
|
onprogress(index, size);
|
|
}, onreaderror);
|
|
else {
|
|
outputData = process.flush();
|
|
if (outputData) {
|
|
outputSize += outputData.length;
|
|
writer.writeUint8Array(outputData, function() {
|
|
onappend(false, outputData);
|
|
onend(outputSize);
|
|
}, onwriteerror);
|
|
} else
|
|
onend(outputSize);
|
|
}
|
|
}
|
|
|
|
step();
|
|
}
|
|
|
|
function inflate(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) {
|
|
var worker, crc32 = new Crc32();
|
|
|
|
function oninflateappend(sending, array) {
|
|
if (computeCrc32 && !sending)
|
|
crc32.append(array);
|
|
}
|
|
|
|
function oninflateend(outputSize) {
|
|
onend(outputSize, crc32.get());
|
|
}
|
|
|
|
if (obj.zip.useWebWorkers) {
|
|
|
|
worker = new Worker(obj.zip.workerScriptsPath + INFLATE_JS);
|
|
launchWorkerProcess(worker, reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror);
|
|
} else
|
|
launchProcess(new obj.zip.Inflater(), reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror);
|
|
return worker;
|
|
}
|
|
|
|
function deflate(reader, writer, level, onend, onprogress, onreaderror, onwriteerror) {
|
|
var worker, crc32 = new Crc32();
|
|
|
|
function ondeflateappend(sending, array) {
|
|
if (sending)
|
|
crc32.append(array);
|
|
}
|
|
|
|
function ondeflateend(outputSize) {
|
|
onend(outputSize, crc32.get());
|
|
}
|
|
|
|
function onmessage() {
|
|
worker.removeEventListener(MESSAGE_EVENT, onmessage, false);
|
|
launchWorkerProcess(worker, reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror);
|
|
}
|
|
|
|
if (obj.zip.useWebWorkers) {
|
|
worker = new Worker(obj.zip.workerScriptsPath + DEFLATE_JS);
|
|
worker.addEventListener(MESSAGE_EVENT, onmessage, false);
|
|
worker.postMessage({
|
|
init : true,
|
|
level : level
|
|
});
|
|
} else
|
|
launchProcess(new obj.zip.Deflater(), reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror);
|
|
return worker;
|
|
}
|
|
|
|
function copy(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) {
|
|
var chunkIndex = 0, crc32 = new Crc32();
|
|
|
|
function step() {
|
|
var index = chunkIndex * CHUNK_SIZE;
|
|
if (index < size)
|
|
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() {
|
|
chunkIndex++;
|
|
step();
|
|
}, onwriteerror);
|
|
}, onreaderror);
|
|
else
|
|
onend(size, crc32.get());
|
|
}
|
|
|
|
step();
|
|
}
|
|
|
|
// ZipReader
|
|
|
|
function decodeASCII(str) {
|
|
var i, out = "", charCode, extendedASCII = [ '\u00C7', '\u00FC', '\u00E9', '\u00E2', '\u00E4', '\u00E0', '\u00E5', '\u00E7', '\u00EA', '\u00EB',
|
|
'\u00E8', '\u00EF', '\u00EE', '\u00EC', '\u00C4', '\u00C5', '\u00C9', '\u00E6', '\u00C6', '\u00F4', '\u00F6', '\u00F2', '\u00FB', '\u00F9',
|
|
'\u00FF', '\u00D6', '\u00DC', '\u00F8', '\u00A3', '\u00D8', '\u00D7', '\u0192', '\u00E1', '\u00ED', '\u00F3', '\u00FA', '\u00F1', '\u00D1',
|
|
'\u00AA', '\u00BA', '\u00BF', '\u00AE', '\u00AC', '\u00BD', '\u00BC', '\u00A1', '\u00AB', '\u00BB', '_', '_', '_', '\u00A6', '\u00A6',
|
|
'\u00C1', '\u00C2', '\u00C0', '\u00A9', '\u00A6', '\u00A6', '+', '+', '\u00A2', '\u00A5', '+', '+', '-', '-', '+', '-', '+', '\u00E3',
|
|
'\u00C3', '+', '+', '-', '-', '\u00A6', '-', '+', '\u00A4', '\u00F0', '\u00D0', '\u00CA', '\u00CB', '\u00C8', 'i', '\u00CD', '\u00CE',
|
|
'\u00CF', '+', '+', '_', '_', '\u00A6', '\u00CC', '_', '\u00D3', '\u00DF', '\u00D4', '\u00D2', '\u00F5', '\u00D5', '\u00B5', '\u00FE',
|
|
'\u00DE', '\u00DA', '\u00DB', '\u00D9', '\u00FD', '\u00DD', '\u00AF', '\u00B4', '\u00AD', '\u00B1', '_', '\u00BE', '\u00B6', '\u00A7',
|
|
'\u00F7', '\u00B8', '\u00B0', '\u00A8', '\u00B7', '\u00B9', '\u00B3', '\u00B2', '_', ' ' ];
|
|
for (i = 0; i < str.length; i++) {
|
|
charCode = str.charCodeAt(i) & 0xFF;
|
|
if (charCode > 127)
|
|
out += extendedASCII[charCode - 128];
|
|
else
|
|
out += String.fromCharCode(charCode);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function decodeUTF8(string) {
|
|
return decodeURIComponent(escape(string));
|
|
}
|
|
|
|
function getString(bytes) {
|
|
var i, str = "";
|
|
for (i = 0; i < bytes.length; i++)
|
|
str += String.fromCharCode(bytes[i]);
|
|
return str;
|
|
}
|
|
|
|
function getDate(timeRaw) {
|
|
var date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & 0x0000ffff;
|
|
try {
|
|
return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5,
|
|
(time & 0x001F) * 2, 0);
|
|
} catch (e) {
|
|
}
|
|
}
|
|
|
|
function readCommonHeader(entry, data, index, centralDirectory, onerror) {
|
|
entry.version = data.view.getUint16(index, true);
|
|
entry.bitFlag = data.view.getUint16(index + 2, true);
|
|
entry.compressionMethod = data.view.getUint16(index + 4, true);
|
|
entry.lastModDateRaw = data.view.getUint32(index + 6, true);
|
|
entry.lastModDate = getDate(entry.lastModDateRaw);
|
|
if ((entry.bitFlag & 0x01) === 0x01) {
|
|
onerror(ERR_ENCRYPTED);
|
|
return;
|
|
}
|
|
if (centralDirectory || (entry.bitFlag & 0x0008) != 0x0008) {
|
|
entry.crc32 = data.view.getUint32(index + 10, true);
|
|
entry.compressedSize = data.view.getUint32(index + 14, true);
|
|
entry.uncompressedSize = data.view.getUint32(index + 18, true);
|
|
}
|
|
if (entry.compressedSize === 0xFFFFFFFF || entry.uncompressedSize === 0xFFFFFFFF) {
|
|
onerror(ERR_ZIP64);
|
|
return;
|
|
}
|
|
entry.filenameLength = data.view.getUint16(index + 22, true);
|
|
entry.extraFieldLength = data.view.getUint16(index + 24, true);
|
|
}
|
|
|
|
function createZipReader(reader, onerror) {
|
|
function Entry() {
|
|
}
|
|
|
|
Entry.prototype.getData = function(writer, onend, onprogress, checkCrc32) {
|
|
var that = this, worker;
|
|
|
|
function terminate(callback, param) {
|
|
if (worker)
|
|
worker.terminate();
|
|
worker = null;
|
|
if (callback)
|
|
callback(param);
|
|
}
|
|
|
|
function testCrc32(crc32) {
|
|
var dataCrc32 = getDataHelper(4);
|
|
dataCrc32.view.setUint32(0, crc32);
|
|
return that.crc32 == dataCrc32.view.getUint32(0);
|
|
}
|
|
|
|
function getWriterData(uncompressedSize, crc32) {
|
|
if (checkCrc32 && !testCrc32(crc32))
|
|
onreaderror();
|
|
else
|
|
writer.getData(function(data) {
|
|
terminate(onend, data);
|
|
});
|
|
}
|
|
|
|
function onreaderror() {
|
|
terminate(onerror, ERR_READ_DATA);
|
|
}
|
|
|
|
function onwriteerror() {
|
|
terminate(onerror, ERR_WRITE_DATA);
|
|
}
|
|
|
|
reader.readUint8Array(that.offset, 30, function(bytes) {
|
|
var data = getDataHelper(bytes.length, bytes), dataOffset;
|
|
if (data.view.getUint32(0) != 0x504b0304) {
|
|
onerror(ERR_BAD_FORMAT);
|
|
return;
|
|
}
|
|
readCommonHeader(that, data, 4, false, onerror);
|
|
dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength;
|
|
writer.init(function() {
|
|
if (that.compressionMethod === 0)
|
|
copy(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror);
|
|
else
|
|
worker = inflate(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror);
|
|
}, onwriteerror);
|
|
}, onreaderror);
|
|
};
|
|
|
|
function seekEOCDR(offset, entriesCallback) {
|
|
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() {
|
|
onerror(ERR_READ);
|
|
});
|
|
}
|
|
|
|
return {
|
|
getEntries : function(callback) {
|
|
if (reader.size < 22) {
|
|
onerror(ERR_BAD_FORMAT);
|
|
return;
|
|
}
|
|
// look for End of central directory record
|
|
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) {
|
|
var i, index = 0, entries = [], entry, filename, comment, data = getDataHelper(bytes.length, bytes);
|
|
for (i = 0; i < fileslength; i++) {
|
|
entry = new Entry();
|
|
if (data.view.getUint32(index) != 0x504b0102) {
|
|
onerror(ERR_BAD_FORMAT);
|
|
return;
|
|
}
|
|
readCommonHeader(entry, data, index + 6, true, onerror);
|
|
entry.commentLength = data.view.getUint16(index + 32, true);
|
|
entry.directory = ((data.view.getUint8(index + 38) & 0x10) == 0x10);
|
|
entry.offset = data.view.getUint32(index + 42, true);
|
|
filename = getString(data.array.subarray(index + 46, index + 46 + entry.filenameLength));
|
|
entry.filename = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(filename) : decodeASCII(filename);
|
|
if (!entry.directory && entry.filename.charAt(entry.filename.length - 1) == "/")
|
|
entry.directory = true;
|
|
comment = getString(data.array.subarray(index + 46 + entry.filenameLength + entry.extraFieldLength, index + 46
|
|
+ entry.filenameLength + entry.extraFieldLength + entry.commentLength));
|
|
entry.comment = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(comment) : decodeASCII(comment);
|
|
entries.push(entry);
|
|
index += 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength;
|
|
}
|
|
callback(entries);
|
|
}, function() {
|
|
onerror(ERR_READ);
|
|
});
|
|
});
|
|
},
|
|
close : function(callback) {
|
|
if (callback)
|
|
callback();
|
|
}
|
|
};
|
|
}
|
|
|
|
// ZipWriter
|
|
|
|
function encodeUTF8(string) {
|
|
return unescape(encodeURIComponent(string));
|
|
}
|
|
|
|
function getBytes(str) {
|
|
var i, array = [];
|
|
for (i = 0; i < str.length; i++)
|
|
array.push(str.charCodeAt(i));
|
|
return array;
|
|
}
|
|
|
|
function createZipWriter(writer, onerror, dontDeflate) {
|
|
var worker, files = {}, filenames = [], datalength = 0;
|
|
|
|
function terminate(callback, message) {
|
|
if (worker)
|
|
worker.terminate();
|
|
worker = null;
|
|
if (callback)
|
|
callback(message);
|
|
}
|
|
|
|
function onwriteerror() {
|
|
terminate(onerror, ERR_WRITE);
|
|
}
|
|
|
|
function onreaderror() {
|
|
terminate(onerror, ERR_READ_DATA);
|
|
}
|
|
|
|
return {
|
|
add : function(name, reader, onend, onprogress, options) {
|
|
var header, filename, date;
|
|
|
|
function writeHeader(callback) {
|
|
var data;
|
|
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 || ""))
|
|
};
|
|
header.view.setUint32(0, 0x14000808);
|
|
if (options.version)
|
|
header.view.setUint8(0, options.version);
|
|
if (!dontDeflate && options.level !== 0 && !options.directory)
|
|
header.view.setUint16(4, 0x0800);
|
|
header.view.setUint16(6, (((date.getHours() << 6) | date.getMinutes()) << 5) | date.getSeconds() / 2, true);
|
|
header.view.setUint16(8, ((((date.getFullYear() - 1980) << 4) | (date.getMonth() + 1)) << 5) | date.getDate(), true);
|
|
header.view.setUint16(22, filename.length, true);
|
|
data = getDataHelper(30 + filename.length);
|
|
data.view.setUint32(0, 0x504b0304);
|
|
data.array.set(header.array, 4);
|
|
data.array.set(filename, 30);
|
|
datalength += data.array.length;
|
|
writer.writeUint8Array(data.array, callback, onwriteerror);
|
|
}
|
|
|
|
function writeFooter(compressedLength, crc32) {
|
|
var footer = getDataHelper(16);
|
|
datalength += compressedLength || 0;
|
|
footer.view.setUint32(0, 0x504b0708);
|
|
if (typeof crc32 != "undefined") {
|
|
header.view.setUint32(10, crc32, true);
|
|
footer.view.setUint32(4, crc32, true);
|
|
}
|
|
if (reader) {
|
|
footer.view.setUint32(8, compressedLength, true);
|
|
header.view.setUint32(14, compressedLength, true);
|
|
footer.view.setUint32(12, reader.size, true);
|
|
header.view.setUint32(18, reader.size, true);
|
|
}
|
|
writer.writeUint8Array(footer.array, function() {
|
|
datalength += 16;
|
|
terminate(onend);
|
|
}, onwriteerror);
|
|
}
|
|
|
|
function writeFile() {
|
|
options = options || {};
|
|
name = name.trim();
|
|
if (options.directory && name.charAt(name.length - 1) != "/")
|
|
name += "/";
|
|
if (files.hasOwnProperty(name)) {
|
|
onerror(ERR_DUPLICATED_NAME);
|
|
return;
|
|
}
|
|
filename = getBytes(encodeUTF8(name));
|
|
filenames.push(name);
|
|
writeHeader(function() {
|
|
if (reader)
|
|
if (dontDeflate || options.level === 0)
|
|
copy(reader, writer, 0, reader.size, true, writeFooter, onprogress, onreaderror, onwriteerror);
|
|
else
|
|
worker = deflate(reader, writer, options.level, writeFooter, onprogress, onreaderror, onwriteerror);
|
|
else
|
|
writeFooter();
|
|
}, onwriteerror);
|
|
}
|
|
|
|
if (reader)
|
|
reader.init(writeFile, onreaderror);
|
|
else
|
|
writeFile();
|
|
},
|
|
close : function(callback) {
|
|
var data, length = 0, index = 0, indexFilename, file;
|
|
for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) {
|
|
file = files[filenames[indexFilename]];
|
|
length += 46 + file.filename.length + file.comment.length;
|
|
}
|
|
data = getDataHelper(length + 22);
|
|
for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) {
|
|
file = files[filenames[indexFilename]];
|
|
data.view.setUint32(index, 0x504b0102);
|
|
data.view.setUint16(index + 4, 0x1400);
|
|
data.array.set(file.headerArray, index + 6);
|
|
data.view.setUint16(index + 32, file.comment.length, true);
|
|
if (file.directory)
|
|
data.view.setUint8(index + 38, 0x10);
|
|
data.view.setUint32(index + 42, file.offset, true);
|
|
data.array.set(file.filename, index + 46);
|
|
data.array.set(file.comment, index + 46 + file.filename.length);
|
|
index += 46 + file.filename.length + file.comment.length;
|
|
}
|
|
data.view.setUint32(index, 0x504b0506);
|
|
data.view.setUint16(index + 8, filenames.length, true);
|
|
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.getData(callback);
|
|
});
|
|
}, onwriteerror);
|
|
}
|
|
};
|
|
}
|
|
|
|
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() {
|
|
callback(createZipWriter(writer, onerror, dontDeflate));
|
|
}, onerror);
|
|
},
|
|
workerScriptsPath : "",
|
|
useWebWorkers : true
|
|
};
|
|
|
|
}(O.esri));
|
|
/**
|
|
* 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){
|
|
|
|
/**
|
|
* Activates the orientation listener and listens for native events.
|
|
*/
|
|
function _setOrientationListener(delay){
|
|
var supportsOrientationChange = "onorientationchange" in window,
|
|
orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";
|
|
|
|
window.addEventListener(orientationEvent, _debounceMap(function(){
|
|
_centerMap();
|
|
},delay))
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 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 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))
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* Pulls a saved location from localStorage
|
|
* Requires that setCenterPt() has been set.
|
|
* @returns String x,y,spatialReference
|
|
*/
|
|
function _getCenterPt(){
|
|
var value = null;
|
|
|
|
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);
|
|
}
|
|
}
|
|
/*
|
|
Copyright (c) 2013 Gildas Lormeau. All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
3. The names of the authors may not be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
|
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
|
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* This program is based on JZlib 1.0.2 ymnk, JCraft,Inc.
|
|
* JZlib is based on zlib-1.1.3, so all credit should go authors
|
|
* Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu)
|
|
* and contributors of zlib.
|
|
*/
|
|
|
|
O.esri.TPK.inflate = function(obj) {
|
|
|
|
// Global
|
|
var MAX_BITS = 15;
|
|
|
|
var Z_OK = 0;
|
|
var Z_STREAM_END = 1;
|
|
var Z_NEED_DICT = 2;
|
|
var Z_STREAM_ERROR = -2;
|
|
var Z_DATA_ERROR = -3;
|
|
var Z_MEM_ERROR = -4;
|
|
var Z_BUF_ERROR = -5;
|
|
|
|
var inflate_mask = [ 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff,
|
|
0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff ];
|
|
|
|
var MANY = 1440;
|
|
|
|
// JZlib version : "1.0.2"
|
|
var Z_NO_FLUSH = 0;
|
|
var Z_FINISH = 4;
|
|
|
|
// InfTree
|
|
var fixed_bl = 9;
|
|
var fixed_bd = 5;
|
|
|
|
var fixed_tl = [ 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0,
|
|
0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40,
|
|
0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13,
|
|
0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60,
|
|
0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7,
|
|
35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8,
|
|
26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80,
|
|
7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0,
|
|
8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0,
|
|
8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97,
|
|
0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210,
|
|
81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117,
|
|
0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154,
|
|
84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83,
|
|
0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230,
|
|
80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139,
|
|
0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174,
|
|
0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111,
|
|
0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9,
|
|
193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8,
|
|
120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8,
|
|
227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8,
|
|
92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9,
|
|
249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8,
|
|
130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9,
|
|
181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8,
|
|
102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9,
|
|
221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0,
|
|
8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9,
|
|
147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8,
|
|
85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9,
|
|
235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8,
|
|
141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9,
|
|
167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8,
|
|
107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9,
|
|
207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8,
|
|
127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255 ];
|
|
var fixed_td = [ 80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5,
|
|
8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5,
|
|
24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577 ];
|
|
|
|
// Tables for deflate from PKZIP's appnote.txt.
|
|
var cplens = [ // Copy lengths for literal codes 257..285
|
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 ];
|
|
|
|
// see note #13 above about 258
|
|
var cplext = [ // Extra bits for literal codes 257..285
|
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112 // 112==invalid
|
|
];
|
|
|
|
var cpdist = [ // Copy offsets for distance codes 0..29
|
|
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 ];
|
|
|
|
var cpdext = [ // Extra bits for distance codes
|
|
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 ];
|
|
|
|
// If BMAX needs to be larger than 16, then h and x[] should be uLong.
|
|
var BMAX = 15; // maximum bit length of any code
|
|
|
|
function InfTree() {
|
|
var that = this;
|
|
|
|
var hn; // hufts used in space
|
|
var v; // work area for huft_build
|
|
var c; // bit length count table
|
|
var r; // table entry for structure assignment
|
|
var u; // table stack
|
|
var x; // bit offsets, then code stack
|
|
|
|
function huft_build(b, // code lengths in bits (all assumed <=
|
|
// BMAX)
|
|
bindex, n, // number of codes (assumed <= 288)
|
|
s, // number of simple-valued codes (0..s-1)
|
|
d, // list of base values for non-simple codes
|
|
e, // list of extra bits for non-simple codes
|
|
t, // result: starting table
|
|
m, // maximum lookup bits, returns actual
|
|
hp,// space for trees
|
|
hn,// hufts used in space
|
|
v // working area: values in order of bit length
|
|
) {
|
|
// Given a list of code lengths and a maximum table size, make a set of
|
|
// tables to decode that set of codes. Return Z_OK on success,
|
|
// Z_BUF_ERROR
|
|
// if the given code set is incomplete (the tables are still built in
|
|
// this
|
|
// case), Z_DATA_ERROR if the input is invalid (an over-subscribed set
|
|
// of
|
|
// lengths), or Z_MEM_ERROR if not enough memory.
|
|
|
|
var a; // counter for codes of length k
|
|
var f; // i repeats in table every f entries
|
|
var g; // maximum code length
|
|
var h; // table level
|
|
var i; // counter, current code
|
|
var j; // counter
|
|
var k; // number of bits in current code
|
|
var l; // bits per table (returned in m)
|
|
var mask; // (1 << w) - 1, to avoid cc -O bug on HP
|
|
var p; // pointer into c[], b[], or v[]
|
|
var q; // points to current table
|
|
var w; // bits before this table == (l * h)
|
|
var xp; // pointer into x
|
|
var y; // number of dummy codes added
|
|
var z; // number of entries in current table
|
|
|
|
// Generate counts for each bit length
|
|
|
|
p = 0;
|
|
i = n;
|
|
do {
|
|
c[b[bindex + p]]++;
|
|
p++;
|
|
i--; // assume all entries <= BMAX
|
|
} while (i !== 0);
|
|
|
|
if (c[0] == n) { // null input--all zero length codes
|
|
t[0] = -1;
|
|
m[0] = 0;
|
|
return Z_OK;
|
|
}
|
|
|
|
// Find minimum and maximum length, bound *m by those
|
|
l = m[0];
|
|
for (j = 1; j <= BMAX; j++)
|
|
if (c[j] !== 0)
|
|
break;
|
|
k = j; // minimum code length
|
|
if (l < j) {
|
|
l = j;
|
|
}
|
|
for (i = BMAX; i !== 0; i--) {
|
|
if (c[i] !== 0)
|
|
break;
|
|
}
|
|
g = i; // maximum code length
|
|
if (l > i) {
|
|
l = i;
|
|
}
|
|
m[0] = l;
|
|
|
|
// Adjust last length count to fill out codes, if needed
|
|
for (y = 1 << j; j < i; j++, y <<= 1) {
|
|
if ((y -= c[j]) < 0) {
|
|
return Z_DATA_ERROR;
|
|
}
|
|
}
|
|
if ((y -= c[i]) < 0) {
|
|
return Z_DATA_ERROR;
|
|
}
|
|
c[i] += y;
|
|
|
|
// Generate starting offsets into the value table for each length
|
|
x[1] = j = 0;
|
|
p = 1;
|
|
xp = 2;
|
|
while (--i !== 0) { // note that i == g from above
|
|
x[xp] = (j += c[p]);
|
|
xp++;
|
|
p++;
|
|
}
|
|
|
|
// Make a table of values in order of bit lengths
|
|
i = 0;
|
|
p = 0;
|
|
do {
|
|
if ((j = b[bindex + p]) !== 0) {
|
|
v[x[j]++] = i;
|
|
}
|
|
p++;
|
|
} while (++i < n);
|
|
n = x[g]; // set n to length of v
|
|
|
|
// Generate the Huffman codes and for each, make the table entries
|
|
x[0] = i = 0; // first Huffman code is zero
|
|
p = 0; // grab values in bit order
|
|
h = -1; // no tables yet--level -1
|
|
w = -l; // bits decoded == (l * h)
|
|
u[0] = 0; // just to keep compilers happy
|
|
q = 0; // ditto
|
|
z = 0; // ditto
|
|
|
|
// go through the bit lengths (k already is bits in shortest code)
|
|
for (; k <= g; k++) {
|
|
a = c[k];
|
|
while (a-- !== 0) {
|
|
// here i is the Huffman code of length k bits for value *p
|
|
// make tables up to required level
|
|
while (k > w + l) {
|
|
h++;
|
|
w += l; // previous table always l bits
|
|
// compute minimum size table less than or equal to l bits
|
|
z = g - w;
|
|
z = (z > l) ? l : z; // table size upper limit
|
|
if ((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table
|
|
// too few codes for
|
|
// k-w bit table
|
|
f -= a + 1; // deduct codes from patterns left
|
|
xp = k;
|
|
if (j < z) {
|
|
while (++j < z) { // try smaller tables up to z bits
|
|
if ((f <<= 1) <= c[++xp])
|
|
break; // enough codes to use up j bits
|
|
f -= c[xp]; // else deduct codes from patterns
|
|
}
|
|
}
|
|
}
|
|
z = 1 << j; // table entries for j-bit table
|
|
|
|
// allocate new table
|
|
if (hn[0] + z > MANY) { // (note: doesn't matter for fixed)
|
|
return Z_DATA_ERROR; // overflow of MANY
|
|
}
|
|
u[h] = q = /* hp+ */hn[0]; // DEBUG
|
|
hn[0] += z;
|
|
|
|
// connect to last table, if there is one
|
|
if (h !== 0) {
|
|
x[h] = i; // save pattern for backing up
|
|
r[0] = /* (byte) */j; // bits in this table
|
|
r[1] = /* (byte) */l; // bits to dump before this table
|
|
j = i >>> (w - l);
|
|
r[2] = /* (int) */(q - u[h - 1] - j); // offset to this table
|
|
hp.set(r, (u[h - 1] + j) * 3);
|
|
// to
|
|
// last
|
|
// table
|
|
} else {
|
|
t[0] = q; // first table is returned result
|
|
}
|
|
}
|
|
|
|
// set up table entry in r
|
|
r[1] = /* (byte) */(k - w);
|
|
if (p >= n) {
|
|
r[0] = 128 + 64; // out of values--invalid code
|
|
} else if (v[p] < s) {
|
|
r[0] = /* (byte) */(v[p] < 256 ? 0 : 32 + 64); // 256 is
|
|
// end-of-block
|
|
r[2] = v[p++]; // simple code is just the value
|
|
} else {
|
|
r[0] = /* (byte) */(e[v[p] - s] + 16 + 64); // non-simple--look
|
|
// up in lists
|
|
r[2] = d[v[p++] - s];
|
|
}
|
|
|
|
// fill code-like entries with r
|
|
f = 1 << (k - w);
|
|
for (j = i >>> w; j < z; j += f) {
|
|
hp.set(r, (q + j) * 3);
|
|
}
|
|
|
|
// backwards increment the k-bit code i
|
|
for (j = 1 << (k - 1); (i & j) !== 0; j >>>= 1) {
|
|
i ^= j;
|
|
}
|
|
i ^= j;
|
|
|
|
// backup over finished tables
|
|
mask = (1 << w) - 1; // needed on HP, cc -O bug
|
|
while ((i & mask) != x[h]) {
|
|
h--; // don't need to update q
|
|
w -= l;
|
|
mask = (1 << w) - 1;
|
|
}
|
|
}
|
|
}
|
|
// Return Z_BUF_ERROR if we were given an incomplete table
|
|
return y !== 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
|
|
}
|
|
|
|
function initWorkArea(vsize) {
|
|
var i;
|
|
if (!hn) {
|
|
hn = []; // []; //new Array(1);
|
|
v = []; // new Array(vsize);
|
|
c = new Int32Array(BMAX + 1); // new Array(BMAX + 1);
|
|
r = []; // new Array(3);
|
|
u = new Int32Array(BMAX); // new Array(BMAX);
|
|
x = new Int32Array(BMAX + 1); // new Array(BMAX + 1);
|
|
}
|
|
if (v.length < vsize) {
|
|
v = []; // new Array(vsize);
|
|
}
|
|
for (i = 0; i < vsize; i++) {
|
|
v[i] = 0;
|
|
}
|
|
for (i = 0; i < BMAX + 1; i++) {
|
|
c[i] = 0;
|
|
}
|
|
for (i = 0; i < 3; i++) {
|
|
r[i] = 0;
|
|
}
|
|
// for(int i=0; i<BMAX; i++){u[i]=0;}
|
|
u.set(c.subarray(0, BMAX), 0);
|
|
// for(int i=0; i<BMAX+1; i++){x[i]=0;}
|
|
x.set(c.subarray(0, BMAX + 1), 0);
|
|
}
|
|
|
|
that.inflate_trees_bits = function(c, // 19 code lengths
|
|
bb, // bits tree desired/actual depth
|
|
tb, // bits tree result
|
|
hp, // space for trees
|
|
z // for messages
|
|
) {
|
|
var result;
|
|
initWorkArea(19);
|
|
hn[0] = 0;
|
|
result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v);
|
|
|
|
if (result == Z_DATA_ERROR) {
|
|
z.msg = "oversubscribed dynamic bit lengths tree";
|
|
} else if (result == Z_BUF_ERROR || bb[0] === 0) {
|
|
z.msg = "incomplete dynamic bit lengths tree";
|
|
result = Z_DATA_ERROR;
|
|
}
|
|
return result;
|
|
};
|
|
|
|
that.inflate_trees_dynamic = function(nl, // number of literal/length codes
|
|
nd, // number of distance codes
|
|
c, // that many (total) code lengths
|
|
bl, // literal desired/actual bit depth
|
|
bd, // distance desired/actual bit depth
|
|
tl, // literal/length tree result
|
|
td, // distance tree result
|
|
hp, // space for trees
|
|
z // for messages
|
|
) {
|
|
var result;
|
|
|
|
// build literal/length tree
|
|
initWorkArea(288);
|
|
hn[0] = 0;
|
|
result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v);
|
|
if (result != Z_OK || bl[0] === 0) {
|
|
if (result == Z_DATA_ERROR) {
|
|
z.msg = "oversubscribed literal/length tree";
|
|
} else if (result != Z_MEM_ERROR) {
|
|
z.msg = "incomplete literal/length tree";
|
|
result = Z_DATA_ERROR;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// build distance tree
|
|
initWorkArea(288);
|
|
result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v);
|
|
|
|
if (result != Z_OK || (bd[0] === 0 && nl > 257)) {
|
|
if (result == Z_DATA_ERROR) {
|
|
z.msg = "oversubscribed distance tree";
|
|
} else if (result == Z_BUF_ERROR) {
|
|
z.msg = "incomplete distance tree";
|
|
result = Z_DATA_ERROR;
|
|
} else if (result != Z_MEM_ERROR) {
|
|
z.msg = "empty distance tree with lengths";
|
|
result = Z_DATA_ERROR;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
return Z_OK;
|
|
};
|
|
|
|
}
|
|
|
|
InfTree.inflate_trees_fixed = function(bl, // literal desired/actual bit depth
|
|
bd, // distance desired/actual bit depth
|
|
tl,// literal/length tree result
|
|
td// distance tree result
|
|
) {
|
|
bl[0] = fixed_bl;
|
|
bd[0] = fixed_bd;
|
|
tl[0] = fixed_tl;
|
|
td[0] = fixed_td;
|
|
return Z_OK;
|
|
};
|
|
|
|
// InfCodes
|
|
|
|
// waiting for "i:"=input,
|
|
// "o:"=output,
|
|
// "x:"=nothing
|
|
var START = 0; // x: set up for LEN
|
|
var LEN = 1; // i: get length/literal/eob next
|
|
var LENEXT = 2; // i: getting length extra (have base)
|
|
var DIST = 3; // i: get distance next
|
|
var DISTEXT = 4;// i: getting distance extra
|
|
var COPY = 5; // o: copying bytes in window, waiting
|
|
// for space
|
|
var LIT = 6; // o: got literal, waiting for output
|
|
// space
|
|
var WASH = 7; // o: got eob, possibly still output
|
|
// waiting
|
|
var END = 8; // x: got eob and all data flushed
|
|
var BADCODE = 9;// x: got error
|
|
|
|
function InfCodes() {
|
|
var that = this;
|
|
|
|
var mode; // current inflate_codes mode
|
|
|
|
// mode dependent information
|
|
var len = 0;
|
|
|
|
var tree; // pointer into tree
|
|
var tree_index = 0;
|
|
var need = 0; // bits needed
|
|
|
|
var lit = 0;
|
|
|
|
// if EXT or COPY, where and how much
|
|
var get = 0; // bits to get for extra
|
|
var dist = 0; // distance back to copy from
|
|
|
|
var lbits = 0; // ltree bits decoded per branch
|
|
var dbits = 0; // dtree bits decoder per branch
|
|
var ltree; // literal/length/eob tree
|
|
var ltree_index = 0; // literal/length/eob tree
|
|
var dtree; // distance tree
|
|
var dtree_index = 0; // distance tree
|
|
|
|
// Called with number of bytes left to write in window at least 258
|
|
// (the maximum string length) and number of input bytes available
|
|
// at least ten. The ten bytes are six bytes for the longest length/
|
|
// distance pair plus four bytes for overloading the bit buffer.
|
|
|
|
function inflate_fast(bl, bd, tl, tl_index, td, td_index, s, z) {
|
|
var t; // temporary pointer
|
|
var tp; // temporary pointer
|
|
var tp_index; // temporary pointer
|
|
var e; // extra bits or operation
|
|
var b; // bit buffer
|
|
var k; // bits in bit buffer
|
|
var p; // input data pointer
|
|
var n; // bytes available there
|
|
var q; // output window write pointer
|
|
var m; // bytes to end of window or read pointer
|
|
var ml; // mask for literal/length tree
|
|
var md; // mask for distance tree
|
|
var c; // bytes to copy
|
|
var d; // distance back to copy from
|
|
var r; // copy source pointer
|
|
|
|
var tp_index_t_3; // (tp_index+t)*3
|
|
|
|
// load input, output, bit values
|
|
p = z.next_in_index;
|
|
n = z.avail_in;
|
|
b = s.bitb;
|
|
k = s.bitk;
|
|
q = s.write;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
|
|
// initialize masks
|
|
ml = inflate_mask[bl];
|
|
md = inflate_mask[bd];
|
|
|
|
// do until not enough input or output space for fast loop
|
|
do { // assume called with m >= 258 && n >= 10
|
|
// get literal/length code
|
|
while (k < (20)) { // max bits for literal/length code
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
t = b & ml;
|
|
tp = tl;
|
|
tp_index = tl_index;
|
|
tp_index_t_3 = (tp_index + t) * 3;
|
|
if ((e = tp[tp_index_t_3]) === 0) {
|
|
b >>= (tp[tp_index_t_3 + 1]);
|
|
k -= (tp[tp_index_t_3 + 1]);
|
|
|
|
s.window[q++] = /* (byte) */tp[tp_index_t_3 + 2];
|
|
m--;
|
|
continue;
|
|
}
|
|
do {
|
|
|
|
b >>= (tp[tp_index_t_3 + 1]);
|
|
k -= (tp[tp_index_t_3 + 1]);
|
|
|
|
if ((e & 16) !== 0) {
|
|
e &= 15;
|
|
c = tp[tp_index_t_3 + 2] + (/* (int) */b & inflate_mask[e]);
|
|
|
|
b >>= e;
|
|
k -= e;
|
|
|
|
// decode distance base of block to copy
|
|
while (k < (15)) { // max bits for distance code
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
t = b & md;
|
|
tp = td;
|
|
tp_index = td_index;
|
|
tp_index_t_3 = (tp_index + t) * 3;
|
|
e = tp[tp_index_t_3];
|
|
|
|
do {
|
|
|
|
b >>= (tp[tp_index_t_3 + 1]);
|
|
k -= (tp[tp_index_t_3 + 1]);
|
|
|
|
if ((e & 16) !== 0) {
|
|
// get extra bits to add to distance base
|
|
e &= 15;
|
|
while (k < (e)) { // get extra bits (up to 13)
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]);
|
|
|
|
b >>= (e);
|
|
k -= (e);
|
|
|
|
// do the copy
|
|
m -= c;
|
|
if (q >= d) { // offset before dest
|
|
// just copy
|
|
r = q - d;
|
|
if (q - r > 0 && 2 > (q - r)) {
|
|
s.window[q++] = s.window[r++]; // minimum
|
|
// count is
|
|
// three,
|
|
s.window[q++] = s.window[r++]; // so unroll
|
|
// loop a
|
|
// little
|
|
c -= 2;
|
|
} else {
|
|
s.window.set(s.window.subarray(r, r + 2), q);
|
|
q += 2;
|
|
r += 2;
|
|
c -= 2;
|
|
}
|
|
} else { // else offset after destination
|
|
r = q - d;
|
|
do {
|
|
r += s.end; // force pointer in window
|
|
} while (r < 0); // covers invalid distances
|
|
e = s.end - r;
|
|
if (c > e) { // if source crosses,
|
|
c -= e; // wrapped copy
|
|
if (q - r > 0 && e > (q - r)) {
|
|
do {
|
|
s.window[q++] = s.window[r++];
|
|
} while (--e !== 0);
|
|
} else {
|
|
s.window.set(s.window.subarray(r, r + e), q);
|
|
q += e;
|
|
r += e;
|
|
e = 0;
|
|
}
|
|
r = 0; // copy rest from start of window
|
|
}
|
|
|
|
}
|
|
|
|
// copy all or what's left
|
|
if (q - r > 0 && c > (q - r)) {
|
|
do {
|
|
s.window[q++] = s.window[r++];
|
|
} while (--c !== 0);
|
|
} else {
|
|
s.window.set(s.window.subarray(r, r + c), q);
|
|
q += c;
|
|
r += c;
|
|
c = 0;
|
|
}
|
|
break;
|
|
} else if ((e & 64) === 0) {
|
|
t += tp[tp_index_t_3 + 2];
|
|
t += (b & inflate_mask[e]);
|
|
tp_index_t_3 = (tp_index + t) * 3;
|
|
e = tp[tp_index_t_3];
|
|
} else {
|
|
z.msg = "invalid distance code";
|
|
|
|
c = z.avail_in - n;
|
|
c = (k >> 3) < c ? k >> 3 : c;
|
|
n += c;
|
|
p -= c;
|
|
k -= c << 3;
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
|
|
return Z_DATA_ERROR;
|
|
}
|
|
} while (true);
|
|
break;
|
|
}
|
|
|
|
if ((e & 64) === 0) {
|
|
t += tp[tp_index_t_3 + 2];
|
|
t += (b & inflate_mask[e]);
|
|
tp_index_t_3 = (tp_index + t) * 3;
|
|
if ((e = tp[tp_index_t_3]) === 0) {
|
|
|
|
b >>= (tp[tp_index_t_3 + 1]);
|
|
k -= (tp[tp_index_t_3 + 1]);
|
|
|
|
s.window[q++] = /* (byte) */tp[tp_index_t_3 + 2];
|
|
m--;
|
|
break;
|
|
}
|
|
} else if ((e & 32) !== 0) {
|
|
|
|
c = z.avail_in - n;
|
|
c = (k >> 3) < c ? k >> 3 : c;
|
|
n += c;
|
|
p -= c;
|
|
k -= c << 3;
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
|
|
return Z_STREAM_END;
|
|
} else {
|
|
z.msg = "invalid literal/length code";
|
|
|
|
c = z.avail_in - n;
|
|
c = (k >> 3) < c ? k >> 3 : c;
|
|
n += c;
|
|
p -= c;
|
|
k -= c << 3;
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
|
|
return Z_DATA_ERROR;
|
|
}
|
|
} while (true);
|
|
} while (m >= 258 && n >= 10);
|
|
|
|
// not enough input or output--restore pointers and return
|
|
c = z.avail_in - n;
|
|
c = (k >> 3) < c ? k >> 3 : c;
|
|
n += c;
|
|
p -= c;
|
|
k -= c << 3;
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
|
|
return Z_OK;
|
|
}
|
|
|
|
that.init = function(bl, bd, tl, tl_index, td, td_index) {
|
|
mode = START;
|
|
lbits = /* (byte) */bl;
|
|
dbits = /* (byte) */bd;
|
|
ltree = tl;
|
|
ltree_index = tl_index;
|
|
dtree = td;
|
|
dtree_index = td_index;
|
|
tree = null;
|
|
};
|
|
|
|
that.proc = function(s, z, r) {
|
|
var j; // temporary storage
|
|
var tindex; // temporary pointer
|
|
var e; // extra bits or operation
|
|
var b = 0; // bit buffer
|
|
var k = 0; // bits in bit buffer
|
|
var p = 0; // input data pointer
|
|
var n; // bytes available there
|
|
var q; // output window write pointer
|
|
var m; // bytes to end of window or read pointer
|
|
var f; // pointer to copy strings from
|
|
|
|
// copy input/output information to locals (UPDATE macro restores)
|
|
p = z.next_in_index;
|
|
n = z.avail_in;
|
|
b = s.bitb;
|
|
k = s.bitk;
|
|
q = s.write;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
|
|
// process input and output based on current state
|
|
while (true) {
|
|
switch (mode) {
|
|
// waiting for "i:"=input, "o:"=output, "x:"=nothing
|
|
case START: // x: set up for LEN
|
|
if (m >= 258 && n >= 10) {
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
r = inflate_fast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, s, z);
|
|
|
|
p = z.next_in_index;
|
|
n = z.avail_in;
|
|
b = s.bitb;
|
|
k = s.bitk;
|
|
q = s.write;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
|
|
if (r != Z_OK) {
|
|
mode = r == Z_STREAM_END ? WASH : BADCODE;
|
|
break;
|
|
}
|
|
}
|
|
need = lbits;
|
|
tree = ltree;
|
|
tree_index = ltree_index;
|
|
|
|
mode = LEN;
|
|
case LEN: // i: get length/literal/eob next
|
|
j = need;
|
|
|
|
while (k < (j)) {
|
|
if (n !== 0)
|
|
r = Z_OK;
|
|
else {
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
tindex = (tree_index + (b & inflate_mask[j])) * 3;
|
|
|
|
b >>>= (tree[tindex + 1]);
|
|
k -= (tree[tindex + 1]);
|
|
|
|
e = tree[tindex];
|
|
|
|
if (e === 0) { // literal
|
|
lit = tree[tindex + 2];
|
|
mode = LIT;
|
|
break;
|
|
}
|
|
if ((e & 16) !== 0) { // length
|
|
get = e & 15;
|
|
len = tree[tindex + 2];
|
|
mode = LENEXT;
|
|
break;
|
|
}
|
|
if ((e & 64) === 0) { // next table
|
|
need = e;
|
|
tree_index = tindex / 3 + tree[tindex + 2];
|
|
break;
|
|
}
|
|
if ((e & 32) !== 0) { // end of block
|
|
mode = WASH;
|
|
break;
|
|
}
|
|
mode = BADCODE; // invalid code
|
|
z.msg = "invalid literal/length code";
|
|
r = Z_DATA_ERROR;
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
|
|
case LENEXT: // i: getting length extra (have base)
|
|
j = get;
|
|
|
|
while (k < (j)) {
|
|
if (n !== 0)
|
|
r = Z_OK;
|
|
else {
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
len += (b & inflate_mask[j]);
|
|
|
|
b >>= j;
|
|
k -= j;
|
|
|
|
need = dbits;
|
|
tree = dtree;
|
|
tree_index = dtree_index;
|
|
mode = DIST;
|
|
case DIST: // i: get distance next
|
|
j = need;
|
|
|
|
while (k < (j)) {
|
|
if (n !== 0)
|
|
r = Z_OK;
|
|
else {
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
tindex = (tree_index + (b & inflate_mask[j])) * 3;
|
|
|
|
b >>= tree[tindex + 1];
|
|
k -= tree[tindex + 1];
|
|
|
|
e = (tree[tindex]);
|
|
if ((e & 16) !== 0) { // distance
|
|
get = e & 15;
|
|
dist = tree[tindex + 2];
|
|
mode = DISTEXT;
|
|
break;
|
|
}
|
|
if ((e & 64) === 0) { // next table
|
|
need = e;
|
|
tree_index = tindex / 3 + tree[tindex + 2];
|
|
break;
|
|
}
|
|
mode = BADCODE; // invalid code
|
|
z.msg = "invalid distance code";
|
|
r = Z_DATA_ERROR;
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
|
|
case DISTEXT: // i: getting distance extra
|
|
j = get;
|
|
|
|
while (k < (j)) {
|
|
if (n !== 0)
|
|
r = Z_OK;
|
|
else {
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
dist += (b & inflate_mask[j]);
|
|
|
|
b >>= j;
|
|
k -= j;
|
|
|
|
mode = COPY;
|
|
case COPY: // o: copying bytes in window, waiting for space
|
|
f = q - dist;
|
|
while (f < 0) { // modulo window size-"while" instead
|
|
f += s.end; // of "if" handles invalid distances
|
|
}
|
|
while (len !== 0) {
|
|
|
|
if (m === 0) {
|
|
if (q == s.end && s.read !== 0) {
|
|
q = 0;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
}
|
|
if (m === 0) {
|
|
s.write = q;
|
|
r = s.inflate_flush(z, r);
|
|
q = s.write;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
|
|
if (q == s.end && s.read !== 0) {
|
|
q = 0;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
}
|
|
|
|
if (m === 0) {
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
}
|
|
}
|
|
}
|
|
|
|
s.window[q++] = s.window[f++];
|
|
m--;
|
|
|
|
if (f == s.end)
|
|
f = 0;
|
|
len--;
|
|
}
|
|
mode = START;
|
|
break;
|
|
case LIT: // o: got literal, waiting for output space
|
|
if (m === 0) {
|
|
if (q == s.end && s.read !== 0) {
|
|
q = 0;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
}
|
|
if (m === 0) {
|
|
s.write = q;
|
|
r = s.inflate_flush(z, r);
|
|
q = s.write;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
|
|
if (q == s.end && s.read !== 0) {
|
|
q = 0;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
}
|
|
if (m === 0) {
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
}
|
|
}
|
|
}
|
|
r = Z_OK;
|
|
|
|
s.window[q++] = /* (byte) */lit;
|
|
m--;
|
|
|
|
mode = START;
|
|
break;
|
|
case WASH: // o: got eob, possibly more output
|
|
if (k > 7) { // return unused byte, if any
|
|
k -= 8;
|
|
n++;
|
|
p--; // can always return one
|
|
}
|
|
|
|
s.write = q;
|
|
r = s.inflate_flush(z, r);
|
|
q = s.write;
|
|
m = q < s.read ? s.read - q - 1 : s.end - q;
|
|
|
|
if (s.read != s.write) {
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
}
|
|
mode = END;
|
|
case END:
|
|
r = Z_STREAM_END;
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
|
|
case BADCODE: // x: got error
|
|
|
|
r = Z_DATA_ERROR;
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
|
|
default:
|
|
r = Z_STREAM_ERROR;
|
|
|
|
s.bitb = b;
|
|
s.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
s.write = q;
|
|
return s.inflate_flush(z, r);
|
|
}
|
|
}
|
|
};
|
|
|
|
that.free = function() {
|
|
// ZFREE(z, c);
|
|
};
|
|
|
|
}
|
|
|
|
// InfBlocks
|
|
|
|
// Table for deflate from PKZIP's appnote.txt.
|
|
var border = [ // Order of the bit length code lengths
|
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
|
|
|
|
var TYPE = 0; // get type bits (3, including end bit)
|
|
var LENS = 1; // get lengths for stored
|
|
var STORED = 2;// processing stored block
|
|
var TABLE = 3; // get table lengths
|
|
var BTREE = 4; // get bit lengths tree for a dynamic
|
|
// block
|
|
var DTREE = 5; // get length, distance trees for a
|
|
// dynamic block
|
|
var CODES = 6; // processing fixed or dynamic block
|
|
var DRY = 7; // output remaining window bytes
|
|
var DONELOCKS = 8; // finished last block, done
|
|
var BADBLOCKS = 9; // ot a data error--stuck here
|
|
|
|
function InfBlocks(z, w) {
|
|
var that = this;
|
|
|
|
var mode = TYPE; // current inflate_block mode
|
|
|
|
var left = 0; // if STORED, bytes left to copy
|
|
|
|
var table = 0; // table lengths (14 bits)
|
|
var index = 0; // index into blens (or border)
|
|
var blens; // bit lengths of codes
|
|
var bb = [ 0 ]; // bit length tree depth
|
|
var tb = [ 0 ]; // bit length decoding tree
|
|
|
|
var codes = new InfCodes(); // if CODES, current state
|
|
|
|
var last = 0; // true if this block is the last block
|
|
|
|
var hufts = new Int32Array(MANY * 3); // single malloc for tree space
|
|
var check = 0; // check on output
|
|
var inftree = new InfTree();
|
|
|
|
that.bitk = 0; // bits in bit buffer
|
|
that.bitb = 0; // bit buffer
|
|
that.window = new Uint8Array(w); // sliding window
|
|
that.end = w; // one byte after sliding window
|
|
that.read = 0; // window read pointer
|
|
that.write = 0; // window write pointer
|
|
|
|
that.reset = function(z, c) {
|
|
if (c)
|
|
c[0] = check;
|
|
// if (mode == BTREE || mode == DTREE) {
|
|
// }
|
|
if (mode == CODES) {
|
|
codes.free(z);
|
|
}
|
|
mode = TYPE;
|
|
that.bitk = 0;
|
|
that.bitb = 0;
|
|
that.read = that.write = 0;
|
|
};
|
|
|
|
that.reset(z, null);
|
|
|
|
// copy as much as possible from the sliding window to the output area
|
|
that.inflate_flush = function(z, r) {
|
|
var n;
|
|
var p;
|
|
var q;
|
|
|
|
// local copies of source and destination pointers
|
|
p = z.next_out_index;
|
|
q = that.read;
|
|
|
|
// compute number of bytes to copy as far as end of window
|
|
n = /* (int) */((q <= that.write ? that.write : that.end) - q);
|
|
if (n > z.avail_out)
|
|
n = z.avail_out;
|
|
if (n !== 0 && r == Z_BUF_ERROR)
|
|
r = Z_OK;
|
|
|
|
// update counters
|
|
z.avail_out -= n;
|
|
z.total_out += n;
|
|
|
|
// copy as far as end of window
|
|
z.next_out.set(that.window.subarray(q, q + n), p);
|
|
p += n;
|
|
q += n;
|
|
|
|
// see if more to copy at beginning of window
|
|
if (q == that.end) {
|
|
// wrap pointers
|
|
q = 0;
|
|
if (that.write == that.end)
|
|
that.write = 0;
|
|
|
|
// compute bytes to copy
|
|
n = that.write - q;
|
|
if (n > z.avail_out)
|
|
n = z.avail_out;
|
|
if (n !== 0 && r == Z_BUF_ERROR)
|
|
r = Z_OK;
|
|
|
|
// update counters
|
|
z.avail_out -= n;
|
|
z.total_out += n;
|
|
|
|
// copy
|
|
z.next_out.set(that.window.subarray(q, q + n), p);
|
|
p += n;
|
|
q += n;
|
|
}
|
|
|
|
// update pointers
|
|
z.next_out_index = p;
|
|
that.read = q;
|
|
|
|
// done
|
|
return r;
|
|
};
|
|
|
|
that.proc = function(z, r) {
|
|
var t; // temporary storage
|
|
var b; // bit buffer
|
|
var k; // bits in bit buffer
|
|
var p; // input data pointer
|
|
var n; // bytes available there
|
|
var q; // output window write pointer
|
|
var m; // bytes to end of window or read pointer
|
|
|
|
var i;
|
|
|
|
// copy input/output information to locals (UPDATE macro restores)
|
|
// {
|
|
p = z.next_in_index;
|
|
n = z.avail_in;
|
|
b = that.bitb;
|
|
k = that.bitk;
|
|
// }
|
|
// {
|
|
q = that.write;
|
|
m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
|
|
// }
|
|
|
|
// process input based on current state
|
|
// DEBUG dtree
|
|
while (true) {
|
|
switch (mode) {
|
|
case TYPE:
|
|
|
|
while (k < (3)) {
|
|
if (n !== 0) {
|
|
r = Z_OK;
|
|
} else {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
t = /* (int) */(b & 7);
|
|
last = t & 1;
|
|
|
|
switch (t >>> 1) {
|
|
case 0: // stored
|
|
// {
|
|
b >>>= (3);
|
|
k -= (3);
|
|
// }
|
|
t = k & 7; // go to byte boundary
|
|
|
|
// {
|
|
b >>>= (t);
|
|
k -= (t);
|
|
// }
|
|
mode = LENS; // get length of stored block
|
|
break;
|
|
case 1: // fixed
|
|
// {
|
|
var bl = []; // new Array(1);
|
|
var bd = []; // new Array(1);
|
|
var tl = [ [] ]; // new Array(1);
|
|
var td = [ [] ]; // new Array(1);
|
|
|
|
InfTree.inflate_trees_fixed(bl, bd, tl, td);
|
|
codes.init(bl[0], bd[0], tl[0], 0, td[0], 0);
|
|
// }
|
|
|
|
// {
|
|
b >>>= (3);
|
|
k -= (3);
|
|
// }
|
|
|
|
mode = CODES;
|
|
break;
|
|
case 2: // dynamic
|
|
|
|
// {
|
|
b >>>= (3);
|
|
k -= (3);
|
|
// }
|
|
|
|
mode = TABLE;
|
|
break;
|
|
case 3: // illegal
|
|
|
|
// {
|
|
b >>>= (3);
|
|
k -= (3);
|
|
// }
|
|
mode = BADBLOCKS;
|
|
z.msg = "invalid block type";
|
|
r = Z_DATA_ERROR;
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
break;
|
|
case LENS:
|
|
|
|
while (k < (32)) {
|
|
if (n !== 0) {
|
|
r = Z_OK;
|
|
} else {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
if ((((~b) >>> 16) & 0xffff) != (b & 0xffff)) {
|
|
mode = BADBLOCKS;
|
|
z.msg = "invalid stored block lengths";
|
|
r = Z_DATA_ERROR;
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
left = (b & 0xffff);
|
|
b = k = 0; // dump bits
|
|
mode = left !== 0 ? STORED : (last !== 0 ? DRY : TYPE);
|
|
break;
|
|
case STORED:
|
|
if (n === 0) {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
|
|
if (m === 0) {
|
|
if (q == that.end && that.read !== 0) {
|
|
q = 0;
|
|
m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
|
|
}
|
|
if (m === 0) {
|
|
that.write = q;
|
|
r = that.inflate_flush(z, r);
|
|
q = that.write;
|
|
m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
|
|
if (q == that.end && that.read !== 0) {
|
|
q = 0;
|
|
m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
|
|
}
|
|
if (m === 0) {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
}
|
|
}
|
|
r = Z_OK;
|
|
|
|
t = left;
|
|
if (t > n)
|
|
t = n;
|
|
if (t > m)
|
|
t = m;
|
|
that.window.set(z.read_buf(p, t), q);
|
|
p += t;
|
|
n -= t;
|
|
q += t;
|
|
m -= t;
|
|
if ((left -= t) !== 0)
|
|
break;
|
|
mode = last !== 0 ? DRY : TYPE;
|
|
break;
|
|
case TABLE:
|
|
|
|
while (k < (14)) {
|
|
if (n !== 0) {
|
|
r = Z_OK;
|
|
} else {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
table = t = (b & 0x3fff);
|
|
if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) {
|
|
mode = BADBLOCKS;
|
|
z.msg = "too many length or distance symbols";
|
|
r = Z_DATA_ERROR;
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
|
|
if (!blens || blens.length < t) {
|
|
blens = []; // new Array(t);
|
|
} else {
|
|
for (i = 0; i < t; i++) {
|
|
blens[i] = 0;
|
|
}
|
|
}
|
|
|
|
// {
|
|
b >>>= (14);
|
|
k -= (14);
|
|
// }
|
|
|
|
index = 0;
|
|
mode = BTREE;
|
|
case BTREE:
|
|
while (index < 4 + (table >>> 10)) {
|
|
while (k < (3)) {
|
|
if (n !== 0) {
|
|
r = Z_OK;
|
|
} else {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
blens[border[index++]] = b & 7;
|
|
|
|
// {
|
|
b >>>= (3);
|
|
k -= (3);
|
|
// }
|
|
}
|
|
|
|
while (index < 19) {
|
|
blens[border[index++]] = 0;
|
|
}
|
|
|
|
bb[0] = 7;
|
|
t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z);
|
|
if (t != Z_OK) {
|
|
r = t;
|
|
if (r == Z_DATA_ERROR) {
|
|
blens = null;
|
|
mode = BADBLOCKS;
|
|
}
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
|
|
index = 0;
|
|
mode = DTREE;
|
|
case DTREE:
|
|
while (true) {
|
|
t = table;
|
|
if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) {
|
|
break;
|
|
}
|
|
|
|
var j, c;
|
|
|
|
t = bb[0];
|
|
|
|
while (k < (t)) {
|
|
if (n !== 0) {
|
|
r = Z_OK;
|
|
} else {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
// if (tb[0] == -1) {
|
|
// System.err.println("null...");
|
|
// }
|
|
|
|
t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1];
|
|
c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2];
|
|
|
|
if (c < 16) {
|
|
b >>>= (t);
|
|
k -= (t);
|
|
blens[index++] = c;
|
|
} else { // c == 16..18
|
|
i = c == 18 ? 7 : c - 14;
|
|
j = c == 18 ? 11 : 3;
|
|
|
|
while (k < (t + i)) {
|
|
if (n !== 0) {
|
|
r = Z_OK;
|
|
} else {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
n--;
|
|
b |= (z.read_byte(p++) & 0xff) << k;
|
|
k += 8;
|
|
}
|
|
|
|
b >>>= (t);
|
|
k -= (t);
|
|
|
|
j += (b & inflate_mask[i]);
|
|
|
|
b >>>= (i);
|
|
k -= (i);
|
|
|
|
i = index;
|
|
t = table;
|
|
if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) {
|
|
blens = null;
|
|
mode = BADBLOCKS;
|
|
z.msg = "invalid bit length repeat";
|
|
r = Z_DATA_ERROR;
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
|
|
c = c == 16 ? blens[i - 1] : 0;
|
|
do {
|
|
blens[i++] = c;
|
|
} while (--j !== 0);
|
|
index = i;
|
|
}
|
|
}
|
|
|
|
tb[0] = -1;
|
|
// {
|
|
var bl_ = []; // new Array(1);
|
|
var bd_ = []; // new Array(1);
|
|
var tl_ = []; // new Array(1);
|
|
var td_ = []; // new Array(1);
|
|
bl_[0] = 9; // must be <= 9 for lookahead assumptions
|
|
bd_[0] = 6; // must be <= 9 for lookahead assumptions
|
|
|
|
t = table;
|
|
t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl_, bd_, tl_, td_, hufts, z);
|
|
|
|
if (t != Z_OK) {
|
|
if (t == Z_DATA_ERROR) {
|
|
blens = null;
|
|
mode = BADBLOCKS;
|
|
}
|
|
r = t;
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
codes.init(bl_[0], bd_[0], hufts, tl_[0], hufts, td_[0]);
|
|
// }
|
|
mode = CODES;
|
|
case CODES:
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
|
|
if ((r = codes.proc(that, z, r)) != Z_STREAM_END) {
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
r = Z_OK;
|
|
codes.free(z);
|
|
|
|
p = z.next_in_index;
|
|
n = z.avail_in;
|
|
b = that.bitb;
|
|
k = that.bitk;
|
|
q = that.write;
|
|
m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
|
|
|
|
if (last === 0) {
|
|
mode = TYPE;
|
|
break;
|
|
}
|
|
mode = DRY;
|
|
case DRY:
|
|
that.write = q;
|
|
r = that.inflate_flush(z, r);
|
|
q = that.write;
|
|
m = /* (int) */(q < that.read ? that.read - q - 1 : that.end - q);
|
|
if (that.read != that.write) {
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
mode = DONELOCKS;
|
|
case DONELOCKS:
|
|
r = Z_STREAM_END;
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
case BADBLOCKS:
|
|
r = Z_DATA_ERROR;
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
|
|
default:
|
|
r = Z_STREAM_ERROR;
|
|
|
|
that.bitb = b;
|
|
that.bitk = k;
|
|
z.avail_in = n;
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
that.write = q;
|
|
return that.inflate_flush(z, r);
|
|
}
|
|
}
|
|
};
|
|
|
|
that.free = function(z) {
|
|
that.reset(z, null);
|
|
that.window = null;
|
|
hufts = null;
|
|
// ZFREE(z, s);
|
|
};
|
|
|
|
that.set_dictionary = function(d, start, n) {
|
|
that.window.set(d.subarray(start, start + n), 0);
|
|
that.read = that.write = n;
|
|
};
|
|
|
|
// Returns true if inflate is currently at the end of a block generated
|
|
// by Z_SYNC_FLUSH or Z_FULL_FLUSH.
|
|
that.sync_point = function() {
|
|
return mode == LENS ? 1 : 0;
|
|
};
|
|
|
|
}
|
|
|
|
// Inflate
|
|
|
|
// preset dictionary flag in zlib header
|
|
var PRESET_DICT = 0x20;
|
|
|
|
var Z_DEFLATED = 8;
|
|
|
|
var METHOD = 0; // waiting for method byte
|
|
var FLAG = 1; // waiting for flag byte
|
|
var DICT4 = 2; // four dictionary check bytes to go
|
|
var DICT3 = 3; // three dictionary check bytes to go
|
|
var DICT2 = 4; // two dictionary check bytes to go
|
|
var DICT1 = 5; // one dictionary check byte to go
|
|
var DICT0 = 6; // waiting for inflateSetDictionary
|
|
var BLOCKS = 7; // decompressing blocks
|
|
var DONE = 12; // finished check, done
|
|
var BAD = 13; // got an error--stay here
|
|
|
|
var mark = [ 0, 0, 0xff, 0xff ];
|
|
|
|
function Inflate() {
|
|
var that = this;
|
|
|
|
that.mode = 0; // current inflate mode
|
|
|
|
// mode dependent information
|
|
that.method = 0; // if FLAGS, method byte
|
|
|
|
// if CHECK, check values to compare
|
|
that.was = [ 0 ]; // new Array(1); // computed check value
|
|
that.need = 0; // stream check value
|
|
|
|
// if BAD, inflateSync's marker bytes count
|
|
that.marker = 0;
|
|
|
|
// mode independent information
|
|
that.wbits = 0; // log2(window size) (8..15, defaults to 15)
|
|
|
|
// this.blocks; // current inflate_blocks state
|
|
|
|
function inflateReset(z) {
|
|
if (!z || !z.istate)
|
|
return Z_STREAM_ERROR;
|
|
|
|
z.total_in = z.total_out = 0;
|
|
z.msg = null;
|
|
z.istate.mode = BLOCKS;
|
|
z.istate.blocks.reset(z, null);
|
|
return Z_OK;
|
|
}
|
|
|
|
that.inflateEnd = function(z) {
|
|
if (that.blocks)
|
|
that.blocks.free(z);
|
|
that.blocks = null;
|
|
// ZFREE(z, z->state);
|
|
return Z_OK;
|
|
};
|
|
|
|
that.inflateInit = function(z, w) {
|
|
z.msg = null;
|
|
that.blocks = null;
|
|
|
|
// set window size
|
|
if (w < 8 || w > 15) {
|
|
that.inflateEnd(z);
|
|
return Z_STREAM_ERROR;
|
|
}
|
|
that.wbits = w;
|
|
|
|
z.istate.blocks = new InfBlocks(z, 1 << w);
|
|
|
|
// reset state
|
|
inflateReset(z);
|
|
return Z_OK;
|
|
};
|
|
|
|
that.inflate = function(z, f) {
|
|
var r;
|
|
var b;
|
|
|
|
if (!z || !z.istate || !z.next_in)
|
|
return Z_STREAM_ERROR;
|
|
f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
|
|
r = Z_BUF_ERROR;
|
|
while (true) {
|
|
// System.out.println("mode: "+z.istate.mode);
|
|
switch (z.istate.mode) {
|
|
case METHOD:
|
|
|
|
if (z.avail_in === 0)
|
|
return r;
|
|
r = f;
|
|
|
|
z.avail_in--;
|
|
z.total_in++;
|
|
if (((z.istate.method = z.read_byte(z.next_in_index++)) & 0xf) != Z_DEFLATED) {
|
|
z.istate.mode = BAD;
|
|
z.msg = "unknown compression method";
|
|
z.istate.marker = 5; // can't try inflateSync
|
|
break;
|
|
}
|
|
if ((z.istate.method >> 4) + 8 > z.istate.wbits) {
|
|
z.istate.mode = BAD;
|
|
z.msg = "invalid window size";
|
|
z.istate.marker = 5; // can't try inflateSync
|
|
break;
|
|
}
|
|
z.istate.mode = FLAG;
|
|
case FLAG:
|
|
|
|
if (z.avail_in === 0)
|
|
return r;
|
|
r = f;
|
|
|
|
z.avail_in--;
|
|
z.total_in++;
|
|
b = (z.read_byte(z.next_in_index++)) & 0xff;
|
|
|
|
if ((((z.istate.method << 8) + b) % 31) !== 0) {
|
|
z.istate.mode = BAD;
|
|
z.msg = "incorrect header check";
|
|
z.istate.marker = 5; // can't try inflateSync
|
|
break;
|
|
}
|
|
|
|
if ((b & PRESET_DICT) === 0) {
|
|
z.istate.mode = BLOCKS;
|
|
break;
|
|
}
|
|
z.istate.mode = DICT4;
|
|
case DICT4:
|
|
|
|
if (z.avail_in === 0)
|
|
return r;
|
|
r = f;
|
|
|
|
z.avail_in--;
|
|
z.total_in++;
|
|
z.istate.need = ((z.read_byte(z.next_in_index++) & 0xff) << 24) & 0xff000000;
|
|
z.istate.mode = DICT3;
|
|
case DICT3:
|
|
|
|
if (z.avail_in === 0)
|
|
return r;
|
|
r = f;
|
|
|
|
z.avail_in--;
|
|
z.total_in++;
|
|
z.istate.need += ((z.read_byte(z.next_in_index++) & 0xff) << 16) & 0xff0000;
|
|
z.istate.mode = DICT2;
|
|
case DICT2:
|
|
|
|
if (z.avail_in === 0)
|
|
return r;
|
|
r = f;
|
|
|
|
z.avail_in--;
|
|
z.total_in++;
|
|
z.istate.need += ((z.read_byte(z.next_in_index++) & 0xff) << 8) & 0xff00;
|
|
z.istate.mode = DICT1;
|
|
case DICT1:
|
|
|
|
if (z.avail_in === 0)
|
|
return r;
|
|
r = f;
|
|
|
|
z.avail_in--;
|
|
z.total_in++;
|
|
z.istate.need += (z.read_byte(z.next_in_index++) & 0xff);
|
|
z.istate.mode = DICT0;
|
|
return Z_NEED_DICT;
|
|
case DICT0:
|
|
z.istate.mode = BAD;
|
|
z.msg = "need dictionary";
|
|
z.istate.marker = 0; // can try inflateSync
|
|
return Z_STREAM_ERROR;
|
|
case BLOCKS:
|
|
|
|
r = z.istate.blocks.proc(z, r);
|
|
if (r == Z_DATA_ERROR) {
|
|
z.istate.mode = BAD;
|
|
z.istate.marker = 0; // can try inflateSync
|
|
break;
|
|
}
|
|
if (r == Z_OK) {
|
|
r = f;
|
|
}
|
|
if (r != Z_STREAM_END) {
|
|
return r;
|
|
}
|
|
r = f;
|
|
z.istate.blocks.reset(z, z.istate.was);
|
|
z.istate.mode = DONE;
|
|
case DONE:
|
|
return Z_STREAM_END;
|
|
case BAD:
|
|
return Z_DATA_ERROR;
|
|
default:
|
|
return Z_STREAM_ERROR;
|
|
}
|
|
}
|
|
};
|
|
|
|
that.inflateSetDictionary = function(z, dictionary, dictLength) {
|
|
var index = 0;
|
|
var length = dictLength;
|
|
if (!z || !z.istate || z.istate.mode != DICT0)
|
|
return Z_STREAM_ERROR;
|
|
|
|
if (length >= (1 << z.istate.wbits)) {
|
|
length = (1 << z.istate.wbits) - 1;
|
|
index = dictLength - length;
|
|
}
|
|
z.istate.blocks.set_dictionary(dictionary, index, length);
|
|
z.istate.mode = BLOCKS;
|
|
return Z_OK;
|
|
};
|
|
|
|
that.inflateSync = function(z) {
|
|
var n; // number of bytes to look at
|
|
var p; // pointer to bytes
|
|
var m; // number of marker bytes found in a row
|
|
var r, w; // temporaries to save total_in and total_out
|
|
|
|
// set up
|
|
if (!z || !z.istate)
|
|
return Z_STREAM_ERROR;
|
|
if (z.istate.mode != BAD) {
|
|
z.istate.mode = BAD;
|
|
z.istate.marker = 0;
|
|
}
|
|
if ((n = z.avail_in) === 0)
|
|
return Z_BUF_ERROR;
|
|
p = z.next_in_index;
|
|
m = z.istate.marker;
|
|
|
|
// search
|
|
while (n !== 0 && m < 4) {
|
|
if (z.read_byte(p) == mark[m]) {
|
|
m++;
|
|
} else if (z.read_byte(p) !== 0) {
|
|
m = 0;
|
|
} else {
|
|
m = 4 - m;
|
|
}
|
|
p++;
|
|
n--;
|
|
}
|
|
|
|
// restore
|
|
z.total_in += p - z.next_in_index;
|
|
z.next_in_index = p;
|
|
z.avail_in = n;
|
|
z.istate.marker = m;
|
|
|
|
// return no joy or set up to restart on a new block
|
|
if (m != 4) {
|
|
return Z_DATA_ERROR;
|
|
}
|
|
r = z.total_in;
|
|
w = z.total_out;
|
|
inflateReset(z);
|
|
z.total_in = r;
|
|
z.total_out = w;
|
|
z.istate.mode = BLOCKS;
|
|
return Z_OK;
|
|
};
|
|
|
|
// Returns true if inflate is currently at the end of a block generated
|
|
// by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
|
|
// implementation to provide an additional safety check. PPP uses
|
|
// Z_SYNC_FLUSH
|
|
// but removes the length bytes of the resulting empty stored block. When
|
|
// decompressing, PPP checks that at the end of input packet, inflate is
|
|
// waiting for these length bytes.
|
|
that.inflateSyncPoint = function(z) {
|
|
if (!z || !z.istate || !z.istate.blocks)
|
|
return Z_STREAM_ERROR;
|
|
return z.istate.blocks.sync_point();
|
|
};
|
|
}
|
|
|
|
// ZStream
|
|
|
|
function ZStream() {
|
|
}
|
|
|
|
ZStream.prototype = {
|
|
inflateInit : function(bits) {
|
|
var that = this;
|
|
that.istate = new Inflate();
|
|
if (!bits)
|
|
bits = MAX_BITS;
|
|
return that.istate.inflateInit(that, bits);
|
|
},
|
|
|
|
inflate : function(f) {
|
|
var that = this;
|
|
if (!that.istate)
|
|
return Z_STREAM_ERROR;
|
|
return that.istate.inflate(that, f);
|
|
},
|
|
|
|
inflateEnd : function() {
|
|
var that = this;
|
|
if (!that.istate)
|
|
return Z_STREAM_ERROR;
|
|
var ret = that.istate.inflateEnd(that);
|
|
that.istate = null;
|
|
return ret;
|
|
},
|
|
|
|
inflateSync : function() {
|
|
var that = this;
|
|
if (!that.istate)
|
|
return Z_STREAM_ERROR;
|
|
return that.istate.inflateSync(that);
|
|
},
|
|
inflateSetDictionary : function(dictionary, dictLength) {
|
|
var that = this;
|
|
if (!that.istate)
|
|
return Z_STREAM_ERROR;
|
|
return that.istate.inflateSetDictionary(that, dictionary, dictLength);
|
|
},
|
|
read_byte : function(start) {
|
|
var that = this;
|
|
return that.next_in.subarray(start, start + 1)[0];
|
|
},
|
|
read_buf : function(start, size) {
|
|
var that = this;
|
|
return that.next_in.subarray(start, start + size);
|
|
}
|
|
};
|
|
|
|
// Inflater
|
|
|
|
function Inflater() {
|
|
var that = this;
|
|
var z = new ZStream();
|
|
var bufsize = 512;
|
|
var flush = Z_NO_FLUSH;
|
|
var buf = new Uint8Array(bufsize);
|
|
var nomoreinput = false;
|
|
|
|
z.inflateInit();
|
|
z.next_out = buf;
|
|
|
|
that.append = function(data, onprogress) {
|
|
var err, buffers = [], lastIndex = 0, bufferIndex = 0, bufferSize = 0, array;
|
|
if (data.length === 0)
|
|
return;
|
|
z.next_in_index = 0;
|
|
z.next_in = data;
|
|
z.avail_in = data.length;
|
|
do {
|
|
z.next_out_index = 0;
|
|
z.avail_out = bufsize;
|
|
if ((z.avail_in === 0) && (!nomoreinput)) { // if buffer is empty and more input is available, refill it
|
|
z.next_in_index = 0;
|
|
nomoreinput = true;
|
|
}
|
|
err = z.inflate(flush);
|
|
if (nomoreinput && (err == Z_BUF_ERROR))
|
|
return -1;
|
|
if (err != Z_OK && err != Z_STREAM_END)
|
|
throw "inflating: " + z.msg;
|
|
if ((nomoreinput || err == Z_STREAM_END) && (z.avail_in == data.length))
|
|
return -1;
|
|
if (z.next_out_index)
|
|
if (z.next_out_index == bufsize)
|
|
buffers.push(new Uint8Array(buf));
|
|
else
|
|
buffers.push(new Uint8Array(buf.subarray(0, z.next_out_index)));
|
|
bufferSize += z.next_out_index;
|
|
if (onprogress && z.next_in_index > 0 && z.next_in_index != lastIndex) {
|
|
onprogress(z.next_in_index);
|
|
lastIndex = z.next_in_index;
|
|
}
|
|
} while (z.avail_in > 0 || z.avail_out === 0);
|
|
array = new Uint8Array(bufferSize);
|
|
buffers.forEach(function(chunk) {
|
|
array.set(chunk, bufferIndex);
|
|
bufferIndex += chunk.length;
|
|
});
|
|
return array;
|
|
};
|
|
that.flush = function() {
|
|
z.inflateEnd();
|
|
};
|
|
}
|
|
|
|
var inflater;
|
|
|
|
if (obj.zip)
|
|
obj.zip.Inflater = Inflater;
|
|
else {
|
|
inflater = new Inflater();
|
|
obj.addEventListener("message", function(event) {
|
|
var message = event.data;
|
|
|
|
if (message.append)
|
|
obj.postMessage({
|
|
onappend : true,
|
|
data : inflater.append(message.data, function(current) {
|
|
obj.postMessage({
|
|
progress : true,
|
|
current : current
|
|
});
|
|
})
|
|
});
|
|
if (message.flush) {
|
|
inflater.flush();
|
|
obj.postMessage({
|
|
onflush : true
|
|
});
|
|
}
|
|
}, false);
|
|
}
|
|
|
|
};
|
|
|
|
// @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;
|
|
|
|
//https://github.com/abdmob/x2js/blob/master/xml2json.js
|
|
/*
|
|
Copyright 2011-2013 Abdulla Abdurakhmanov
|
|
Original sources are available at https://code.google.com/p/x2js/
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
O.esri.TPK.X2JS = function(config){
|
|
'use strict';
|
|
|
|
var VERSION = "1.1.5";
|
|
|
|
var config = config || {};
|
|
initConfigDefaults();
|
|
initRequiredPolyfills();
|
|
|
|
function initConfigDefaults() {
|
|
if (config.escapeMode === undefined) {
|
|
config.escapeMode = true;
|
|
}
|
|
config.attributePrefix = config.attributePrefix || "_";
|
|
config.arrayAccessForm = config.arrayAccessForm || "none";
|
|
config.emptyNodeForm = config.emptyNodeForm || "text";
|
|
if (config.enableToStringFunc === undefined) {
|
|
config.enableToStringFunc = true;
|
|
}
|
|
config.arrayAccessFormPaths = config.arrayAccessFormPaths || [];
|
|
if (config.skipEmptyTextNodesForObj === undefined) {
|
|
config.skipEmptyTextNodesForObj = true;
|
|
}
|
|
if (config.stripWhitespaces === undefined) {
|
|
config.stripWhitespaces = true;
|
|
}
|
|
config.datetimeAccessFormPaths = config.datetimeAccessFormPaths || [];
|
|
}
|
|
|
|
var DOMNodeTypes = {
|
|
ELEMENT_NODE: 1,
|
|
TEXT_NODE: 3,
|
|
CDATA_SECTION_NODE: 4,
|
|
COMMENT_NODE: 8,
|
|
DOCUMENT_NODE: 9
|
|
};
|
|
|
|
function initRequiredPolyfills() {
|
|
function pad(number) {
|
|
var r = String(number);
|
|
if (r.length === 1) {
|
|
r = '0' + r;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// Hello IE8-
|
|
if (typeof String.prototype.trim !== 'function') {
|
|
String.prototype.trim = function () {
|
|
return this.replace(/^\s+|^\n+|(\s|\n)+$/g, '');
|
|
}
|
|
}
|
|
if (typeof Date.prototype.toISOString !== 'function') {
|
|
// Implementation from http://stackoverflow.com/questions/2573521/how-do-i-output-an-iso-8601-formatted-string-in-javascript
|
|
Date.prototype.toISOString = function () {
|
|
return this.getUTCFullYear()
|
|
+ '-' + pad(this.getUTCMonth() + 1)
|
|
+ '-' + pad(this.getUTCDate())
|
|
+ 'T' + pad(this.getUTCHours())
|
|
+ ':' + pad(this.getUTCMinutes())
|
|
+ ':' + pad(this.getUTCSeconds())
|
|
+ '.' + String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5)
|
|
+ 'Z';
|
|
};
|
|
}
|
|
}
|
|
|
|
function getNodeLocalName(node) {
|
|
var nodeLocalName = node.localName;
|
|
if (nodeLocalName == null) // Yeah, this is IE!!
|
|
nodeLocalName = node.baseName;
|
|
if (nodeLocalName == null || nodeLocalName == "") // =="" is IE too
|
|
nodeLocalName = node.nodeName;
|
|
return nodeLocalName;
|
|
}
|
|
|
|
function getNodePrefix(node) {
|
|
return node.prefix;
|
|
}
|
|
|
|
function escapeXmlChars(str) {
|
|
if (typeof(str) == "string")
|
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
|
|
else
|
|
return str;
|
|
}
|
|
|
|
function unescapeXmlChars(str) {
|
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(///g, '\/');
|
|
}
|
|
|
|
function toArrayAccessForm(obj, childName, path) {
|
|
switch (config.arrayAccessForm) {
|
|
case "property":
|
|
if (!(obj[childName] instanceof Array))
|
|
obj[childName + "_asArray"] = [obj[childName]];
|
|
else
|
|
obj[childName + "_asArray"] = obj[childName];
|
|
break;
|
|
/*case "none":
|
|
break;*/
|
|
}
|
|
|
|
if (!(obj[childName] instanceof Array) && config.arrayAccessFormPaths.length > 0) {
|
|
var idx = 0;
|
|
for (; idx < config.arrayAccessFormPaths.length; idx++) {
|
|
var arrayPath = config.arrayAccessFormPaths[idx];
|
|
if (typeof arrayPath === "string") {
|
|
if (arrayPath == path)
|
|
break;
|
|
}
|
|
else if (arrayPath instanceof RegExp) {
|
|
if (arrayPath.test(path))
|
|
break;
|
|
}
|
|
else if (typeof arrayPath === "function") {
|
|
if (arrayPath(obj, childName, path))
|
|
break;
|
|
}
|
|
}
|
|
if (idx != config.arrayAccessFormPaths.length) {
|
|
obj[childName] = [obj[childName]];
|
|
}
|
|
}
|
|
}
|
|
|
|
function fromXmlDateTime(prop) {
|
|
// Implementation based up on http://stackoverflow.com/questions/8178598/xml-datetime-to-javascript-date-object
|
|
// Improved to support full spec and optional parts
|
|
var bits = prop.split(/[-T:+Z]/g);
|
|
|
|
var d = new Date(bits[0], bits[1] - 1, bits[2]);
|
|
var secondBits = bits[5].split("\.");
|
|
d.setHours(bits[3], bits[4], secondBits[0]);
|
|
if (secondBits.length > 1)
|
|
d.setMilliseconds(secondBits[1]);
|
|
|
|
// Get supplied time zone offset in minutes
|
|
if (bits[6] && bits[7]) {
|
|
var offsetMinutes = bits[6] * 60 + Number(bits[7]);
|
|
var sign = /\d\d-\d\d:\d\d$/.test(prop) ? '-' : '+';
|
|
|
|
// Apply the sign
|
|
offsetMinutes = 0 + (sign == '-' ? -1 * offsetMinutes : offsetMinutes);
|
|
|
|
// Apply offset and local timezone
|
|
d.setMinutes(d.getMinutes() - offsetMinutes - d.getTimezoneOffset())
|
|
}
|
|
else if (prop.indexOf("Z", prop.length - 1) !== -1) {
|
|
d = new Date(Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds(), d.getMilliseconds()));
|
|
}
|
|
|
|
// d is now a local time equivalent to the supplied time
|
|
return d;
|
|
}
|
|
|
|
function checkFromXmlDateTimePaths(value, childName, fullPath) {
|
|
if (config.datetimeAccessFormPaths.length > 0) {
|
|
var path = fullPath.split("\.#")[0];
|
|
var idx = 0;
|
|
for (; idx < config.datetimeAccessFormPaths.length; idx++) {
|
|
var dtPath = config.datetimeAccessFormPaths[idx];
|
|
if (typeof dtPath === "string") {
|
|
if (dtPath == path)
|
|
break;
|
|
}
|
|
else if (dtPath instanceof RegExp) {
|
|
if (dtPath.test(path))
|
|
break;
|
|
}
|
|
else if (typeof dtPath === "function") {
|
|
if (dtPath(obj, childName, path))
|
|
break;
|
|
}
|
|
}
|
|
if (idx != config.datetimeAccessFormPaths.length) {
|
|
return fromXmlDateTime(value);
|
|
}
|
|
else
|
|
return value;
|
|
}
|
|
else
|
|
return value;
|
|
}
|
|
|
|
function parseDOMChildren(node, path) {
|
|
if (node.nodeType == DOMNodeTypes.DOCUMENT_NODE) {
|
|
var result = new Object;
|
|
var nodeChildren = node.childNodes;
|
|
// Alternative for firstElementChild which is not supported in some environments
|
|
for (var cidx = 0; cidx < nodeChildren.length; cidx++) {
|
|
var child = nodeChildren.item(cidx);
|
|
if (child.nodeType == DOMNodeTypes.ELEMENT_NODE) {
|
|
var childName = getNodeLocalName(child);
|
|
result[childName] = parseDOMChildren(child, childName);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
else if (node.nodeType == DOMNodeTypes.ELEMENT_NODE) {
|
|
var result = new Object;
|
|
result.__cnt = 0;
|
|
|
|
var nodeChildren = node.childNodes;
|
|
|
|
// Children nodes
|
|
for (var cidx = 0; cidx < nodeChildren.length; cidx++) {
|
|
var child = nodeChildren.item(cidx); // nodeChildren[cidx];
|
|
var childName = getNodeLocalName(child);
|
|
|
|
if (child.nodeType != DOMNodeTypes.COMMENT_NODE) {
|
|
result.__cnt++;
|
|
if (result[childName] == null) {
|
|
result[childName] = parseDOMChildren(child, path + "." + childName);
|
|
toArrayAccessForm(result, childName, path + "." + childName);
|
|
}
|
|
else {
|
|
if (result[childName] != null) {
|
|
if (!(result[childName] instanceof Array)) {
|
|
result[childName] = [result[childName]];
|
|
toArrayAccessForm(result, childName, path + "." + childName);
|
|
}
|
|
}
|
|
(result[childName])[result[childName].length] = parseDOMChildren(child, path + "." + childName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Attributes
|
|
for (var aidx = 0; aidx < node.attributes.length; aidx++) {
|
|
var attr = node.attributes.item(aidx); // [aidx];
|
|
result.__cnt++;
|
|
result[config.attributePrefix + attr.name] = attr.value;
|
|
}
|
|
|
|
// Node namespace prefix
|
|
var nodePrefix = getNodePrefix(node);
|
|
if (nodePrefix != null && nodePrefix != "") {
|
|
result.__cnt++;
|
|
result.__prefix = nodePrefix;
|
|
}
|
|
|
|
if (result["#text"] != null) {
|
|
result.__text = result["#text"];
|
|
if (result.__text instanceof Array) {
|
|
result.__text = result.__text.join("\n");
|
|
}
|
|
if (config.escapeMode)
|
|
result.__text = unescapeXmlChars(result.__text);
|
|
if (config.stripWhitespaces)
|
|
result.__text = result.__text.trim();
|
|
delete result["#text"];
|
|
if (config.arrayAccessForm == "property")
|
|
delete result["#text_asArray"];
|
|
result.__text = checkFromXmlDateTimePaths(result.__text, childName, path + "." + childName);
|
|
}
|
|
if (result["#cdata-section"] != null) {
|
|
result.__cdata = result["#cdata-section"];
|
|
delete result["#cdata-section"];
|
|
if (config.arrayAccessForm == "property")
|
|
delete result["#cdata-section_asArray"];
|
|
}
|
|
|
|
if (result.__cnt == 1 && result.__text != null) {
|
|
result = result.__text;
|
|
}
|
|
else if (result.__cnt == 0 && config.emptyNodeForm == "text") {
|
|
result = '';
|
|
}
|
|
else if (result.__cnt > 1 && result.__text != null && config.skipEmptyTextNodesForObj) {
|
|
if ((config.stripWhitespaces && result.__text == "") || (result.__text.trim() == "")) {
|
|
delete result.__text;
|
|
}
|
|
}
|
|
delete result.__cnt;
|
|
|
|
if (config.enableToStringFunc && (result.__text != null || result.__cdata != null )) {
|
|
result.toString = function () {
|
|
return (this.__text != null ? this.__text : '') + ( this.__cdata != null ? this.__cdata : '');
|
|
};
|
|
}
|
|
|
|
return result;
|
|
}
|
|
else if (node.nodeType == DOMNodeTypes.TEXT_NODE || node.nodeType == DOMNodeTypes.CDATA_SECTION_NODE) {
|
|
return node.nodeValue;
|
|
}
|
|
}
|
|
|
|
function startTag(jsonObj, element, attrList, closed) {
|
|
var resultStr = "<" + ( (jsonObj != null && jsonObj.__prefix != null) ? (jsonObj.__prefix + ":") : "") + element;
|
|
if (attrList != null) {
|
|
for (var aidx = 0; aidx < attrList.length; aidx++) {
|
|
var attrName = attrList[aidx];
|
|
var attrVal = jsonObj[attrName];
|
|
if (config.escapeMode)
|
|
attrVal = escapeXmlChars(attrVal);
|
|
resultStr += " " + attrName.substr(config.attributePrefix.length) + "='" + attrVal + "'";
|
|
}
|
|
}
|
|
if (!closed)
|
|
resultStr += ">";
|
|
else
|
|
resultStr += "/>";
|
|
return resultStr;
|
|
}
|
|
|
|
function endTag(jsonObj, elementName) {
|
|
return "</" + (jsonObj.__prefix != null ? (jsonObj.__prefix + ":") : "") + elementName + ">";
|
|
}
|
|
|
|
function endsWith(str, suffix) {
|
|
return str.indexOf(suffix, str.length - suffix.length) !== -1;
|
|
}
|
|
|
|
function jsonXmlSpecialElem(jsonObj, jsonObjField) {
|
|
if ((config.arrayAccessForm == "property" && endsWith(jsonObjField.toString(), ("_asArray")))
|
|
|| jsonObjField.toString().indexOf(config.attributePrefix) == 0
|
|
|| jsonObjField.toString().indexOf("__") == 0
|
|
|| (jsonObj[jsonObjField] instanceof Function))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
function jsonXmlElemCount(jsonObj) {
|
|
var elementsCnt = 0;
|
|
if (jsonObj instanceof Object) {
|
|
for (var it in jsonObj) {
|
|
if (jsonXmlSpecialElem(jsonObj, it))
|
|
continue;
|
|
elementsCnt++;
|
|
}
|
|
}
|
|
return elementsCnt;
|
|
}
|
|
|
|
function parseJSONAttributes(jsonObj) {
|
|
var attrList = [];
|
|
if (jsonObj instanceof Object) {
|
|
for (var ait in jsonObj) {
|
|
if (ait.toString().indexOf("__") == -1 && ait.toString().indexOf(config.attributePrefix) == 0) {
|
|
attrList.push(ait);
|
|
}
|
|
}
|
|
}
|
|
return attrList;
|
|
}
|
|
|
|
function parseJSONTextAttrs(jsonTxtObj) {
|
|
var result = "";
|
|
|
|
if (jsonTxtObj.__cdata != null) {
|
|
result += "<![CDATA[" + jsonTxtObj.__cdata + "]]>";
|
|
}
|
|
|
|
if (jsonTxtObj.__text != null) {
|
|
if (config.escapeMode)
|
|
result += escapeXmlChars(jsonTxtObj.__text);
|
|
else
|
|
result += jsonTxtObj.__text;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function parseJSONTextObject(jsonTxtObj) {
|
|
var result = "";
|
|
|
|
if (jsonTxtObj instanceof Object) {
|
|
result += parseJSONTextAttrs(jsonTxtObj);
|
|
}
|
|
else if (jsonTxtObj != null) {
|
|
if (config.escapeMode)
|
|
result += escapeXmlChars(jsonTxtObj);
|
|
else
|
|
result += jsonTxtObj;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function parseJSONArray(jsonArrRoot, jsonArrObj, attrList) {
|
|
var result = "";
|
|
if (jsonArrRoot.length == 0) {
|
|
result += startTag(jsonArrRoot, jsonArrObj, attrList, true);
|
|
}
|
|
else {
|
|
for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) {
|
|
result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false);
|
|
result += parseJSONObject(jsonArrRoot[arIdx]);
|
|
result += endTag(jsonArrRoot[arIdx], jsonArrObj);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function parseJSONObject(jsonObj) {
|
|
var result = "";
|
|
|
|
var elementsCnt = jsonXmlElemCount(jsonObj);
|
|
|
|
if (elementsCnt > 0) {
|
|
for (var it in jsonObj) {
|
|
|
|
if (jsonXmlSpecialElem(jsonObj, it))
|
|
continue;
|
|
|
|
var subObj = jsonObj[it];
|
|
|
|
var attrList = parseJSONAttributes(subObj)
|
|
|
|
if (subObj == null || subObj == undefined) {
|
|
result += startTag(subObj, it, attrList, true);
|
|
}
|
|
else if (subObj instanceof Object) {
|
|
|
|
if (subObj instanceof Array) {
|
|
result += parseJSONArray(subObj, it, attrList);
|
|
}
|
|
else if (subObj instanceof Date) {
|
|
result += startTag(subObj, it, attrList, false);
|
|
result += subObj.toISOString();
|
|
result += endTag(subObj, it);
|
|
}
|
|
else {
|
|
var subObjElementsCnt = jsonXmlElemCount(subObj);
|
|
if (subObjElementsCnt > 0 || subObj.__text != null || subObj.__cdata != null) {
|
|
result += startTag(subObj, it, attrList, false);
|
|
result += parseJSONObject(subObj);
|
|
result += endTag(subObj, it);
|
|
}
|
|
else {
|
|
result += startTag(subObj, it, attrList, true);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
result += startTag(subObj, it, attrList, false);
|
|
result += parseJSONTextObject(subObj);
|
|
result += endTag(subObj, it);
|
|
}
|
|
}
|
|
}
|
|
result += parseJSONTextObject(jsonObj);
|
|
|
|
return result;
|
|
}
|
|
|
|
this.parseXmlString = function (xmlDocStr) {
|
|
var isIEParser = window.ActiveXObject || "ActiveXObject" in window;
|
|
if (xmlDocStr === undefined) {
|
|
return null;
|
|
}
|
|
var xmlDoc;
|
|
if (window.DOMParser) {
|
|
var parser = new window.DOMParser();
|
|
var parsererrorNS = null;
|
|
// IE9+ now is here
|
|
if (!isIEParser) {
|
|
try {
|
|
parsererrorNS = parser.parseFromString("INVALID", "text/xml").childNodes[0].namespaceURI;
|
|
}
|
|
catch (err) {
|
|
parsererrorNS = null;
|
|
}
|
|
}
|
|
try {
|
|
xmlDoc = parser.parseFromString(xmlDocStr, "text/xml");
|
|
if (parsererrorNS != null && xmlDoc.getElementsByTagNameNS(parsererrorNS, "parsererror").length > 0) {
|
|
//throw new Error('Error parsing XML: '+xmlDocStr);
|
|
xmlDoc = null;
|
|
}
|
|
}
|
|
catch (err) {
|
|
xmlDoc = null;
|
|
}
|
|
}
|
|
else {
|
|
// IE :(
|
|
if (xmlDocStr.indexOf("<?") == 0) {
|
|
xmlDocStr = xmlDocStr.substr(xmlDocStr.indexOf("?>") + 2);
|
|
}
|
|
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
|
|
xmlDoc.async = "false";
|
|
xmlDoc.loadXML(xmlDocStr);
|
|
}
|
|
return xmlDoc;
|
|
};
|
|
|
|
this.asArray = function (prop) {
|
|
if (prop instanceof Array)
|
|
return prop;
|
|
else
|
|
return [prop];
|
|
};
|
|
|
|
this.toXmlDateTime = function (dt) {
|
|
if (dt instanceof Date)
|
|
return dt.toISOString();
|
|
else if (typeof(dt) === 'number')
|
|
return new Date(dt).toISOString();
|
|
else
|
|
return null;
|
|
};
|
|
|
|
this.asDateTime = function (prop) {
|
|
if (typeof(prop) == "string") {
|
|
return fromXmlDateTime(prop);
|
|
}
|
|
else
|
|
return prop;
|
|
};
|
|
|
|
this.xml2json = function (xmlDoc) {
|
|
return parseDOMChildren(xmlDoc);
|
|
};
|
|
|
|
this.xml_str2json = function(xmlDocStr) {
|
|
var xmlDoc = this.parseXmlString(xmlDocStr);
|
|
if (xmlDoc != null)
|
|
return this.xml2json(xmlDoc);
|
|
else
|
|
return null;
|
|
};
|
|
|
|
this.json2xml_str = function (jsonObj) {
|
|
return parseJSONObject(jsonObj);
|
|
};
|
|
|
|
this.json2xml = function (jsonObj) {
|
|
var xmlDocStr = this.json2xml_str(jsonObj);
|
|
return this.parseXmlString(xmlDocStr);
|
|
};
|
|
|
|
this.getVersion = function () {
|
|
return VERSION;
|
|
};
|
|
|
|
} |