zip-js 0.1.0 → 0.2.0

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