123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- 'use strict';
- var utf8 = require('./utf8');
- var utils = require('./utils');
- var GenericWorker = require('./stream/GenericWorker');
- var StreamHelper = require('./stream/StreamHelper');
- var defaults = require('./defaults');
- var CompressedObject = require('./compressedObject');
- var ZipObject = require('./zipObject');
- var generate = require("./generate");
- var nodejsUtils = require("./nodejsUtils");
- var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter");
-
-
- /**
- * 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} originalOptions the options of the file
- * @return {Object} the new file.
- */
- var fileAdd = function(name, data, originalOptions) {
- // be sure sub folders exist
- var dataType = utils.getTypeOf(data),
- parent;
-
-
- /*
- * Correct options.
- */
-
- var o = utils.extend(originalOptions || {}, defaults);
- o.date = o.date || new Date();
- if (o.compression !== null) {
- o.compression = o.compression.toUpperCase();
- }
-
- 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);
- }
-
- var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false;
- if (!originalOptions || typeof originalOptions.binary === "undefined") {
- o.binary = !isUnicodeString;
- }
-
-
- var isCompressedEmpty = (data instanceof CompressedObject) && data.uncompressedSize === 0;
-
- if (isCompressedEmpty || o.dir || !data || data.length === 0) {
- o.base64 = false;
- o.binary = true;
- data = "";
- o.compression = "STORE";
- dataType = "string";
- }
-
- /*
- * Convert content to fit.
- */
-
- var zipObjectContent = null;
- if (data instanceof CompressedObject || data instanceof GenericWorker) {
- zipObjectContent = data;
- } else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
- zipObjectContent = new NodejsStreamInputAdapter(name, data);
- } else {
- zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64);
- }
-
- var object = new ZipObject(name, zipObjectContent, o);
- this.files[name] = object;
- /*
- TODO: we can't throw an exception because we have async promises
- (we can have a promise of a Date() for example) but returning a
- promise is useless because file(name, data) returns the JSZip
- object for chaining. Should we break that to allow the user
- to catch the error ?
-
- return external.Promise.resolve(zipObjectContent)
- .then(function () {
- 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 : defaults.createFolders;
-
- 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];
- };
-
- /**
- * Cross-window, cross-Node-context regular expression detection
- * @param {Object} object Anything
- * @return {Boolean} true if the object is a regular expression,
- * false otherwise
- */
- function isRegExp(object) {
- return Object.prototype.toString.call(object) === "[object RegExp]";
- }
-
- // return the actual prototype of JSZip
- var out = {
- /**
- * @see loadAsync
- */
- load: function() {
- throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
- },
-
-
- /**
- * Call a callback function for each entry at this folder level.
- * @param {Function} cb the callback function:
- * function (relativePath, file) {...}
- * It takes 2 arguments : the relative path and the file.
- */
- forEach: function(cb) {
- var filename, relativePath, file;
- for (filename in this.files) {
- if (!this.files.hasOwnProperty(filename)) {
- continue;
- }
- file = this.files[filename];
- relativePath = filename.slice(this.root.length, filename.length);
- if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root
- cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn...
- }
- }
- },
-
- /**
- * 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 = [];
- this.forEach(function (relativePath, entry) {
- if (search(relativePath, entry)) { // the file matches the function
- result.push(entry);
- }
-
- });
- 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 (isRegExp(name)) {
- var regexp = name;
- return this.filter(function(relativePath, file) {
- return !file.dir && regexp.test(relativePath);
- });
- }
- else { // text
- var obj = this.files[this.root + name];
- if (obj && !obj.dir) {
- return obj;
- } else {
- return 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 (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 :
- * - 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) {
- throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
- },
-
- /**
- * Generate the complete zip file as an internal stream.
- * @param {Object} options the options to generate the zip file :
- * - compression, "STORE" by default.
- * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
- * @return {StreamHelper} the streamed zip file.
- */
- generateInternalStream: function(options) {
- var worker, opts = {};
- try {
- opts = utils.extend(options || {}, {
- streamFiles: false,
- compression: "STORE",
- compressionOptions : null,
- type: "",
- platform: "DOS",
- comment: null,
- mimeType: 'application/zip',
- encodeFileName: utf8.utf8encode
- });
-
- opts.type = opts.type.toLowerCase();
- opts.compression = opts.compression.toUpperCase();
-
- // "binarystring" is prefered but the internals use "string".
- if(opts.type === "binarystring") {
- opts.type = "string";
- }
-
- if (!opts.type) {
- throw new Error("No output type specified.");
- }
-
- utils.checkSupport(opts.type);
-
- // accept nodejs `process.platform`
- if(
- opts.platform === 'darwin' ||
- opts.platform === 'freebsd' ||
- opts.platform === 'linux' ||
- opts.platform === 'sunos'
- ) {
- opts.platform = "UNIX";
- }
- if (opts.platform === 'win32') {
- opts.platform = "DOS";
- }
-
- var comment = opts.comment || this.comment || "";
- worker = generate.generateWorker(this, opts, comment);
- } catch (e) {
- worker = new GenericWorker("error");
- worker.error(e);
- }
- return new StreamHelper(worker, opts.type || "string", opts.mimeType);
- },
- /**
- * Generate the complete zip file asynchronously.
- * @see generateInternalStream
- */
- generateAsync: function(options, onUpdate) {
- return this.generateInternalStream(options).accumulate(onUpdate);
- },
- /**
- * Generate the complete zip file asynchronously.
- * @see generateInternalStream
- */
- generateNodeStream: function(options, onUpdate) {
- options = options || {};
- if (!options.type) {
- options.type = "nodebuffer";
- }
- return this.generateInternalStream(options).toNodejsStream(onUpdate);
- }
- };
- module.exports = out;
|