0
|
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:
|