UNPKG

51 kBJavaScriptView Raw
1/**
2
3JSZip - A Javascript class for generating and reading zip files
4<http://stuartk.com/jszip>
5
6(c) 2009-2012 Stuart Knightley <stuart [at] stuartk.com>
7Dual licenced under the MIT license or GPLv3. See LICENSE.markdown.
8
9Usage:
10 zip = new JSZip();
11 zip.file("hello.txt", "Hello, World!").file("tempfile", "nothing");
12 zip.folder("images").file("smile.gif", base64Data, {base64: true});
13 zip.file("Xmas.txt", "Ho ho ho !", {date : new Date("December 25, 2007 00:00:01")});
14 zip.remove("tempfile");
15
16 base64zip = zip.generate();
17
18**/
19// We use strict, but it should not be placed outside of a function because
20// the environment is shared inside the browser.
21// "use strict";
22
23/**
24 * Representation a of zip file in js
25 * @constructor
26 * @param {String=|ArrayBuffer=|Uint8Array=|Buffer=} data the data to load, if any (optional).
27 * @param {Object=} options the options for creating this objects (optional).
28 */
29var JSZip = function(data, options) {
30 // object containing the files :
31 // {
32 // "folder/" : {...},
33 // "folder/data.txt" : {...}
34 // }
35 this.files = {};
36
37 // Where we are in the hierarchy
38 this.root = "";
39
40 if (data) {
41 this.load(data, options);
42 }
43};
44
45JSZip.signature = {
46 LOCAL_FILE_HEADER : "\x50\x4b\x03\x04",
47 CENTRAL_FILE_HEADER : "\x50\x4b\x01\x02",
48 CENTRAL_DIRECTORY_END : "\x50\x4b\x05\x06",
49 ZIP64_CENTRAL_DIRECTORY_LOCATOR : "\x50\x4b\x06\x07",
50 ZIP64_CENTRAL_DIRECTORY_END : "\x50\x4b\x06\x06",
51 DATA_DESCRIPTOR : "\x50\x4b\x07\x08"
52};
53
54// Default properties for a new file
55JSZip.defaults = {
56 base64: false,
57 binary: false,
58 dir: false,
59 date: null,
60 compression: null
61};
62
63/*
64 * List features that require a modern browser, and if the current browser support them.
65 */
66JSZip.support = {
67 // contains true if JSZip can read/generate ArrayBuffer, false otherwise.
68 arraybuffer : (function(){
69 return typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
70 })(),
71 // contains true if JSZip can read/generate nodejs Buffer, false otherwise.
72 nodebuffer : (function(){
73 return typeof Buffer !== "undefined";
74 })(),
75 // contains true if JSZip can read/generate Uint8Array, false otherwise.
76 uint8array : (function(){
77 return typeof Uint8Array !== "undefined";
78 })(),
79 // contains true if JSZip can read/generate Blob, false otherwise.
80 blob : (function(){
81 // the spec started with BlobBuilder then replaced it with a construtor for Blob.
82 // Result : we have browsers that :
83 // * know the BlobBuilder (but with prefix)
84 // * know the Blob constructor
85 // * know about Blob but not about how to build them
86 // About the "=== 0" test : if given the wrong type, it may be converted to a string.
87 // Instead of an empty content, we will get "[object Uint8Array]" for example.
88 if (typeof ArrayBuffer === "undefined") {
89 return false;
90 }
91 var buffer = new ArrayBuffer(0);
92 try {
93 return new Blob([buffer], { type: "application/zip" }).size === 0;
94 }
95 catch(e) {}
96
97 try {
98 var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
99 var builder = new BlobBuilder();
100 builder.append(buffer);
101 return builder.getBlob('application/zip').size === 0;
102 }
103 catch(e) {}
104
105 return false;
106 })()
107};
108
109JSZip.prototype = (function () {
110 var textEncoder, textDecoder;
111 if (
112 JSZip.support.uint8array &&
113 typeof TextEncoder === "function" &&
114 typeof TextDecoder === "function"
115 ) {
116 textEncoder = new TextEncoder("utf-8");
117 textDecoder = new TextDecoder("utf-8");
118 }
119
120 /**
121 * Returns the raw data of a ZipObject, decompress the content if necessary.
122 * @param {ZipObject} file the file to use.
123 * @return {String|ArrayBuffer|Uint8Array|Buffer} the data.
124 */
125 var getRawData = function (file) {
126 if (file._data instanceof JSZip.CompressedObject) {
127 file._data = file._data.getContent();
128 file.options.binary = true;
129 file.options.base64 = false;
130
131 if (JSZip.utils.getTypeOf(file._data) === "uint8array") {
132 var copy = file._data;
133 // when reading an arraybuffer, the CompressedObject mechanism will keep it and subarray() a Uint8Array.
134 // if we request a file in the same format, we might get the same Uint8Array or its ArrayBuffer (the original zip file).
135 file._data = new Uint8Array(copy.length);
136 // with an empty Uint8Array, Opera fails with a "Offset larger than array size"
137 if (copy.length !== 0) {
138 file._data.set(copy, 0);
139 }
140 }
141 }
142 return file._data;
143 };
144
145 /**
146 * Returns the data of a ZipObject in a binary form. If the content is an unicode string, encode it.
147 * @param {ZipObject} file the file to use.
148 * @return {String|ArrayBuffer|Uint8Array|Buffer} the data.
149 */
150 var getBinaryData = function (file) {
151 var result = getRawData(file), type = JSZip.utils.getTypeOf(result);
152 if (type === "string") {
153 if (!file.options.binary) {
154 // unicode text !
155 // unicode string => binary string is a painful process, check if we can avoid it.
156 if (textEncoder) {
157 return textEncoder.encode(result);
158 }
159 if (JSZip.support.nodebuffer) {
160 return new Buffer(result, "utf-8");
161 }
162 }
163 return file.asBinary();
164 }
165 return result;
166 };
167
168 /**
169 * Transform this._data into a string.
170 * @param {function} filter a function String -> String, applied if not null on the result.
171 * @return {String} the string representing this._data.
172 */
173 var dataToString = function (asUTF8) {
174 var result = getRawData(this);
175 if (result === null || typeof result === "undefined") {
176 return "";
177 }
178 // if the data is a base64 string, we decode it before checking the encoding !
179 if (this.options.base64) {
180 result = JSZip.base64.decode(result);
181 }
182 if (asUTF8 && this.options.binary) {
183 // JSZip.prototype.utf8decode supports arrays as input
184 // skip to array => string step, utf8decode will do it.
185 result = JSZip.prototype.utf8decode(result);
186 } else {
187 // no utf8 transformation, do the array => string step.
188 result = JSZip.utils.transformTo("string", result);
189 }
190
191 if (!asUTF8 && !this.options.binary) {
192 result = JSZip.prototype.utf8encode(result);
193 }
194 return result;
195 };
196 /**
197 * A simple object representing a file in the zip file.
198 * @constructor
199 * @param {string} name the name of the file
200 * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data
201 * @param {Object} options the options of the file
202 */
203 var ZipObject = function (name, data, options) {
204 this.name = name;
205 this._data = data;
206 this.options = options;
207 };
208
209 ZipObject.prototype = {
210 /**
211 * Return the content as UTF8 string.
212 * @return {string} the UTF8 string.
213 */
214 asText : function () {
215 return dataToString.call(this, true);
216 },
217 /**
218 * Returns the binary content.
219 * @return {string} the content as binary.
220 */
221 asBinary : function () {
222 return dataToString.call(this, false);
223 },
224 /**
225 * Returns the content as a nodejs Buffer.
226 * @return {Buffer} the content as a Buffer.
227 */
228 asNodeBuffer : function () {
229 var result = getBinaryData(this);
230 return JSZip.utils.transformTo("nodebuffer", result);
231 },
232 /**
233 * Returns the content as an Uint8Array.
234 * @return {Uint8Array} the content as an Uint8Array.
235 */
236 asUint8Array : function () {
237 var result = getBinaryData(this);
238 return JSZip.utils.transformTo("uint8array", result);
239 },
240 /**
241 * Returns the content as an ArrayBuffer.
242 * @return {ArrayBuffer} the content as an ArrayBufer.
243 */
244 asArrayBuffer : function () {
245 return this.asUint8Array().buffer;
246 }
247 };
248
249 /**
250 * Transform an integer into a string in hexadecimal.
251 * @private
252 * @param {number} dec the number to convert.
253 * @param {number} bytes the number of bytes to generate.
254 * @returns {string} the result.
255 */
256 var decToHex = function(dec, bytes) {
257 var hex = "", i;
258 for(i = 0; i < bytes; i++) {
259 hex += String.fromCharCode(dec&0xff);
260 dec=dec>>>8;
261 }
262 return hex;
263 };
264
265 /**
266 * Merge the objects passed as parameters into a new one.
267 * @private
268 * @param {...Object} var_args All objects to merge.
269 * @return {Object} a new object with the data of the others.
270 */
271 var extend = function () {
272 var result = {}, i, attr;
273 for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
274 for (attr in arguments[i]) {
275 if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
276 result[attr] = arguments[i][attr];
277 }
278 }
279 }
280 return result;
281 };
282
283 /**
284 * Transforms the (incomplete) options from the user into the complete
285 * set of options to create a file.
286 * @private
287 * @param {Object} o the options from the user.
288 * @return {Object} the complete set of options.
289 */
290 var prepareFileAttrs = function (o) {
291 o = o || {};
292 /*jshint -W041 */
293 if (o.base64 === true && o.binary == null) {
294 o.binary = true;
295 }
296 /*jshint +W041 */
297 o = extend(o, JSZip.defaults);
298 o.date = o.date || new Date();
299 if (o.compression !== null) o.compression = o.compression.toUpperCase();
300
301 return o;
302 };
303
304 /**
305 * Add a file in the current folder.
306 * @private
307 * @param {string} name the name of the file
308 * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file
309 * @param {Object} o the options of the file
310 * @return {Object} the new file.
311 */
312 var fileAdd = function (name, data, o) {
313 // be sure sub folders exist
314 var parent = parentFolder(name), dataType = JSZip.utils.getTypeOf(data);
315 if (parent) {
316 folderAdd.call(this, parent);
317 }
318
319 o = prepareFileAttrs(o);
320
321 if (o.dir || data === null || typeof data === "undefined") {
322 o.base64 = false;
323 o.binary = false;
324 data = null;
325 } else if (dataType === "string") {
326 if (o.binary && !o.base64) {
327 // optimizedBinaryString == true means that the file has already been filtered with a 0xFF mask
328 if (o.optimizedBinaryString !== true) {
329 // this is a string, not in a base64 format.
330 // Be sure that this is a correct "binary string"
331 data = JSZip.utils.string2binary(data);
332 }
333 }
334 } else { // arraybuffer, uint8array, ...
335 o.base64 = false;
336 o.binary = true;
337
338 if (!dataType && !(data instanceof JSZip.CompressedObject)) {
339 throw new Error("The data of '" + name + "' is in an unsupported format !");
340 }
341
342 // special case : it's way easier to work with Uint8Array than with ArrayBuffer
343 if (dataType === "arraybuffer") {
344 data = JSZip.utils.transformTo("uint8array", data);
345 }
346 }
347
348 var object = new ZipObject(name, data, o);
349 this.files[name] = object;
350 return object;
351 };
352
353
354 /**
355 * Find the parent folder of the path.
356 * @private
357 * @param {string} path the path to use
358 * @return {string} the parent folder, or ""
359 */
360 var parentFolder = function (path) {
361 if (path.slice(-1) == '/') {
362 path = path.substring(0, path.length - 1);
363 }
364 var lastSlash = path.lastIndexOf('/');
365 return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
366 };
367
368 /**
369 * Add a (sub) folder in the current folder.
370 * @private
371 * @param {string} name the folder's name
372 * @return {Object} the new folder.
373 */
374 var folderAdd = function (name) {
375 // Check the name ends with a /
376 if (name.slice(-1) != "/") {
377 name += "/"; // IE doesn't like substr(-1)
378 }
379
380 // Does this folder already exist?
381 if (!this.files[name]) {
382 fileAdd.call(this, name, null, {dir:true});
383 }
384 return this.files[name];
385 };
386
387 /**
388 * Generate a JSZip.CompressedObject for a given zipOject.
389 * @param {ZipObject} file the object to read.
390 * @param {JSZip.compression} compression the compression to use.
391 * @return {JSZip.CompressedObject} the compressed result.
392 */
393 var generateCompressedObjectFrom = function (file, compression) {
394 var result = new JSZip.CompressedObject(), content;
395
396 // the data has not been decompressed, we might reuse things !
397 if (file._data instanceof JSZip.CompressedObject) {
398 result.uncompressedSize = file._data.uncompressedSize;
399 result.crc32 = file._data.crc32;
400
401 if (result.uncompressedSize === 0 || file.options.dir) {
402 compression = JSZip.compressions['STORE'];
403 result.compressedContent = "";
404 result.crc32 = 0;
405 } else if (file._data.compressionMethod === compression.magic) {
406 result.compressedContent = file._data.getCompressedContent();
407 } else {
408 content = file._data.getContent();
409 // need to decompress / recompress
410 result.compressedContent = compression.compress(JSZip.utils.transformTo(compression.compressInputType, content));
411 }
412 } else {
413 // have uncompressed data
414 content = getBinaryData(file);
415 if (!content || content.length === 0 || file.options.dir) {
416 compression = JSZip.compressions['STORE'];
417 content = "";
418 }
419 result.uncompressedSize = content.length;
420 result.crc32 = this.crc32(content);
421 result.compressedContent = compression.compress(JSZip.utils.transformTo(compression.compressInputType, content));
422 }
423
424 result.compressedSize = result.compressedContent.length;
425 result.compressionMethod = compression.magic;
426
427 return result;
428 };
429
430 /**
431 * Generate the various parts used in the construction of the final zip file.
432 * @param {string} name the file name.
433 * @param {ZipObject} file the file content.
434 * @param {JSZip.CompressedObject} compressedObject the compressed object.
435 * @param {number} offset the current offset from the start of the zip file.
436 * @return {object} the zip parts.
437 */
438 var generateZipParts = function(name, file, compressedObject, offset) {
439 var data = compressedObject.compressedContent,
440 utfEncodedFileName = this.utf8encode(file.name),
441 useUTF8 = utfEncodedFileName !== file.name,
442 o = file.options,
443 dosTime,
444 dosDate;
445
446 // date
447 // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
448 // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
449 // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
450
451 dosTime = o.date.getHours();
452 dosTime = dosTime << 6;
453 dosTime = dosTime | o.date.getMinutes();
454 dosTime = dosTime << 5;
455 dosTime = dosTime | o.date.getSeconds() / 2;
456
457 dosDate = o.date.getFullYear() - 1980;
458 dosDate = dosDate << 4;
459 dosDate = dosDate | (o.date.getMonth() + 1);
460 dosDate = dosDate << 5;
461 dosDate = dosDate | o.date.getDate();
462
463
464 var header = "";
465
466 // version needed to extract
467 header += "\x0A\x00";
468 // general purpose bit flag
469 // set bit 11 if utf8
470 header += useUTF8 ? "\x00\x08" : "\x00\x00";
471 // compression method
472 header += compressedObject.compressionMethod;
473 // last mod file time
474 header += decToHex(dosTime, 2);
475 // last mod file date
476 header += decToHex(dosDate, 2);
477 // crc-32
478 header += decToHex(compressedObject.crc32, 4);
479 // compressed size
480 header += decToHex(compressedObject.compressedSize, 4);
481 // uncompressed size
482 header += decToHex(compressedObject.uncompressedSize, 4);
483 // file name length
484 header += decToHex(utfEncodedFileName.length, 2);
485 // extra field length
486 header += "\x00\x00";
487
488
489 var fileRecord = JSZip.signature.LOCAL_FILE_HEADER + header + utfEncodedFileName;
490
491 var dirRecord = JSZip.signature.CENTRAL_FILE_HEADER +
492 // version made by (00: DOS)
493 "\x14\x00" +
494 // file header (common to file and central directory)
495 header +
496 // file comment length
497 "\x00\x00" +
498 // disk number start
499 "\x00\x00" +
500 // internal file attributes TODO
501 "\x00\x00" +
502 // external file attributes
503 (file.options.dir===true?"\x10\x00\x00\x00":"\x00\x00\x00\x00")+
504 // relative offset of local header
505 decToHex(offset, 4) +
506 // file name
507 utfEncodedFileName;
508
509
510 return {
511 fileRecord : fileRecord,
512 dirRecord : dirRecord,
513 compressedObject : compressedObject
514 };
515 };
516
517 /**
518 * An object to write any content to a string.
519 * @constructor
520 */
521 var StringWriter = function () {
522 this.data = [];
523 };
524 StringWriter.prototype = {
525 /**
526 * Append any content to the current string.
527 * @param {Object} input the content to add.
528 */
529 append : function (input) {
530 input = JSZip.utils.transformTo("string", input);
531 this.data.push(input);
532 },
533 /**
534 * Finalize the construction an return the result.
535 * @return {string} the generated string.
536 */
537 finalize : function () {
538 return this.data.join("");
539 }
540 };
541 /**
542 * An object to write any content to an Uint8Array.
543 * @constructor
544 * @param {number} length The length of the array.
545 */
546 var Uint8ArrayWriter = function (length) {
547 this.data = new Uint8Array(length);
548 this.index = 0;
549 };
550 Uint8ArrayWriter.prototype = {
551 /**
552 * Append any content to the current array.
553 * @param {Object} input the content to add.
554 */
555 append : function (input) {
556 if (input.length !== 0) {
557 // with an empty Uint8Array, Opera fails with a "Offset larger than array size"
558 input = JSZip.utils.transformTo("uint8array", input);
559 this.data.set(input, this.index);
560 this.index += input.length;
561 }
562 },
563 /**
564 * Finalize the construction an return the result.
565 * @return {Uint8Array} the generated array.
566 */
567 finalize : function () {
568 return this.data;
569 }
570 };
571
572 // return the actual prototype of JSZip
573 return {
574 /**
575 * Read an existing zip and merge the data in the current JSZip object.
576 * The implementation is in jszip-load.js, don't forget to include it.
577 * @param {String|ArrayBuffer|Uint8Array|Buffer} stream The stream to load
578 * @param {Object} options Options for loading the stream.
579 * options.base64 : is the stream in base64 ? default : false
580 * @return {JSZip} the current JSZip object
581 */
582 load : function (stream, options) {
583 throw new Error("Load method is not defined. Is the file jszip-load.js included ?");
584 },
585
586 /**
587 * Filter nested files/folders with the specified function.
588 * @param {Function} search the predicate to use :
589 * function (relativePath, file) {...}
590 * It takes 2 arguments : the relative path and the file.
591 * @return {Array} An array of matching elements.
592 */
593 filter : function (search) {
594 var result = [], filename, relativePath, file, fileClone;
595 for (filename in this.files) {
596 if ( !this.files.hasOwnProperty(filename) ) { continue; }
597 file = this.files[filename];
598 // return a new object, don't let the user mess with our internal objects :)
599 fileClone = new ZipObject(file.name, file._data, extend(file.options));
600 relativePath = filename.slice(this.root.length, filename.length);
601 if (filename.slice(0, this.root.length) === this.root && // the file is in the current root
602 search(relativePath, fileClone)) { // and the file matches the function
603 result.push(fileClone);
604 }
605 }
606 return result;
607 },
608
609 /**
610 * Add a file to the zip file, or search a file.
611 * @param {string|RegExp} name The name of the file to add (if data is defined),
612 * the name of the file to find (if no data) or a regex to match files.
613 * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded
614 * @param {Object} o File options
615 * @return {JSZip|Object|Array} this JSZip object (when adding a file),
616 * a file (when searching by string) or an array of files (when searching by regex).
617 */
618 file : function(name, data, o) {
619 if (arguments.length === 1) {
620 if (JSZip.utils.isRegExp(name)) {
621 var regexp = name;
622 return this.filter(function(relativePath, file) {
623 return !file.options.dir && regexp.test(relativePath);
624 });
625 } else { // text
626 return this.filter(function (relativePath, file) {
627 return !file.options.dir && relativePath === name;
628 })[0]||null;
629 }
630 } else { // more than one argument : we have data !
631 name = this.root+name;
632 fileAdd.call(this, name, data, o);
633 }
634 return this;
635 },
636
637 /**
638 * Add a directory to the zip file, or search.
639 * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders.
640 * @return {JSZip} an object with the new directory as the root, or an array containing matching folders.
641 */
642 folder : function(arg) {
643 if (!arg) {
644 return this;
645 }
646
647 if (JSZip.utils.isRegExp(arg)) {
648 return this.filter(function(relativePath, file) {
649 return file.options.dir && arg.test(relativePath);
650 });
651 }
652
653 // else, name is a new folder
654 var name = this.root + arg;
655 var newFolder = folderAdd.call(this, name);
656
657 // Allow chaining by returning a new object with this folder as the root
658 var ret = this.clone();
659 ret.root = newFolder.name;
660 return ret;
661 },
662
663 /**
664 * Delete a file, or a directory and all sub-files, from the zip
665 * @param {string} name the name of the file to delete
666 * @return {JSZip} this JSZip object
667 */
668 remove : function(name) {
669 name = this.root + name;
670 var file = this.files[name];
671 if (!file) {
672 // Look for any folders
673 if (name.slice(-1) != "/") {
674 name += "/";
675 }
676 file = this.files[name];
677 }
678
679 if (file) {
680 if (!file.options.dir) {
681 // file
682 delete this.files[name];
683 } else {
684 // folder
685 var kids = this.filter(function (relativePath, file) {
686 return file.name.slice(0, name.length) === name;
687 });
688 for (var i = 0; i < kids.length; i++) {
689 delete this.files[kids[i].name];
690 }
691 }
692 }
693
694 return this;
695 },
696
697 /**
698 * Generate the complete zip file
699 * @param {Object} options the options to generate the zip file :
700 * - base64, (deprecated, use type instead) true to generate base64.
701 * - compression, "STORE" by default.
702 * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
703 * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file
704 */
705 generate : function(options) {
706 options = extend(options || {}, {
707 base64 : true,
708 compression : "STORE",
709 type : "base64"
710 });
711
712 JSZip.utils.checkSupport(options.type);
713
714 var zipData = [], localDirLength = 0, centralDirLength = 0, writer, i;
715
716
717 // first, generate all the zip parts.
718 for (var name in this.files) {
719 if ( !this.files.hasOwnProperty(name) ) { continue; }
720 var file = this.files[name];
721
722 var compressionName = file.options.compression || options.compression.toUpperCase();
723 var compression = JSZip.compressions[compressionName];
724 if (!compression) {
725 throw new Error(compressionName + " is not a valid compression method !");
726 }
727
728 var compressedObject = generateCompressedObjectFrom.call(this, file, compression);
729
730 var zipPart = generateZipParts.call(this, name, file, compressedObject, localDirLength);
731 localDirLength += zipPart.fileRecord.length + compressedObject.compressedSize;
732 centralDirLength += zipPart.dirRecord.length;
733 zipData.push(zipPart);
734 }
735
736 var dirEnd = "";
737
738 // end of central dir signature
739 dirEnd = JSZip.signature.CENTRAL_DIRECTORY_END +
740 // number of this disk
741 "\x00\x00" +
742 // number of the disk with the start of the central directory
743 "\x00\x00" +
744 // total number of entries in the central directory on this disk
745 decToHex(zipData.length, 2) +
746 // total number of entries in the central directory
747 decToHex(zipData.length, 2) +
748 // size of the central directory 4 bytes
749 decToHex(centralDirLength, 4) +
750 // offset of start of central directory with respect to the starting disk number
751 decToHex(localDirLength, 4) +
752 // .ZIP file comment length
753 "\x00\x00";
754
755
756 // we have all the parts (and the total length)
757 // time to create a writer !
758 switch(options.type.toLowerCase()) {
759 case "uint8array" :
760 case "arraybuffer" :
761 case "blob" :
762 case "nodebuffer" :
763 writer = new Uint8ArrayWriter(localDirLength + centralDirLength + dirEnd.length);
764 break;
765 // case "base64" :
766 // case "string" :
767 default :
768 writer = new StringWriter(localDirLength + centralDirLength + dirEnd.length);
769 break;
770 }
771
772 for (i = 0; i < zipData.length; i++) {
773 writer.append(zipData[i].fileRecord);
774 writer.append(zipData[i].compressedObject.compressedContent);
775 }
776 for (i = 0; i < zipData.length; i++) {
777 writer.append(zipData[i].dirRecord);
778 }
779
780 writer.append(dirEnd);
781
782 var zip = writer.finalize();
783
784
785
786 switch(options.type.toLowerCase()) {
787 // case "zip is an Uint8Array"
788 case "uint8array" :
789 case "arraybuffer" :
790 case "nodebuffer" :
791 return JSZip.utils.transformTo(options.type.toLowerCase(), zip);
792 case "blob" :
793 return JSZip.utils.arrayBuffer2Blob(JSZip.utils.transformTo("arraybuffer", zip));
794
795 // case "zip is a string"
796 case "base64" :
797 return (options.base64) ? JSZip.base64.encode(zip) : zip;
798 default : // case "string" :
799 return zip;
800 }
801 },
802
803 /**
804 *
805 * Javascript crc32
806 * http://www.webtoolkit.info/
807 *
808 */
809 crc32 : function crc32(input, crc) {
810 if (typeof input === "undefined" || !input.length) {
811 return 0;
812 }
813
814 var isArray = JSZip.utils.getTypeOf(input) !== "string";
815
816 var table = [
817 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
818 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
819 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
820 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
821 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
822 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
823 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
824 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
825 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
826 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
827 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
828 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
829 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
830 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
831 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
832 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
833 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
834 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
835 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
836 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
837 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
838 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
839 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
840 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
841 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
842 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
843 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
844 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
845 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
846 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
847 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
848 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
849 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
850 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
851 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
852 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
853 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
854 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
855 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
856 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
857 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
858 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
859 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
860 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
861 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
862 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
863 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
864 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
865 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
866 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
867 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
868 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
869 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
870 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
871 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
872 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
873 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
874 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
875 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
876 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
877 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
878 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
879 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
880 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
881 ];
882
883 if (typeof(crc) == "undefined") { crc = 0; }
884 var x = 0;
885 var y = 0;
886 var byte = 0;
887
888 crc = crc ^ (-1);
889 for( var i = 0, iTop = input.length; i < iTop; i++ ) {
890 byte = isArray ? input[i] : input.charCodeAt(i);
891 y = ( crc ^ byte ) & 0xFF;
892 x = table[y];
893 crc = ( crc >>> 8 ) ^ x;
894 }
895
896 return crc ^ (-1);
897 },
898
899 // Inspired by http://my.opera.com/GreyWyvern/blog/show.dml/1725165
900 clone : function() {
901 var newObj = new JSZip();
902 for (var i in this) {
903 if (typeof this[i] !== "function") {
904 newObj[i] = this[i];
905 }
906 }
907 return newObj;
908 },
909
910
911 /**
912 * http://www.webtoolkit.info/javascript-utf8.html
913 */
914 utf8encode : function (string) {
915 // TextEncoder + Uint8Array to binary string is faster than checking every bytes on long strings.
916 // http://jsperf.com/utf8encode-vs-textencoder
917 // On short strings (file names for example), the TextEncoder API is (currently) slower.
918 if (textEncoder) {
919 var u8 = textEncoder.encode(string);
920 return JSZip.utils.transformTo("string", u8);
921 }
922 if (JSZip.support.nodebuffer) {
923 return JSZip.utils.transformTo("string", new Buffer(string, "utf-8"));
924 }
925
926 // array.join may be slower than string concatenation but generates less objects (less time spent garbage collecting).
927 // See also http://jsperf.com/array-direct-assignment-vs-push/31
928 var result = [], resIndex = 0;
929
930 for (var n = 0; n < string.length; n++) {
931
932 var c = string.charCodeAt(n);
933
934 if (c < 128) {
935 result[resIndex++] = String.fromCharCode(c);
936 } else if ((c > 127) && (c < 2048)) {
937 result[resIndex++] = String.fromCharCode((c >> 6) | 192);
938 result[resIndex++] = String.fromCharCode((c & 63) | 128);
939 } else {
940 result[resIndex++] = String.fromCharCode((c >> 12) | 224);
941 result[resIndex++] = String.fromCharCode(((c >> 6) & 63) | 128);
942 result[resIndex++] = String.fromCharCode((c & 63) | 128);
943 }
944
945 }
946
947 return result.join("");
948 },
949
950 /**
951 * http://www.webtoolkit.info/javascript-utf8.html
952 */
953 utf8decode : function (input) {
954 var result = [], resIndex = 0;
955 var type = JSZip.utils.getTypeOf(input);
956 var isArray = type !== "string";
957 var i = 0;
958 var c = 0, c1 = 0, c2 = 0, c3 = 0;
959
960 // check if we can use the TextDecoder API
961 // see http://encoding.spec.whatwg.org/#api
962 if (textDecoder) {
963 return textDecoder.decode(
964 JSZip.utils.transformTo("uint8array", input)
965 );
966 }
967 if (JSZip.support.nodebuffer) {
968 return JSZip.utils.transformTo("nodebuffer", input).toString("utf-8");
969 }
970
971 while ( i < input.length ) {
972
973 c = isArray ? input[i] : input.charCodeAt(i);
974
975 if (c < 128) {
976 result[resIndex++] = String.fromCharCode(c);
977 i++;
978 } else if ((c > 191) && (c < 224)) {
979 c2 = isArray ? input[i+1] : input.charCodeAt(i+1);
980 result[resIndex++] = String.fromCharCode(((c & 31) << 6) | (c2 & 63));
981 i += 2;
982 } else {
983 c2 = isArray ? input[i+1] : input.charCodeAt(i+1);
984 c3 = isArray ? input[i+2] : input.charCodeAt(i+2);
985 result[resIndex++] = String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
986 i += 3;
987 }
988
989 }
990
991 return result.join("");
992 }
993 };
994}());
995
996/*
997 * Compression methods
998 * This object is filled in as follow :
999 * name : {
1000 * magic // the 2 bytes indentifying the compression method
1001 * compress // function, take the uncompressed content and return it compressed.
1002 * uncompress // function, take the compressed content and return it uncompressed.
1003 * compressInputType // string, the type accepted by the compress method. null to accept everything.
1004 * uncompressInputType // string, the type accepted by the uncompress method. null to accept everything.
1005 * }
1006 *
1007 * STORE is the default compression method, so it's included in this file.
1008 * Other methods should go to separated files : the user wants modularity.
1009 */
1010JSZip.compressions = {
1011 "STORE" : {
1012 magic : "\x00\x00",
1013 compress : function (content) {
1014 return content; // no compression
1015 },
1016 uncompress : function (content) {
1017 return content; // no compression
1018 },
1019 compressInputType : null,
1020 uncompressInputType : null
1021 }
1022};
1023
1024(function () {
1025 JSZip.utils = {
1026 /**
1027 * Convert a string to a "binary string" : a string containing only char codes between 0 and 255.
1028 * @param {string} str the string to transform.
1029 * @return {String} the binary string.
1030 */
1031 string2binary : function (str) {
1032 var result = "";
1033 for (var i = 0; i < str.length; i++) {
1034 result += String.fromCharCode(str.charCodeAt(i) & 0xff);
1035 }
1036 return result;
1037 },
1038 /**
1039 * Create a Uint8Array from the string.
1040 * @param {string} str the string to transform.
1041 * @return {Uint8Array} the typed array.
1042 * @throws {Error} an Error if the browser doesn't support the requested feature.
1043 * @deprecated : use JSZip.utils.transformTo instead.
1044 */
1045 string2Uint8Array : function (str) {
1046 return JSZip.utils.transformTo("uint8array", str);
1047 },
1048
1049 /**
1050 * Create a string from the Uint8Array.
1051 * @param {Uint8Array} array the array to transform.
1052 * @return {string} the string.
1053 * @throws {Error} an Error if the browser doesn't support the requested feature.
1054 * @deprecated : use JSZip.utils.transformTo instead.
1055 */
1056 uint8Array2String : function (array) {
1057 return JSZip.utils.transformTo("string", array);
1058 },
1059 /**
1060 * Create a blob from the given ArrayBuffer.
1061 * @param {ArrayBuffer} buffer the buffer to transform.
1062 * @return {Blob} the result.
1063 * @throws {Error} an Error if the browser doesn't support the requested feature.
1064 */
1065 arrayBuffer2Blob : function (buffer) {
1066 JSZip.utils.checkSupport("blob");
1067
1068 try {
1069 // Blob constructor
1070 return new Blob([buffer], { type: "application/zip" });
1071 }
1072 catch(e) {}
1073
1074 try {
1075 // deprecated, browser only, old way
1076 var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
1077 var builder = new BlobBuilder();
1078 builder.append(buffer);
1079 return builder.getBlob('application/zip');
1080 }
1081 catch(e) {}
1082
1083 // well, fuck ?!
1084 throw new Error("Bug : can't construct the Blob.");
1085 },
1086 /**
1087 * Create a blob from the given string.
1088 * @param {string} str the string to transform.
1089 * @return {Blob} the result.
1090 * @throws {Error} an Error if the browser doesn't support the requested feature.
1091 */
1092 string2Blob : function (str) {
1093 var buffer = JSZip.utils.transformTo("arraybuffer", str);
1094 return JSZip.utils.arrayBuffer2Blob(buffer);
1095 }
1096 };
1097
1098 /**
1099 * The identity function.
1100 * @param {Object} input the input.
1101 * @return {Object} the same input.
1102 */
1103 function identity(input) {
1104 return input;
1105 }
1106
1107 /**
1108 * Fill in an array with a string.
1109 * @param {String} str the string to use.
1110 * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated).
1111 * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array.
1112 */
1113 function stringToArrayLike(str, array) {
1114 for (var i = 0; i < str.length; ++i) {
1115 array[i] = str.charCodeAt(i) & 0xFF;
1116 }
1117 return array;
1118 }
1119
1120 /**
1121 * Transform an array-like object to a string.
1122 * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
1123 * @return {String} the result.
1124 */
1125 function arrayLikeToString(array) {
1126 // Performances notes :
1127 // --------------------
1128 // String.fromCharCode.apply(null, array) is the fastest, see
1129 // see http://jsperf.com/converting-a-uint8array-to-a-string/2
1130 // but the stack is limited (and we can get huge arrays !).
1131 //
1132 // result += String.fromCharCode(array[i]); generate too many strings !
1133 //
1134 // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2
1135 var chunk = 65536;
1136 var result = [], len = array.length, type = JSZip.utils.getTypeOf(array), k = 0;
1137
1138 var canUseApply = true;
1139 try {
1140 switch(type) {
1141 case "uint8array":
1142 String.fromCharCode.apply(null, new Uint8Array(0));
1143 break;
1144 case "nodebuffer":
1145 String.fromCharCode.apply(null, new Buffer(0));
1146 break;
1147 }
1148 } catch(e) {
1149 canUseApply = false;
1150 }
1151
1152 // no apply : slow and painful algorithm
1153 // default browser on android 4.*
1154 if (!canUseApply) {
1155 var resultStr = "";
1156 for(var i = 0; i < array.length;i++) {
1157 resultStr += String.fromCharCode(array[i]);
1158 }
1159 return resultStr;
1160 }
1161
1162 while (k < len && chunk > 1) {
1163 try {
1164 if (type === "array" || type === "nodebuffer") {
1165 result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len))));
1166 } else {
1167 result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len))));
1168 }
1169 k += chunk;
1170 } catch (e) {
1171 chunk = Math.floor(chunk / 2);
1172 }
1173 }
1174 return result.join("");
1175 }
1176
1177 /**
1178 * Copy the data from an array-like to an other array-like.
1179 * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array.
1180 * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated.
1181 * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array.
1182 */
1183 function arrayLikeToArrayLike(arrayFrom, arrayTo) {
1184 for(var i = 0; i < arrayFrom.length; i++) {
1185 arrayTo[i] = arrayFrom[i];
1186 }
1187 return arrayTo;
1188 }
1189
1190 // a matrix containing functions to transform everything into everything.
1191 var transform = {};
1192
1193 // string to ?
1194 transform["string"] = {
1195 "string" : identity,
1196 "array" : function (input) {
1197 return stringToArrayLike(input, new Array(input.length));
1198 },
1199 "arraybuffer" : function (input) {
1200 return transform["string"]["uint8array"](input).buffer;
1201 },
1202 "uint8array" : function (input) {
1203 return stringToArrayLike(input, new Uint8Array(input.length));
1204 },
1205 "nodebuffer" : function (input) {
1206 return stringToArrayLike(input, new Buffer(input.length));
1207 }
1208 };
1209
1210 // array to ?
1211 transform["array"] = {
1212 "string" : arrayLikeToString,
1213 "array" : identity,
1214 "arraybuffer" : function (input) {
1215 return (new Uint8Array(input)).buffer;
1216 },
1217 "uint8array" : function (input) {
1218 return new Uint8Array(input);
1219 },
1220 "nodebuffer" : function (input) {
1221 return new Buffer(input);
1222 }
1223 };
1224
1225 // arraybuffer to ?
1226 transform["arraybuffer"] = {
1227 "string" : function (input) {
1228 return arrayLikeToString(new Uint8Array(input));
1229 },
1230 "array" : function (input) {
1231 return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength));
1232 },
1233 "arraybuffer" : identity,
1234 "uint8array" : function (input) {
1235 return new Uint8Array(input);
1236 },
1237 "nodebuffer" : function (input) {
1238 return new Buffer(new Uint8Array(input));
1239 }
1240 };
1241
1242 // uint8array to ?
1243 transform["uint8array"] = {
1244 "string" : arrayLikeToString,
1245 "array" : function (input) {
1246 return arrayLikeToArrayLike(input, new Array(input.length));
1247 },
1248 "arraybuffer" : function (input) {
1249 return input.buffer;
1250 },
1251 "uint8array" : identity,
1252 "nodebuffer" : function(input) {
1253 return new Buffer(input);
1254 }
1255 };
1256
1257 // nodebuffer to ?
1258 transform["nodebuffer"] = {
1259 "string" : arrayLikeToString,
1260 "array" : function (input) {
1261 return arrayLikeToArrayLike(input, new Array(input.length));
1262 },
1263 "arraybuffer" : function (input) {
1264 return transform["nodebuffer"]["uint8array"](input).buffer;
1265 },
1266 "uint8array" : function (input) {
1267 return arrayLikeToArrayLike(input, new Uint8Array(input.length));
1268 },
1269 "nodebuffer" : identity
1270 };
1271
1272 /**
1273 * Transform an input into any type.
1274 * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer.
1275 * If no output type is specified, the unmodified input will be returned.
1276 * @param {String} outputType the output type.
1277 * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert.
1278 * @throws {Error} an Error if the browser doesn't support the requested output type.
1279 */
1280 JSZip.utils.transformTo = function (outputType, input) {
1281 if (!input) {
1282 // undefined, null, etc
1283 // an empty string won't harm.
1284 input = "";
1285 }
1286 if (!outputType) {
1287 return input;
1288 }
1289 JSZip.utils.checkSupport(outputType);
1290 var inputType = JSZip.utils.getTypeOf(input);
1291 var result = transform[inputType][outputType](input);
1292 return result;
1293 };
1294
1295 /**
1296 * Return the type of the input.
1297 * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer.
1298 * @param {Object} input the input to identify.
1299 * @return {String} the (lowercase) type of the input.
1300 */
1301 JSZip.utils.getTypeOf = function (input) {
1302 if (typeof input === "string") {
1303 return "string";
1304 }
1305 if (Object.prototype.toString.call(input) === "[object Array]") {
1306 return "array";
1307 }
1308 if (JSZip.support.nodebuffer && Buffer.isBuffer(input)) {
1309 return "nodebuffer";
1310 }
1311 if (JSZip.support.uint8array && input instanceof Uint8Array) {
1312 return "uint8array";
1313 }
1314 if (JSZip.support.arraybuffer && input instanceof ArrayBuffer) {
1315 return "arraybuffer";
1316 }
1317 };
1318
1319 /**
1320 * Cross-window, cross-Node-context regular expression detection
1321 * @param {Object} object Anything
1322 * @return {Boolean} true if the object is a regular expression,
1323 * false otherwise
1324 */
1325 JSZip.utils.isRegExp = function (object) {
1326 return Object.prototype.toString.call(object) === "[object RegExp]";
1327 };
1328
1329 /**
1330 * Throw an exception if the type is not supported.
1331 * @param {String} type the type to check.
1332 * @throws {Error} an Error if the browser doesn't support the requested type.
1333 */
1334 JSZip.utils.checkSupport = function (type) {
1335 var supported = true;
1336 switch (type.toLowerCase()) {
1337 case "uint8array":
1338 supported = JSZip.support.uint8array;
1339 break;
1340 case "arraybuffer":
1341 supported = JSZip.support.arraybuffer;
1342 break;
1343 case "nodebuffer":
1344 supported = JSZip.support.nodebuffer;
1345 break;
1346 case "blob":
1347 supported = JSZip.support.blob;
1348 break;
1349 }
1350 if (!supported) {
1351 throw new Error(type + " is not supported by this browser");
1352 }
1353 };
1354
1355
1356})();
1357
1358(function (){
1359 /**
1360 * Represents an entry in the zip.
1361 * The content may or may not be compressed.
1362 * @constructor
1363 */
1364 JSZip.CompressedObject = function () {
1365 this.compressedSize = 0;
1366 this.uncompressedSize = 0;
1367 this.crc32 = 0;
1368 this.compressionMethod = null;
1369 this.compressedContent = null;
1370 };
1371
1372 JSZip.CompressedObject.prototype = {
1373 /**
1374 * Return the decompressed content in an unspecified format.
1375 * The format will depend on the decompressor.
1376 * @return {Object} the decompressed content.
1377 */
1378 getContent : function () {
1379 return null; // see implementation
1380 },
1381 /**
1382 * Return the compressed content in an unspecified format.
1383 * The format will depend on the compressed conten source.
1384 * @return {Object} the compressed content.
1385 */
1386 getCompressedContent : function () {
1387 return null; // see implementation
1388 }
1389 };
1390})();
1391
1392/**
1393 *
1394 * Base64 encode / decode
1395 * http://www.webtoolkit.info/
1396 *
1397 * Hacked so that it doesn't utf8 en/decode everything
1398 **/
1399JSZip.base64 = (function() {
1400 // private property
1401 var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1402
1403 return {
1404 // public method for encoding
1405 encode : function(input, utf8) {
1406 var output = "";
1407 var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
1408 var i = 0;
1409
1410 while (i < input.length) {
1411
1412 chr1 = input.charCodeAt(i++);
1413 chr2 = input.charCodeAt(i++);
1414 chr3 = input.charCodeAt(i++);
1415
1416 enc1 = chr1 >> 2;
1417 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
1418 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
1419 enc4 = chr3 & 63;
1420
1421 if (isNaN(chr2)) {
1422 enc3 = enc4 = 64;
1423 } else if (isNaN(chr3)) {
1424 enc4 = 64;
1425 }
1426
1427 output = output +
1428 _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
1429 _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
1430
1431 }
1432
1433 return output;
1434 },
1435
1436 // public method for decoding
1437 decode : function(input, utf8) {
1438 var output = "";
1439 var chr1, chr2, chr3;
1440 var enc1, enc2, enc3, enc4;
1441 var i = 0;
1442
1443 input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
1444
1445 while (i < input.length) {
1446
1447 enc1 = _keyStr.indexOf(input.charAt(i++));
1448 enc2 = _keyStr.indexOf(input.charAt(i++));
1449 enc3 = _keyStr.indexOf(input.charAt(i++));
1450 enc4 = _keyStr.indexOf(input.charAt(i++));
1451
1452 chr1 = (enc1 << 2) | (enc2 >> 4);
1453 chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
1454 chr3 = ((enc3 & 3) << 6) | enc4;
1455
1456 output = output + String.fromCharCode(chr1);
1457
1458 if (enc3 != 64) {
1459 output = output + String.fromCharCode(chr2);
1460 }
1461 if (enc4 != 64) {
1462 output = output + String.fromCharCode(chr3);
1463 }
1464
1465 }
1466
1467 return output;
1468
1469 }
1470 };
1471}());
1472
1473// enforcing Stuk's coding style
1474// vim: set shiftwidth=3 softtabstop=3: