--- /dev/null
+var ZipEntry = require("./zipEntry"),
+ Headers = require("./headers"),
+ Utils = require("./util");
+
+module.exports = function(/*String|Buffer*/input, /*Number*/inputType) {
+ var entryList = [],
+ entryTable = {},
+ _comment = new Buffer(0),
+ filename = "",
+ fs = require("fs"),
+ inBuffer = null,
+ mainHeader = new Headers.MainHeader();
+
+ if (inputType == Utils.Constants.FILE) {
+ // is a filename
+ filename = input;
+ inBuffer = fs.readFileSync(filename);
+ readMainHeader();
+ } else if (inputType == Utils.Constants.BUFFER) {
+ // is a memory buffer
+ inBuffer = input;
+ readMainHeader();
+ } else {
+ // none. is a new file
+ }
+
+ function readEntries() {
+ entryTable = {};
+ entryList = new Array(mainHeader.diskEntries); // total number of entries
+ var index = mainHeader.offset; // offset of first CEN header
+ for(var i = 0; i < entryList.length; i++) {
+
+ var tmp = index,
+ entry = new ZipEntry(inBuffer);
+ entry.header = inBuffer.slice(tmp, tmp += Utils.Constants.CENHDR);
+
+ entry.entryName = inBuffer.slice(tmp, tmp += entry.header.fileNameLength);
+
+ if (entry.header.extraLength) {
+ entry.extra = inBuffer.slice(tmp, tmp += entry.header.extraLength);
+ }
+
+ if (entry.header.commentLength)
+ entry.comment = inBuffer.slice(tmp, tmp + entry.header.commentLength);
+
+ index += entry.header.entryHeaderSize;
+
+ entryList[i] = entry;
+ entryTable[entry.entryName] = entry;
+ }
+ }
+
+ function readMainHeader() {
+ var i = inBuffer.length - Utils.Constants.ENDHDR, // END header size
+ n = Math.max(0, i - 0xFFFF), // 0xFFFF is the max zip file comment length
+ endOffset = 0; // Start offset of the END header
+
+ for (i; i >= n; i--) {
+ if (inBuffer[i] != 0x50) continue; // quick check that the byte is 'P'
+ if (inBuffer.readUInt32LE(i) == Utils.Constants.ENDSIG) { // "PK\005\006"
+ endOffset = i;
+ break;
+ }
+ }
+ if (!endOffset)
+ throw Utils.Errors.INVALID_FORMAT;
+
+ mainHeader.loadFromBinary(inBuffer.slice(endOffset, endOffset + Utils.Constants.ENDHDR));
+ if (mainHeader.commentLength) {
+ _comment = inBuffer.slice(endOffset + Utils.Constants.ENDHDR);
+ }
+ readEntries();
+ }
+
+ return {
+ /**
+ * Returns an array of ZipEntry objects existent in the current opened archive
+ * @return Array
+ */
+ get entries () {
+ return entryList;
+ },
+
+ /**
+ * Archive comment
+ * @return {String}
+ */
+ get comment () { return _comment.toString(); },
+ set comment(val) {
+ mainHeader.commentLength = val.length;
+ _comment = val;
+ },
+
+ /**
+ * Returns a reference to the entry with the given name or null if entry is inexistent
+ *
+ * @param entryName
+ * @return ZipEntry
+ */
+ getEntry : function(/*String*/entryName) {
+ return entryTable[entryName] || null;
+ },
+
+ /**
+ * Adds the given entry to the entry list
+ *
+ * @param entry
+ */
+ setEntry : function(/*ZipEntry*/entry) {
+ entryList.push(entry);
+ entryTable[entry.entryName] = entry;
+ mainHeader.totalEntries = entryList.length;
+ },
+
+ /**
+ * Removes the entry with the given name from the entry list.
+ *
+ * If the entry is a directory, then all nested files and directories will be removed
+ * @param entryName
+ */
+ deleteEntry : function(/*String*/entryName) {
+ var entry = entryTable[entryName];
+ if (entry && entry.isDirectory) {
+ var _self = this;
+ this.getEntryChildren(entry).forEach(function(child) {
+ if (child.entryName != entryName) {
+ _self.deleteEntry(child.entryName)
+ }
+ })
+ }
+ entryList.splice(entryList.indexOf(entry), 1);
+ delete(entryTable[entryName]);
+ mainHeader.totalEntries = entryList.length;
+ },
+
+ /**
+ * Iterates and returns all nested files and directories of the given entry
+ *
+ * @param entry
+ * @return Array
+ */
+ getEntryChildren : function(/*ZipEntry*/entry) {
+ if (entry.isDirectory) {
+ var list = [],
+ name = entry.entryName,
+ len = name.length;
+
+ entryList.forEach(function(zipEntry) {
+ if (zipEntry.entryName.substr(0, len) == name) {
+ list.push(zipEntry);
+ }
+ });
+ return list;
+ }
+ return []
+ },
+
+ /**
+ * Returns the zip file
+ *
+ * @return Buffer
+ */
+ compressToBuffer : function() {
+ if (entryList.length > 1) {
+ entryList.sort(function(a, b) {
+ var nameA = a.entryName.toLowerCase();
+ var nameB = b.entryName.toLowerCase();
+ if (nameA < nameB) {return -1}
+ if (nameA > nameB) {return 1}
+ return 0;
+ });
+ }
+
+ var totalSize = 0,
+ dataBlock = [],
+ entryHeaders = [],
+ dindex = 0;
+
+ mainHeader.size = 0;
+ mainHeader.offset = 0;
+
+ entryList.forEach(function(entry) {
+ entry.header.offset = dindex;
+
+ // compress data and set local and entry header accordingly. Reason why is called first
+ var compressedData = entry.getCompressedData();
+ // data header
+ var dataHeader = entry.header.dataHeaderToBinary();
+ var postHeader = new Buffer(entry.entryName + entry.extra.toString());
+ var dataLength = dataHeader.length + postHeader.length + compressedData.length;
+
+ dindex += dataLength;
+
+ dataBlock.push(dataHeader);
+ dataBlock.push(postHeader);
+ dataBlock.push(compressedData);
+
+ var entryHeader = entry.packHeader();
+ entryHeaders.push(entryHeader);
+ mainHeader.size += entryHeader.length;
+ totalSize += (dataLength + entryHeader.length);
+ });
+
+ totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length
+ // point to end of data and begining of central directory first record
+ mainHeader.offset = dindex;
+
+ dindex = 0;
+ var outBuffer = new Buffer(totalSize);
+ dataBlock.forEach(function(content) {
+ content.copy(outBuffer, dindex); // write data blocks
+ dindex += content.length;
+ });
+ entryHeaders.forEach(function(content) {
+ content.copy(outBuffer, dindex); // write central directory entries
+ dindex += content.length;
+ });
+
+ var mh = mainHeader.toBinary();
+ if (_comment) {
+ _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment
+ }
+
+ mh.copy(outBuffer, dindex); // write main header
+
+ return outBuffer
+ },
+
+ toAsyncBuffer : function(/*Function*/onSuccess,/*Function*/onFail,/*Function*/onItemStart,/*Function*/onItemEnd) {
+ if (entryList.length > 1) {
+ entryList.sort(function(a, b) {
+ var nameA = a.entryName.toLowerCase();
+ var nameB = b.entryName.toLowerCase();
+ if (nameA > nameB) {return -1}
+ if (nameA < nameB) {return 1}
+ return 0;
+ });
+ }
+
+ var totalSize = 0,
+ dataBlock = [],
+ entryHeaders = [],
+ dindex = 0;
+
+ mainHeader.size = 0;
+ mainHeader.offset = 0;
+
+ var compress=function(entryList){
+ var self=arguments.callee;
+ var entry;
+ if(entryList.length){
+ var entry=entryList.pop();
+ var name=entry.entryName + entry.extra.toString();
+ if(onItemStart)onItemStart(name);
+ entry.getCompressedDataAsync(function(compressedData){
+ if(onItemEnd)onItemEnd(name);
+
+ entry.header.offset = dindex;
+ // data header
+ var dataHeader = entry.header.dataHeaderToBinary();
+ var postHeader = new Buffer(name);
+ var dataLength = dataHeader.length + postHeader.length + compressedData.length;
+
+ dindex += dataLength;
+
+ dataBlock.push(dataHeader);
+ dataBlock.push(postHeader);
+ dataBlock.push(compressedData);
+
+ var entryHeader = entry.packHeader();
+ entryHeaders.push(entryHeader);
+ mainHeader.size += entryHeader.length;
+ totalSize += (dataLength + entryHeader.length);
+
+ if(entryList.length){
+ self(entryList);
+ }else{
+
+
+ totalSize += mainHeader.mainHeaderSize; // also includes zip file comment length
+ // point to end of data and begining of central directory first record
+ mainHeader.offset = dindex;
+
+ dindex = 0;
+ var outBuffer = new Buffer(totalSize);
+ dataBlock.forEach(function(content) {
+ content.copy(outBuffer, dindex); // write data blocks
+ dindex += content.length;
+ });
+ entryHeaders.forEach(function(content) {
+ content.copy(outBuffer, dindex); // write central directory entries
+ dindex += content.length;
+ });
+
+ var mh = mainHeader.toBinary();
+ if (_comment) {
+ _comment.copy(mh, Utils.Constants.ENDHDR); // add zip file comment
+ }
+
+ mh.copy(outBuffer, dindex); // write main header
+
+ onSuccess(outBuffer);
+ }
+ });
+ }
+ };
+
+ compress(entryList);
+ }
+ }
+};