| 'use strict'; |
| |
| var utils = require('../utils'); |
| var ConvertWorker = require('./ConvertWorker'); |
| var GenericWorker = require('./GenericWorker'); |
| var base64 = require('../base64'); |
| var support = require("../support"); |
| var external = require("../external"); |
| |
| var NodejsStreamOutputAdapter = null; |
| if (support.nodestream) { |
| try { |
| NodejsStreamOutputAdapter = require('../nodejs/NodejsStreamOutputAdapter'); |
| } catch(e) {} |
| } |
| |
| /** |
| * Apply the final transformation of the data. If the user wants a Blob for |
| * example, it's easier to work with an U8intArray and finally do the |
| * ArrayBuffer/Blob conversion. |
| * @param {String} type the name of the final type |
| * @param {String|Uint8Array|Buffer} content the content to transform |
| * @param {String} mimeType the mime type of the content, if applicable. |
| * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format. |
| */ |
| function transformZipOutput(type, content, mimeType) { |
| switch(type) { |
| case "blob" : |
| return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType); |
| case "base64" : |
| return base64.encode(content); |
| default : |
| return utils.transformTo(type, content); |
| } |
| } |
| |
| /** |
| * Concatenate an array of data of the given type. |
| * @param {String} type the type of the data in the given array. |
| * @param {Array} dataArray the array containing the data chunks to concatenate |
| * @return {String|Uint8Array|Buffer} the concatenated data |
| * @throws Error if the asked type is unsupported |
| */ |
| function concat (type, dataArray) { |
| var i, index = 0, res = null, totalLength = 0; |
| for(i = 0; i < dataArray.length; i++) { |
| totalLength += dataArray[i].length; |
| } |
| switch(type) { |
| case "string": |
| return dataArray.join(""); |
| case "array": |
| return Array.prototype.concat.apply([], dataArray); |
| case "uint8array": |
| res = new Uint8Array(totalLength); |
| for(i = 0; i < dataArray.length; i++) { |
| res.set(dataArray[i], index); |
| index += dataArray[i].length; |
| } |
| return res; |
| case "nodebuffer": |
| return Buffer.concat(dataArray); |
| default: |
| throw new Error("concat : unsupported type '" + type + "'"); |
| } |
| } |
| |
| /** |
| * Listen a StreamHelper, accumulate its content and concatenate it into a |
| * complete block. |
| * @param {StreamHelper} helper the helper to use. |
| * @param {Function} updateCallback a callback called on each update. Called |
| * with one arg : |
| * - the metadata linked to the update received. |
| * @return Promise the promise for the accumulation. |
| */ |
| function accumulate(helper, updateCallback) { |
| return new external.Promise(function (resolve, reject){ |
| var dataArray = []; |
| var chunkType = helper._internalType, |
| resultType = helper._outputType, |
| mimeType = helper._mimeType; |
| helper |
| .on('data', function (data, meta) { |
| dataArray.push(data); |
| if(updateCallback) { |
| updateCallback(meta); |
| } |
| }) |
| .on('error', function(err) { |
| dataArray = []; |
| reject(err); |
| }) |
| .on('end', function (){ |
| try { |
| var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType); |
| resolve(result); |
| } catch (e) { |
| reject(e); |
| } |
| dataArray = []; |
| }) |
| .resume(); |
| }); |
| } |
| |
| /** |
| * An helper to easily use workers outside of JSZip. |
| * @constructor |
| * @param {Worker} worker the worker to wrap |
| * @param {String} outputType the type of data expected by the use |
| * @param {String} mimeType the mime type of the content, if applicable. |
| */ |
| function StreamHelper(worker, outputType, mimeType) { |
| var internalType = outputType; |
| switch(outputType) { |
| case "blob": |
| case "arraybuffer": |
| internalType = "uint8array"; |
| break; |
| case "base64": |
| internalType = "string"; |
| break; |
| } |
| |
| try { |
| // the type used internally |
| this._internalType = internalType; |
| // the type used to output results |
| this._outputType = outputType; |
| // the mime type |
| this._mimeType = mimeType; |
| utils.checkSupport(internalType); |
| this._worker = worker.pipe(new ConvertWorker(internalType)); |
| // the last workers can be rewired without issues but we need to |
| // prevent any updates on previous workers. |
| worker.lock(); |
| } catch(e) { |
| this._worker = new GenericWorker("error"); |
| this._worker.error(e); |
| } |
| } |
| |
| StreamHelper.prototype = { |
| /** |
| * Listen a StreamHelper, accumulate its content and concatenate it into a |
| * complete block. |
| * @param {Function} updateCb the update callback. |
| * @return Promise the promise for the accumulation. |
| */ |
| accumulate : function (updateCb) { |
| return accumulate(this, updateCb); |
| }, |
| /** |
| * Add a listener on an event triggered on a stream. |
| * @param {String} evt the name of the event |
| * @param {Function} fn the listener |
| * @return {StreamHelper} the current helper. |
| */ |
| on : function (evt, fn) { |
| var self = this; |
| |
| if(evt === "data") { |
| this._worker.on(evt, function (chunk) { |
| fn.call(self, chunk.data, chunk.meta); |
| }); |
| } else { |
| this._worker.on(evt, function () { |
| utils.delay(fn, arguments, self); |
| }); |
| } |
| return this; |
| }, |
| /** |
| * Resume the flow of chunks. |
| * @return {StreamHelper} the current helper. |
| */ |
| resume : function () { |
| utils.delay(this._worker.resume, [], this._worker); |
| return this; |
| }, |
| /** |
| * Pause the flow of chunks. |
| * @return {StreamHelper} the current helper. |
| */ |
| pause : function () { |
| this._worker.pause(); |
| return this; |
| }, |
| /** |
| * Return a nodejs stream for this helper. |
| * @param {Function} updateCb the update callback. |
| * @return {NodejsStreamOutputAdapter} the nodejs stream. |
| */ |
| toNodejsStream : function (updateCb) { |
| utils.checkSupport("nodestream"); |
| if (this._outputType !== "nodebuffer") { |
| // an object stream containing blob/arraybuffer/uint8array/string |
| // is strange and I don't know if it would be useful. |
| // I you find this comment and have a good usecase, please open a |
| // bug report ! |
| throw new Error(this._outputType + " is not supported by this method"); |
| } |
| |
| return new NodejsStreamOutputAdapter(this, { |
| objectMode : this._outputType !== "nodebuffer" |
| }, updateCb); |
| } |
| }; |
| |
| |
| module.exports = StreamHelper; |