zip.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. /*
  2. Copyright (c) 2012 Gildas Lormeau. All rights reserved.
  3. Redistribution and use in source and binary forms, with or without
  4. modification, are permitted provided that the following conditions are met:
  5. 1. Redistributions of source code must retain the above copyright notice,
  6. this list of conditions and the following disclaimer.
  7. 2. Redistributions in binary form must reproduce the above copyright
  8. notice, this list of conditions and the following disclaimer in
  9. the documentation and/or other materials provided with the distribution.
  10. 3. The names of the authors may not be used to endorse or promote products
  11. derived from this software without specific prior written permission.
  12. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  13. INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  14. FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
  15. INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
  16. INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  17. LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  18. OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  19. LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  20. NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  21. EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  22. */
  23. (function(obj) {
  24. var ERR_BAD_FORMAT = "File format is not recognized.";
  25. var ERR_ENCRYPTED = "File contains encrypted entry.";
  26. var ERR_ZIP64 = "File is using Zip64 (4gb+ file size).";
  27. var ERR_READ = "Error while reading zip file.";
  28. var ERR_WRITE = "Error while writing zip file.";
  29. var ERR_WRITE_DATA = "Error while writing file data.";
  30. var ERR_READ_DATA = "Error while reading file data.";
  31. var ERR_DUPLICATED_NAME = "File already exists.";
  32. var ERR_HTTP_RANGE = "HTTP Range not supported.";
  33. var CHUNK_SIZE = 512 * 1024;
  34. var INFLATE_JS = "inflate.js";
  35. var DEFLATE_JS = "deflate.js";
  36. var appendABViewSupported;
  37. try {
  38. appendABViewSupported = new Blob([ getDataHelper(0).view ]).size == 0;
  39. } catch (e) {
  40. }
  41. function Crc32() {
  42. var crc = -1, that = this;
  43. that.append = function(data) {
  44. var offset, table = that.table;
  45. for (offset = 0; offset < data.length; offset++)
  46. crc = (crc >>> 8) ^ table[(crc ^ data[offset]) & 0xFF];
  47. };
  48. that.get = function() {
  49. return ~crc;
  50. };
  51. }
  52. Crc32.prototype.table = (function() {
  53. var i, j, t, table = [];
  54. for (i = 0; i < 256; i++) {
  55. t = i;
  56. for (j = 0; j < 8; j++)
  57. if (t & 1)
  58. t = (t >>> 1) ^ 0xEDB88320;
  59. else
  60. t = t >>> 1;
  61. table[i] = t;
  62. }
  63. return table;
  64. })();
  65. function blobSlice(blob, index, length) {
  66. if (blob.slice)
  67. return blob.slice(index, index + length);
  68. else if (blob.webkitSlice)
  69. return blob.webkitSlice(index, index + length);
  70. else if (blob.mozSlice)
  71. return blob.mozSlice(index, index + length);
  72. else if (blob.msSlice)
  73. return blob.msSlice(index, index + length);
  74. }
  75. function getDataHelper(byteLength, bytes) {
  76. var dataBuffer, dataArray;
  77. dataBuffer = new ArrayBuffer(byteLength);
  78. dataArray = new Uint8Array(dataBuffer);
  79. if (bytes)
  80. dataArray.set(bytes, 0);
  81. return {
  82. buffer : dataBuffer,
  83. array : dataArray,
  84. view : new DataView(dataBuffer)
  85. };
  86. }
  87. // Readers
  88. function Reader() {
  89. }
  90. function TextReader(text) {
  91. var that = this, blobReader;
  92. function init(callback, onerror) {
  93. var blob = new Blob([ text ], {
  94. type : "text/plain"
  95. });
  96. blobReader = new BlobReader(blob);
  97. blobReader.init(function() {
  98. that.size = blobReader.size;
  99. callback();
  100. }, onerror);
  101. }
  102. function readUint8Array(index, length, callback, onerror) {
  103. blobReader.readUint8Array(index, length, callback, onerror);
  104. }
  105. that.size = 0;
  106. that.init = init;
  107. that.readUint8Array = readUint8Array;
  108. }
  109. TextReader.prototype = new Reader();
  110. TextReader.prototype.constructor = TextReader;
  111. function Data64URIReader(dataURI) {
  112. var that = this, dataStart;
  113. function init(callback) {
  114. var dataEnd = dataURI.length;
  115. while (dataURI.charAt(dataEnd - 1) == "=")
  116. dataEnd--;
  117. dataStart = dataURI.indexOf(",") + 1;
  118. that.size = Math.floor((dataEnd - dataStart) * 0.75);
  119. callback();
  120. }
  121. function readUint8Array(index, length, callback) {
  122. var i, data = getDataHelper(length);
  123. var start = Math.floor(index / 3) * 4;
  124. var end = Math.ceil((index + length) / 3) * 4;
  125. var bytes = obj.atob(dataURI.substring(start + dataStart, end + dataStart));
  126. var delta = index - Math.floor(start / 4) * 3;
  127. for (i = delta; i < delta + length; i++)
  128. data.array[i - delta] = bytes.charCodeAt(i);
  129. callback(data.array);
  130. }
  131. that.size = 0;
  132. that.init = init;
  133. that.readUint8Array = readUint8Array;
  134. }
  135. Data64URIReader.prototype = new Reader();
  136. Data64URIReader.prototype.constructor = Data64URIReader;
  137. function BlobReader(blob) {
  138. var that = this;
  139. function init(callback) {
  140. this.size = blob.size;
  141. callback();
  142. }
  143. function readUint8Array(index, length, callback, onerror) {
  144. var reader = new FileReader();
  145. reader.onload = function(e) {
  146. callback(new Uint8Array(e.target.result));
  147. };
  148. reader.onerror = onerror;
  149. reader.readAsArrayBuffer(blobSlice(blob, index, length));
  150. }
  151. that.size = 0;
  152. that.init = init;
  153. that.readUint8Array = readUint8Array;
  154. }
  155. BlobReader.prototype = new Reader();
  156. BlobReader.prototype.constructor = BlobReader;
  157. function HttpReader(url) {
  158. var that = this;
  159. function getData(callback, onerror) {
  160. var request;
  161. if (!that.data) {
  162. request = new XMLHttpRequest();
  163. request.addEventListener("load", function() {
  164. if (!that.size)
  165. that.size = Number(request.getResponseHeader("Content-Length"));
  166. that.data = new Uint8Array(request.response);
  167. callback();
  168. }, false);
  169. request.addEventListener("error", onerror, false);
  170. request.open("GET", url);
  171. request.responseType = "arraybuffer";
  172. request.send();
  173. } else
  174. callback();
  175. }
  176. function init(callback, onerror) {
  177. var request = new XMLHttpRequest();
  178. request.addEventListener("load", function() {
  179. that.size = Number(request.getResponseHeader("Content-Length"));
  180. callback();
  181. }, false);
  182. request.addEventListener("error", onerror, false);
  183. request.open("HEAD", url);
  184. request.send();
  185. }
  186. function readUint8Array(index, length, callback, onerror) {
  187. getData(function() {
  188. callback(new Uint8Array(that.data.subarray(index, index + length)));
  189. }, onerror);
  190. }
  191. that.size = 0;
  192. that.init = init;
  193. that.readUint8Array = readUint8Array;
  194. }
  195. HttpReader.prototype = new Reader();
  196. HttpReader.prototype.constructor = HttpReader;
  197. function HttpRangeReader(url) {
  198. var that = this;
  199. function init(callback, onerror) {
  200. var request = new XMLHttpRequest();
  201. request.addEventListener("load", function() {
  202. that.size = Number(request.getResponseHeader("Content-Length"));
  203. if (request.getResponseHeader("Accept-Ranges") == "bytes")
  204. callback();
  205. else
  206. onerror(ERR_HTTP_RANGE);
  207. }, false);
  208. request.addEventListener("error", onerror, false);
  209. request.open("HEAD", url);
  210. request.send();
  211. }
  212. function readArrayBuffer(index, length, callback, onerror) {
  213. var request = new XMLHttpRequest();
  214. request.open("GET", url);
  215. request.responseType = "arraybuffer";
  216. request.setRequestHeader("Range", "bytes=" + index + "-" + (index + length - 1));
  217. request.addEventListener("load", function() {
  218. callback(request.response);
  219. }, false);
  220. request.addEventListener("error", onerror, false);
  221. request.send();
  222. }
  223. function readUint8Array(index, length, callback, onerror) {
  224. readArrayBuffer(index, length, function(arraybuffer) {
  225. callback(new Uint8Array(arraybuffer));
  226. }, onerror);
  227. }
  228. that.size = 0;
  229. that.init = init;
  230. that.readUint8Array = readUint8Array;
  231. }
  232. HttpRangeReader.prototype = new Reader();
  233. HttpRangeReader.prototype.constructor = HttpRangeReader;
  234. // Writers
  235. function Writer() {
  236. }
  237. Writer.prototype.getData = function(callback) {
  238. callback(this.data);
  239. };
  240. function TextWriter() {
  241. var that = this, blob;
  242. function init(callback) {
  243. blob = new Blob([], {
  244. type : "text/plain"
  245. });
  246. callback();
  247. }
  248. function writeUint8Array(array, callback) {
  249. blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], {
  250. type : "text/plain"
  251. });
  252. callback();
  253. }
  254. function getData(callback, onerror) {
  255. var reader = new FileReader();
  256. reader.onload = function(e) {
  257. callback(e.target.result);
  258. };
  259. reader.onerror = onerror;
  260. reader.readAsText(blob);
  261. }
  262. that.init = init;
  263. that.writeUint8Array = writeUint8Array;
  264. that.getData = getData;
  265. }
  266. TextWriter.prototype = new Writer();
  267. TextWriter.prototype.constructor = TextWriter;
  268. function Data64URIWriter(contentType) {
  269. var that = this, data = "", pending = "";
  270. function init(callback) {
  271. data += "data:" + (contentType || "") + ";base64,";
  272. callback();
  273. }
  274. function writeUint8Array(array, callback) {
  275. var i, delta = pending.length, dataString = pending;
  276. pending = "";
  277. for (i = 0; i < (Math.floor((delta + array.length) / 3) * 3) - delta; i++)
  278. dataString += String.fromCharCode(array[i]);
  279. for (; i < array.length; i++)
  280. pending += String.fromCharCode(array[i]);
  281. if (dataString.length > 2)
  282. data += obj.btoa(dataString);
  283. else
  284. pending = dataString;
  285. callback();
  286. }
  287. function getData(callback) {
  288. callback(data + obj.btoa(pending));
  289. }
  290. that.init = init;
  291. that.writeUint8Array = writeUint8Array;
  292. that.getData = getData;
  293. }
  294. Data64URIWriter.prototype = new Writer();
  295. Data64URIWriter.prototype.constructor = Data64URIWriter;
  296. function FileWriter(fileEntry, contentType) {
  297. var writer, that = this;
  298. function init(callback, onerror) {
  299. fileEntry.createWriter(function(fileWriter) {
  300. writer = fileWriter;
  301. callback();
  302. }, onerror);
  303. }
  304. function writeUint8Array(array, callback, onerror) {
  305. var blob = new Blob([ appendABViewSupported ? array : array.buffer ], {
  306. type : contentType
  307. });
  308. writer.onwrite = function() {
  309. writer.onwrite = null;
  310. callback();
  311. };
  312. writer.onerror = onerror;
  313. writer.write(blob);
  314. }
  315. function getData(callback) {
  316. fileEntry.file(callback);
  317. }
  318. that.init = init;
  319. that.writeUint8Array = writeUint8Array;
  320. that.getData = getData;
  321. }
  322. FileWriter.prototype = new Writer();
  323. FileWriter.prototype.constructor = FileWriter;
  324. function BlobWriter(contentType) {
  325. var blob, that = this;
  326. function init(callback) {
  327. blob = new Blob([], {
  328. type : contentType
  329. });
  330. callback();
  331. }
  332. function writeUint8Array(array, callback) {
  333. blob = new Blob([ blob, appendABViewSupported ? array : array.buffer ], {
  334. type : contentType
  335. });
  336. callback();
  337. }
  338. function getData(callback) {
  339. callback(blob);
  340. }
  341. that.init = init;
  342. that.writeUint8Array = writeUint8Array;
  343. that.getData = getData;
  344. }
  345. BlobWriter.prototype = new Writer();
  346. BlobWriter.prototype.constructor = BlobWriter;
  347. // inflate/deflate core functions
  348. function launchWorkerProcess(worker, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) {
  349. var chunkIndex = 0, index, outputSize;
  350. function onflush() {
  351. worker.removeEventListener("message", onmessage, false);
  352. onend(outputSize);
  353. }
  354. function onmessage(event) {
  355. var message = event.data, data = message.data;
  356. if (message.onappend) {
  357. outputSize += data.length;
  358. writer.writeUint8Array(data, function() {
  359. onappend(false, data);
  360. step();
  361. }, onwriteerror);
  362. }
  363. if (message.onflush)
  364. if (data) {
  365. outputSize += data.length;
  366. writer.writeUint8Array(data, function() {
  367. onappend(false, data);
  368. onflush();
  369. }, onwriteerror);
  370. } else
  371. onflush();
  372. if (message.progress && onprogress)
  373. onprogress(index + message.current, size);
  374. }
  375. function step() {
  376. index = chunkIndex * CHUNK_SIZE;
  377. if (index < size)
  378. reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(array) {
  379. worker.postMessage({
  380. append : true,
  381. data : array
  382. });
  383. chunkIndex++;
  384. if (onprogress)
  385. onprogress(index, size);
  386. onappend(true, array);
  387. }, onreaderror);
  388. else
  389. worker.postMessage({
  390. flush : true
  391. });
  392. }
  393. outputSize = 0;
  394. worker.addEventListener("message", onmessage, false);
  395. step();
  396. }
  397. function launchProcess(process, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) {
  398. var chunkIndex = 0, index, outputSize = 0;
  399. function step() {
  400. var outputData;
  401. index = chunkIndex * CHUNK_SIZE;
  402. if (index < size)
  403. reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(inputData) {
  404. var outputData = process.append(inputData, function() {
  405. if (onprogress)
  406. onprogress(offset + index, size);
  407. });
  408. outputSize += outputData.length;
  409. onappend(true, inputData);
  410. writer.writeUint8Array(outputData, function() {
  411. onappend(false, outputData);
  412. chunkIndex++;
  413. setTimeout(step, 1);
  414. }, onwriteerror);
  415. if (onprogress)
  416. onprogress(index, size);
  417. }, onreaderror);
  418. else {
  419. outputData = process.flush();
  420. if (outputData) {
  421. outputSize += outputData.length;
  422. writer.writeUint8Array(outputData, function() {
  423. onappend(false, outputData);
  424. onend(outputSize);
  425. }, onwriteerror);
  426. } else
  427. onend(outputSize);
  428. }
  429. }
  430. step();
  431. }
  432. function inflate(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) {
  433. var worker, crc32 = new Crc32();
  434. function oninflateappend(sending, array) {
  435. if (computeCrc32 && !sending)
  436. crc32.append(array);
  437. }
  438. function oninflateend(outputSize) {
  439. onend(outputSize, crc32.get());
  440. }
  441. if (obj.zip.useWebWorkers) {
  442. worker = new Worker(obj.zip.inflateJSPath || INFLATE_JS);//INFLATE_JS
  443. launchWorkerProcess(worker, reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror);
  444. } else
  445. launchProcess(new obj.zip.Inflater(), reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror);
  446. return worker;
  447. }
  448. function deflate(reader, writer, level, onend, onprogress, onreaderror, onwriteerror) {
  449. var worker, crc32 = new Crc32();
  450. function ondeflateappend(sending, array) {
  451. if (sending)
  452. crc32.append(array);
  453. }
  454. function ondeflateend(outputSize) {
  455. onend(outputSize, crc32.get());
  456. }
  457. function onmessage() {
  458. worker.removeEventListener("message", onmessage, false);
  459. launchWorkerProcess(worker, reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror);
  460. }
  461. if (obj.zip.useWebWorkers) {
  462. worker = new Worker(obj.zip.workerScriptsPath + DEFLATE_JS);
  463. worker.addEventListener("message", onmessage, false);
  464. worker.postMessage({
  465. init : true,
  466. level : level
  467. });
  468. } else
  469. launchProcess(new obj.zip.Deflater(), reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror);
  470. return worker;
  471. }
  472. function copy(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) {
  473. var chunkIndex = 0, crc32 = new Crc32();
  474. function step() {
  475. var index = chunkIndex * CHUNK_SIZE;
  476. if (index < size)
  477. reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function(array) {
  478. if (computeCrc32)
  479. crc32.append(array);
  480. if (onprogress)
  481. onprogress(index, size, array);
  482. writer.writeUint8Array(array, function() {
  483. chunkIndex++;
  484. step();
  485. }, onwriteerror);
  486. }, onreaderror);
  487. else
  488. onend(size, crc32.get());
  489. }
  490. step();
  491. }
  492. // ZipReader
  493. function decodeASCII(str) {
  494. var i, out = "", charCode, extendedASCII = [ '\u00C7', '\u00FC', '\u00E9', '\u00E2', '\u00E4', '\u00E0', '\u00E5', '\u00E7', '\u00EA', '\u00EB',
  495. '\u00E8', '\u00EF', '\u00EE', '\u00EC', '\u00C4', '\u00C5', '\u00C9', '\u00E6', '\u00C6', '\u00F4', '\u00F6', '\u00F2', '\u00FB', '\u00F9',
  496. '\u00FF', '\u00D6', '\u00DC', '\u00F8', '\u00A3', '\u00D8', '\u00D7', '\u0192', '\u00E1', '\u00ED', '\u00F3', '\u00FA', '\u00F1', '\u00D1',
  497. '\u00AA', '\u00BA', '\u00BF', '\u00AE', '\u00AC', '\u00BD', '\u00BC', '\u00A1', '\u00AB', '\u00BB', '_', '_', '_', '\u00A6', '\u00A6',
  498. '\u00C1', '\u00C2', '\u00C0', '\u00A9', '\u00A6', '\u00A6', '+', '+', '\u00A2', '\u00A5', '+', '+', '-', '-', '+', '-', '+', '\u00E3',
  499. '\u00C3', '+', '+', '-', '-', '\u00A6', '-', '+', '\u00A4', '\u00F0', '\u00D0', '\u00CA', '\u00CB', '\u00C8', 'i', '\u00CD', '\u00CE',
  500. '\u00CF', '+', '+', '_', '_', '\u00A6', '\u00CC', '_', '\u00D3', '\u00DF', '\u00D4', '\u00D2', '\u00F5', '\u00D5', '\u00B5', '\u00FE',
  501. '\u00DE', '\u00DA', '\u00DB', '\u00D9', '\u00FD', '\u00DD', '\u00AF', '\u00B4', '\u00AD', '\u00B1', '_', '\u00BE', '\u00B6', '\u00A7',
  502. '\u00F7', '\u00B8', '\u00B0', '\u00A8', '\u00B7', '\u00B9', '\u00B3', '\u00B2', '_', ' ' ];
  503. for (i = 0; i < str.length; i++) {
  504. charCode = str.charCodeAt(i) & 0xFF;
  505. if (charCode > 127)
  506. out += extendedASCII[charCode - 128];
  507. else
  508. out += String.fromCharCode(charCode);
  509. }
  510. return out;
  511. }
  512. function decodeUTF8(str_data) {
  513. var tmp_arr = [], i = 0, ac = 0, c1 = 0, c2 = 0, c3 = 0;
  514. str_data += '';
  515. while (i < str_data.length) {
  516. c1 = str_data.charCodeAt(i);
  517. if (c1 < 128) {
  518. tmp_arr[ac++] = String.fromCharCode(c1);
  519. i++;
  520. } else if (c1 > 191 && c1 < 224) {
  521. c2 = str_data.charCodeAt(i + 1);
  522. tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
  523. i += 2;
  524. } else {
  525. c2 = str_data.charCodeAt(i + 1);
  526. c3 = str_data.charCodeAt(i + 2);
  527. tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  528. i += 3;
  529. }
  530. }
  531. return tmp_arr.join('');
  532. }
  533. function getString(bytes) {
  534. var i, str = "";
  535. for (i = 0; i < bytes.length; i++)
  536. str += String.fromCharCode(bytes[i]);
  537. return str;
  538. }
  539. function getDate(timeRaw) {
  540. var date = (timeRaw & 0xffff0000) >> 16, time = timeRaw & 0x0000ffff;
  541. try {
  542. return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5,
  543. (time & 0x001F) * 2, 0);
  544. } catch (e) {
  545. }
  546. }
  547. function readCommonHeader(entry, data, index, centralDirectory, onerror) {
  548. entry.version = data.view.getUint16(index, true);
  549. entry.bitFlag = data.view.getUint16(index + 2, true);
  550. entry.compressionMethod = data.view.getUint16(index + 4, true);
  551. entry.lastModDateRaw = data.view.getUint32(index + 6, true);
  552. entry.lastModDate = getDate(entry.lastModDateRaw);
  553. if ((entry.bitFlag & 0x01) === 0x01) {
  554. onerror(ERR_ENCRYPTED);
  555. return;
  556. }
  557. if (centralDirectory || (entry.bitFlag & 0x0008) != 0x0008) {
  558. entry.crc32 = data.view.getUint32(index + 10, true);
  559. entry.compressedSize = data.view.getUint32(index + 14, true);
  560. entry.uncompressedSize = data.view.getUint32(index + 18, true);
  561. }
  562. if (entry.compressedSize === 0xFFFFFFFF || entry.uncompressedSize === 0xFFFFFFFF) {
  563. onerror(ERR_ZIP64);
  564. return;
  565. }
  566. entry.filenameLength = data.view.getUint16(index + 22, true);
  567. entry.extraFieldLength = data.view.getUint16(index + 24, true);
  568. }
  569. function createZipReader(reader, onerror) {
  570. function Entry() {
  571. }
  572. Entry.prototype.getData = function(writer, onend, onprogress, checkCrc32) {
  573. var that = this, worker;
  574. function terminate(callback, param) {
  575. if (worker)
  576. worker.terminate();
  577. worker = null;
  578. if (callback)
  579. callback(param);
  580. }
  581. function testCrc32(crc32) {
  582. var dataCrc32 = getDataHelper(4);
  583. dataCrc32.view.setUint32(0, crc32);
  584. return that.crc32 == dataCrc32.view.getUint32(0);
  585. }
  586. function getWriterData(uncompressedSize, crc32) {
  587. if (checkCrc32 && !testCrc32(crc32))
  588. onreaderror();
  589. else
  590. writer.getData(function(data) {
  591. terminate(onend, data);
  592. });
  593. }
  594. function onreaderror() {
  595. terminate(onerror, ERR_READ_DATA);
  596. }
  597. function onwriteerror() {
  598. terminate(onerror, ERR_WRITE_DATA);
  599. }
  600. reader.readUint8Array(that.offset, 30, function(bytes) {
  601. var data = getDataHelper(bytes.length, bytes), dataOffset;
  602. if (data.view.getUint32(0) != 0x504b0304) {
  603. onerror(ERR_BAD_FORMAT);
  604. return;
  605. }
  606. readCommonHeader(that, data, 4, false, function(error) {
  607. onerror(error);
  608. return;
  609. });
  610. dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength;
  611. writer.init(function() {
  612. if (that.compressionMethod === 0)
  613. copy(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror);
  614. else
  615. worker = inflate(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror);
  616. }, onwriteerror);
  617. }, onreaderror);
  618. };
  619. function seekEOCDR(offset, entriesCallback) {
  620. reader.readUint8Array(reader.size - offset, offset, function(bytes) {
  621. var dataView = getDataHelper(bytes.length, bytes).view;
  622. try{
  623. if (dataView.getUint32(0) != 0x504b0506) {
  624. seekEOCDR(offset + 1, entriesCallback);
  625. } else {
  626. entriesCallback(dataView);
  627. }
  628. }catch(e){
  629. onerror(ERR_READ);
  630. }
  631. }, function() {
  632. onerror(ERR_READ);
  633. });
  634. }
  635. return {
  636. getEntries : function(callback) {
  637. if (reader.size < 22) {
  638. onerror(ERR_BAD_FORMAT);
  639. return;
  640. }
  641. // look for End of central directory record
  642. seekEOCDR(22, function(dataView) {
  643. var datalength, fileslength;
  644. datalength = dataView.getUint32(16, true);
  645. fileslength = dataView.getUint16(8, true);
  646. reader.readUint8Array(datalength, reader.size - datalength, function(bytes) {
  647. var i, index = 0, entries = [], entry, filename, comment, data = getDataHelper(bytes.length, bytes);
  648. for (i = 0; i < fileslength; i++) {
  649. entry = new Entry();
  650. if (data.view.getUint32(index) != 0x504b0102) {
  651. onerror(ERR_BAD_FORMAT);
  652. return;
  653. }
  654. readCommonHeader(entry, data, index + 6, true, function(error) {
  655. onerror(error);
  656. return;
  657. });
  658. entry.commentLength = data.view.getUint16(index + 32, true);
  659. entry.directory = ((data.view.getUint8(index + 38) & 0x10) == 0x10);
  660. entry.offset = data.view.getUint32(index + 42, true);
  661. filename = getString(data.array.subarray(index + 46, index + 46 + entry.filenameLength));
  662. entry.filename = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(filename) : decodeASCII(filename);
  663. if (!entry.directory && entry.filename.charAt(entry.filename.length - 1) == "/")
  664. entry.directory = true;
  665. comment = getString(data.array.subarray(index + 46 + entry.filenameLength + entry.extraFieldLength, index + 46
  666. + entry.filenameLength + entry.extraFieldLength + entry.commentLength));
  667. entry.comment = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(comment) : decodeASCII(comment);
  668. entries.push(entry);
  669. index += 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength;
  670. }
  671. callback(entries);
  672. }, function() {
  673. onerror(ERR_READ);
  674. });
  675. });
  676. },
  677. close : function(callback) {
  678. if (callback)
  679. callback();
  680. }
  681. };
  682. }
  683. // ZipWriter
  684. function encodeUTF8(string) {
  685. var n, c1, enc, utftext = [], start = 0, end = 0, stringl = string.length;
  686. for (n = 0; n < stringl; n++) {
  687. c1 = string.charCodeAt(n);
  688. enc = null;
  689. if (c1 < 128)
  690. end++;
  691. else if (c1 > 127 && c1 < 2048)
  692. enc = String.fromCharCode((c1 >> 6) | 192) + String.fromCharCode((c1 & 63) | 128);
  693. else
  694. enc = String.fromCharCode((c1 >> 12) | 224) + String.fromCharCode(((c1 >> 6) & 63) | 128) + String.fromCharCode((c1 & 63) | 128);
  695. if (enc != null) {
  696. if (end > start)
  697. utftext += string.slice(start, end);
  698. utftext += enc;
  699. start = end = n + 1;
  700. }
  701. }
  702. if (end > start)
  703. utftext += string.slice(start, stringl);
  704. return utftext;
  705. }
  706. function getBytes(str) {
  707. var i, array = [];
  708. for (i = 0; i < str.length; i++)
  709. array.push(str.charCodeAt(i));
  710. return array;
  711. }
  712. function createZipWriter(writer, onerror, dontDeflate) {
  713. var worker, files = [], filenames = [], datalength = 0;
  714. function terminate(callback, message) {
  715. if (worker)
  716. worker.terminate();
  717. worker = null;
  718. if (callback)
  719. callback(message);
  720. }
  721. function onwriteerror() {
  722. terminate(onerror, ERR_WRITE);
  723. }
  724. function onreaderror() {
  725. terminate(onerror, ERR_READ_DATA);
  726. }
  727. return {
  728. add : function(name, reader, onend, onprogress, options) {
  729. var header, filename, date;
  730. function writeHeader(callback) {
  731. var data;
  732. date = options.lastModDate || new Date();
  733. header = getDataHelper(26);
  734. files[name] = {
  735. headerArray : header.array,
  736. directory : options.directory,
  737. filename : filename,
  738. offset : datalength,
  739. comment : getBytes(encodeUTF8(options.comment || ""))
  740. };
  741. header.view.setUint32(0, 0x14000808);
  742. if (options.version)
  743. header.view.setUint8(0, options.version);
  744. if (!dontDeflate && options.level != 0 && !options.directory)
  745. header.view.setUint16(4, 0x0800);
  746. header.view.setUint16(6, (((date.getHours() << 6) | date.getMinutes()) << 5) | date.getSeconds() / 2, true);
  747. header.view.setUint16(8, ((((date.getFullYear() - 1980) << 4) | (date.getMonth() + 1)) << 5) | date.getDate(), true);
  748. header.view.setUint16(22, filename.length, true);
  749. data = getDataHelper(30 + filename.length);
  750. data.view.setUint32(0, 0x504b0304);
  751. data.array.set(header.array, 4);
  752. data.array.set(filename, 30);
  753. datalength += data.array.length;
  754. writer.writeUint8Array(data.array, callback, onwriteerror);
  755. }
  756. function writeFooter(compressedLength, crc32) {
  757. var footer = getDataHelper(16);
  758. datalength += compressedLength || 0;
  759. footer.view.setUint32(0, 0x504b0708);
  760. if (typeof crc32 != "undefined") {
  761. header.view.setUint32(10, crc32, true);
  762. footer.view.setUint32(4, crc32, true);
  763. }
  764. if (reader) {
  765. footer.view.setUint32(8, compressedLength, true);
  766. header.view.setUint32(14, compressedLength, true);
  767. footer.view.setUint32(12, reader.size, true);
  768. header.view.setUint32(18, reader.size, true);
  769. }
  770. writer.writeUint8Array(footer.array, function() {
  771. datalength += 16;
  772. terminate(onend);
  773. }, onwriteerror);
  774. }
  775. function writeFile() {
  776. options = options || {};
  777. name = name.trim();
  778. if (options.directory && name.charAt(name.length - 1) != "/")
  779. name += "/";
  780. if (files[name])
  781. throw ERR_DUPLICATED_NAME;
  782. filename = getBytes(encodeUTF8(name));
  783. filenames.push(name);
  784. writeHeader(function() {
  785. if (reader)
  786. if (dontDeflate || options.level == 0)
  787. copy(reader, writer, 0, reader.size, true, writeFooter, onprogress, onreaderror, onwriteerror);
  788. else
  789. worker = deflate(reader, writer, options.level, writeFooter, onprogress, onreaderror, onwriteerror);
  790. else
  791. writeFooter();
  792. }, onwriteerror);
  793. }
  794. if (reader)
  795. reader.init(writeFile, onreaderror);
  796. else
  797. writeFile();
  798. },
  799. close : function(callback) {
  800. var data, length = 0, index = 0;
  801. filenames.forEach(function(name) {
  802. var file = files[name];
  803. length += 46 + file.filename.length + file.comment.length;
  804. });
  805. data = getDataHelper(length + 22);
  806. filenames.forEach(function(name) {
  807. var file = files[name];
  808. data.view.setUint32(index, 0x504b0102);
  809. data.view.setUint16(index + 4, 0x1400);
  810. data.array.set(file.headerArray, index + 6);
  811. data.view.setUint16(index + 32, file.comment.length, true);
  812. if (file.directory)
  813. data.view.setUint8(index + 38, 0x10);
  814. data.view.setUint32(index + 42, file.offset, true);
  815. data.array.set(file.filename, index + 46);
  816. data.array.set(file.comment, index + 46 + file.filename.length);
  817. index += 46 + file.filename.length + file.comment.length;
  818. });
  819. data.view.setUint32(index, 0x504b0506);
  820. data.view.setUint16(index + 8, filenames.length, true);
  821. data.view.setUint16(index + 10, filenames.length, true);
  822. data.view.setUint32(index + 12, length, true);
  823. data.view.setUint32(index + 16, datalength, true);
  824. writer.writeUint8Array(data.array, function() {
  825. terminate(function() {
  826. writer.getData(callback);
  827. });
  828. }, onwriteerror);
  829. }
  830. };
  831. }
  832. obj.zip = {
  833. Reader : Reader,
  834. Writer : Writer,
  835. BlobReader : BlobReader,
  836. HttpReader : HttpReader,
  837. HttpRangeReader : HttpRangeReader,
  838. Data64URIReader : Data64URIReader,
  839. TextReader : TextReader,
  840. BlobWriter : BlobWriter,
  841. FileWriter : FileWriter,
  842. Data64URIWriter : Data64URIWriter,
  843. TextWriter : TextWriter,
  844. createReader : function(reader, callback, onerror) {
  845. reader.init(function() {
  846. callback(createZipReader(reader, onerror));
  847. }, onerror);
  848. },
  849. createWriter : function(writer, callback, onerror, dontDeflate) {
  850. writer.init(function() {
  851. callback(createZipWriter(writer, onerror, dontDeflate));
  852. }, onerror);
  853. },
  854. workerScriptsPath : "",
  855. inflateJSPath:"",
  856. useWebWorkers : true
  857. };
  858. })(this);