text: 'use strict';
  var support = require('./support');
  var utils = require('./utils');
  var crc32 = require('./crc32');
  var signature = require('./signature');
  var defaults = require('./defaults');
  var base64 = require('./base64');
  var compressions = require('./compressions');
  var CompressedObject = require('./compressedObject');
  var nodeBuffer = require('./nodeBuffer');
  var utf8 = require('./utf8');
  var StringWriter = require('./stringWriter');
  var Uint8ArrayWriter = require('./uint8ArrayWriter');
  
  /**
   * Returns the raw data of a ZipObject, decompress the content if necessary.
   * @param {ZipObject} file the file to use.
   * @return {String|ArrayBuffer|Uint8Array|Buffer} the data.
   */
  var getRawData = function(file) {
      if (file._data instanceof CompressedObject) {
          file._data = file._data.getContent();
          file.options.binary = true;
          file.options.base64 = false;
  
          if (utils.getTypeOf(file._data) === "uint8array") {
              var copy = file._data;
              // when reading an arraybuffer, the CompressedObject mechanism will keep it and subarray() a Uint8Array.
              // if we request a file in the same format, we might get the same Uint8Array or its ArrayBuffer (the original zip file).
              file._data = new Uint8Array(copy.length);
              // with an empty Uint8Array, Opera fails with a "Offset larger than array size"
              if (copy.length !== 0) {
                  file._data.set(copy, 0);
              }
          }
      }
      return file._data;
  };
  
  /**
   * Returns the data of a ZipObject in a binary form. If the content is an unicode string, encode it.
   * @param {ZipObject} file the file to use.
   * @return {String|ArrayBuffer|Uint8Array|Buffer} the data.
   */
  var getBinaryData = function(file) {
      var result = getRawData(file),
          type = utils.getTypeOf(result);
      if (type === "string") {
          if (!file.options.binary) {
              // unicode text !
              // unicode string => binary string is a painful process, check if we can avoid it.
              if (support.nodebuffer) {
                  return nodeBuffer(result, "utf-8");
              }
          }
          return file.asBinary();
      }
      return result;
  };
  
  /**
   * Transform this._data into a string.
   * @param {function} filter a function String -> String, applied if not null on the exam_result.
   * @return {String} the string representing this._data.
   */
  var dataToString = function(asUTF8) {
      var result = getRawData(this);
      if (result === null || typeof result === "undefined") {
          return "";
      }
      // if the data is a base64 string, we decode it before checking the encoding !
      if (this.options.base64) {
          result = base64.decode(result);
      }
      if (asUTF8 && this.options.binary) {
          // JSZip.prototype.utf8decode supports arrays as input
          // skip to array => string step, utf8decode will do it.
          result = out.utf8decode(result);
      }
      else {
          // no utf8 transformation, do the array => string step.
          result = utils.transformTo("string", result);
      }
  
      if (!asUTF8 && !this.options.binary) {
          result = utils.transformTo("string", out.utf8encode(result));
      }
      return result;
  };
  /**
   * A simple object representing a file in the zip file.
   * @constructor
   * @param {string} name the name of the file
   * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data
   * @param {Object} options the options of the file
   */
  var ZipObject = function(name, data, options) {
      this.name = name;
      this.dir = options.dir;
      this.date = options.date;
      this.comment = options.comment;
      this.unixPermissions = options.unixPermissions;
      this.dosPermissions = options.dosPermissions;
  
      this._data = data;
      this.options = options;
  
      /*
       * This object contains initial values for dir and date.
       * With them, we can check if the user changed the deprecated metadata in
       * `ZipObject#options` or not.
       */
      this._initialMetadata = {
        dir : options.dir,
        date : options.date
      };
  };
  
  ZipObject.prototype = {
      /**
       * Return the content as UTF8 string.
       * @return {string} the UTF8 string.
       */
      asText: function() {
          return dataToString.call(this, true);
      },
      /**
       * Returns the binary content.
       * @return {string} the content as binary.
       */
      asBinary: function() {
          return dataToString.call(this, false);
      },
      /**
       * Returns the content as a nodejs Buffer.
       * @return {Buffer} the content as a Buffer.
       */
      asNodeBuffer: function() {
          var result = getBinaryData(this);
          return utils.transformTo("nodebuffer", result);
      },
      /**
       * Returns the content as an Uint8Array.
       * @return {Uint8Array} the content as an Uint8Array.
       */
      asUint8Array: function() {
          var result = getBinaryData(this);
          return utils.transformTo("uint8array", result);
      },
      /**
       * Returns the content as an ArrayBuffer.
       * @return {ArrayBuffer} the content as an ArrayBufer.
       */
      asArrayBuffer: function() {
          return this.asUint8Array().buffer;
      }
  };
  
  /**
   * Transform an integer into a string in hexadecimal.
   * @private
   * @param {number} dec the number to convert.
   * @param {number} bytes the number of bytes to generate.
   * @returns {string} the exam_result.
   */
  var decToHex = function(dec, bytes) {
      var hex = "",
          i;
      for (i = 0; i < bytes; i++) {
          hex += String.fromCharCode(dec & 0xff);
          dec = dec >>> 8;
      }
      return hex;
  };
  
  /**
   * Transforms the (incomplete) options from the user into the complete
   * set of options to create a file.
   * @private
   * @param {Object} o the options from the user.
   * @return {Object} the complete set of options.
   */
  var prepareFileAttrs = function(o) {
      o = o || {};
      if (o.base64 === true && (o.binary === null || o.binary === undefined)) {
          o.binary = true;
      }
      o = utils.extend(o, defaults);
      o.date = o.date || new Date();
      if (o.compression !== null) o.compression = o.compression.toUpperCase();
  
      return o;
  };
  
  /**
   * Add a file in the current folder.
   * @private
   * @param {string} name the name of the file
   * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file
   * @param {Object} o the options of the file
   * @return {Object} the new file.
   */
  var fileAdd = function(name, data, o) {
      // be sure sub folders exist
      var dataType = utils.getTypeOf(data),
          parent;
  
      o = prepareFileAttrs(o);
  
      if (typeof o.unixPermissions === "string") {
          o.unixPermissions = parseInt(o.unixPermissions, 8);
      }
  
      // UNX_IFDIR  0040000 see zipinfo.c
      if (o.unixPermissions && (o.unixPermissions & 0x4000)) {
          o.dir = true;
      }
      // Bit 4    Directory
      if (o.dosPermissions && (o.dosPermissions & 0x0010)) {
          o.dir = true;
      }
  
      if (o.dir) {
          name = forceTrailingSlash(name);
      }
  
      if (o.createFolders && (parent = parentFolder(name))) {
          folderAdd.call(this, parent, true);
      }
  
      if (o.dir || data === null || typeof data === "undefined") {
          o.base64 = false;
          o.binary = false;
          data = null;
          dataType = null;
      }
      else if (dataType === "string") {
          if (o.binary && !o.base64) {
              // optimizedBinaryString == true means that the file has already been filtered with a 0xFF mask
              if (o.optimizedBinaryString !== true) {
                  // this is a string, not in a base64 format.
                  // Be sure that this is a correct "binary string"
                  data = utils.string2binary(data);
              }
          }
      }
      else { // arraybuffer, uint8array, ...
          o.base64 = false;
          o.binary = true;
  
          if (!dataType && !(data instanceof CompressedObject)) {
              throw new Error("The data of '" + name + "' is in an unsupported format !");
          }
  
          // special case : it's way easier to work with Uint8Array than with ArrayBuffer
          if (dataType === "arraybuffer") {
              data = utils.transformTo("uint8array", data);
          }
      }
  
      var object = new ZipObject(name, data, o);
      this.files[name] = object;
      return object;
  };
  
  /**
   * Find the parent folder of the path.
   * @private
   * @param {string} path the path to use
   * @return {string} the parent folder, or ""
   */
  var parentFolder = function (path) {
      if (path.slice(-1) == '/') {
          path = path.substring(0, path.length - 1);
      }
      var lastSlash = path.lastIndexOf('/');
      return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
  };
  
  
  /**
   * Returns the path with a slash at the end.
   * @private
   * @param {String} path the path to check.
   * @return {String} the path with a trailing slash.
   */
  var forceTrailingSlash = function(path) {
      // Check the name ends with a /
      if (path.slice(-1) != "/") {
          path += "/"; // IE doesn't like substr(-1)
      }
      return path;
  };
  /**
   * Add a (sub) folder in the current folder.
   * @private
   * @param {string} name the folder's name
   * @param {boolean=} [createFolders] If true, automatically create sub
   *  folders. Defaults to false.
   * @return {Object} the new folder.
   */
  var folderAdd = function(name, createFolders) {
      createFolders = (typeof createFolders !== 'undefined') ? createFolders : false;
  
      name = forceTrailingSlash(name);
  
      // Does this folder already exist?
      if (!this.files[name]) {
          fileAdd.call(this, name, null, {
              dir: true,
              createFolders: createFolders
          });
      }
      return this.files[name];
  };
  
  /**
   * Generate a JSZip.CompressedObject for a given zipOject.
   * @param {ZipObject} file the object to read.
   * @param {JSZip.compression} compression the compression to use.
   * @param {Object} compressionOptions the options to use when compressing.
   * @return {JSZip.CompressedObject} the compressed exam_result.
   */
  var generateCompressedObjectFrom = function(file, compression, compressionOptions) {
      var result = new CompressedObject(),
          content;
  
      // the data has not been decompressed, we might reuse things !
      if (file._data instanceof CompressedObject) {
          result.uncompressedSize = file._data.uncompressedSize;
          result.crc32 = file._data.crc32;
  
          if (result.uncompressedSize === 0 || file.dir) {
              compression = compressions['STORE'];
              result.compressedContent = "";
              result.crc32 = 0;
          }
          else if (file._data.compressionMethod === compression.magic) {
              result.compressedContent = file._data.getCompressedContent();
          }
          else {
              content = file._data.getContent();
              // need to decompress / recompress
              result.compressedContent = compression.compress(utils.transformTo(compression.compressInputType, content), compressionOptions);
          }
      }
      else {
          // have uncompressed data
          content = getBinaryData(file);
          if (!content || content.length === 0 || file.dir) {
              compression = compressions['STORE'];
              content = "";
          }
          result.uncompressedSize = content.length;
          result.crc32 = crc32(content);
          result.compressedContent = compression.compress(utils.transformTo(compression.compressInputType, content), compressionOptions);
      }
  
      result.compressedSize = result.compressedContent.length;
      result.compressionMethod = compression.magic;
  
      return result;
  };
  
  
  
  
  /**
   * Generate the UNIX part of the external file attributes.
   * @param {Object} unixPermissions the unix permissions or null.
   * @param {Boolean} isDir true if the entry is a directory, false otherwise.
   * @return {Number} a 32 bit integer.
   *
   * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute :
   *
   * TTTTsstrwxrwxrwx0000000000ADVSHR
   * ^^^^____________________________ file type, see zipinfo.c (UNX_*)
   *     ^^^_________________________ setuid, setgid, sticky
   *        ^^^^^^^^^________________ permissions
   *                 ^^^^^^^^^^______ not used ?
   *                           ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only
   */
  var generateUnixExternalFileAttr = function (unixPermissions, isDir) {
  
      var result = unixPermissions;
      if (!unixPermissions) {
          // I can't use octal values in strict mode, hence the hexa.
          //  040775 => 0x41fd
          // 0100664 => 0x81b4
          result = isDir ? 0x41fd : 0x81b4;
      }
  
      return (result & 0xFFFF) << 16;
  };
  
  /**
   * Generate the DOS part of the external file attributes.
   * @param {Object} dosPermissions the dos permissions or null.
   * @param {Boolean} isDir true if the entry is a directory, false otherwise.
   * @return {Number} a 32 bit integer.
   *
   * Bit 0     Read-Only
   * Bit 1     Hidden
   * Bit 2     System
   * Bit 3     Volume Label
   * Bit 4     Directory
   * Bit 5     Archive
   */
  var generateDosExternalFileAttr = function (dosPermissions, isDir) {
  
      // the dir flag is already set for compatibility
  
      return (dosPermissions || 0)  & 0x3F;
  };
  
  /**
   * Generate the various parts used in the construction of the final zip file.
   * @param {string} name the file name.
   * @param {ZipObject} file the file content.
   * @param {JSZip.CompressedObject} compressedObject the compressed object.
   * @param {number} offset the current offset from the start of the zip file.
   * @param {String} platform let's pretend we are this platform (change platform dependents fields)
   * @param {Function} encodeFileName the function to encode the file name / comment.
   * @return {object} the zip parts.
   */
  var generateZipParts = function(name, file, compressedObject, offset, platform, encodeFileName) {
      var data = compressedObject.compressedContent,
          useCustomEncoding = encodeFileName !== utf8.utf8encode,
          encodedFileName = utils.transformTo("string", encodeFileName(file.name)),
          utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)),
          comment = file.comment || "",
          encodedComment = utils.transformTo("string", encodeFileName(comment)),
          utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)),
          useUTF8ForFileName = utfEncodedFileName.length !== file.name.length,
          useUTF8ForComment = utfEncodedComment.length !== comment.length,
          o = file.options,
          dosTime,
          dosDate,
          extraFields = "",
          unicodePathExtraField = "",
          unicodeCommentExtraField = "",
          dir, date;
  
  
      // handle the deprecated options.dir
      if (file._initialMetadata.dir !== file.dir) {
          dir = file.dir;
      } else {
          dir = o.dir;
      }
  
      // handle the deprecated options.date
      if(file._initialMetadata.date !== file.date) {
          date = file.date;
      } else {
          date = o.date;
      }
  
      var extFileAttr = 0;
      var versionMadeBy = 0;
      if (dir) {
          // dos or unix, we set the dos dir flag
          extFileAttr |= 0x00010;
      }
      if(platform === "UNIX") {
          versionMadeBy = 0x031E; // UNIX, version 3.0
          extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir);
      } else { // DOS or other, fallback to DOS
          versionMadeBy = 0x0014; // DOS, version 2.0
          extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir);
      }
  
      // date
      // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
      // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
      // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
  
      dosTime = date.getHours();
      dosTime = dosTime << 6;
      dosTime = dosTime | date.getMinutes();
      dosTime = dosTime << 5;
      dosTime = dosTime | date.getSeconds() / 2;
  
      dosDate = date.getFullYear() - 1980;
      dosDate = dosDate << 4;
      dosDate = dosDate | (date.getMonth() + 1);
      dosDate = dosDate << 5;
      dosDate = dosDate | date.getDate();
  
      if (useUTF8ForFileName) {
          // set the unicode path extra field. unzip needs at least one extra
          // field to correctly handle unicode path, so using the path is as good
          // as any other information. This could improve the situation with
          // other archive managers too.
          // This field is usually used without the utf8 flag, with a non
          // unicode path in the header (winrar, winzip). This helps (a bit)
          // with the messy Windows' default compressed folders feature but
          // breaks on p7zip which doesn't seek the unicode path extra field.
          // So for now, UTF-8 everywhere !
          unicodePathExtraField =
              // Version
              decToHex(1, 1) +
              // NameCRC32
              decToHex(crc32(encodedFileName), 4) +
              // UnicodeName
              utfEncodedFileName;
  
          extraFields +=
              // Info-ZIP Unicode Path Extra Field
              "\x75\x70" +
              // size
              decToHex(unicodePathExtraField.length, 2) +
              // content
              unicodePathExtraField;
      }
  
      if(useUTF8ForComment) {
  
          unicodeCommentExtraField =
              // Version
              decToHex(1, 1) +
              // CommentCRC32
              decToHex(this.crc32(encodedComment), 4) +
              // UnicodeName
              utfEncodedComment;
  
          extraFields +=
              // Info-ZIP Unicode Path Extra Field
              "\x75\x63" +
              // size
              decToHex(unicodeCommentExtraField.length, 2) +
              // content
              unicodeCommentExtraField;
      }
  
      var header = "";
  
      // version needed to extract
      header += "\x0A\x00";
      // general purpose bit flag
      // set bit 11 if utf8
      header += !useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment) ? "\x00\x08" : "\x00\x00";
      // compression method
      header += compressedObject.compressionMethod;
      // last mod file time
      header += decToHex(dosTime, 2);
      // last mod file date
      header += decToHex(dosDate, 2);
      // crc-32
      header += decToHex(compressedObject.crc32, 4);
      // compressed size
      header += decToHex(compressedObject.compressedSize, 4);
      // uncompressed size
      header += decToHex(compressedObject.uncompressedSize, 4);
      // file name length
      header += decToHex(encodedFileName.length, 2);
      // extra field length
      header += decToHex(extraFields.length, 2);
  
  
      var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields;
  
      var dirRecord = signature.CENTRAL_FILE_HEADER +
      // version made by (00: DOS)
      decToHex(versionMadeBy, 2) +
      // file header (common to file and central directory)
      header +
      // file comment length
      decToHex(encodedComment.length, 2) +
      // disk number start
      "\x00\x00" +
      // internal file attributes TODO
      "\x00\x00" +
      // external file attributes
      decToHex(extFileAttr, 4) +
      // relative offset of local header
      decToHex(offset, 4) +
      // file name
      encodedFileName +
      // extra field
      extraFields +
      // file comment
      encodedComment;
  
      return {
          fileRecord: fileRecord,
          dirRecord: dirRecord,
          compressedObject: compressedObject
      };
  };
  
  
  // return the actual prototype of JSZip
  var out = {
      /**
       * Read an existing zip and merge the data in the current JSZip object.
       * The implementation is in jszip-load.js, don't forget to include it.
       * @param {String|ArrayBuffer|Uint8Array|Buffer} stream  The stream to load
       * @param {Object} options Options for loading the stream.
       *  options.base64 : is the stream in base64 ? default : false
       * @return {JSZip} the current JSZip object
       */
      load: function(stream, options) {
          throw new Error("Load method is not defined. Is the file jszip-load.js included ?");
      },
  
      /**
       * Filter nested files/folders with the specified function.
       * @param {Function} search the predicate to use :
       * function (relativePath, file) {...}
       * It takes 2 arguments : the relative path and the file.
       * @return {Array} An array of matching elements.
       */
      filter: function(search) {
          var result = [],
              filename, relativePath, file, fileClone;
          for (filename in this.files) {
              if (!this.files.hasOwnProperty(filename)) {
                  continue;
              }
              file = this.files[filename];
              // return a new object, don't let the user mess with our internal objects :)
              fileClone = new ZipObject(file.name, file._data, utils.extend(file.options));
              relativePath = filename.slice(this.root.length, filename.length);
              if (filename.slice(0, this.root.length) === this.root && // the file is in the current root
              search(relativePath, fileClone)) { // and the file matches the function
                  result.push(fileClone);
              }
          }
          return result;
      },
  
      /**
       * Add a file to the zip file, or search a file.
       * @param   {string|RegExp} name The name of the file to add (if data is defined),
       * the name of the file to find (if no data) or a regex to match files.
       * @param   {String|ArrayBuffer|Uint8Array|Buffer} data  The file data, either raw or base64 encoded
       * @param   {Object} o     File options
       * @return  {JSZip|Object|Array} this JSZip object (when adding a file),
       * a file (when searching by string) or an array of files (when searching by regex).
       */
      file: function(name, data, o) {
          if (arguments.length === 1) {
              if (utils.isRegExp(name)) {
                  var regexp = name;
                  return this.filter(function(relativePath, file) {
                      return !file.dir && regexp.test(relativePath);
                  });
              }
              else { // text
                  return this.filter(function(relativePath, file) {
                      return !file.dir && relativePath === name;
                  })[0] || null;
              }
          }
          else { // more than one argument : we have data !
              name = this.root + name;
              fileAdd.call(this, name, data, o);
          }
          return this;
      },
  
      /**
       * Add a directory to the zip file, or search.
       * @param   {String|RegExp} arg The name of the directory to add, or a regex to search folders.
       * @return  {JSZip} an object with the new directory as the root, or an array containing matching folders.
       */
      folder: function(arg) {
          if (!arg) {
              return this;
          }
  
          if (utils.isRegExp(arg)) {
              return this.filter(function(relativePath, file) {
                  return file.dir && arg.test(relativePath);
              });
          }
  
          // else, name is a new folder
          var name = this.root + arg;
          var newFolder = folderAdd.call(this, name);
  
          // Allow chaining by returning a new object with this folder as the root
          var ret = this.clone();
          ret.root = newFolder.name;
          return ret;
      },
  
      /**
       * Delete a file, or a directory and all sub-files, from the zip
       * @param {string} name the name of the file to delete
       * @return {JSZip} this JSZip object
       */
      remove: function(name) {
          name = this.root + name;
          var file = this.files[name];
          if (!file) {
              // Look for any folders
              if (name.slice(-1) != "/") {
                  name += "/";
              }
              file = this.files[name];
          }
  
          if (file && !file.dir) {
              // file
              delete this.files[name];
          } else {
              // maybe a folder, delete recursively
              var kids = this.filter(function(relativePath, file) {
                  return file.name.slice(0, name.length) === name;
              });
              for (var i = 0; i < kids.length; i++) {
                  delete this.files[kids[i].name];
              }
          }
  
          return this;
      },
  
      /**
       * Generate the complete zip file
       * @param {Object} options the options to generate the zip file :
       * - base64, (deprecated, use type instead) true to generate base64.
       * - compression, "STORE" by default.
       * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
       * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file
       */
      generate: function(options) {
          options = utils.extend(options || {}, {
              base64: true,
              compression: "STORE",
              compressionOptions : null,
              type: "base64",
              platform: "DOS",
              comment: null,
              mimeType: 'application/zip',
              encodeFileName: utf8.utf8encode
          });
  
          utils.checkSupport(options.type);
  
          // accept nodejs `process.platform`
          if(
            options.platform === 'darwin' ||
            options.platform === 'freebsd' ||
            options.platform === 'linux' ||
            options.platform === 'sunos'
          ) {
            options.platform = "UNIX";
          }
          if (options.platform === 'win32') {
            options.platform = "DOS";
          }
  
          var zipData = [],
              localDirLength = 0,
              centralDirLength = 0,
              writer, i,
              encodedComment = utils.transformTo("string", options.encodeFileName(options.comment || this.comment || ""));
  
          // first, generate all the zip parts.
          for (var name in this.files) {
              if (!this.files.hasOwnProperty(name)) {
                  continue;
              }
              var file = this.files[name];
  
              var compressionName = file.options.compression || options.compression.toUpperCase();
              var compression = compressions[compressionName];
              if (!compression) {
                  throw new Error(compressionName + " is not a valid compression method !");
              }
              var compressionOptions = file.options.compressionOptions || options.compressionOptions || {};
  
              var compressedObject = generateCompressedObjectFrom.call(this, file, compression, compressionOptions);
  
              var zipPart = generateZipParts.call(this, name, file, compressedObject, localDirLength, options.platform, options.encodeFileName);
              localDirLength += zipPart.fileRecord.length + compressedObject.compressedSize;
              centralDirLength += zipPart.dirRecord.length;
              zipData.push(zipPart);
          }
  
          var dirEnd = "";
  
          // end of central dir signature
          dirEnd = signature.CENTRAL_DIRECTORY_END +
          // number of this disk
          "\x00\x00" +
          // number of the disk with the start of the central directory
          "\x00\x00" +
          // total number of entries in the central directory on this disk
          decToHex(zipData.length, 2) +
          // total number of entries in the central directory
          decToHex(zipData.length, 2) +
          // size of the central directory   4 bytes
          decToHex(centralDirLength, 4) +
          // offset of start of central directory with respect to the starting disk number
          decToHex(localDirLength, 4) +
          // .ZIP file comment length
          decToHex(encodedComment.length, 2) +
          // .ZIP file comment
          encodedComment;
  
  
          // we have all the parts (and the total length)
          // time to create a writer !
          var typeName = options.type.toLowerCase();
          if(typeName==="uint8array"||typeName==="arraybuffer"||typeName==="blob"||typeName==="nodebuffer") {
              writer = new Uint8ArrayWriter(localDirLength + centralDirLength + dirEnd.length);
          }else{
              writer = new StringWriter(localDirLength + centralDirLength + dirEnd.length);
          }
  
          for (i = 0; i < zipData.length; i++) {
              writer.append(zipData[i].fileRecord);
              writer.append(zipData[i].compressedObject.compressedContent);
          }
          for (i = 0; i < zipData.length; i++) {
              writer.append(zipData[i].dirRecord);
          }
  
          writer.append(dirEnd);
  
          var zip = writer.finalize();
  
  
  
          switch(options.type.toLowerCase()) {
              // case "zip is an Uint8Array"
              case "uint8array" :
              case "arraybuffer" :
              case "nodebuffer" :
                 return utils.transformTo(options.type.toLowerCase(), zip);
              case "blob" :
                 return utils.arrayBuffer2Blob(utils.transformTo("arraybuffer", zip), options.mimeType);
              // case "zip is a string"
              case "base64" :
                 return (options.base64) ? base64.encode(zip) : zip;
              default : // case "string" :
                 return zip;
           }
  
      },
  
      /**
       * @deprecated
       * This method will be removed in a future version without replacement.
       */
      crc32: function (input, crc) {
          return crc32(input, crc);
      },
  
      /**
       * @deprecated
       * This method will be removed in a future version without replacement.
       */
      utf8encode: function (string) {
          return utils.transformTo("string", utf8.utf8encode(string));
      },
  
      /**
       * @deprecated
       * This method will be removed in a future version without replacement.
       */
      utf8decode: function (input) {
          return utf8.utf8decode(input);
      }
  };
  module.exports = out;
  