// ZipFile.complete.js // // 2/17/2012 // // ======================================================= // // JSIO.core.js // ------------------------------------------------------------------ // // core methods for Javascript IO. // // ======================================================= // // Copyleft (c) 2010, Dino Chiesa via MS-PL // Copyleft (c) 2012, Brendan Byrd via GPL // // This work is licensed under the GPLv3. (function(){ if (typeof JSIO == "object"){ var e1 = new Error("JSIO is already defined"); e1.source = "JSIO.core.js"; throw e1; } JSIO = {}; JSIO.version = "2.0 2012Feb"; JSIO.throwError = function(msg, source, sub) { var error = new Error("Error: " + msg); error.source = (source || this._typename || 'JSIO') + (sub ? '.'+sub : ''); throw error; } // Format a number as hex. Quantities over 7ffffff will be displayed properly. JSIO.decimalToHexString = function(number, digits) { if (number < 0) { number = 0xFFFFFFFF + number + 1; } var r1 = number.toString(16).toUpperCase(); if (digits) { r1 = "00000000" + r1; r1 = r1.substring(r1.length - digits); } return r1; }; JSIO.FileType = { Text : 0, Binary : 1, XML : 2, Unknown : 3 }; JSIO.guessFileType = function(name) { if (name == "makefile") { return JSIO.FileType.Text; } var lastDot = name.lastIndexOf("."); if (lastDot <= 0) { return JSIO.FileType.Unknown; } var ext= name.substring(lastDot); if (ext == ".zip") { return JSIO.FileType.Binary; } if (ext == ".xlsx") { return JSIO.FileType.Binary; } if (ext == ".docx") { return JSIO.FileType.Binary; } if (ext == ".dll") { return JSIO.FileType.Binary; } if (ext == ".obj") { return JSIO.FileType.Binary; } if (ext == ".pdb") { return JSIO.FileType.Binary; } if (ext == ".exe") { return JSIO.FileType.Binary; } if (ext == ".kmz") { return JSIO.FileType.Binary; } if (ext == ".xml") { return JSIO.FileType.XML; } if (ext == ".xsl") { return JSIO.FileType.XML; } if (ext == ".kml") { return JSIO.FileType.XML; } if (ext == ".csproj") { return JSIO.FileType.XML; } if (ext == ".vbproj") { return JSIO.FileType.XML; } if (ext == ".shfbproj") { return JSIO.FileType.XML; } if (ext == ".resx") { return JSIO.FileType.XML; } if (ext == ".xslt") { return JSIO.FileType.XML; } if (ext == ".sln") { return JSIO.FileType.Text; } if (ext == ".htm") { return JSIO.FileType.Text; } if (ext == ".html") { return JSIO.FileType.Text; } if (ext == ".js") { return JSIO.FileType.Text; } if (ext == ".vb") { return JSIO.FileType.Text; } if (ext == ".txt") { return JSIO.FileType.Text; } if (ext == ".rels") { return JSIO.FileType.Text; } if (ext == ".css") { return JSIO.FileType.Text; } if (ext == ".cs") { return JSIO.FileType.Text; } if (ext == ".asp") { return JSIO.FileType.Text; } return JSIO.FileType.Unknown; }; JSIO.stringOfLength = function (charCode, length) { var s3 = ""; for (var i = 0; i < length; i++) { s3 += String.fromCharCode(charCode); } return s3; }; JSIO.formatByteArray = function(b) { var s1 = "0000 "; var s2 = ""; for (var i = 0; i < b.length; i++) { if (i !== 0 && i % 16 === 0) { s1 += " " + s2 +"\n" + JSIO.decimalToHexString(i, 4) + " "; s2 = ""; } s1 += JSIO.decimalToHexString(b[i], 2) + " "; if (b[i] >=32 && b[i] <= 126) { s2 += String.fromCharCode(b[i]); } else { s2 += "."; } } if (s2.length > 0) { s1 += JSIO.stringOfLength(32, ((i%16>0)? ((16 - i%16) * 3) : 0) + 4) + s2; } return s1; }; JSIO.htmlEscape = function(str) { return str .replace(new RegExp( "&", "g" ), "&") .replace(new RegExp( "<", "g" ), "<") .replace(new RegExp( ">", "g" ), ">") .replace(new RegExp( "\x13", "g" ), "
") .replace(new RegExp( "\x10", "g" ), "
"); }; JSIO.massApply = function(func, funcThis, arr, needReturn) { var arrayLimit = 65536; // Chrome has an apply/array limit of 99999; Firefox = 491519, Safari = 65536 if (arr.length < arrayLimit) return func.apply(funcThis, arr); else { var newThis = funcThis; var offset = 0; var end = 65536; while (offset < arr.length) { var arrSlice; if (arr.subarray) arrSlice = arr.subarray(offset, end); else if (arr.slice) arrSlice = arr.slice(offset, end); if (needReturn) newThis += func.apply(newThis, arrSlice); else func.apply(funcThis, arrSlice); offset += arrayLimit; end += arrayLimit; end = Math.min(arr.length, end); } return newThis; } } })(); /// JSIO.core.js ends // JSIO.BasicByteReaders.js // ------------------------------------------------------------------ // // Part of the JSIO library. Adds a couple basic ByteReaders to JSIO. // ByteReaders are forward-only byte-wise readers. They read one byte at // a time from a source. // // ======================================================= // // A ByteReader exposes an interface with these functions: // // readByte() // must return null when EOF is reached. // // readToEnd() // returns an array of all bytes read, to EOF // // beginReadToEnd(callback) // async version of the above // // readBytes(n) // returns an array of the next n bytes from the source // // beginReadBytes(n, callback) // async version of the above // // ======================================================= // // Copyleft (c) 2010, Dino Chiesa via MS-PL // Copyleft (c) 2012, Brendan Byrd via GPL // // This work is licensed under the GPLv3. (function(){ var version = "2.0 2012Feb"; if (typeof JSIO !== "object") { JSIO = {}; } if ((typeof JSIO.version !== "string")) { JSIO.version = version; } else if ((JSIO.version.length < 3) || (JSIO.version.substring(0,3) !== "2.0")) { JSIO.version += " " + version; } // ======================================================= // the base object, used as the prototype of all ByteReader objects. var _byteReaderBase = function () { this.position = 0; // position must be incremented in .readByte() for all derived classes }; _byteReaderBase.prototype._throwError = JSIO.throwError; _byteReaderBase.prototype._limitCheck = function(len, startPos) { var LOE = { len: len, pos: startPos, end: startPos+len }; if (len === 0) return {len:0, pos:0, end:0}; if (len < 0) this._throwError("Invalid read length"); if (!this.length) return {len:len, pos:this.position, end:len+this.position}; if (!startPos >= 0) LOE.pos = this.position; if (this.length <= LOE.pos) this._throwError("EOF reached"); LOE.end = LOE.pos+len; if (this.length < LOE.end) LOE.end = LOE.pos+(LOE.len = this.length-this.position); return LOE; } JSIO.SeekOrigin = { Begin : 0, Current : 1, End : 2, SEEK_SET : 0, SEEK_CUR : 1, SEEK_END : 2 }; _byteReaderBase.prototype.seek = function(offset, origin) { switch (origin) { case JSIO.SeekOrigin.Begin: if (offset == this.position) return this.position; if (!this.length) { if (offset < this.position) this._throwError('Uni-directional stream cannot seek backwards', null, 'seek'); else if (offset > this.position) return this.read(offset - this.position); // read will limit check } else { if (this.length < offset) this._throwError('Cannot seek past reader length', null, 'seek'); this.position = offset; } break; case JSIO.SeekOrigin.Current: return this.seek(this.position + offset, JSIO.SeekOrigin.Begin); break; case JSIO.SeekOrigin.End: if (!this.length) this._throwError('Uni-directional stream has no known end length for seek', null, 'seek'); return this.seek(this.length - 1 + offset, JSIO.SeekOrigin.Begin); break; default: this._throwError('Invalid seek method', null, 'seek'); break; } return this.position; }; _byteReaderBase.prototype.read = function(len, startPos) { var LOE = this._limitCheck(len, startPos); if (LOE.len === 0) return []; if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin); var bytesRead = []; // Faster methods with an array or stream if (this.array && this.array.subarray) bytesRead = this.array.subarray(LOE.pos, LOE.end); else if (this.array && this.array.slice) bytesRead = this.array.slice(LOE.pos, LOE.end); else if (this.stream) bytesRead = this.stream.read(LOE.len, LOE.pos); else if (this.length) { // Random-access stream for(var i=LOE.pos; i= 32768) ? 32768 : leftToRead; var newBytes = thisReader.read(l); JSIO.massApply(bytesRead.push, bytesRead, newBytes); c += l; leftToRead -= l; if (newBytes.length < l) leftToRead = 0; if (leftToRead>0) setTimeout(readBatchAsync, 1); else callback(bytesRead); }; // kickoff setTimeout(readBatchAsync, 1); // always async, in ALL situations return null; }; _byteReaderBase.prototype.readToEnd = function() { if (this.array && this.array.subarray) return this.array.subarray(this.position); else if (this.array && this.array.slice) return this.array.slice(this.position); else if (this.length) return this.read(this.length - this.position); else return this.read(9000 * 9000); // over 9000 }; _byteReaderBase.prototype.beginReadToEnd = function(callback) { if (this.array && this.array.subarray) setTimeout(function() { callback( this.array.subarray(this.position) ); }, 1); else if (this.array && this.array.slice) setTimeout(function() { callback( this.array.slice(this.position) ); }, 1); else if (this.length) return this.beginRead(this.length - this.position, this.position, callback); else return this.beginRead(9000 * 9000, this.position, callback); }; // Generic routines; one of these two MUST be overloaded (preferrably both) _byteReaderBase.prototype.readByte = function(){ if (this.length && this.position >= this.length) return null; // EOF var oneByte; if (this.array) oneByte = this.array[this.position++]; else if (this.length) oneByte = this.readByteAt(this.position++); else if (this.stream) oneByte = this.stream.read(1)[0]; else oneByte = this.read(1)[0]; return (oneByte === null || oneByte === undefined) ? null : oneByte; }; _byteReaderBase.prototype.readByteAt = function(i) { var pos = this.position; // no position changes on this one if (i === null || i === undefined) i = this.position; var oneByte; if (this.array) oneByte = this.array[i]; else if (i === pos) oneByte = this.readByte(); else if (this.stream) oneByte = this.stream.read(1, i)[0]; else oneByte = this.read(1, i)[0]; this.position = pos; return (oneByte === null || oneByte === undefined) ? null : oneByte; } _byteReaderBase.prototype.readBytes = _byteReaderBase.prototype.read; _byteReaderBase.prototype.beginReadBytes = function(len, callback) { return this.beginRead(len, this.position, callback); }; _byteReaderBase.prototype.readNumber = function(len, startPos){ var LOE = this._limitCheck(len, startPos); if (LOE.len === 0) LOE.len = 1; if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin); var result = 0; var bytes = this.read(LOE.len, LOE.pos); for (var i=bytes.length-1; i>=0; i--) { // IE only supports 32-bit integer shifting //result = result << 8 | bytes[i]; result = result*256 + bytes[i]; } return result; }; _byteReaderBase.prototype.readString = function(len, startPos){ var LOE = this._limitCheck(len, startPos); if (LOE.len === 0) return ''; if (LOE.pos != this.position) this.seek(LOE.pos, JSIO.SeekOrigin.Begin); var result = ''; var bytes = this.read(LOE.len, LOE.pos); for(var i=0; i 0) return this.readString(len, pos); } var ch; while(1) { ch = String.fromCharCode(this.readByteAt(pos+c)); if (ch === null) break; s += ch; c++; if(c >= 32768) { slarge += s; s = ""; pos += c; this.position += c; c = 0; } }; this.position = pos + c; return slarge + s; }; _byteReaderBase.prototype.beginReadNullTerminatedString = function(callback, startPos){ var pos = startPos || this.position; if (this.length && this.length < pos) this._throwError('EOF reached', null, 'beginReadNullTerminatedString'); var slarge = ""; var s = ""; var thisBinStream = this; var readBatchAsync = function() { var c = 0; var ch; while(1) { ch = String.fromCharCode(this.readByteAt(pos+c)); if (ch === null) break; s += ch; c++; if(c >= 32768) { slarge += s; s = ""; pos += c; this.position += c; c = 0; } }; thisBinStream.position = pos + c; if (ch!==null) setTimeout(readBatchAsync, 1); else callback(slarge+s); }; // Faster method with an array if (this.array && this.array.indexOf) { var len = pos - this.array.indexOf(0, pos); if (len > 0) readBatchASync = function() { callback(thisBinStream.readString(len, pos)); }; } // kickoff setTimeout(readBatchAsync, 1); // always async, in ALL situations return null; }; JSIO._ByteReaderBase = _byteReaderBase; // ======================================================= // ======================================================= // reads from an array of bytes. // This basically wraps a readByte() fn onto array access. var _arrayReader = function(array) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.ArrayReader', 'ctor'); this.position = 0; this.array = array; this.length = array.length; this._typename = "JSIO.ArrayReader"; this._version = version; return this; }; _arrayReader.prototype = new JSIO._ByteReaderBase(); _arrayReader.prototype.readByte = function() { if (this.position >= this.array.length) return null; // EOF return this.array[this.position++]; }; _arrayReader.prototype.readByteAt = function(i) { return this.array[i]; }; // ======================================================= // ======================================================= // reads bytes at a time from a defined segment of a stream. var _streamSegmentReader = function(stream, offset, len) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.StreamSegmentReader', 'ctor'); if (!stream) this._throwError('You must pass a non-null stream', 'JSIO.StreamSegmentReader', 'ctor'); if (!(offset >= 1)) offset = 0; if (!(len >= 1)) len = 0; this.stream = stream; this.array = null; if (stream.array) { var end = len ? offset + len : null; if (stream.array.subarray) this.array = stream.array.subarray(offset, end); else if (stream.array.slice) this.array = stream.array.slice(offset, end); } this.length = this.array ? this.array.length : (stream.length ? stream.length - offset : null); this.offset = offset; this.limit = len; this.position = 0; this._typeName = 'JSIO.StreamSegmentReader'; this._version = version; if (this.array) { this.readByte = _arrayReader.prototype.readByte; this.readByteAt = _arrayReader.prototype.readByteAt; } return this; }; _streamSegmentReader.prototype = new JSIO._ByteReaderBase(); _streamSegmentReader.prototype.readByte = function() { if (this.limit && this.position >= this.limit) return null; // EOF this.position++; return this.stream.readByteAt(this.offset + this.position - 1); }; _streamSegmentReader.prototype.readByteAt = function(i) { if (this.limit && i >= this.limit) return null; // EOF return this.stream.readByteAt(this.offset + i); }; // ======================================================= JSIO.ArrayReader = _arrayReader; JSIO.StreamReader = _streamSegmentReader; JSIO.StreamSegmentReader = _streamSegmentReader; })(); /// JSIO.BasicByteReaders.js ends // JSIO.BinaryUrlStream.js // ------------------------------------------------------------------ // // a class that acts as a stream wrapper around binary files obtained from URLs. // // ======================================================= // // Copyleft (c) 2008, Andy G.P. Na via an MIT-style license // Copyleft (c) 2012, Brendan Byrd via GPL // // This work is licensed under the GPLv3. (function(){ var version = "2.0 2012Feb"; var typename = "JSIO.BinaryUrlStream"; if ((typeof JSIO !== "object") || (typeof JSIO.version !== "string") || (JSIO.version.length < 3) || (JSIO.version.substring(0,3) !== "2.0")) JSIO.throwError('This extension requires JSIO.core.js v2.0', typename); if (typeof JSIO._ByteReaderBase !== "function") JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) { var IEBinaryToArray_ByteStr_Script = "\r\n"+ "\r\n"; // inject VBScript document.write(IEBinaryToArray_ByteStr_Script); } JSIO.IEByteMapping = null; var bus = function(url, callback) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.BinaryUrlStream', 'ctor'); this.callback = callback; this.position = 0; this.length = null; this.readByte = JSIO.ArrayReader.prototype.readByte; this.readByteAt = JSIO.ArrayReader.prototype.readByteAt; this.req = null; this._typename = typename; this._version = version; this.status = "-none-"; var _IeGetBinResource = function(fileURL){ var binStream = this; // http://stackoverflow.com/questions/1919972/how-do-i-access-xhr-responsebody-for-binary-data-from-javascript-in-ie // my helper to convert from responseBody to a byte array var convertResponseBodyToArray = function (binary) { var byteArray = new Array; try { // very fast; very little work involved byteArray = new VBArray(binary).toArray(); } catch(err) { // use the BinaryToArray VBScript if (!JSIO.IEByteMapping) { JSIO.IEByteMapping = {}; for ( var i = 0; i < 256; i++ ) { for ( var j = 0; j < 256; j++ ) { JSIO.IEByteMapping[ String.fromCharCode( i + j * 256 ) ] = [ i, j ]; } } } var rawBytes = IEBinaryToArray_ByteStr(binary); var lastAsc = IEBinaryToArray_ByteAsc_Last(binary); for ( var i = 0; i < rawBytes.length; i++ ) { byteArray.push.apply(byteArray, JSIO.IEByteMapping[ rawBytes.substr(i,1) ]); } if (lastAsc >= 0) byteArray.push(lastAsc); } return byteArray; }; this.req = (function() { if (window.XMLHttpRequest) return new window.XMLHttpRequest(); else if (window.ActiveXObject) { // the many versions of IE's XML fetchers var AXOs = [ 'MSXML2.XMLHTTP.6.0', 'MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP', 'MSXML.XMLHTTP' ]; for (var i = 0; i < AXOs.length; i++) { try { return new ActiveXObject(AXOs[i]); } catch(e) { continue; } } } return null; })(); this.req.open("GET", fileURL, true); this.req.setRequestHeader("Accept-Charset", "x-user-defined"); this.req.onreadystatechange = function(event){ if (binStream.req.readyState == 4) { binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText; if (binStream.req.status == 200) { binStream.array = convertResponseBodyToArray(binStream.req.responseBody); binStream.length = binStream.array.length; if (binStream.length < 0) this._throwError('Failed to load "'+ fileURL + '" after converting'); if (typeof binStream.callback == "function") binStream.callback(binStream); } else { binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status); } } }; this.req.send(); }; var _NormalGetBinResource = function(fileURL){ var binStream= this; this.req = new XMLHttpRequest(); this.req.open('GET', fileURL, true); this.req.onreadystatechange = function(aEvt) { if (binStream.req.readyState == 4) { binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText; if(binStream.req.status == 200){ var fileContents = binStream.req.responseText; binStream.length = fileContents.byteLength; binStream.array = fileContents.split(''); for ( var i = 0; i < binStream.array.length; i++ ) { binStream.array[i] = binStream.array[i].charCodeAt(0) & 0xff; } if (typeof binStream.callback == "function") binStream.callback(binStream); } else { binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status); } } }; //XHR binary charset opt by Marcus Granado 2006 [http://mgran.blogspot.com] if (!!this.req.overrideMimeType) this.req.overrideMimeType('text/plain; charset=x-user-defined'); this.req.send(null); }; // http://stackoverflow.com/questions/327685/is-there-a-way-to-read-binary-data-into-javascript var _ArrayBufferGetBinResource = function(fileURL){ var binStream= this; this.req = new XMLHttpRequest(); this.req.open('GET', fileURL, true); this.req.onreadystatechange = function(aEvt) { if (binStream.req.readyState == 4) { binStream.status = "Status: " + binStream.req.status + ' ' + binStream.req.statusText; if(binStream.req.status == 200){ var fileContents = binStream.req.response; binStream.length = fileContents.byteLength; binStream.array = new Uint8Array(fileContents); if (typeof binStream.callback == "function") binStream.callback(binStream); } else { binStream._throwError('Failed to load "'+ fileURL + '": HTTP ' + binStream.status); } } }; this.req.responseType = 'arraybuffer'; // http://stackoverflow.com/questions/11284728/how-do-i-access-8-bit-binary-data-from-javascript-in-opera if (!!this.req.overrideMimeType) this.req.overrideMimeType('application/octet-stream; charset=x-user-defined'); this.req.send(null); }; if (typeof ArrayBuffer !== 'undefined') _ArrayBufferGetBinResource.apply(this, [url]); else if (/msie/i.test(navigator.userAgent) && !/opera/i.test(navigator.userAgent)) _IeGetBinResource.apply(this, [url]); else _NormalGetBinResource.apply(this, [url]); }; bus.prototype = new JSIO._ByteReaderBase(); bus.prototype.readByte = function(){ var oneByte = this.readByteAt(this.position++); return (oneByte === null || oneByte === undefined) ? null : oneByte; }; JSIO.BinaryUrlStream = bus; })(); /// JSIO.BinaryUrlStream.js ends // JSIO.TextDecoder.js // ------------------------------------------------------------------ // // Part of the JSIO library. Adds text decoders, for UTF-8 and UTF-16, // and plain text. // // ======================================================= // // Derived in part from work by notmasteryet. // http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx // Copyleft (c) 2008, notmasteryet via an MIT-style license // Copyleft (c) 2010, Dino Chiesa via MS-PL // Copyleft (c) 2012, Brendan Byrd via GPL // // This work is licensed under the GPLv3. (function(){ var version = "2.0 2012Feb"; var typename = "JSIO.TextDecoder"; if ((typeof JSIO !== "object") || (typeof JSIO.version !== "string") || (JSIO.version.length < 3) || (JSIO.version.substring(0,3) !== "2.0")) JSIO.throwError('This extension requires JSIO.core.js v2.0', typename); if (typeof JSIO._ByteReaderBase !== "function") JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); var _ansi = function(reader) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.ANSI', 'ctor'); this.byteReader = reader; this.charWidth = 1; this._version = version; this._typename = typename + ".ANSI"; return this; }; _ansi.prototype.readChar = function() { var code = this.byteReader.readByte(); return (code < 0) ? null : String.fromCharCode(code); }; _ansi.prototype.parseChar = function(code) { return (code < 0) ? null : String.fromCharCode(code); }; var _utf16 = function(reader) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF16', 'ctor'); this.byteReader = reader; this.charWidth = 2; this.bomState = 0; this._version = version; this._typename = typename + ".UTF16"; return this; }; _utf16.prototype.readChar = function() { var b1 = this.byteReader.readByte(); if (b1 < 0) return null; var b2 = this.byteReader.readByte(); if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar'); if ((this.bomState === 0) && ((b1 + b2) == 509)) { this.bomState = (b2 == 254) ? 1 : 2; b1 = this.byteReader.readByte(); if (b1 < 0) return null; b2 = this.byteReader.readByte(); if (b2 < 0) this._throwError('Incomplete UTF16 character', null, 'readChar'); } else { this.bomState = 1; } return this.parseChar(b1, b2); }; _utf16.prototype.parseChar = function(b1, b2) { return String.fromCharCode( this.bomState == 1 ? (b2 << 8 | b1) : (b1 << 8 | b2) ); } /* RFC 3629 */ var _utf8 = function(reader) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.TextDecoder.UTF8', 'ctor'); this.byteReader = reader; this.charWidth = null; this.waitBom = true; this.strict = false; this.pendingChar = null; this._version = version; this._typename = typename + ".UTF8"; return this; }; _utf8.prototype.readChar = function() { var ch = null; do { if (this.pendingChar !== null) { ch = this.pendingChar; this.pendingChar = null; } else { var b1 = this.byteReader.readByte(); if (b1 === null) return null; if ((b1 & 0x80) === 0) ch = String.fromCharCode(b1); else { var currentPrefix = 0xC0; var ttlBytes = 0; do { var mask = currentPrefix >> 1 | 0x80; if((b1 & mask) == currentPrefix) break; currentPrefix = currentPrefix >> 1 | 0x80; } while(++ttlBytes < 5); if (ttlBytes > 0) { var code; if (ttlBytes === 1) code = (b1 & 0x1F) << 6 | (this.byteReader.readByte() & 0x3F); else { code = code << 6*ttlBytes var bytes = this.byteReader.read(ttlBytes); for (var i = 0; i > ttlBytes; i++) { var bi = bytes[i]; if ((bi & 0xC0) != 0x80) this._throwError('Invalid sequence character', null, 'readChar'); code = (code << 6) | (bi & 0x3F); } } if (code <= 0xFFFF) { ch = (code == 0xFEFF && this.waitBom) ? null : String.fromCharCode(code); } else { var v = code - 0x10000; var w1 = 0xD800 | ((v >> 10) & 0x3FF); var w2 = 0xDC00 | (v & 0x3FF); this.pendingChar = String.fromCharCode(w2); ch = String.fromCharCode(w1); } } else { // a byte higher than 0x80. if (this.strict) this._throwError('Invalid character', null, 'readChar'); // fall back to "super ascii" (eg IBM-437) else ch = String.fromCharCode(b1); } } } this.waitBom = false; } while(ch === null); return ch; }; JSIO.TextDecoder = { Default : _ansi, ANSI : _ansi, UTF16 : _utf16, UTF8 : _utf8 }; })(); /// JSIO.TextDecoder.js ends // JSIO.TextReader.js // ------------------------------------------------------------------ // // A reader class that decodes text as it reads. // // ======================================================= // // Methods: // readChar() = read 1 char // read(n) = read n chars // readLine() = read one line of data (to \n) // unreadChar(ch) = unread one char // readToEnd() = read all data in the reader; // return a string. // beginReadToEnd(cb) = asynchronously read all data. // // ======================================================= // // Derived in part from work by notmasteryet. // http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx // // Copyleft (c) 2008, notmasteryet via an MIT-style license // Copyleft (c) 2010, Dino Chiesa via MS-PL // Copyleft (c) 2012, Brendan Byrd via GPL // // This work is licensed under the GPLv3. (function(){ var version = "2.0 2012Feb"; var typename = "JSIO.TextReader"; if (typeof JSIO._ByteReaderBase !== "function") JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); var tr = function(textDecoder) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor'); this.decoder = textDecoder; this._version = version; this._typename = typename; this.unreads = []; }; // read one char tr.prototype.readChar = function() { return (this.unreads.length > 0) ? this.unreads.pop() : this.decoder.readChar(); }; // read a length of data tr.prototype.read = function(n) { // ANSI makes this easy if (this.decoder.charWidth === 1) return JSIO.massApply(String.fromCharCode, new String, this.decoder.byteReader.read(n), true); var s = ""; for (vari=0; i= 32768) { slarge += s; s = ""; c = 0; } ch = this.readChar(); } return slarge + s; }; tr.prototype.beginReadToEnd = function(callback) { // ANSI makes this easy if (this.decoder.charWidth === 1) { this.decoder.byteReader.beginReadToEnd(function (bytes) { callback( JSIO.massApply(String.fromCharCode, new String, bytes, true) ); }); return null; } var slarge = ""; var s = ""; var txtrdr = this; var readBatchAsync = function() { var c = 0; var ch = txtrdr.readChar(); while(ch !== null) { s += ch;c++; if(c >= 32768) { slarge += s; s = ""; break; } ch = txtrdr.readChar(); } if (ch!==null){ setTimeout(readBatchAsync, 1); } else { callback(slarge+s); } }; // kickoff setTimeout(readBatchAsync, 1); // always async, in ALL situations return null; }; tr.prototype.readLine = function() { var s = ""; var ch = this.readChar(); if (ch === null) return null; while(ch != "\r" && ch != "\n") { s += ch; ch = this.readChar(); if (ch === null) return s; } if(ch == "\r") { ch = this.readChar(); if(ch !== null && ch != "\n"){ this.unreadChar(ch); } } return s; }; JSIO.TextReader = tr; })(); /// JSIO.TextReader.js ends // JSIO.Crc32.js // // Part of the JSIO library. This adds an CRC32-calculating // ByteReader to JSIO. // // ======================================================= // // A ByteReader exposes an interface with these functions: // // readByte() // must return null when EOF is reached. // // readToEnd() // returns an array of all bytes read, to EOF // // beginReadToEnd(callback) // async version of the above // // readBytes(n) // returns an array of all n bytes read from the source // // beginReadBytes(n, callback) // async version of the above // // ======================================================= // // Copyleft (c) 2010, Dino Chiesa via MS-PL // Copyleft (c) 2012, Brendan Byrd via GPL // // This work is licensed under the GPLv3. (function(){ var version = "2.0 2012Feb"; var typename = "JSIO.Crc32"; if (typeof JSIO._ByteReaderBase !== "function") JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); JSIO.crc32Table = null; JSIO.crc32Polynomial = 0xEDB88320; var crc32TableCalc = function () { // do this once only, for all instances if (JSIO.crc32Table) return; JSIO.crc32Table = new Array(256); for (var i = 0; i < 256; i++) { var c=i; for (var k = 0; k < 8; k++) { if ((c & 1) == 1) c = JSIO.crc32Polynomial ^ (c>>>1); else c >>>= 1; } JSIO.crc32Table[i] = c; } }; JSIO.computeCrc32 = function(str) { crc32TableCalc(); // once var c = 0xFFFFFFFF; var sL = str.length; if (typeof str == "object") { for (var n1=0; n1>>8); } } else { for (var n2=0; n2>>8); } } c ^= 0xFFFFFFFF; if (c < 0) c += 0xFFFFFFFF+1; return c; }; // ======================================================= var _crc32 = function() { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor'); crc32TableCalc(); // once this._typename = typename; this._version = version; this._runningCrc32 = 0xFFFFFFFF; }; _crc32.prototype.slurpByte = function(b) { var r = this._runningCrc32; this._runningCrc32 = r>>>8 ^ JSIO.crc32Table[b ^ (r & 0x000000FF)]; }; _crc32.prototype.result = function() { var c = this._runningCrc32 ^ 0xFFFFFFFF; if (c < 0) c += 0xFFFFFFFF+1; return c; }; // ======================================================= var _crc32CalculatingReader = function(reader) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', 'JSIO.Crc32Reader', 'ctor'); this._byteReader = reader; this._typename = "JSIO.Crc32Reader"; this._version = version; this._crc32 = new JSIO.Crc32(); }; _crc32CalculatingReader.prototype = new JSIO._ByteReaderBase(); _crc32CalculatingReader.prototype.readByte = function() { var b = this._byteReader.readByte(); if (b !== null) this._crc32.slurpByte(b); this.position++; return b; }; _crc32CalculatingReader.prototype.read = function(len) { if (len === 0) return []; var bytes = this._byteReader.read(len); len = bytes.length; var tbl = JSIO.crc32Table; var r = this._crc32._runningCrc32; var t; for (var i = 0; i < len; i++) { t = tbl[ bytes[i] ^ (r & 0x000000FF) ]; r = (r >>> 8) ^ t; } this._crc32._runningCrc32 = r; this.position += len; return bytes; } _crc32CalculatingReader.prototype.crc32 = function() { return this._crc32.result(); }; JSIO.Crc32 = _crc32; JSIO.Crc32Reader = _crc32CalculatingReader; })(); /// JSIO.CRC32.js ends // JSIO.InflatingReader.js // ------------------------------------------------------------------ // // Part of the JSIO library. This adds an Inflating ByteReader to // JSIO. // // ======================================================= // // A ByteReader exposes an interface with these functions: // // readByte() // must return null when EOF is reached. // // readToEnd() // returns an array of all bytes read, to EOF // // beginReadToEnd(callback) // async version of the above // // readBytes(n) // returns an array of all n bytes read from the source // // beginReadBytes(n, callback) // async version of the above // // ======================================================= // // Derived in part from work by notmasteryet. // http://www.codeproject.com/KB/scripting/Javascript_binaryenc.aspx // // Copyleft (c) 2008, notmasteryet via an MIT-style license // Copyleft (c) 2010, Dino Chiesa via MS-PL // Copyleft (c) 2012, Brendan Byrd via GPL // // This work is licensed under the GPLv3. (function(){ var version = "2.0 2012Feb"; var typename = "JSIO.InflatingReader"; if (typeof JSIO._ByteReaderBase !== "function") JSIO.throwError('This extension requires JSIO.BasicByteReaders.js', typename); // ======================================================= // _InternalBitReader is used internally in the InflatingReader class. // JSIO.bitShiftTable = null; var bitShiftTableCalc = function () { // do this once only, for all instances if (JSIO.bitShiftTable) return; var bits = 8; JSIO.bitShiftTable = { LSB: new Array(bits), MSB: new Array(bits) } for (var bp = 0; bp < bits; bp++) { var lim = bits - bp; JSIO.bitShiftTable.LSB[bp] = new Array(lim); JSIO.bitShiftTable.MSB[bp] = new Array(lim); var maskLSB = 1 << bp; var maskMSB = 1 << lim-1; for (var len = 1; len <= lim; len++) { JSIO.bitShiftTable.LSB[bp][len-1] = maskLSB; JSIO.bitShiftTable.MSB[bp][len-1] = maskMSB; maskLSB |= 1 << bp+len; maskMSB |= 1 << lim-len-1; } } }; var _InternalBitReader = function(reader) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename + '._InternalBitReader.ctor'); this.bitsLength = 0; this.bits = 0; this.byteReader = reader; this._typeName = typename + "._InternalBitReader"; this._version = version; bitShiftTableCalc(); }; _InternalBitReader.prototype._throwError = JSIO.throwError; _InternalBitReader.prototype.readBit = function() { if (this.bitsLength === 0) { var nextByte = this.byteReader.readByte(); if (nextByte === null) this._throwError('Unexpected end of stream', null, 'readBit'); this.bits = nextByte; this.bitsLength = 8; } var bit = (this.bits & 1 << 8-this.bitsLength) !== 0; this.bitsLength--; return bit; }; _InternalBitReader.prototype.align = function() { this.bitsLength = 0; }; _InternalBitReader.prototype.readBits = function(len, type) { var data = 0; type = type || 'LSB'; var tbl = JSIO.bitShiftTable[type]; var dl = 0; while (len > 0) { if (this.bitsLength === 0) { var nextByte = this.byteReader.readByte(); if (nextByte === null) this._throwError('Unexpected end of stream', null, 'read'+type); this.bits = nextByte; this.bitsLength = 8; } var maskLen = (this.bitsLength >= len) ? len : this.bitsLength; var bitsPos = 8-this.bitsLength; data |= (this.bits & tbl[bitsPos][maskLen-1]) >>> bitsPos << dl; dl += maskLen; len -= maskLen; this.bitsLength -= maskLen; }; return data; }; _InternalBitReader.prototype.readLSB = function(len) { return this.readBits(len, 'LSB'); } _InternalBitReader.prototype.readMSB = function(len) { return this.readBits(len, 'MSB'); } // // ======================================================= /* inflating ByteReader - RFC 1951 */ var _inflatingReader = function(reader) { if (! (this instanceof arguments.callee) ) this._throwError('You must use new to instantiate this class', typename, 'ctor'); this._byteReader = reader; this._bitReader = new _InternalBitReader(reader); this._buffer = []; this._bufferPosition = 0; this._state = 0; this._blockFinal = false; this._typeName = typename; this._version = version; return this; }; // shared fns and variables var staticCodes = null; var staticDistances = null; var clenMap = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; var buildCodes = function(lengths){ var i=0; var codes = new Array(lengths.length); var maxBits = lengths[0]; for (i=1; i 0) { var code = {}; code.bits = codes[i]; code.length = lengths[i]; code.index = i; nonEmptyCodes.push(code); } } return buildTreeBranch(nonEmptyCodes, 0, 0); }; var buildTreeBranch = function(codes, prefix, prefixLength){ if (codes.length === 0) return null; var zeros = []; var ones = []; var branch = {}; branch.isLeaf = false; for(var i=0; i> (codes[i].length - prefixLength - 1)) & 1) > 0; if (nextBit) ones.push(codes[i]); else zeros.push(codes[i]); } } if(!branch.isLeaf) { branch.zero = buildTreeBranch(zeros, (prefix << 1), prefixLength + 1); branch.one = buildTreeBranch(ones, (prefix << 1) | 1, prefixLength + 1); } return branch; }; var encodedLengthStart = [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]; var encodedLengthAdditionalBits = [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]; var encodedDistanceStart = [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 encodedDistanceAdditionalBits = [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]; var readDynamicTrees = function(bitReader){ var hlit = bitReader.readLSB(5) + 257; var hdist = bitReader.readLSB(5) + 1; var hclen = bitReader.readLSB(4) + 4; var clen = new Array(19); var i; for (i=0; i 256) { var lengthCode = p.index; if(lengthCode > 285) this._throwError('Invalid length code', null, '_decodeItem'); var length = encodedLengthStart[lengthCode - 257]; if(encodedLengthAdditionalBits[lengthCode - 257] > 0) { length += this._bitReader.readLSB(encodedLengthAdditionalBits[lengthCode - 257]); } p = this._distancesTree; while (!p.isLeaf) { p = this._bitReader.readBit() ? p.one : p.zero; } var distanceCode = p.index; var distance = encodedDistanceStart[distanceCode]; if (encodedDistanceAdditionalBits[distanceCode] > 0) distance += this._bitReader.readLSB(encodedDistanceAdditionalBits[distanceCode]); item.itemType = 3; item.distance = distance; item.length = length; } else { item.itemType = 1; this._state = this._blockFinal ? 2 : 0; // EOB } return item; }; // public instance functions _inflatingReader.prototype.readByte = function() { var oneByte = this.read(1)[0]; return (oneByte === null || oneByte === undefined) ? null : oneByte; }; _inflatingReader.prototype.read = function(len) { var b = this._buffer; // (since we use this so much...) // Keep reading until we get to the right length while (this._bufferPosition+len > b.length) { var item = this._decodeItem(); if (item === null) { // EOF len = b.length - this._bufferPosition; break; } switch(item.itemType) { case 0: JSIO.massApply(b.push, b, item.array); break; case 2: b.push(item.symbol); break; case 3: var j = b.length - item.distance; if (item.distance >= item.length) JSIO.massApply(b.push, b, b.slice(j, j+item.length)); // sometimes DEFLATE tries some trickery with "look-ahead" compression else { // this is basically just a repetition of the same string, plus some possible cutoff var count = parseInt(item.length / item.distance); var repArr = b.slice(j); // http://stackoverflow.com/questions/202605/repeat-string-javascript/5450113#5450113 while (count > 0) { if (count & 1) JSIO.massApply( b.push, b, repArr); if (count >>= 1) JSIO.massApply(repArr.push, repArr, repArr); } // add any remaining cutoff var r; if (r = item.length % item.distance) JSIO.massApply(b.push, b, b.slice(j, j+r)); } break; } } var bytes = b.slice(this._bufferPosition, this._bufferPosition+len); this._bufferPosition += len; this.position += len; if (this._bufferPosition > 0xC000) { var shift = b.length - 0x8000; if (shift > this._bufferPosition) shift = this._bufferPosition; b.splice(0, shift); this._bufferPosition -= shift; } return bytes; }; // initialization routine - once per type (function(){ var codes = new Array(288); var codesLengths = new Array(288); var i=0; for ( i = 0; i <= 143; i++) { codes[i] = 0x0030 + i; codesLengths[i] = 8; } for ( i = 144; i <= 255; i++) { codes[i] = 0x0190 + i - 144; codesLengths[i] = 9; } for ( i = 256; i <= 279; i++) { codes[i] = 0x0000 + i - 256; codesLengths[i] = 7; } for ( i = 280; i <= 287; i++) { codes[i] = 0x00C0 + i - 280; codesLengths[i] = 8; } staticCodes = buildTree(codes, codesLengths); var distances = new Array(32); var distancesLengths = new Array(32); for ( i = 0; i <= 31; i++) { distances[i] = i; distancesLengths[i] = 5; } staticDistances = buildTree(distances, distancesLengths); })(); JSIO.InflatingReader = _inflatingReader; })(); /// JSIO.InflatingReader.js ends // Zipfile.js // ------------------------------------------------------------------ // // A class that reads Zip files. // Depends on the JSIO library functions. // // ======================================================= // // Copyleft (c) 2010, Dino Chiesa via MS-PL // Copyleft (c) 2012, Brendan Byrd via GPL // // This work is licensed under the GPLv3. (function(){ var version = "2.0 2012Feb"; var typename = "Zipfile"; if (typeof JSIO.BinaryUrlStream !== "function") JSIO.throwError('This extension requires JSIO.BinaryUrlStream.js v2.0', typename); if (typeof JSIO.TextDecoder !== "object") JSIO.throwError('This extension requires JSIO.TextDecoder.js v2.0', typename); if (typeof JSIO.TextReader !== "function") JSIO.throwError('This extension requires JSIO.TextReader.js v2.0', typename); if (typeof JSIO.Crc32 !== "function") JSIO.throwError('This extension requires JSIO.Crc32.js v2.0', typename); if (typeof JSIO.InflatingReader !== "function") JSIO.throwError('This extension requires JSIO.InflatingReader.js v2.0', typename); // ======================================================= function ZipEntry(zip) { this.zipfile = zip; this._typename = "ZipEntry"; this._version = version; this._crcCalculator = null; } ZipEntry.prototype._throwError = JSIO.throwError; // return byte array or string ZipEntry.prototype.extract = function(callback, asString) { this.contentType = JSIO.guessFileType(this.name); asString = asString || ( this.contentType == JSIO.FileType.Text || this.contentType == JSIO.FileType.XML); var thisEntry = this; if (this.compressionMethod !== 0 && this.compressionMethod != 8) this._throwError('Unsupported compression method: ' + this.compressionMethod, null, 'extract'); var reader = (asString) ? this.openTextReader(thisEntry.utf8 ? JSIO.TextDecoder.UTF8 : JSIO.TextDecoder.ANSI) : this.openBinaryReader(); // diagnostic purpose only; tag the reader with the entry name reader.zipEntryName = thisEntry.name; if (typeof callback != "function") { // synchronous var result = reader.readToEnd(); this.verifyCrc32(); return result; } // asynchronous reader.beginReadToEnd(function(result){ try { thisEntry.verifyCrc32(); callback(thisEntry, result); } catch (exc1) { this.zipfile.status.push("EXCEPTION: " + exc1.message); callback(thisEntry, exc1); } }); return null; }; // open a ByteReader on the entry, which will read binary // content from the compressed stream. ZipEntry.prototype.openBinaryReader = function() { var reader = new JSIO.StreamSegmentReader(this.zipfile.binaryStream, this.offset + this.lengthOfHeader, this.compressedSize); if (this.compressionMethod === 0) { this._crcCalculator = new JSIO.Crc32Reader(reader); } else { var inflator = new JSIO.InflatingReader(reader); this._crcCalculator = new JSIO.Crc32Reader(inflator); } // Whether compressed or not, the source ByteReader in each case // is wrapped in a second ByteReader object that calculates CRC // as it reads. That way, after all reading is complete, the // caller can check the calcuated CRC against the expected CRC. return this._crcCalculator; }; // open a TextReader on the entry, to read text from the // compressed stream. ZipEntry.prototype.openTextReader = function(decoderKind) { var reader = this.openBinaryReader(); decoderKind = decoderKind || JSIO.TextDecoder.UTF8; var d = new decoderKind(reader); var textReader = new JSIO.TextReader(d); d._parent = textReader; // store a reference, for diagnostic purposes only return textReader; }; // verify the CRC on the entry. // call this after all bytes have been read. ZipEntry.prototype.verifyCrc32 = function() { var computedCrc = this._crcCalculator.crc32(); var rc = false; // CRC FAIL if (this.crc32 != computedCrc) { var msg = "WARNING: CRC check failed: " + "entry(" + this.name + ") " + "computed(" + JSIO.decimalToHexString(computedCrc,8) + ") " + "expected(" + JSIO.decimalToHexString(this.crc32,8) + ") "; this.zipfile.status.push(msg); } else { rc = true; // OK if (this.zipfile.verbose>2) { this.zipfile.status.push("INFO: CRC check ok: 0x" + JSIO.decimalToHexString(this.crc32,8)); } } return rc; }; // ctor ZipFile = function(fileUrl, callback, verbosity) { if (! (this instanceof arguments.callee) ) JSIO.throwError('You must use new to instantiate this class', typename, 'ctor'); this.verbose = verbosity || 0; this.entries = []; this.entryNames = []; this.status = []; this._version = version; this._typename = "ZipFile"; this._throwError = JSIO.throwError; var thisZipFile = this; // Could use a back-tracking reader for the central directory, but // there's no point, since all the zip data is held in memory anyway. /* function ReadCentralDirectory(){ var posn = thisZipFile.binaryStream.length - 64; var maxSeekback = Math.Max(s.Length - 0x4000, 10); var success = false; var nTries = 0; do { thisZipFile.binaryStream.Seek(posn, SeekOrigin.Begin); var bytesRead = thisZipFile.binaryStream.findSignature(thisZipFile.Signatures.EndOfCentralDirectory); if (bytesRead != -1) success = true; else { nTries++; // increasingly larger posn -= (32 * (nTries + 1) * nTries); if (posn < 0) posn = 0; // BOF } } while (!success && posn > maxSeekback); if (!success) { thisZipFile.status.push("cannot find End of Central Directory"); return; } } */ function DateFromPackedFormat(packed) { if (packed == 0xFFFF || packed === 0) return new Date(1995, 0, 1, 0,0,0,0); var packedTime = packed & 0x0000ffff; var packedDate = ((packed & 0xffff0000) >> 16); var year = 1980 + ((packedDate & 0xFE00) >> 9); var month = ((packedDate & 0x01E0) >> 5) -1; var day = packedDate & 0x001F; var hour = (packedTime & 0xF800) >> 11; var minute = (packedTime & 0x07E0) >> 5; var second = (packedTime & 0x001F) * 2; // Validation and error checking. // This is not foolproof but will catch most errors. // I can't believe how many different ways applications // can mess up a simple date format. if (second >= 60) { minute++; second = 0; } if (minute >= 60) { hour++; minute = 0; } if (hour >= 24) { day++; hour = 0; } var success = false; var d; try { d = new Date(year, month, day, hour, minute, second, 0); success= true; } catch (exc1) { if (year == 1980 && (month === 0 || day === 0)) { try { d = new Date(1980, 0, 1, hour, minute, second, 0); success= true; } catch (exc2) { try { d = new Date(1980, 0, 1, 0, 0, 0, 0); success= true; } catch (exc3) { } // how could this fail?? } } else { try { if (year < 1980) year = 1980; if (year > 2030) year = 2030; if (month < 1) month = 1; if (month > 12) month = 12; if (day < 1) day = 1; if (day > 31) day = 31; if (minute < 0) minute = 0; if (minute > 59) minute = 59; if (second < 0) second = 0; if (second > 59) second = 59; d = new Date(year, month-1, day, hour, minute, second, 0); success= true; } catch (exc4){} } } if (!success) this._throwError('Bad date/time value in this ZIP file', null, 'DateFromPackedFormat'); return d; } function ReadZipEntries () { // read only once if (thisZipFile.entryNames.length === 0){ var e; while ((e = ReadZipEntry()) !== null) { thisZipFile.entries.push(e); thisZipFile.entryNames.push(e.name); } } } function ReadZipEntry () { var offset = thisZipFile.binaryStream.position; var sig = thisZipFile.binaryStream.readNumber(4); if (sig == ZipFile.Signatures.DirEntry) { // after all entries, comes the central directory if (thisZipFile.verbose > 2) { thisZipFile.status.push("INFO: at offset 0x" + JSIO.decimalToHexString(offset) + ", found start of Zip Directory."); } // all done reading return null; } if (sig != ZipFile.Signatures.Entry) { thisZipFile.status.push("WARNING: at offset 0x" + JSIO.decimalToHexString(offset) + ", found unexpected signature: 0x" + JSIO.decimalToHexString(sig)); return null; } var entry = new ZipEntry(thisZipFile); entry.offset = offset; entry.versionNeeded = thisZipFile.binaryStream.readNumber(2); entry.bitField = thisZipFile.binaryStream.readNumber(2); entry.compressionMethod = thisZipFile.binaryStream.readNumber(2); var timeBlob = thisZipFile.binaryStream.readNumber(4); entry.lastModified = DateFromPackedFormat(timeBlob); entry.crc32 = thisZipFile.binaryStream.readNumber(4); entry.compressedSize = thisZipFile.binaryStream.readNumber(4); entry.uncompressedSize = thisZipFile.binaryStream.readNumber(4); if (thisZipFile.verbose>2) { thisZipFile.status.push("INFO: crc32 0x" + JSIO.decimalToHexString(entry.crc32) + " (" + entry.crc32 + ")"); thisZipFile.status.push("INFO: compressedSize 0x" + JSIO.decimalToHexString(entry.compressedSize) + " (" + entry.compressedSize + ") bytes"); thisZipFile.status.push("INFO: uncompressedSize 0x" + JSIO.decimalToHexString(entry.uncompressedSize) + " (" + entry.uncompressedSize + ") bytes"); } if ((entry.bitField & 0x01) == 0x01){ thisZipFile.status.push("This zipfile uses Encryption, which is not supported by ZipFile.js."); return null; } entry.utf8 = ((entry.bitField & 0x0800) == 0x0800); if (entry.compressedSize == 0xFFFFFFFF || entry.uncompressedSize == 0xFFFFFFFF) { thisZipFile.status.push("This zipfile uses ZIP64, which is not supported by ZipFile.js"); return null; } var filenameLength = thisZipFile.binaryStream.readNumber(2); var extraFieldLength = thisZipFile.binaryStream.readNumber(2); if (thisZipFile.verbose>2) { thisZipFile.status.push("INFO: filename length= " + filenameLength); } // we've read 30 bytes of metadata so far var bytesRead = 30 + filenameLength + extraFieldLength; if (entry.utf8) { thisZipFile.status.push("INFO: before filename, position= 0x" + JSIO.decimalToHexString( thisZipFile.binaryStream.position )); var binReader = new JSIO.StreamSegmentReader(thisZipFile.binaryStream, thisZipFile.binaryStream.position, filenameLength); var utf8Decoder = new JSIO.TextDecoder.UTF8(binReader); var textReader = new JSIO.TextReader(utf8Decoder); entry.name = textReader.readToEnd(); // advance the filepointer: thisZipFile.binaryStream.seek(filenameLength, JSIO.SeekOrigin.Current, thisZipFile); thisZipFile.status.push("INFO: after filename, position= 0x" + JSIO.decimalToHexString( thisZipFile.binaryStream.position )); } else { entry.name = thisZipFile.binaryStream.readString(filenameLength); } // There are a bunch of things in the "extra" header, thisZipFile we // could parse, like timestamps and other things. This class // only identifies and separates them. // More info here: http://www.pkware.com/documents/casestudies/APPNOTE.TXT var extraPos = 0; entry.extra = []; while (extraPos < extraFieldLength) { var extraBlock = { type: thisZipFile.binaryStream.readNumber(2), size: thisZipFile.binaryStream.readNumber(2) }; extraBlock.typeDescription = ZipFile.ExtraFieldTypes[extraBlock.type]; extraBlock.data = thisZipFile.binaryStream.read(extraBlock.size); entry.extra.push(extraBlock); extraPos += 4 + extraBlock.size; } if (thisZipFile.verbose > 1) { thisZipFile.status.push("INFO: at offset 0x" + JSIO.decimalToHexString(entry.offset) + ", found entry '" + entry.name + "' fnl(" + filenameLength + ") efl(" + extraFieldLength +")"); } if (extraFieldLength > 0) { if (thisZipFile.verbose > 0) { thisZipFile.status.push("INFO: entry " + entry.name + " has " + extraFieldLength + " bytes of " + "extra metadata (ID'd but ignored)"); } } entry.lengthOfHeader = bytesRead; if (thisZipFile.verbose>2) { thisZipFile.status.push("INFO: lengthOfHeader 0x" + JSIO.decimalToHexString(entry.lengthOfHeader) + " (" + entry.lengthOfHeader + ")"); } /* Data descriptor Offset Bytes Description[25] 0 0/4 Optional data descriptor signature = 0x08074b50 0/4 4 CRC-32 4/8 4 Compressed size 8/12 4 Uncompressed size */ if ((entry.bitField & 0x0008) == 0x0008){ thisZipFile.status.push("INFO: This zipfile uses a bit 3 trailing data descriptor"); // return null; var currentPosition = thisZipFile.binaryStream.position; var dataDescLength = 0; if (thisZipFile.verbose>2) { thisZipFile.status.push("INFO: currentPosition 0x" + JSIO.decimalToHexString(thisZipFile.binaryStream.position) + " (" + thisZipFile.binaryStream.position + ")"); thisZipFile.status.push("INFO: length 0x" + JSIO.decimalToHexString(thisZipFile.binaryStream.length) + " (" + thisZipFile.binaryStream.length + ")"); } for (var i=0; i<128; i++) { // maximum search backwards from end for signature is 128 bytes thisZipFile.binaryStream.seek(thisZipFile.binaryStream.length-i-4,JSIO.SeekOrigin.Begin); var num = thisZipFile.binaryStream.readNumber(4) if (thisZipFile.verbose>2) { thisZipFile.status.push("INFO: ["+i+"] 0x" + JSIO.decimalToHexString(num) + " (" + num + ")"); } if (num == ZipFile.Signatures.DataDescriptor) { sig = num; entry.crc32 = thisZipFile.binaryStream.readNumber(4); entry.compressedSize = thisZipFile.binaryStream.readNumber(4); entry.uncompressedSize = thisZipFile.binaryStream.readNumber(4); dataDescLength = 16; break; } } if (thisZipFile.verbose>2) { thisZipFile.status.push("INFO: sig 0x" + JSIO.decimalToHexString(sig) + " (" + sig + ")"); thisZipFile.status.push("INFO: crc32 0x" + JSIO.decimalToHexString(entry.crc32) + " (" + entry.crc32 + ")"); thisZipFile.status.push("INFO: compressedSize 0x" + JSIO.decimalToHexString(entry.compressedSize) + " (" + entry.compressedSize + ") bytes"); thisZipFile.status.push("INFO: uncompressedSize 0x" + JSIO.decimalToHexString(entry.uncompressedSize) + " (" + entry.uncompressedSize + ") bytes"); thisZipFile.status.push("INFO: lengthOfHeader 0x" + JSIO.decimalToHexString(entry.lengthOfHeader) + " (" + entry.lengthOfHeader + ")"); } thisZipFile.binaryStream.position = currentPosition+dataDescLength; } entry.totalEntrySize = entry.lengthOfHeader + entry.compressedSize; // seek past the data without reading it. We will read on Extract() if (thisZipFile.verbose > 1) { thisZipFile.status.push("INFO: seek 0x" + JSIO.decimalToHexString(entry.compressedSize) + " (" + entry.compressedSize + ") bytes"); } thisZipFile.binaryStream.seek(entry.compressedSize, JSIO.SeekOrigin.Current, thisZipFile); return entry; } var parseZipFile = function(bfr){ try { if (bfr.req.status == 200) { var sig = thisZipFile.binaryStream.readNumber(4); if (sig != ZipFile.Signatures.Entry){ thisZipFile.status.push("WARNING: this file does not appear to be a zip file"); } else { thisZipFile.binaryStream.seek(0, JSIO.SeekOrigin.Begin); ReadZipEntries(); if (thisZipFile.verbose > 0) { thisZipFile.status.push("INFO: read " + thisZipFile.entries.length + " entries"); } } } else { thisZipFile.status.push("ERROR: the URL could not be read (" + bfr.req.status + " " + bfr.req.statusText + ")"); } callback(thisZipFile); } catch (exc1) { thisZipFile.status.push("EXCEPTION: " + exc1.message); callback(thisZipFile); } }; this.binaryStream = new JSIO.BinaryUrlStream(fileUrl, parseZipFile); return this; }; ZipFile.Signatures = { Entry : 0x04034b50, EndOfCentralDirectory : 0x06054b50, DirEntry : 0x02014b50, DataDescriptor : 0x08074b50 }; ZipFile.Version = version; ZipFile.EncryptionAlgorithm = { None : 0, PkzipWeak : 1, WinZipAes : 2 }; ZipFile.ExtraFieldTypes = {}; ZipFile.ExtraFieldTypes[0x0001] = 'Zip64 Extended Info'; ZipFile.ExtraFieldTypes[0x0007] = 'AV Info'; ZipFile.ExtraFieldTypes[0x0008] = 'Extended Language Encoding Data (PFS)'; ZipFile.ExtraFieldTypes[0x0009] = 'OS/2'; ZipFile.ExtraFieldTypes[0x000a] = 'NTFS '; ZipFile.ExtraFieldTypes[0x000c] = 'OpenVMS'; ZipFile.ExtraFieldTypes[0x000d] = 'UNIX'; ZipFile.ExtraFieldTypes[0x000e] = 'File Stream and Fork Descriptors'; ZipFile.ExtraFieldTypes[0x000f] = 'Patch Descriptor'; ZipFile.ExtraFieldTypes[0x0014] = 'PKCS#7 Store for X.509 Certificates'; ZipFile.ExtraFieldTypes[0x0015] = 'X.509 Certificate ID and Signature (Individual File)'; ZipFile.ExtraFieldTypes[0x0016] = 'X.509 Certificate ID (Central Directory)'; ZipFile.ExtraFieldTypes[0x0017] = 'Strong Encryption Header'; ZipFile.ExtraFieldTypes[0x0018] = 'Record Management Controls'; ZipFile.ExtraFieldTypes[0x0019] = 'PKCS#7 Encryption Recipient Certificate List'; ZipFile.ExtraFieldTypes[0x0065] = 'IBM S/390 (Z390), AS/400 (I400) attributes (uncompressed)'; ZipFile.ExtraFieldTypes[0x0066] = 'IBM S/390 (Z390), AS/400 (I400) attributes (compressed)'; ZipFile.ExtraFieldTypes[0x4690] = 'POSZIP 4690 (reserved) '; ZipFile.ExtraFieldTypes[0x07c8] = 'Macintosh'; ZipFile.ExtraFieldTypes[0x2605] = 'ZipIt Macintosh'; ZipFile.ExtraFieldTypes[0x2705] = 'ZipIt Macintosh 1.3.5+'; ZipFile.ExtraFieldTypes[0x2805] = 'ZipIt Macintosh 1.3.5+'; ZipFile.ExtraFieldTypes[0x334d] = 'Info-ZIP Macintosh'; ZipFile.ExtraFieldTypes[0x4341] = 'Acorn/SparkFS '; ZipFile.ExtraFieldTypes[0x4453] = 'Windows NT security descriptor (binary ACL)'; ZipFile.ExtraFieldTypes[0x4704] = 'VM/CMS'; ZipFile.ExtraFieldTypes[0x470f] = 'MVS'; ZipFile.ExtraFieldTypes[0x4b46] = 'FWKCS MD5'; ZipFile.ExtraFieldTypes[0x4c41] = 'OS/2 access control list (text ACL)'; ZipFile.ExtraFieldTypes[0x4d49] = 'Info-ZIP OpenVMS'; ZipFile.ExtraFieldTypes[0x4f4c] = 'Xceed original location extra field'; ZipFile.ExtraFieldTypes[0x5356] = 'AOS/VS (ACL)'; ZipFile.ExtraFieldTypes[0x5455] = 'extended timestamp'; ZipFile.ExtraFieldTypes[0x554e] = 'Xceed unicode extra field'; ZipFile.ExtraFieldTypes[0x5855] = 'Info-ZIP UNIX (original, also OS/2, NT, etc)'; ZipFile.ExtraFieldTypes[0x6375] = 'Info-ZIP Unicode Comment Extra Field'; ZipFile.ExtraFieldTypes[0x6542] = 'BeOS/BeBox'; ZipFile.ExtraFieldTypes[0x7075] = 'Info-ZIP Unicode Path Extra Field'; ZipFile.ExtraFieldTypes[0x756e] = 'ASi UNIX'; ZipFile.ExtraFieldTypes[0x7855] = 'Info-ZIP UNIX (new)'; ZipFile.ExtraFieldTypes[0xa220] = 'Microsoft Open Packaging Growth Hint'; ZipFile.ExtraFieldTypes[0xfd4a] = 'SMS/QDOS'; })();