Implemented URL query parsing for initial token /opa/?token=abcde
[src/app-framework-demo.git] / afb-client / bower_components / jszip / lib / object.js
1 'use strict';
2 var support = require('./support');
3 var utils = require('./utils');
4 var crc32 = require('./crc32');
5 var signature = require('./signature');
6 var defaults = require('./defaults');
7 var base64 = require('./base64');
8 var compressions = require('./compressions');
9 var CompressedObject = require('./compressedObject');
10 var nodeBuffer = require('./nodeBuffer');
11 var utf8 = require('./utf8');
12 var StringWriter = require('./stringWriter');
13 var Uint8ArrayWriter = require('./uint8ArrayWriter');
14
15 /**
16  * Returns the raw data of a ZipObject, decompress the content if necessary.
17  * @param {ZipObject} file the file to use.
18  * @return {String|ArrayBuffer|Uint8Array|Buffer} the data.
19  */
20 var getRawData = function(file) {
21     if (file._data instanceof CompressedObject) {
22         file._data = file._data.getContent();
23         file.options.binary = true;
24         file.options.base64 = false;
25
26         if (utils.getTypeOf(file._data) === "uint8array") {
27             var copy = file._data;
28             // when reading an arraybuffer, the CompressedObject mechanism will keep it and subarray() a Uint8Array.
29             // if we request a file in the same format, we might get the same Uint8Array or its ArrayBuffer (the original zip file).
30             file._data = new Uint8Array(copy.length);
31             // with an empty Uint8Array, Opera fails with a "Offset larger than array size"
32             if (copy.length !== 0) {
33                 file._data.set(copy, 0);
34             }
35         }
36     }
37     return file._data;
38 };
39
40 /**
41  * Returns the data of a ZipObject in a binary form. If the content is an unicode string, encode it.
42  * @param {ZipObject} file the file to use.
43  * @return {String|ArrayBuffer|Uint8Array|Buffer} the data.
44  */
45 var getBinaryData = function(file) {
46     var result = getRawData(file),
47         type = utils.getTypeOf(result);
48     if (type === "string") {
49         if (!file.options.binary) {
50             // unicode text !
51             // unicode string => binary string is a painful process, check if we can avoid it.
52             if (support.nodebuffer) {
53                 return nodeBuffer(result, "utf-8");
54             }
55         }
56         return file.asBinary();
57     }
58     return result;
59 };
60
61 /**
62  * Transform this._data into a string.
63  * @param {function} filter a function String -> String, applied if not null on the result.
64  * @return {String} the string representing this._data.
65  */
66 var dataToString = function(asUTF8) {
67     var result = getRawData(this);
68     if (result === null || typeof result === "undefined") {
69         return "";
70     }
71     // if the data is a base64 string, we decode it before checking the encoding !
72     if (this.options.base64) {
73         result = base64.decode(result);
74     }
75     if (asUTF8 && this.options.binary) {
76         // JSZip.prototype.utf8decode supports arrays as input
77         // skip to array => string step, utf8decode will do it.
78         result = out.utf8decode(result);
79     }
80     else {
81         // no utf8 transformation, do the array => string step.
82         result = utils.transformTo("string", result);
83     }
84
85     if (!asUTF8 && !this.options.binary) {
86         result = utils.transformTo("string", out.utf8encode(result));
87     }
88     return result;
89 };
90 /**
91  * A simple object representing a file in the zip file.
92  * @constructor
93  * @param {string} name the name of the file
94  * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data
95  * @param {Object} options the options of the file
96  */
97 var ZipObject = function(name, data, options) {
98     this.name = name;
99     this.dir = options.dir;
100     this.date = options.date;
101     this.comment = options.comment;
102     this.unixPermissions = options.unixPermissions;
103     this.dosPermissions = options.dosPermissions;
104
105     this._data = data;
106     this.options = options;
107
108     /*
109      * This object contains initial values for dir and date.
110      * With them, we can check if the user changed the deprecated metadata in
111      * `ZipObject#options` or not.
112      */
113     this._initialMetadata = {
114       dir : options.dir,
115       date : options.date
116     };
117 };
118
119 ZipObject.prototype = {
120     /**
121      * Return the content as UTF8 string.
122      * @return {string} the UTF8 string.
123      */
124     asText: function() {
125         return dataToString.call(this, true);
126     },
127     /**
128      * Returns the binary content.
129      * @return {string} the content as binary.
130      */
131     asBinary: function() {
132         return dataToString.call(this, false);
133     },
134     /**
135      * Returns the content as a nodejs Buffer.
136      * @return {Buffer} the content as a Buffer.
137      */
138     asNodeBuffer: function() {
139         var result = getBinaryData(this);
140         return utils.transformTo("nodebuffer", result);
141     },
142     /**
143      * Returns the content as an Uint8Array.
144      * @return {Uint8Array} the content as an Uint8Array.
145      */
146     asUint8Array: function() {
147         var result = getBinaryData(this);
148         return utils.transformTo("uint8array", result);
149     },
150     /**
151      * Returns the content as an ArrayBuffer.
152      * @return {ArrayBuffer} the content as an ArrayBufer.
153      */
154     asArrayBuffer: function() {
155         return this.asUint8Array().buffer;
156     }
157 };
158
159 /**
160  * Transform an integer into a string in hexadecimal.
161  * @private
162  * @param {number} dec the number to convert.
163  * @param {number} bytes the number of bytes to generate.
164  * @returns {string} the result.
165  */
166 var decToHex = function(dec, bytes) {
167     var hex = "",
168         i;
169     for (i = 0; i < bytes; i++) {
170         hex += String.fromCharCode(dec & 0xff);
171         dec = dec >>> 8;
172     }
173     return hex;
174 };
175
176 /**
177  * Merge the objects passed as parameters into a new one.
178  * @private
179  * @param {...Object} var_args All objects to merge.
180  * @return {Object} a new object with the data of the others.
181  */
182 var extend = function() {
183     var result = {}, i, attr;
184     for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
185         for (attr in arguments[i]) {
186             if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
187                 result[attr] = arguments[i][attr];
188             }
189         }
190     }
191     return result;
192 };
193
194 /**
195  * Transforms the (incomplete) options from the user into the complete
196  * set of options to create a file.
197  * @private
198  * @param {Object} o the options from the user.
199  * @return {Object} the complete set of options.
200  */
201 var prepareFileAttrs = function(o) {
202     o = o || {};
203     if (o.base64 === true && (o.binary === null || o.binary === undefined)) {
204         o.binary = true;
205     }
206     o = extend(o, defaults);
207     o.date = o.date || new Date();
208     if (o.compression !== null) o.compression = o.compression.toUpperCase();
209
210     return o;
211 };
212
213 /**
214  * Add a file in the current folder.
215  * @private
216  * @param {string} name the name of the file
217  * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file
218  * @param {Object} o the options of the file
219  * @return {Object} the new file.
220  */
221 var fileAdd = function(name, data, o) {
222     // be sure sub folders exist
223     var dataType = utils.getTypeOf(data),
224         parent;
225
226     o = prepareFileAttrs(o);
227
228     if (typeof o.unixPermissions === "string") {
229         o.unixPermissions = parseInt(o.unixPermissions, 8);
230     }
231
232     // UNX_IFDIR  0040000 see zipinfo.c
233     if (o.unixPermissions && (o.unixPermissions & 0x4000)) {
234         o.dir = true;
235     }
236     // Bit 4    Directory
237     if (o.dosPermissions && (o.dosPermissions & 0x0010)) {
238         o.dir = true;
239     }
240
241     if (o.dir) {
242         name = forceTrailingSlash(name);
243     }
244
245     if (o.createFolders && (parent = parentFolder(name))) {
246         folderAdd.call(this, parent, true);
247     }
248
249     if (o.dir || data === null || typeof data === "undefined") {
250         o.base64 = false;
251         o.binary = false;
252         data = null;
253         dataType = null;
254     }
255     else if (dataType === "string") {
256         if (o.binary && !o.base64) {
257             // optimizedBinaryString == true means that the file has already been filtered with a 0xFF mask
258             if (o.optimizedBinaryString !== true) {
259                 // this is a string, not in a base64 format.
260                 // Be sure that this is a correct "binary string"
261                 data = utils.string2binary(data);
262             }
263         }
264     }
265     else { // arraybuffer, uint8array, ...
266         o.base64 = false;
267         o.binary = true;
268
269         if (!dataType && !(data instanceof CompressedObject)) {
270             throw new Error("The data of '" + name + "' is in an unsupported format !");
271         }
272
273         // special case : it's way easier to work with Uint8Array than with ArrayBuffer
274         if (dataType === "arraybuffer") {
275             data = utils.transformTo("uint8array", data);
276         }
277     }
278
279     var object = new ZipObject(name, data, o);
280     this.files[name] = object;
281     return object;
282 };
283
284 /**
285  * Find the parent folder of the path.
286  * @private
287  * @param {string} path the path to use
288  * @return {string} the parent folder, or ""
289  */
290 var parentFolder = function (path) {
291     if (path.slice(-1) == '/') {
292         path = path.substring(0, path.length - 1);
293     }
294     var lastSlash = path.lastIndexOf('/');
295     return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
296 };
297
298
299 /**
300  * Returns the path with a slash at the end.
301  * @private
302  * @param {String} path the path to check.
303  * @return {String} the path with a trailing slash.
304  */
305 var forceTrailingSlash = function(path) {
306     // Check the name ends with a /
307     if (path.slice(-1) != "/") {
308         path += "/"; // IE doesn't like substr(-1)
309     }
310     return path;
311 };
312 /**
313  * Add a (sub) folder in the current folder.
314  * @private
315  * @param {string} name the folder's name
316  * @param {boolean=} [createFolders] If true, automatically create sub
317  *  folders. Defaults to false.
318  * @return {Object} the new folder.
319  */
320 var folderAdd = function(name, createFolders) {
321     createFolders = (typeof createFolders !== 'undefined') ? createFolders : false;
322
323     name = forceTrailingSlash(name);
324
325     // Does this folder already exist?
326     if (!this.files[name]) {
327         fileAdd.call(this, name, null, {
328             dir: true,
329             createFolders: createFolders
330         });
331     }
332     return this.files[name];
333 };
334
335 /**
336  * Generate a JSZip.CompressedObject for a given zipOject.
337  * @param {ZipObject} file the object to read.
338  * @param {JSZip.compression} compression the compression to use.
339  * @param {Object} compressionOptions the options to use when compressing.
340  * @return {JSZip.CompressedObject} the compressed result.
341  */
342 var generateCompressedObjectFrom = function(file, compression, compressionOptions) {
343     var result = new CompressedObject(),
344         content;
345
346     // the data has not been decompressed, we might reuse things !
347     if (file._data instanceof CompressedObject) {
348         result.uncompressedSize = file._data.uncompressedSize;
349         result.crc32 = file._data.crc32;
350
351         if (result.uncompressedSize === 0 || file.dir) {
352             compression = compressions['STORE'];
353             result.compressedContent = "";
354             result.crc32 = 0;
355         }
356         else if (file._data.compressionMethod === compression.magic) {
357             result.compressedContent = file._data.getCompressedContent();
358         }
359         else {
360             content = file._data.getContent();
361             // need to decompress / recompress
362             result.compressedContent = compression.compress(utils.transformTo(compression.compressInputType, content), compressionOptions);
363         }
364     }
365     else {
366         // have uncompressed data
367         content = getBinaryData(file);
368         if (!content || content.length === 0 || file.dir) {
369             compression = compressions['STORE'];
370             content = "";
371         }
372         result.uncompressedSize = content.length;
373         result.crc32 = crc32(content);
374         result.compressedContent = compression.compress(utils.transformTo(compression.compressInputType, content), compressionOptions);
375     }
376
377     result.compressedSize = result.compressedContent.length;
378     result.compressionMethod = compression.magic;
379
380     return result;
381 };
382
383
384
385
386 /**
387  * Generate the UNIX part of the external file attributes.
388  * @param {Object} unixPermissions the unix permissions or null.
389  * @param {Boolean} isDir true if the entry is a directory, false otherwise.
390  * @return {Number} a 32 bit integer.
391  *
392  * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute :
393  *
394  * TTTTsstrwxrwxrwx0000000000ADVSHR
395  * ^^^^____________________________ file type, see zipinfo.c (UNX_*)
396  *     ^^^_________________________ setuid, setgid, sticky
397  *        ^^^^^^^^^________________ permissions
398  *                 ^^^^^^^^^^______ not used ?
399  *                           ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only
400  */
401 var generateUnixExternalFileAttr = function (unixPermissions, isDir) {
402
403     var result = unixPermissions;
404     if (!unixPermissions) {
405         // I can't use octal values in strict mode, hence the hexa.
406         //  040775 => 0x41fd
407         // 0100664 => 0x81b4
408         result = isDir ? 0x41fd : 0x81b4;
409     }
410
411     return (result & 0xFFFF) << 16;
412 };
413
414 /**
415  * Generate the DOS part of the external file attributes.
416  * @param {Object} dosPermissions the dos permissions or null.
417  * @param {Boolean} isDir true if the entry is a directory, false otherwise.
418  * @return {Number} a 32 bit integer.
419  *
420  * Bit 0     Read-Only
421  * Bit 1     Hidden
422  * Bit 2     System
423  * Bit 3     Volume Label
424  * Bit 4     Directory
425  * Bit 5     Archive
426  */
427 var generateDosExternalFileAttr = function (dosPermissions, isDir) {
428
429     // the dir flag is already set for compatibility
430
431     return (dosPermissions || 0)  & 0x3F;
432 };
433
434 /**
435  * Generate the various parts used in the construction of the final zip file.
436  * @param {string} name the file name.
437  * @param {ZipObject} file the file content.
438  * @param {JSZip.CompressedObject} compressedObject the compressed object.
439  * @param {number} offset the current offset from the start of the zip file.
440  * @param {String} platform let's pretend we are this platform (change platform dependents fields)
441  * @return {object} the zip parts.
442  */
443 var generateZipParts = function(name, file, compressedObject, offset, platform) {
444     var data = compressedObject.compressedContent,
445         utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)),
446         comment = file.comment || "",
447         utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)),
448         useUTF8ForFileName = utfEncodedFileName.length !== file.name.length,
449         useUTF8ForComment = utfEncodedComment.length !== comment.length,
450         o = file.options,
451         dosTime,
452         dosDate,
453         extraFields = "",
454         unicodePathExtraField = "",
455         unicodeCommentExtraField = "",
456         dir, date;
457
458
459     // handle the deprecated options.dir
460     if (file._initialMetadata.dir !== file.dir) {
461         dir = file.dir;
462     } else {
463         dir = o.dir;
464     }
465
466     // handle the deprecated options.date
467     if(file._initialMetadata.date !== file.date) {
468         date = file.date;
469     } else {
470         date = o.date;
471     }
472
473     var extFileAttr = 0;
474     var versionMadeBy = 0;
475     if (dir) {
476         // dos or unix, we set the dos dir flag
477         extFileAttr |= 0x00010;
478     }
479     if(platform === "UNIX") {
480         versionMadeBy = 0x031E; // UNIX, version 3.0
481         extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir);
482     } else { // DOS or other, fallback to DOS
483         versionMadeBy = 0x0014; // DOS, version 2.0
484         extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir);
485     }
486
487     // date
488     // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
489     // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
490     // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
491
492     dosTime = date.getHours();
493     dosTime = dosTime << 6;
494     dosTime = dosTime | date.getMinutes();
495     dosTime = dosTime << 5;
496     dosTime = dosTime | date.getSeconds() / 2;
497
498     dosDate = date.getFullYear() - 1980;
499     dosDate = dosDate << 4;
500     dosDate = dosDate | (date.getMonth() + 1);
501     dosDate = dosDate << 5;
502     dosDate = dosDate | date.getDate();
503
504     if (useUTF8ForFileName) {
505         // set the unicode path extra field. unzip needs at least one extra
506         // field to correctly handle unicode path, so using the path is as good
507         // as any other information. This could improve the situation with
508         // other archive managers too.
509         // This field is usually used without the utf8 flag, with a non
510         // unicode path in the header (winrar, winzip). This helps (a bit)
511         // with the messy Windows' default compressed folders feature but
512         // breaks on p7zip which doesn't seek the unicode path extra field.
513         // So for now, UTF-8 everywhere !
514         unicodePathExtraField =
515             // Version
516             decToHex(1, 1) +
517             // NameCRC32
518             decToHex(crc32(utfEncodedFileName), 4) +
519             // UnicodeName
520             utfEncodedFileName;
521
522         extraFields +=
523             // Info-ZIP Unicode Path Extra Field
524             "\x75\x70" +
525             // size
526             decToHex(unicodePathExtraField.length, 2) +
527             // content
528             unicodePathExtraField;
529     }
530
531     if(useUTF8ForComment) {
532
533         unicodeCommentExtraField =
534             // Version
535             decToHex(1, 1) +
536             // CommentCRC32
537             decToHex(this.crc32(utfEncodedComment), 4) +
538             // UnicodeName
539             utfEncodedComment;
540
541         extraFields +=
542             // Info-ZIP Unicode Path Extra Field
543             "\x75\x63" +
544             // size
545             decToHex(unicodeCommentExtraField.length, 2) +
546             // content
547             unicodeCommentExtraField;
548     }
549
550     var header = "";
551
552     // version needed to extract
553     header += "\x0A\x00";
554     // general purpose bit flag
555     // set bit 11 if utf8
556     header += (useUTF8ForFileName || useUTF8ForComment) ? "\x00\x08" : "\x00\x00";
557     // compression method
558     header += compressedObject.compressionMethod;
559     // last mod file time
560     header += decToHex(dosTime, 2);
561     // last mod file date
562     header += decToHex(dosDate, 2);
563     // crc-32
564     header += decToHex(compressedObject.crc32, 4);
565     // compressed size
566     header += decToHex(compressedObject.compressedSize, 4);
567     // uncompressed size
568     header += decToHex(compressedObject.uncompressedSize, 4);
569     // file name length
570     header += decToHex(utfEncodedFileName.length, 2);
571     // extra field length
572     header += decToHex(extraFields.length, 2);
573
574
575     var fileRecord = signature.LOCAL_FILE_HEADER + header + utfEncodedFileName + extraFields;
576
577     var dirRecord = signature.CENTRAL_FILE_HEADER +
578     // version made by (00: DOS)
579     decToHex(versionMadeBy, 2) +
580     // file header (common to file and central directory)
581     header +
582     // file comment length
583     decToHex(utfEncodedComment.length, 2) +
584     // disk number start
585     "\x00\x00" +
586     // internal file attributes TODO
587     "\x00\x00" +
588     // external file attributes
589     decToHex(extFileAttr, 4) +
590     // relative offset of local header
591     decToHex(offset, 4) +
592     // file name
593     utfEncodedFileName +
594     // extra field
595     extraFields +
596     // file comment
597     utfEncodedComment;
598
599     return {
600         fileRecord: fileRecord,
601         dirRecord: dirRecord,
602         compressedObject: compressedObject
603     };
604 };
605
606
607 // return the actual prototype of JSZip
608 var out = {
609     /**
610      * Read an existing zip and merge the data in the current JSZip object.
611      * The implementation is in jszip-load.js, don't forget to include it.
612      * @param {String|ArrayBuffer|Uint8Array|Buffer} stream  The stream to load
613      * @param {Object} options Options for loading the stream.
614      *  options.base64 : is the stream in base64 ? default : false
615      * @return {JSZip} the current JSZip object
616      */
617     load: function(stream, options) {
618         throw new Error("Load method is not defined. Is the file jszip-load.js included ?");
619     },
620
621     /**
622      * Filter nested files/folders with the specified function.
623      * @param {Function} search the predicate to use :
624      * function (relativePath, file) {...}
625      * It takes 2 arguments : the relative path and the file.
626      * @return {Array} An array of matching elements.
627      */
628     filter: function(search) {
629         var result = [],
630             filename, relativePath, file, fileClone;
631         for (filename in this.files) {
632             if (!this.files.hasOwnProperty(filename)) {
633                 continue;
634             }
635             file = this.files[filename];
636             // return a new object, don't let the user mess with our internal objects :)
637             fileClone = new ZipObject(file.name, file._data, extend(file.options));
638             relativePath = filename.slice(this.root.length, filename.length);
639             if (filename.slice(0, this.root.length) === this.root && // the file is in the current root
640             search(relativePath, fileClone)) { // and the file matches the function
641                 result.push(fileClone);
642             }
643         }
644         return result;
645     },
646
647     /**
648      * Add a file to the zip file, or search a file.
649      * @param   {string|RegExp} name The name of the file to add (if data is defined),
650      * the name of the file to find (if no data) or a regex to match files.
651      * @param   {String|ArrayBuffer|Uint8Array|Buffer} data  The file data, either raw or base64 encoded
652      * @param   {Object} o     File options
653      * @return  {JSZip|Object|Array} this JSZip object (when adding a file),
654      * a file (when searching by string) or an array of files (when searching by regex).
655      */
656     file: function(name, data, o) {
657         if (arguments.length === 1) {
658             if (utils.isRegExp(name)) {
659                 var regexp = name;
660                 return this.filter(function(relativePath, file) {
661                     return !file.dir && regexp.test(relativePath);
662                 });
663             }
664             else { // text
665                 return this.filter(function(relativePath, file) {
666                     return !file.dir && relativePath === name;
667                 })[0] || null;
668             }
669         }
670         else { // more than one argument : we have data !
671             name = this.root + name;
672             fileAdd.call(this, name, data, o);
673         }
674         return this;
675     },
676
677     /**
678      * Add a directory to the zip file, or search.
679      * @param   {String|RegExp} arg The name of the directory to add, or a regex to search folders.
680      * @return  {JSZip} an object with the new directory as the root, or an array containing matching folders.
681      */
682     folder: function(arg) {
683         if (!arg) {
684             return this;
685         }
686
687         if (utils.isRegExp(arg)) {
688             return this.filter(function(relativePath, file) {
689                 return file.dir && arg.test(relativePath);
690             });
691         }
692
693         // else, name is a new folder
694         var name = this.root + arg;
695         var newFolder = folderAdd.call(this, name);
696
697         // Allow chaining by returning a new object with this folder as the root
698         var ret = this.clone();
699         ret.root = newFolder.name;
700         return ret;
701     },
702
703     /**
704      * Delete a file, or a directory and all sub-files, from the zip
705      * @param {string} name the name of the file to delete
706      * @return {JSZip} this JSZip object
707      */
708     remove: function(name) {
709         name = this.root + name;
710         var file = this.files[name];
711         if (!file) {
712             // Look for any folders
713             if (name.slice(-1) != "/") {
714                 name += "/";
715             }
716             file = this.files[name];
717         }
718
719         if (file && !file.dir) {
720             // file
721             delete this.files[name];
722         } else {
723             // maybe a folder, delete recursively
724             var kids = this.filter(function(relativePath, file) {
725                 return file.name.slice(0, name.length) === name;
726             });
727             for (var i = 0; i < kids.length; i++) {
728                 delete this.files[kids[i].name];
729             }
730         }
731
732         return this;
733     },
734
735     /**
736      * Generate the complete zip file
737      * @param {Object} options the options to generate the zip file :
738      * - base64, (deprecated, use type instead) true to generate base64.
739      * - compression, "STORE" by default.
740      * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
741      * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file
742      */
743     generate: function(options) {
744         options = extend(options || {}, {
745             base64: true,
746             compression: "STORE",
747             compressionOptions : null,
748             type: "base64",
749             platform: "DOS",
750             comment: null,
751             mimeType: 'application/zip'
752         });
753
754         utils.checkSupport(options.type);
755
756         // accept nodejs `process.platform`
757         if(
758           options.platform === 'darwin' ||
759           options.platform === 'freebsd' ||
760           options.platform === 'linux' ||
761           options.platform === 'sunos'
762         ) {
763           options.platform = "UNIX";
764         }
765         if (options.platform === 'win32') {
766           options.platform = "DOS";
767         }
768
769         var zipData = [],
770             localDirLength = 0,
771             centralDirLength = 0,
772             writer, i,
773             utfEncodedComment = utils.transformTo("string", this.utf8encode(options.comment || this.comment || ""));
774
775         // first, generate all the zip parts.
776         for (var name in this.files) {
777             if (!this.files.hasOwnProperty(name)) {
778                 continue;
779             }
780             var file = this.files[name];
781
782             var compressionName = file.options.compression || options.compression.toUpperCase();
783             var compression = compressions[compressionName];
784             if (!compression) {
785                 throw new Error(compressionName + " is not a valid compression method !");
786             }
787             var compressionOptions = file.options.compressionOptions || options.compressionOptions || {};
788
789             var compressedObject = generateCompressedObjectFrom.call(this, file, compression, compressionOptions);
790
791             var zipPart = generateZipParts.call(this, name, file, compressedObject, localDirLength, options.platform);
792             localDirLength += zipPart.fileRecord.length + compressedObject.compressedSize;
793             centralDirLength += zipPart.dirRecord.length;
794             zipData.push(zipPart);
795         }
796
797         var dirEnd = "";
798
799         // end of central dir signature
800         dirEnd = signature.CENTRAL_DIRECTORY_END +
801         // number of this disk
802         "\x00\x00" +
803         // number of the disk with the start of the central directory
804         "\x00\x00" +
805         // total number of entries in the central directory on this disk
806         decToHex(zipData.length, 2) +
807         // total number of entries in the central directory
808         decToHex(zipData.length, 2) +
809         // size of the central directory   4 bytes
810         decToHex(centralDirLength, 4) +
811         // offset of start of central directory with respect to the starting disk number
812         decToHex(localDirLength, 4) +
813         // .ZIP file comment length
814         decToHex(utfEncodedComment.length, 2) +
815         // .ZIP file comment
816         utfEncodedComment;
817
818
819         // we have all the parts (and the total length)
820         // time to create a writer !
821         var typeName = options.type.toLowerCase();
822         if(typeName==="uint8array"||typeName==="arraybuffer"||typeName==="blob"||typeName==="nodebuffer") {
823             writer = new Uint8ArrayWriter(localDirLength + centralDirLength + dirEnd.length);
824         }else{
825             writer = new StringWriter(localDirLength + centralDirLength + dirEnd.length);
826         }
827
828         for (i = 0; i < zipData.length; i++) {
829             writer.append(zipData[i].fileRecord);
830             writer.append(zipData[i].compressedObject.compressedContent);
831         }
832         for (i = 0; i < zipData.length; i++) {
833             writer.append(zipData[i].dirRecord);
834         }
835
836         writer.append(dirEnd);
837
838         var zip = writer.finalize();
839
840
841
842         switch(options.type.toLowerCase()) {
843             // case "zip is an Uint8Array"
844             case "uint8array" :
845             case "arraybuffer" :
846             case "nodebuffer" :
847                return utils.transformTo(options.type.toLowerCase(), zip);
848             case "blob" :
849                return utils.arrayBuffer2Blob(utils.transformTo("arraybuffer", zip), options.mimeType);
850             // case "zip is a string"
851             case "base64" :
852                return (options.base64) ? base64.encode(zip) : zip;
853             default : // case "string" :
854                return zip;
855          }
856
857     },
858
859     /**
860      * @deprecated
861      * This method will be removed in a future version without replacement.
862      */
863     crc32: function (input, crc) {
864         return crc32(input, crc);
865     },
866
867     /**
868      * @deprecated
869      * This method will be removed in a future version without replacement.
870      */
871     utf8encode: function (string) {
872         return utils.transformTo("string", utf8.utf8encode(string));
873     },
874
875     /**
876      * @deprecated
877      * This method will be removed in a future version without replacement.
878      */
879     utf8decode: function (input) {
880         return utf8.utf8decode(input);
881     }
882 };
883 module.exports = out;