comparison geotemco/lib/jszip/jszip.js @ 0:57bde4830927

first commit
author Zoe Hong <zhong@mpiwg-berlin.mpg.de>
date Tue, 24 Mar 2015 11:37:17 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:57bde4830927
1 /**
2
3 JSZip - 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>
7 Dual licenced under the MIT license or GPLv3. See LICENSE.markdown.
8
9 Usage:
10 zip = new JSZip();
11 zip.file("hello.txt", "Hello, World!").add("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
20 /**
21 * Representation a of zip file in js
22 * @constructor
23 * @param {String=|ArrayBuffer=|Uint8Array=} data the data to load, if any (optional).
24 * @param {Object=} options the options for creating this objects (optional).
25 */
26 var JSZip = function(data, options) {
27 // object containing the files :
28 // {
29 // "folder/" : {...},
30 // "folder/data.txt" : {...}
31 // }
32 this.files = {};
33
34 // Where we are in the hierarchy
35 this.root = "";
36
37 if (data) {
38 this.load(data, options);
39 }
40 };
41
42 JSZip.signature = {
43 LOCAL_FILE_HEADER : "\x50\x4b\x03\x04",
44 CENTRAL_FILE_HEADER : "\x50\x4b\x01\x02",
45 CENTRAL_DIRECTORY_END : "\x50\x4b\x05\x06",
46 ZIP64_CENTRAL_DIRECTORY_LOCATOR : "\x50\x4b\x06\x07",
47 ZIP64_CENTRAL_DIRECTORY_END : "\x50\x4b\x06\x06",
48 DATA_DESCRIPTOR : "\x50\x4b\x07\x08"
49 };
50
51 // Default properties for a new file
52 JSZip.defaults = {
53 base64: false,
54 binary: false,
55 dir: false,
56 date: null
57 };
58
59
60 JSZip.prototype = (function () {
61 /**
62 * A simple object representing a file in the zip file.
63 * @constructor
64 * @param {string} name the name of the file
65 * @param {string} data the data
66 * @param {Object} options the options of the file
67 */
68 var ZipObject = function (name, data, options) {
69 this.name = name;
70 this.data = data;
71 this.options = options;
72 };
73
74 ZipObject.prototype = {
75 /**
76 * Return the content as UTF8 string.
77 * @return {string} the UTF8 string.
78 */
79 asText : function () {
80 var result = this.data;
81 if (result === null || typeof result === "undefined") {
82 return "";
83 }
84 if (this.options.base64) {
85 result = JSZipBase64.decode(result);
86 }
87 if (this.options.binary) {
88 result = JSZip.prototype.utf8decode(result);
89 }
90 return result;
91 },
92 /**
93 * Returns the binary content.
94 * @return {string} the content as binary.
95 */
96 asBinary : function () {
97 var result = this.data;
98 if (result === null || typeof result === "undefined") {
99 return "";
100 }
101 if (this.options.base64) {
102 result = JSZipBase64.decode(result);
103 }
104 if (!this.options.binary) {
105 result = JSZip.prototype.utf8encode(result);
106 }
107 return result;
108 },
109 /**
110 * Returns the content as an Uint8Array.
111 * @return {Uint8Array} the content as an Uint8Array.
112 */
113 asUint8Array : function () {
114 return JSZip.utils.string2Uint8Array(this.asBinary());
115 },
116 /**
117 * Returns the content as an ArrayBuffer.
118 * @return {ArrayBuffer} the content as an ArrayBufer.
119 */
120 asArrayBuffer : function () {
121 return JSZip.utils.string2Uint8Array(this.asBinary()).buffer;
122 }
123 };
124
125 /**
126 * Transform an integer into a string in hexadecimal.
127 * @private
128 * @param {number} dec the number to convert.
129 * @param {number} bytes the number of bytes to generate.
130 * @returns {string} the result.
131 */
132 var decToHex = function(dec, bytes) {
133 var hex = "", i;
134 for(i = 0; i < bytes; i++) {
135 hex += String.fromCharCode(dec&0xff);
136 dec=dec>>>8;
137 }
138 return hex;
139 };
140
141 /**
142 * Merge the objects passed as parameters into a new one.
143 * @private
144 * @param {...Object} var_args All objects to merge.
145 * @return {Object} a new object with the data of the others.
146 */
147 var extend = function () {
148 var result = {}, i, attr;
149 for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
150 for (attr in arguments[i]) {
151 if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
152 result[attr] = arguments[i][attr];
153 }
154 }
155 }
156 return result;
157 };
158
159 /**
160 * Transforms the (incomplete) options from the user into the complete
161 * set of options to create a file.
162 * @private
163 * @param {Object} o the options from the user.
164 * @return {Object} the complete set of options.
165 */
166 var prepareFileAttrs = function (o) {
167 o = o || {};
168 if (o.base64 === true && o.binary == null) {
169 o.binary = true;
170 }
171 o = extend(o, JSZip.defaults);
172 o.date = o.date || new Date();
173
174 return o;
175 };
176
177 /**
178 * Add a file in the current folder.
179 * @private
180 * @param {string} name the name of the file
181 * @param {String|ArrayBuffer|Uint8Array} data the data of the file
182 * @param {Object} o the options of the file
183 * @return {Object} the new file.
184 */
185 var fileAdd = function (name, data, o) {
186 // be sure sub folders exist
187 var parent = parentFolder(name);
188 if (parent) {
189 folderAdd.call(this, parent);
190 }
191
192 o = prepareFileAttrs(o);
193
194 if (o.dir || data === null || typeof data === "undefined") {
195 o.base64 = false;
196 o.binary = false;
197 data = null;
198 } else if (JSZip.support.uint8array && data instanceof Uint8Array) {
199 o.base64 = false;
200 o.binary = true;
201 data = JSZip.utils.uint8Array2String(data);
202 } else if (JSZip.support.arraybuffer && data instanceof ArrayBuffer) {
203 o.base64 = false;
204 o.binary = true;
205 var bufferView = new Uint8Array(data);
206 data = JSZip.utils.uint8Array2String(bufferView);
207 } else if (o.binary && !o.base64) {
208 // optimizedBinaryString == true means that the file has already been filtered with a 0xFF mask
209 if (o.optimizedBinaryString !== true) {
210 // this is a string, not in a base64 format.
211 // Be sure that this is a correct "binary string"
212 data = JSZip.utils.string2binary(data);
213 }
214 // we remove this option since it's only relevant here
215 delete o.optimizedBinaryString;
216 }
217
218 return this.files[name] = new ZipObject(name, data, o);
219 };
220
221
222 /**
223 * Find the parent folder of the path.
224 * @private
225 * @param {string} path the path to use
226 * @return {string} the parent folder, or ""
227 */
228 var parentFolder = function (path) {
229 if (path.slice(-1) == '/') {
230 path = path.substring(0, path.length - 1);
231 }
232 var lastSlash = path.lastIndexOf('/');
233 return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
234 };
235
236 /**
237 * Add a (sub) folder in the current folder.
238 * @private
239 * @param {string} name the folder's name
240 * @return {Object} the new folder.
241 */
242 var folderAdd = function (name) {
243 // Check the name ends with a /
244 if (name.slice(-1) != "/") {
245 name += "/"; // IE doesn't like substr(-1)
246 }
247
248 // Does this folder already exist?
249 if (!this.files[name]) {
250 // be sure sub folders exist
251 var parent = parentFolder(name);
252 if (parent) {
253 folderAdd.call(this, parent);
254 }
255
256 fileAdd.call(this, name, null, {dir:true});
257 }
258 return this.files[name];
259 };
260
261 /**
262 * Generate the data found in the local header of a zip file.
263 * Do not create it now, as some parts are re-used later.
264 * @private
265 * @param {Object} file the file to use.
266 * @param {string} utfEncodedFileName the file name, utf8 encoded.
267 * @param {string} compressionType the compression to use.
268 * @return {Object} an object containing header and compressedData.
269 */
270 var prepareLocalHeaderData = function(file, utfEncodedFileName, compressionType) {
271 var useUTF8 = utfEncodedFileName !== file.name,
272 data = file.asBinary(),
273 o = file.options,
274 dosTime,
275 dosDate;
276
277 // date
278 // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
279 // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
280 // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
281
282 dosTime = o.date.getHours();
283 dosTime = dosTime << 6;
284 dosTime = dosTime | o.date.getMinutes();
285 dosTime = dosTime << 5;
286 dosTime = dosTime | o.date.getSeconds() / 2;
287
288 dosDate = o.date.getFullYear() - 1980;
289 dosDate = dosDate << 4;
290 dosDate = dosDate | (o.date.getMonth() + 1);
291 dosDate = dosDate << 5;
292 dosDate = dosDate | o.date.getDate();
293
294 var hasData = data !== null && data.length !== 0;
295
296 var compression = JSZip.compressions[compressionType];
297 var compressedData = hasData ? compression.compress(data) : '';
298
299 var header = "";
300
301 // version needed to extract
302 header += "\x0A\x00";
303 // general purpose bit flag
304 // set bit 11 if utf8
305 header += useUTF8 ? "\x00\x08" : "\x00\x00";
306 // compression method
307 header += hasData ? compression.magic : JSZip.compressions['STORE'].magic;
308 // last mod file time
309 header += decToHex(dosTime, 2);
310 // last mod file date
311 header += decToHex(dosDate, 2);
312 // crc-32
313 header += hasData ? decToHex(this.crc32(data), 4) : '\x00\x00\x00\x00';
314 // compressed size
315 header += hasData ? decToHex(compressedData.length, 4) : '\x00\x00\x00\x00';
316 // uncompressed size
317 header += hasData ? decToHex(data.length, 4) : '\x00\x00\x00\x00';
318 // file name length
319 header += decToHex(utfEncodedFileName.length, 2);
320 // extra field length
321 header += "\x00\x00";
322
323 return {
324 header:header,
325 compressedData:compressedData
326 };
327 };
328
329
330 // return the actual prototype of JSZip
331 return {
332 /**
333 * Read an existing zip and merge the data in the current JSZip object.
334 * The implementation is in jszip-load.js, don't forget to include it.
335 * @param {String|ArrayBuffer|Uint8Array} stream The stream to load
336 * @param {Object} options Options for loading the stream.
337 * options.base64 : is the stream in base64 ? default : false
338 * @return {JSZip} the current JSZip object
339 */
340 load : function (stream, options) {
341 throw new Error("Load method is not defined. Is the file jszip-load.js included ?");
342 },
343
344 /**
345 * Filter nested files/folders with the specified function.
346 * @param {Function} search the predicate to use :
347 * function (relativePath, file) {...}
348 * It takes 2 arguments : the relative path and the file.
349 * @return {Array} An array of matching elements.
350 */
351 filter : function (search) {
352 var result = [], filename, relativePath, file, fileClone;
353 for (filename in this.files) {
354 if ( !this.files.hasOwnProperty(filename) ) { continue; }
355 file = this.files[filename];
356 // return a new object, don't let the user mess with our internal objects :)
357 fileClone = new ZipObject(file.name, file.data, extend(file.options));
358 relativePath = filename.slice(this.root.length, filename.length);
359 if (filename.slice(0, this.root.length) === this.root && // the file is in the current root
360 search(relativePath, fileClone)) { // and the file matches the function
361 result.push(fileClone);
362 }
363 }
364 return result;
365 },
366
367 /**
368 * Add a file to the zip file, or search a file.
369 * @param {string|RegExp} name The name of the file to add (if data is defined),
370 * the name of the file to find (if no data) or a regex to match files.
371 * @param {String|ArrayBuffer|Uint8Array} data The file data, either raw or base64 encoded
372 * @param {Object} o File options
373 * @return {JSZip|Object|Array} this JSZip object (when adding a file),
374 * a file (when searching by string) or an array of files (when searching by regex).
375 */
376 file : function(name, data, o) {
377 if (arguments.length === 1) {
378 if (name instanceof RegExp) {
379 var regexp = name;
380 return this.filter(function(relativePath, file) {
381 return !file.options.dir && regexp.test(relativePath);
382 });
383 } else { // text
384 return this.filter(function (relativePath, file) {
385 return !file.options.dir && relativePath === name;
386 })[0]||null;
387 }
388 } else { // more than one argument : we have data !
389 name = this.root+name;
390 fileAdd.call(this, name, data, o);
391 }
392 return this;
393 },
394
395 /**
396 * Add a directory to the zip file, or search.
397 * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders.
398 * @return {JSZip} an object with the new directory as the root, or an array containing matching folders.
399 */
400 folder : function(arg) {
401 if (!arg) {
402 return this;
403 }
404
405 if (arg instanceof RegExp) {
406 return this.filter(function(relativePath, file) {
407 return file.options.dir && arg.test(relativePath);
408 });
409 }
410
411 // else, name is a new folder
412 var name = this.root + arg;
413 var newFolder = folderAdd.call(this, name);
414
415 // Allow chaining by returning a new object with this folder as the root
416 var ret = this.clone();
417 ret.root = newFolder.name;
418 return ret;
419 },
420
421 /**
422 * Delete a file, or a directory and all sub-files, from the zip
423 * @param {string} name the name of the file to delete
424 * @return {JSZip} this JSZip object
425 */
426 remove : function(name) {
427 name = this.root + name;
428 var file = this.files[name];
429 if (!file) {
430 // Look for any folders
431 if (name.slice(-1) != "/") {
432 name += "/";
433 }
434 file = this.files[name];
435 }
436
437 if (file) {
438 if (!file.options.dir) {
439 // file
440 delete this.files[name];
441 } else {
442 // folder
443 var kids = this.filter(function (relativePath, file) {
444 return file.name.slice(0, name.length) === name;
445 });
446 for (var i = 0; i < kids.length; i++) {
447 delete this.files[kids[i].name];
448 }
449 }
450 }
451
452 return this;
453 },
454
455 /**
456 * Generate the complete zip file
457 * @param {Object} options the options to generate the zip file :
458 * - base64, (deprecated, use type instead) true to generate base64.
459 * - compression, "STORE" by default.
460 * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
461 * @return {String|Uint8Array|ArrayBuffer|Blob} the zip file
462 */
463 generate : function(options) {
464 options = extend(options || {}, {
465 base64 : true,
466 compression : "STORE",
467 type : "base64"
468 });
469 var compression = options.compression.toUpperCase();
470
471 // The central directory, and files data
472 var directory = [], files = [], fileOffset = 0;
473
474 if (!JSZip.compressions[compression]) {
475 throw compression + " is not a valid compression method !";
476 }
477
478 for (var name in this.files) {
479 if ( !this.files.hasOwnProperty(name) ) { continue; }
480
481 var file = this.files[name];
482
483 var utfEncodedFileName = this.utf8encode(file.name);
484
485 var fileRecord = "",
486 dirRecord = "",
487 data = prepareLocalHeaderData.call(this, file, utfEncodedFileName, compression);
488 fileRecord = JSZip.signature.LOCAL_FILE_HEADER + data.header + utfEncodedFileName + data.compressedData;
489
490 dirRecord = JSZip.signature.CENTRAL_FILE_HEADER +
491 // version made by (00: DOS)
492 "\x14\x00" +
493 // file header (common to file and central directory)
494 data.header +
495 // file comment length
496 "\x00\x00" +
497 // disk number start
498 "\x00\x00" +
499 // internal file attributes TODO
500 "\x00\x00" +
501 // external file attributes
502 (this.files[name].options.dir===true?"\x10\x00\x00\x00":"\x00\x00\x00\x00")+
503 // relative offset of local header
504 decToHex(fileOffset, 4) +
505 // file name
506 utfEncodedFileName;
507
508 fileOffset += fileRecord.length;
509
510 files.push(fileRecord);
511 directory.push(dirRecord);
512 }
513
514 var fileData = files.join("");
515 var dirData = directory.join("");
516
517 var dirEnd = "";
518
519 // end of central dir signature
520 dirEnd = JSZip.signature.CENTRAL_DIRECTORY_END +
521 // number of this disk
522 "\x00\x00" +
523 // number of the disk with the start of the central directory
524 "\x00\x00" +
525 // total number of entries in the central directory on this disk
526 decToHex(files.length, 2) +
527 // total number of entries in the central directory
528 decToHex(files.length, 2) +
529 // size of the central directory 4 bytes
530 decToHex(dirData.length, 4) +
531 // offset of start of central directory with respect to the starting disk number
532 decToHex(fileData.length, 4) +
533 // .ZIP file comment length
534 "\x00\x00";
535
536 var zip = fileData + dirData + dirEnd;
537
538
539 switch(options.type.toLowerCase()) {
540 case "uint8array" :
541 return JSZip.utils.string2Uint8Array(zip);
542 case "arraybuffer" :
543 return JSZip.utils.string2Uint8Array(zip).buffer;
544 case "blob" :
545 return JSZip.utils.string2Blob(zip);
546 case "base64" :
547 return (options.base64) ? JSZipBase64.encode(zip) : zip;
548 default : // case "string" :
549 return zip;
550 }
551 },
552
553 /**
554 *
555 * Javascript crc32
556 * http://www.webtoolkit.info/
557 *
558 */
559 crc32 : function(str, crc) {
560
561 if (str === "" || typeof str === "undefined") {
562 return 0;
563 }
564
565 var table = [
566 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
567 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
568 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
569 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
570 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
571 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
572 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
573 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
574 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
575 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
576 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
577 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
578 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
579 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
580 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
581 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
582 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
583 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
584 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
585 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
586 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
587 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
588 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
589 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
590 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
591 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
592 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
593 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
594 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
595 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
596 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
597 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
598 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
599 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
600 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
601 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
602 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
603 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
604 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
605 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
606 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
607 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
608 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
609 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
610 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
611 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
612 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
613 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
614 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
615 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
616 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
617 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
618 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
619 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
620 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
621 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
622 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
623 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
624 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
625 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
626 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
627 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
628 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
629 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
630 ];
631
632 if (typeof(crc) == "undefined") { crc = 0; }
633 var x = 0;
634 var y = 0;
635
636 crc = crc ^ (-1);
637 for( var i = 0, iTop = str.length; i < iTop; i++ ) {
638 y = ( crc ^ str.charCodeAt( i ) ) & 0xFF;
639 x = table[y];
640 crc = ( crc >>> 8 ) ^ x;
641 }
642
643 return crc ^ (-1);
644 },
645
646 // Inspired by http://my.opera.com/GreyWyvern/blog/show.dml/1725165
647 clone : function() {
648 var newObj = new JSZip();
649 for (var i in this) {
650 if (typeof this[i] !== "function") {
651 newObj[i] = this[i];
652 }
653 }
654 return newObj;
655 },
656
657
658 /**
659 * http://www.webtoolkit.info/javascript-utf8.html
660 */
661 utf8encode : function (string) {
662 string = string.replace(/\r\n/g,"\n");
663 var utftext = "";
664
665 for (var n = 0; n < string.length; n++) {
666
667 var c = string.charCodeAt(n);
668
669 if (c < 128) {
670 utftext += String.fromCharCode(c);
671 } else if ((c > 127) && (c < 2048)) {
672 utftext += String.fromCharCode((c >> 6) | 192);
673 utftext += String.fromCharCode((c & 63) | 128);
674 } else {
675 utftext += String.fromCharCode((c >> 12) | 224);
676 utftext += String.fromCharCode(((c >> 6) & 63) | 128);
677 utftext += String.fromCharCode((c & 63) | 128);
678 }
679
680 }
681
682 return utftext;
683 },
684
685 /**
686 * http://www.webtoolkit.info/javascript-utf8.html
687 */
688 utf8decode : function (utftext) {
689 var string = "";
690 var i = 0;
691 var c = 0, c1 = 0, c2 = 0, c3 = 0;
692
693 while ( i < utftext.length ) {
694
695 c = utftext.charCodeAt(i);
696
697 if (c < 128) {
698 string += String.fromCharCode(c);
699 i++;
700 } else if ((c > 191) && (c < 224)) {
701 c2 = utftext.charCodeAt(i+1);
702 string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
703 i += 2;
704 } else {
705 c2 = utftext.charCodeAt(i+1);
706 c3 = utftext.charCodeAt(i+2);
707 string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
708 i += 3;
709 }
710
711 }
712
713 return string;
714 }
715 };
716 }());
717
718 /*
719 * Compression methods
720 * This object is filled in as follow :
721 * name : {
722 * magic // the 2 bytes indentifying the compression method
723 * compress // function, take the uncompressed content and return it compressed.
724 * uncompress // function, take the compressed content and return it uncompressed.
725 * }
726 *
727 * STORE is the default compression method, so it's included in this file.
728 * Other methods should go to separated files : the user wants modularity.
729 */
730 JSZip.compressions = {
731 "STORE" : {
732 magic : "\x00\x00",
733 compress : function (content) {
734 return content; // no compression
735 },
736 uncompress : function (content) {
737 return content; // no compression
738 }
739 }
740 };
741
742 /*
743 * List features that require a modern browser, and if the current browser support them.
744 */
745 JSZip.support = {
746 // contains true if JSZip can read/generate ArrayBuffer, false otherwise.
747 arraybuffer : (function(){
748 return typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
749 })(),
750 // contains true if JSZip can read/generate Uint8Array, false otherwise.
751 uint8array : (function(){
752 return typeof Uint8Array !== "undefined";
753 })(),
754 // contains true if JSZip can read/generate Blob, false otherwise.
755 blob : (function(){
756 // the spec started with BlobBuilder then replaced it with a construtor for Blob.
757 // Result : we have browsers that :
758 // * know the BlobBuilder (but with prefix)
759 // * know the Blob constructor
760 // * know about Blob but not about how to build them
761 // About the "=== 0" test : if given the wrong type, it may be converted to a string.
762 // Instead of an empty content, we will get "[object Uint8Array]" for example.
763 if (typeof ArrayBuffer === "undefined") {
764 return false;
765 }
766 var buffer = new ArrayBuffer(0);
767 try {
768 return new Blob([buffer], { type: "application/zip" }).size === 0;
769 }
770 catch(e) {}
771
772 try {
773 var builder = new (window.BlobBuilder || window.WebKitBlobBuilder ||
774 window.MozBlobBuilder || window.MSBlobBuilder)();
775 builder.append(buffer);
776 return builder.getBlob('application/zip').size === 0;
777 }
778 catch(e) {}
779
780 return false;
781 })()
782 };
783
784 JSZip.utils = {
785 /**
786 * Convert a string to a "binary string" : a string containing only char codes between 0 and 255.
787 * @param {string} str the string to transform.
788 * @return {String} the binary string.
789 */
790 string2binary : function (str) {
791 var result = "";
792 for (var i = 0; i < str.length; i++) {
793 result += String.fromCharCode(str.charCodeAt(i) & 0xff);
794 }
795 return result;
796 },
797 /**
798 * Create a Uint8Array from the string.
799 * @param {string} str the string to transform.
800 * @return {Uint8Array} the typed array.
801 * @throws {Error} an Error if the browser doesn't support the requested feature.
802 */
803 string2Uint8Array : function (str) {
804 if (!JSZip.support.uint8array) {
805 throw new Error("Uint8Array is not supported by this browser");
806 }
807 var buffer = new ArrayBuffer(str.length);
808 var bufferView = new Uint8Array(buffer);
809 for(var i = 0; i < str.length; i++) {
810 bufferView[i] = str.charCodeAt(i);
811 }
812
813 return bufferView;
814 },
815
816 /**
817 * Create a string from the Uint8Array.
818 * @param {Uint8Array} array the array to transform.
819 * @return {string} the string.
820 * @throws {Error} an Error if the browser doesn't support the requested feature.
821 */
822 uint8Array2String : function (array) {
823 if (!JSZip.support.uint8array) {
824 throw new Error("Uint8Array is not supported by this browser");
825 }
826 var result = "";
827 for(var i = 0; i < array.length; i++) {
828 result += String.fromCharCode(array[i]);
829 }
830
831 return result;
832 },
833 /**
834 * Create a blob from the given string.
835 * @param {string} str the string to transform.
836 * @return {Blob} the string.
837 * @throws {Error} an Error if the browser doesn't support the requested feature.
838 */
839 string2Blob : function (str) {
840 if (!JSZip.support.blob) {
841 throw new Error("Blob is not supported by this browser");
842 }
843
844 var buffer = JSZip.utils.string2Uint8Array(str).buffer;
845 try {
846 // Blob constructor
847 return new Blob([buffer], { type: "application/zip" });
848 }
849 catch(e) {}
850
851 try {
852 // deprecated, browser only, old way
853 var builder = new (window.BlobBuilder || window.WebKitBlobBuilder ||
854 window.MozBlobBuilder || window.MSBlobBuilder)();
855 builder.append(buffer);
856 return builder.getBlob('application/zip');
857 }
858 catch(e) {}
859
860 // well, fuck ?!
861 throw new Error("Bug : can't construct the Blob.");
862 }
863 };
864
865 /**
866 *
867 * Base64 encode / decode
868 * http://www.webtoolkit.info/
869 *
870 * Hacked so that it doesn't utf8 en/decode everything
871 **/
872 var JSZipBase64 = (function() {
873 // private property
874 var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
875
876 return {
877 // public method for encoding
878 encode : function(input, utf8) {
879 var output = "";
880 var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
881 var i = 0;
882
883 while (i < input.length) {
884
885 chr1 = input.charCodeAt(i++);
886 chr2 = input.charCodeAt(i++);
887 chr3 = input.charCodeAt(i++);
888
889 enc1 = chr1 >> 2;
890 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
891 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
892 enc4 = chr3 & 63;
893
894 if (isNaN(chr2)) {
895 enc3 = enc4 = 64;
896 } else if (isNaN(chr3)) {
897 enc4 = 64;
898 }
899
900 output = output +
901 _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
902 _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
903
904 }
905
906 return output;
907 },
908
909 // public method for decoding
910 decode : function(input, utf8) {
911 var output = "";
912 var chr1, chr2, chr3;
913 var enc1, enc2, enc3, enc4;
914 var i = 0;
915
916 input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
917
918 while (i < input.length) {
919
920 enc1 = _keyStr.indexOf(input.charAt(i++));
921 enc2 = _keyStr.indexOf(input.charAt(i++));
922 enc3 = _keyStr.indexOf(input.charAt(i++));
923 enc4 = _keyStr.indexOf(input.charAt(i++));
924
925 chr1 = (enc1 << 2) | (enc2 >> 4);
926 chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
927 chr3 = ((enc3 & 3) << 6) | enc4;
928
929 output = output + String.fromCharCode(chr1);
930
931 if (enc3 != 64) {
932 output = output + String.fromCharCode(chr2);
933 }
934 if (enc4 != 64) {
935 output = output + String.fromCharCode(chr3);
936 }
937
938 }
939
940 return output;
941
942 }
943 };
944 }());
945
946 // enforcing Stuk's coding style
947 // vim: set shiftwidth=3 softtabstop=3: