994 lines
38 KiB
JavaScript
994 lines
38 KiB
JavaScript
// jshint ignore: start
|
||
// jscs:disable
|
||
|
||
modules.define(
|
||
'unzip',
|
||
[],
|
||
function (
|
||
provide
|
||
) {
|
||
|
||
var zip;
|
||
var ERR_BAD_FORMAT = "File format is not recognized.";
|
||
var ERR_ENCRYPTED = "File contains encrypted entry.";
|
||
var ERR_ZIP64 = "File is using Zip64 (4gb+ file size).";
|
||
var ERR_READ = "Error while reading zip file.";
|
||
var ERR_WRITE = "Error while writing zip file.";
|
||
var ERR_WRITE_DATA = "Error while writing file data.";
|
||
var ERR_READ_DATA = "Error while reading file data.";
|
||
var ERR_DUPLICATED_NAME = "File already exists.";
|
||
var CHUNK_SIZE = 512 * 1024;
|
||
|
||
var INFLATE_JS = "inflate.js";
|
||
var DEFLATE_JS = "deflate.js";
|
||
|
||
var TEXT_PLAIN = "text/plain";
|
||
|
||
var MESSAGE_EVENT = "message";
|
||
|
||
var appendABViewSupported;
|
||
try {
|
||
appendABViewSupported = new Blob([new DataView(new ArrayBuffer(0))]).size === 0;
|
||
} catch (e) {}
|
||
|
||
function Crc32() {
|
||
var crc = -1,
|
||
that = this;
|
||
that.append = function (data) {
|
||
var offset, table = that.table;
|
||
for (offset = 0; offset < data.length; offset++)
|
||
crc = (crc >>> 8) ^ table[(crc ^ data[offset]) & 0xFF];
|
||
};
|
||
that.get = function () {
|
||
return~crc;
|
||
};
|
||
}
|
||
Crc32.prototype.table = (function () {
|
||
var i, j, t, table = [];
|
||
for (i = 0; i < 256; i++) {
|
||
t = i;
|
||
for (j = 0; j < 8; j++)
|
||
if (t & 1) t = (t >>> 1) ^ 0xEDB88320;
|
||
else t = t >>> 1;
|
||
table[i] = t;
|
||
}
|
||
return table;
|
||
})();
|
||
|
||
function blobSlice(blob, index, length) {
|
||
if (blob.slice) return blob.slice(index, index + length);
|
||
else if (blob.webkitSlice) return blob.webkitSlice(index, index + length);
|
||
else if (blob.mozSlice) return blob.mozSlice(index, index + length);
|
||
else if (blob.msSlice) return blob.msSlice(index, index + length);
|
||
}
|
||
|
||
function getDataHelper(byteLength, bytes) {
|
||
var dataBuffer, dataArray;
|
||
dataBuffer = new ArrayBuffer(byteLength);
|
||
dataArray = new Uint8Array(dataBuffer);
|
||
if (bytes) dataArray.set(bytes, 0);
|
||
return {
|
||
buffer: dataBuffer,
|
||
array: dataArray,
|
||
view: new DataView(dataBuffer)
|
||
};
|
||
}
|
||
|
||
// Readers
|
||
function Reader() {}
|
||
|
||
function TextReader(text) {
|
||
var that = this,
|
||
blobReader;
|
||
|
||
function init(callback, onerror) {
|
||
var blob = new Blob([text], {
|
||
type: TEXT_PLAIN
|
||
});
|
||
blobReader = new BlobReader(blob);
|
||
blobReader.init(function () {
|
||
that.size = blobReader.size;
|
||
callback();
|
||
}, onerror);
|
||
}
|
||
|
||
function readUint8Array(index, length, callback, onerror) {
|
||
blobReader.readUint8Array(index, length, callback, onerror);
|
||
}
|
||
|
||
that.size = 0;
|
||
that.init = init;
|
||
that.readUint8Array = readUint8Array;
|
||
}
|
||
TextReader.prototype = new Reader();
|
||
TextReader.prototype.constructor = TextReader;
|
||
|
||
function Data64URIReader(dataURI) {
|
||
var that = this,
|
||
dataStart;
|
||
|
||
function init(callback) {
|
||
var dataEnd = dataURI.length;
|
||
while (dataURI.charAt(dataEnd - 1) == "=")
|
||
dataEnd--;
|
||
dataStart = dataURI.indexOf(",") + 1;
|
||
that.size = Math.floor((dataEnd - dataStart) * 0.75);
|
||
callback();
|
||
}
|
||
|
||
function readUint8Array(index, length, callback) {
|
||
var i, data = getDataHelper(length);
|
||
var start = Math.floor(index / 3) * 4;
|
||
var end = Math.ceil((index + length) / 3) * 4;
|
||
var bytes = window.atob(dataURI.substring(start + dataStart, end + dataStart));
|
||
var delta = index - Math.floor(start / 4) * 3;
|
||
for (i = delta; i < delta + length; i++)
|
||
data.array[i - delta] = bytes.charCodeAt(i);
|
||
callback(data.array);
|
||
}
|
||
|
||
that.size = 0;
|
||
that.init = init;
|
||
that.readUint8Array = readUint8Array;
|
||
}
|
||
Data64URIReader.prototype = new Reader();
|
||
Data64URIReader.prototype.constructor = Data64URIReader;
|
||
|
||
function BlobReader(blob) {
|
||
var that = this;
|
||
|
||
function init(callback) {
|
||
this.size = blob.size;
|
||
callback();
|
||
}
|
||
|
||
function readUint8Array(index, length, callback, onerror) {
|
||
var reader = new FileReader();
|
||
reader.onload = function (e) {
|
||
callback(new Uint8Array(e.target.result));
|
||
};
|
||
reader.onerror = onerror;
|
||
reader.readAsArrayBuffer(blobSlice(blob, index, length));
|
||
}
|
||
|
||
that.size = 0;
|
||
that.init = init;
|
||
that.readUint8Array = readUint8Array;
|
||
}
|
||
BlobReader.prototype = new Reader();
|
||
BlobReader.prototype.constructor = BlobReader;
|
||
|
||
// Writers
|
||
|
||
function Writer() {}
|
||
Writer.prototype.getData = function (callback) {
|
||
callback(this.data);
|
||
};
|
||
|
||
function TextWriter(encoding) {
|
||
var that = this,
|
||
blob;
|
||
|
||
function init(callback) {
|
||
blob = new Blob([], {
|
||
type: TEXT_PLAIN
|
||
});
|
||
callback();
|
||
}
|
||
|
||
function writeUint8Array(array, callback) {
|
||
blob = new Blob([blob, appendABViewSupported ? array : array.buffer], {
|
||
type: TEXT_PLAIN
|
||
});
|
||
callback();
|
||
}
|
||
|
||
function getData(callback, onerror) {
|
||
var reader = new FileReader();
|
||
reader.onload = function (e) {
|
||
callback(e.target.result);
|
||
};
|
||
reader.onerror = onerror;
|
||
reader.readAsText(blob, encoding);
|
||
}
|
||
|
||
that.init = init;
|
||
that.writeUint8Array = writeUint8Array;
|
||
that.getData = getData;
|
||
}
|
||
TextWriter.prototype = new Writer();
|
||
TextWriter.prototype.constructor = TextWriter;
|
||
|
||
function Data64URIWriter(contentType) {
|
||
var that = this,
|
||
data = "",
|
||
pending = "";
|
||
|
||
function init(callback) {
|
||
data += "data:" + (contentType || "") + ";base64,";
|
||
callback();
|
||
}
|
||
|
||
function writeUint8Array(array, callback) {
|
||
var i, delta = pending.length,
|
||
dataString = pending;
|
||
pending = "";
|
||
for (i = 0; i < (Math.floor((delta + array.length) / 3) * 3) - delta; i++)
|
||
dataString += String.fromCharCode(array[i]);
|
||
for (; i < array.length; i++)
|
||
pending += String.fromCharCode(array[i]);
|
||
if (dataString.length > 2) data += window.btoa(dataString);
|
||
else pending = dataString;
|
||
callback();
|
||
}
|
||
|
||
function getData(callback) {
|
||
callback(data + window.btoa(pending));
|
||
}
|
||
|
||
that.init = init;
|
||
that.writeUint8Array = writeUint8Array;
|
||
that.getData = getData;
|
||
}
|
||
Data64URIWriter.prototype = new Writer();
|
||
Data64URIWriter.prototype.constructor = Data64URIWriter;
|
||
|
||
function BlobWriter(contentType) {
|
||
var blob, that = this;
|
||
|
||
function init(callback) {
|
||
blob = new Blob([], {
|
||
type: contentType
|
||
});
|
||
callback();
|
||
}
|
||
|
||
function writeUint8Array(array, callback) {
|
||
blob = new Blob([blob, appendABViewSupported ? array : array.buffer], {
|
||
type: contentType
|
||
});
|
||
callback();
|
||
}
|
||
|
||
function getData(callback) {
|
||
callback(blob);
|
||
}
|
||
|
||
that.init = init;
|
||
that.writeUint8Array = writeUint8Array;
|
||
that.getData = getData;
|
||
}
|
||
BlobWriter.prototype = new Writer();
|
||
BlobWriter.prototype.constructor = BlobWriter;
|
||
|
||
// inflate/deflate core functions
|
||
|
||
function launchWorkerProcess(worker, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) {
|
||
var chunkIndex = 0,
|
||
index, outputSize;
|
||
|
||
function onflush() {
|
||
worker.removeEventListener(MESSAGE_EVENT, onmessage, false);
|
||
onend(outputSize);
|
||
}
|
||
|
||
function onmessage(event) {
|
||
var message = event.data,
|
||
data = message.data;
|
||
|
||
if (message.onappend) {
|
||
outputSize += data.length;
|
||
writer.writeUint8Array(data, function () {
|
||
onappend(false, data);
|
||
step();
|
||
}, onwriteerror);
|
||
}
|
||
if (message.onflush) if (data) {
|
||
outputSize += data.length;
|
||
writer.writeUint8Array(data, function () {
|
||
onappend(false, data);
|
||
onflush();
|
||
}, onwriteerror);
|
||
} else onflush();
|
||
if (message.progress && onprogress) onprogress(index + message.current, size);
|
||
}
|
||
|
||
function step() {
|
||
index = chunkIndex * CHUNK_SIZE;
|
||
if (index < size) reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function (array) {
|
||
worker.postMessage({
|
||
append: true,
|
||
data: array
|
||
});
|
||
chunkIndex++;
|
||
if (onprogress) onprogress(index, size);
|
||
onappend(true, array);
|
||
}, onreaderror);
|
||
else worker.postMessage({
|
||
flush: true
|
||
});
|
||
}
|
||
|
||
outputSize = 0;
|
||
worker.addEventListener(MESSAGE_EVENT, onmessage, false);
|
||
step();
|
||
}
|
||
|
||
function launchProcess(process, reader, writer, offset, size, onappend, onprogress, onend, onreaderror, onwriteerror) {
|
||
var chunkIndex = 0,
|
||
index, outputSize = 0;
|
||
|
||
function step() {
|
||
var outputData;
|
||
index = chunkIndex * CHUNK_SIZE;
|
||
if (index < size) reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function (inputData) {
|
||
var outputData = process.append(inputData, function () {
|
||
if (onprogress) onprogress(offset + index, size);
|
||
});
|
||
outputSize += outputData.length;
|
||
onappend(true, inputData);
|
||
writer.writeUint8Array(outputData, function () {
|
||
onappend(false, outputData);
|
||
chunkIndex++;
|
||
setTimeout(step, 1);
|
||
}, onwriteerror);
|
||
if (onprogress) onprogress(index, size);
|
||
}, onreaderror);
|
||
else {
|
||
outputData = process.flush();
|
||
if (outputData) {
|
||
outputSize += outputData.length;
|
||
writer.writeUint8Array(outputData, function () {
|
||
onappend(false, outputData);
|
||
onend(outputSize);
|
||
}, onwriteerror);
|
||
} else onend(outputSize);
|
||
}
|
||
}
|
||
|
||
step();
|
||
}
|
||
|
||
function inflate(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) {
|
||
var worker, crc32 = new Crc32();
|
||
|
||
function oninflateappend(sending, array) {
|
||
if (computeCrc32 && !sending) crc32.append(array);
|
||
}
|
||
|
||
function oninflateend(outputSize) {
|
||
onend(outputSize, crc32.get());
|
||
}
|
||
|
||
if (zip.useWebWorkers) {
|
||
worker = new Worker(zip.workerScriptsPath + INFLATE_JS);
|
||
launchWorkerProcess(worker, reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror);
|
||
} else launchProcess(new zip.Inflater(), reader, writer, offset, size, oninflateappend, onprogress, oninflateend, onreaderror, onwriteerror);
|
||
return worker;
|
||
}
|
||
|
||
function deflate(reader, writer, level, onend, onprogress, onreaderror, onwriteerror) {
|
||
var worker, crc32 = new Crc32();
|
||
|
||
function ondeflateappend(sending, array) {
|
||
if (sending) crc32.append(array);
|
||
}
|
||
|
||
function ondeflateend(outputSize) {
|
||
onend(outputSize, crc32.get());
|
||
}
|
||
|
||
function onmessage() {
|
||
worker.removeEventListener(MESSAGE_EVENT, onmessage, false);
|
||
launchWorkerProcess(worker, reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror);
|
||
}
|
||
|
||
if (zip.useWebWorkers) {
|
||
worker = new Worker(zip.workerScriptsPath + DEFLATE_JS);
|
||
worker.addEventListener(MESSAGE_EVENT, onmessage, false);
|
||
worker.postMessage({
|
||
init: true,
|
||
level: level
|
||
});
|
||
} else launchProcess(new zip.Deflater(), reader, writer, 0, reader.size, ondeflateappend, onprogress, ondeflateend, onreaderror, onwriteerror);
|
||
return worker;
|
||
}
|
||
|
||
function copy(reader, writer, offset, size, computeCrc32, onend, onprogress, onreaderror, onwriteerror) {
|
||
var chunkIndex = 0,
|
||
crc32 = new Crc32();
|
||
|
||
function step() {
|
||
var index = chunkIndex * CHUNK_SIZE;
|
||
if (index < size) reader.readUint8Array(offset + index, Math.min(CHUNK_SIZE, size - index), function (array) {
|
||
if (computeCrc32) crc32.append(array);
|
||
if (onprogress) onprogress(index, size, array);
|
||
writer.writeUint8Array(array, function () {
|
||
chunkIndex++;
|
||
step();
|
||
}, onwriteerror);
|
||
}, onreaderror);
|
||
else onend(size, crc32.get());
|
||
}
|
||
|
||
step();
|
||
}
|
||
|
||
// ZipReader
|
||
|
||
function decodeASCII(str) {
|
||
var i, out = "",
|
||
charCode, extendedASCII = ['Ç', 'ü', 'é', 'â', 'ä', 'à', 'å', 'ç', 'ê', 'ë',
|
||
'è', 'ï', 'î', 'ì', 'Ä', 'Å', 'É', 'æ', 'Æ', 'ô', 'ö', 'ò', 'û', 'ù',
|
||
'ÿ', 'Ö', 'Ü', 'ø', '£', 'Ø', '×', 'ƒ', 'á', 'í', 'ó', 'ú', 'ñ', 'Ñ',
|
||
'ª', 'º', '¿', '®', '¬', '½', '¼', '¡', '«', '»', '_', '_', '_', '¦', '¦',
|
||
'Á', 'Â', 'À', '©', '¦', '¦', '+', '+', '¢', '¥', '+', '+', '-', '-', '+', '-', '+', 'ã',
|
||
'Ã', '+', '+', '-', '-', '¦', '-', '+', '¤', 'ð', 'Ð', 'Ê', 'Ë', 'È', 'i', 'Í', 'Î',
|
||
'Ï', '+', '+', '_', '_', '¦', 'Ì', '_', 'Ó', 'ß', 'Ô', 'Ò', 'õ', 'Õ', 'µ', 'þ',
|
||
'Þ', 'Ú', 'Û', 'Ù', 'ý', 'Ý', '¯', '´', '', '±', '_', '¾', '¶', '§',
|
||
'÷', '¸', '°', '¨', '·', '¹', '³', '²', '_', ' '];
|
||
for (i = 0; i < str.length; i++) {
|
||
charCode = str.charCodeAt(i) & 0xFF;
|
||
if (charCode > 127) out += extendedASCII[charCode - 128];
|
||
else out += String.fromCharCode(charCode);
|
||
}
|
||
return out;
|
||
}
|
||
|
||
function decodeUTF8(string) {
|
||
return decodeURIComponent(escape(string));
|
||
}
|
||
|
||
function getString(bytes) {
|
||
var i, str = "";
|
||
for (i = 0; i < bytes.length; i++)
|
||
str += String.fromCharCode(bytes[i]);
|
||
return str;
|
||
}
|
||
|
||
function getDate(timeRaw) {
|
||
var date = (timeRaw & 0xffff0000) >> 16,
|
||
time = timeRaw & 0x0000ffff;
|
||
try {
|
||
return new Date(1980 + ((date & 0xFE00) >> 9), ((date & 0x01E0) >> 5) - 1, date & 0x001F, (time & 0xF800) >> 11, (time & 0x07E0) >> 5, (time & 0x001F) * 2, 0);
|
||
} catch (e) {}
|
||
}
|
||
|
||
function readCommonHeader(entry, data, index, centralDirectory, onerror) {
|
||
entry.version = data.view.getUint16(index, true);
|
||
entry.bitFlag = data.view.getUint16(index + 2, true);
|
||
entry.compressionMethod = data.view.getUint16(index + 4, true);
|
||
entry.lastModDateRaw = data.view.getUint32(index + 6, true);
|
||
entry.lastModDate = getDate(entry.lastModDateRaw);
|
||
if ((entry.bitFlag & 0x01) === 0x01) {
|
||
onerror(ERR_ENCRYPTED);
|
||
return;
|
||
}
|
||
if (centralDirectory || (entry.bitFlag & 0x0008) != 0x0008) {
|
||
entry.crc32 = data.view.getUint32(index + 10, true);
|
||
entry.compressedSize = data.view.getUint32(index + 14, true);
|
||
entry.uncompressedSize = data.view.getUint32(index + 18, true);
|
||
}
|
||
if (entry.compressedSize === 0xFFFFFFFF || entry.uncompressedSize === 0xFFFFFFFF) {
|
||
onerror(ERR_ZIP64);
|
||
return;
|
||
}
|
||
entry.filenameLength = data.view.getUint16(index + 22, true);
|
||
entry.extraFieldLength = data.view.getUint16(index + 24, true);
|
||
}
|
||
|
||
function createZipReader(reader, onerror) {
|
||
function Entry() {}
|
||
|
||
Entry.prototype.getData = function (writer, onend, onprogress, checkCrc32) {
|
||
var that = this,
|
||
worker;
|
||
|
||
function terminate(callback, param) {
|
||
if (worker) worker.terminate();
|
||
worker = null;
|
||
if (callback) callback(param);
|
||
}
|
||
|
||
function testCrc32(crc32) {
|
||
var dataCrc32 = getDataHelper(4);
|
||
dataCrc32.view.setUint32(0, crc32);
|
||
return that.crc32 == dataCrc32.view.getUint32(0);
|
||
}
|
||
|
||
function getWriterData(uncompressedSize, crc32) {
|
||
if (checkCrc32 && !testCrc32(crc32)) onreaderror();
|
||
else writer.getData(function (data) {
|
||
terminate(onend, data);
|
||
});
|
||
}
|
||
|
||
function onreaderror() {
|
||
terminate(onerror, ERR_READ_DATA);
|
||
}
|
||
|
||
function onwriteerror() {
|
||
terminate(onerror, ERR_WRITE_DATA);
|
||
}
|
||
|
||
reader.readUint8Array(that.offset, 30, function (bytes) {
|
||
var data = getDataHelper(bytes.length, bytes),
|
||
dataOffset;
|
||
if (data.view.getUint32(0) != 0x504b0304) {
|
||
onerror(ERR_BAD_FORMAT);
|
||
return;
|
||
}
|
||
readCommonHeader(that, data, 4, false, onerror);
|
||
dataOffset = that.offset + 30 + that.filenameLength + that.extraFieldLength;
|
||
writer.init(function () {
|
||
if (that.compressionMethod === 0) copy(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror);
|
||
else worker = inflate(reader, writer, dataOffset, that.compressedSize, checkCrc32, getWriterData, onprogress, onreaderror, onwriteerror);
|
||
}, onwriteerror);
|
||
}, onreaderror);
|
||
};
|
||
|
||
function seekEOCDR(offset, entriesCallback) {
|
||
reader.readUint8Array(reader.size - offset, offset, function (bytes) {
|
||
var dataView = getDataHelper(bytes.length, bytes).view;
|
||
if (dataView.getUint32(0) != 0x504b0506) {
|
||
seekEOCDR(offset + 1, entriesCallback);
|
||
} else {
|
||
entriesCallback(dataView);
|
||
}
|
||
}, function () {
|
||
onerror(ERR_READ);
|
||
});
|
||
}
|
||
|
||
return {
|
||
getEntries: function (callback) {
|
||
if (reader.size < 22) {
|
||
onerror(ERR_BAD_FORMAT);
|
||
return;
|
||
}
|
||
// look for End of central directory record
|
||
seekEOCDR(22, function (dataView) {
|
||
var datalength, fileslength;
|
||
datalength = dataView.getUint32(16, true);
|
||
fileslength = dataView.getUint16(8, true);
|
||
reader.readUint8Array(datalength, reader.size - datalength, function (bytes) {
|
||
var i, index = 0,
|
||
entries = [],
|
||
entry, filename, comment, data = getDataHelper(bytes.length, bytes);
|
||
for (i = 0; i < fileslength; i++) {
|
||
entry = new Entry();
|
||
if (data.view.getUint32(index) != 0x504b0102) {
|
||
onerror(ERR_BAD_FORMAT);
|
||
return;
|
||
}
|
||
readCommonHeader(entry, data, index + 6, true, onerror);
|
||
entry.commentLength = data.view.getUint16(index + 32, true);
|
||
entry.directory = ((data.view.getUint8(index + 38) & 0x10) == 0x10);
|
||
entry.offset = data.view.getUint32(index + 42, true);
|
||
filename = getString(data.array.subarray(index + 46, index + 46 + entry.filenameLength));
|
||
entry.filename = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(filename) : decodeASCII(filename);
|
||
if (!entry.directory && entry.filename.charAt(entry.filename.length - 1) == "/") entry.directory = true;
|
||
comment = getString(data.array.subarray(index + 46 + entry.filenameLength + entry.extraFieldLength, index + 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength));
|
||
entry.comment = ((entry.bitFlag & 0x0800) === 0x0800) ? decodeUTF8(comment) : decodeASCII(comment);
|
||
entries.push(entry);
|
||
index += 46 + entry.filenameLength + entry.extraFieldLength + entry.commentLength;
|
||
}
|
||
callback(entries);
|
||
}, function () {
|
||
onerror(ERR_READ);
|
||
});
|
||
});
|
||
},
|
||
close: function (callback) {
|
||
if (callback) callback();
|
||
}
|
||
};
|
||
}
|
||
|
||
// ZipWriter
|
||
|
||
function encodeUTF8(string) {
|
||
return unescape(encodeURIComponent(string));
|
||
}
|
||
|
||
function getBytes(str) {
|
||
var i, array = [];
|
||
for (i = 0; i < str.length; i++)
|
||
array.push(str.charCodeAt(i));
|
||
return array;
|
||
}
|
||
|
||
function createZipWriter(writer, onerror, dontDeflate) {
|
||
var worker, files = {}, filenames = [],
|
||
datalength = 0;
|
||
|
||
function terminate(callback, message) {
|
||
if (worker) worker.terminate();
|
||
worker = null;
|
||
if (callback) callback(message);
|
||
}
|
||
|
||
function onwriteerror() {
|
||
terminate(onerror, ERR_WRITE);
|
||
}
|
||
|
||
function onreaderror() {
|
||
terminate(onerror, ERR_READ_DATA);
|
||
}
|
||
|
||
return {
|
||
add: function (name, reader, onend, onprogress, options) {
|
||
var header, filename, date;
|
||
|
||
function writeHeader(callback) {
|
||
var data;
|
||
date = options.lastModDate || new Date();
|
||
header = getDataHelper(26);
|
||
files[name] = {
|
||
headerArray: header.array,
|
||
directory: options.directory,
|
||
filename: filename,
|
||
offset: datalength,
|
||
comment: getBytes(encodeUTF8(options.comment || ""))
|
||
};
|
||
header.view.setUint32(0, 0x14000808);
|
||
if (options.version) header.view.setUint8(0, options.version);
|
||
if (!dontDeflate && options.level !== 0 && !options.directory) header.view.setUint16(4, 0x0800);
|
||
header.view.setUint16(6, (((date.getHours() << 6) | date.getMinutes()) << 5) | date.getSeconds() / 2, true);
|
||
header.view.setUint16(8, ((((date.getFullYear() - 1980) << 4) | (date.getMonth() + 1)) << 5) | date.getDate(), true);
|
||
header.view.setUint16(22, filename.length, true);
|
||
data = getDataHelper(30 + filename.length);
|
||
data.view.setUint32(0, 0x504b0304);
|
||
data.array.set(header.array, 4);
|
||
data.array.set(filename, 30);
|
||
datalength += data.array.length;
|
||
writer.writeUint8Array(data.array, callback, onwriteerror);
|
||
}
|
||
|
||
function writeFooter(compressedLength, crc32) {
|
||
var footer = getDataHelper(16);
|
||
datalength += compressedLength || 0;
|
||
footer.view.setUint32(0, 0x504b0708);
|
||
if (typeof crc32 != "undefined") {
|
||
header.view.setUint32(10, crc32, true);
|
||
footer.view.setUint32(4, crc32, true);
|
||
}
|
||
if (reader) {
|
||
footer.view.setUint32(8, compressedLength, true);
|
||
header.view.setUint32(14, compressedLength, true);
|
||
footer.view.setUint32(12, reader.size, true);
|
||
header.view.setUint32(18, reader.size, true);
|
||
}
|
||
writer.writeUint8Array(footer.array, function () {
|
||
datalength += 16;
|
||
terminate(onend);
|
||
}, onwriteerror);
|
||
}
|
||
|
||
function writeFile() {
|
||
options = options || {};
|
||
name = name.trim();
|
||
if (options.directory && name.charAt(name.length - 1) != "/") name += "/";
|
||
if (files.hasOwnProperty(name)) {
|
||
onerror(ERR_DUPLICATED_NAME);
|
||
return;
|
||
}
|
||
filename = getBytes(encodeUTF8(name));
|
||
filenames.push(name);
|
||
writeHeader(function () {
|
||
if (reader) if (dontDeflate || options.level === 0) copy(reader, writer, 0, reader.size, true, writeFooter, onprogress, onreaderror, onwriteerror);
|
||
else worker = deflate(reader, writer, options.level, writeFooter, onprogress, onreaderror, onwriteerror);
|
||
else writeFooter();
|
||
}, onwriteerror);
|
||
}
|
||
|
||
if (reader) reader.init(writeFile, onreaderror);
|
||
else writeFile();
|
||
},
|
||
close: function (callback) {
|
||
var data, length = 0,
|
||
index = 0,
|
||
indexFilename, file;
|
||
for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) {
|
||
file = files[filenames[indexFilename]];
|
||
length += 46 + file.filename.length + file.comment.length;
|
||
}
|
||
data = getDataHelper(length + 22);
|
||
for (indexFilename = 0; indexFilename < filenames.length; indexFilename++) {
|
||
file = files[filenames[indexFilename]];
|
||
data.view.setUint32(index, 0x504b0102);
|
||
data.view.setUint16(index + 4, 0x1400);
|
||
data.array.set(file.headerArray, index + 6);
|
||
data.view.setUint16(index + 32, file.comment.length, true);
|
||
if (file.directory) data.view.setUint8(index + 38, 0x10);
|
||
data.view.setUint32(index + 42, file.offset, true);
|
||
data.array.set(file.filename, index + 46);
|
||
data.array.set(file.comment, index + 46 + file.filename.length);
|
||
index += 46 + file.filename.length + file.comment.length;
|
||
}
|
||
data.view.setUint32(index, 0x504b0506);
|
||
data.view.setUint16(index + 8, filenames.length, true);
|
||
data.view.setUint16(index + 10, filenames.length, true);
|
||
data.view.setUint32(index + 12, length, true);
|
||
data.view.setUint32(index + 16, datalength, true);
|
||
writer.writeUint8Array(data.array, function () {
|
||
terminate(function () {
|
||
writer.getData(callback);
|
||
});
|
||
}, onwriteerror);
|
||
}
|
||
};
|
||
}
|
||
|
||
zip = {
|
||
Reader: Reader,
|
||
Writer: Writer,
|
||
BlobReader: BlobReader,
|
||
Data64URIReader: Data64URIReader,
|
||
TextReader: TextReader,
|
||
BlobWriter: BlobWriter,
|
||
Data64URIWriter: Data64URIWriter,
|
||
TextWriter: TextWriter,
|
||
createReader: function (reader, callback, onerror) {
|
||
reader.init(function () {
|
||
callback(createZipReader(reader, onerror));
|
||
}, onerror);
|
||
},
|
||
createWriter: function (writer, callback, onerror, dontDeflate) {
|
||
writer.init(function () {
|
||
callback(createZipWriter(writer, onerror, dontDeflate));
|
||
}, onerror);
|
||
},
|
||
workerScriptsPath: "",
|
||
useWebWorkers: true
|
||
};
|
||
/*
|
||
Copyright (c) 2013 Gildas Lormeau. All rights reserved.
|
||
|
||
Redistribution and use in source and binary forms, with or without
|
||
modification, are permitted provided that the following conditions are met:
|
||
|
||
1. Redistributions of source code must retain the above copyright notice,
|
||
this list of conditions and the following disclaimer.
|
||
|
||
2. Redistributions in binary form must reproduce the above copyright
|
||
notice, this list of conditions and the following disclaimer in
|
||
the documentation and/or other materials provided with the distribution.
|
||
|
||
3. The names of the authors may not be used to endorse or promote products
|
||
derived from this software without specific prior written permission.
|
||
|
||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
|
||
INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
*/
|
||
|
||
(function () {
|
||
|
||
var ERR_HTTP_RANGE = "HTTP Range not supported.";
|
||
|
||
var Reader = zip.Reader;
|
||
var Writer = zip.Writer;
|
||
|
||
var ZipDirectoryEntry;
|
||
|
||
var appendABViewSupported;
|
||
try {
|
||
appendABViewSupported = new Blob([new DataView(new ArrayBuffer(0))]).size === 0;
|
||
} catch (e) {}
|
||
|
||
function HttpReader(url) {
|
||
var that = this;
|
||
|
||
function getData(callback, onerror) {
|
||
var request;
|
||
if (!that.data) {
|
||
request = new XMLHttpRequest();
|
||
request.addEventListener("load", function (data) {
|
||
// При 500-ка не срабатывает error - триггерим руками
|
||
if (data.target.status >= 500) {
|
||
onerror();
|
||
return;
|
||
}
|
||
|
||
if (!that.size) that.size = Number(request.getResponseHeader("Content-Length"));
|
||
that.data = new Uint8Array(request.response);
|
||
callback();
|
||
}, false);
|
||
request.addEventListener("error", onerror, false);
|
||
request.open("GET", url);
|
||
request.responseType = "arraybuffer";
|
||
request.send();
|
||
} else callback();
|
||
}
|
||
|
||
function init(callback, onerror) {
|
||
var request = new XMLHttpRequest();
|
||
request.addEventListener("load", function (data) {
|
||
// При 500-ка не срабатывает error - триггерим руками
|
||
if (data.target.status >= 500) {
|
||
onerror();
|
||
return;
|
||
}
|
||
|
||
that.size = Number(request.getResponseHeader("Content-Length"));
|
||
callback();
|
||
}, false);
|
||
request.addEventListener("error", onerror, false);
|
||
request.open("HEAD", url);
|
||
request.send();
|
||
}
|
||
|
||
function readUint8Array(index, length, callback, onerror) {
|
||
getData(function () {
|
||
callback(new Uint8Array(that.data.subarray(index, index + length)));
|
||
}, onerror);
|
||
}
|
||
|
||
that.size = 0;
|
||
that.init = init;
|
||
that.readUint8Array = readUint8Array;
|
||
}
|
||
HttpReader.prototype = new Reader();
|
||
HttpReader.prototype.constructor = HttpReader;
|
||
|
||
function HttpRangeReader(url) {
|
||
var that = this;
|
||
|
||
function init(callback, onerror) {
|
||
var request = new XMLHttpRequest();
|
||
request.addEventListener("load", function () {
|
||
that.size = Number(request.getResponseHeader("Content-Length"));
|
||
if (request.getResponseHeader("Accept-Ranges") == "bytes") callback();
|
||
else onerror(ERR_HTTP_RANGE);
|
||
}, false);
|
||
request.addEventListener("error", onerror, false);
|
||
request.open("HEAD", url);
|
||
request.send();
|
||
}
|
||
|
||
function readArrayBuffer(index, length, callback, onerror) {
|
||
var request = new XMLHttpRequest();
|
||
request.open("GET", url);
|
||
request.responseType = "arraybuffer";
|
||
request.setRequestHeader("Range", "bytes=" + index + "-" + (index + length - 1));
|
||
request.addEventListener("load", function () {
|
||
callback(request.response);
|
||
}, false);
|
||
request.addEventListener("error", onerror, false);
|
||
request.send();
|
||
}
|
||
|
||
function readUint8Array(index, length, callback, onerror) {
|
||
readArrayBuffer(index, length, function (arraybuffer) {
|
||
callback(new Uint8Array(arraybuffer));
|
||
}, onerror);
|
||
}
|
||
|
||
that.size = 0;
|
||
that.init = init;
|
||
that.readUint8Array = readUint8Array;
|
||
}
|
||
HttpRangeReader.prototype = new Reader();
|
||
HttpRangeReader.prototype.constructor = HttpRangeReader;
|
||
|
||
function ArrayBufferReader(arrayBuffer) {
|
||
var that = this;
|
||
|
||
function init(callback, onerror) {
|
||
that.size = arrayBuffer.byteLength;
|
||
callback();
|
||
}
|
||
|
||
function readUint8Array(index, length, callback, onerror) {
|
||
callback(new Uint8Array(arrayBuffer.slice(index, index + length)));
|
||
}
|
||
|
||
that.size = 0;
|
||
that.init = init;
|
||
that.readUint8Array = readUint8Array;
|
||
}
|
||
ArrayBufferReader.prototype = new Reader();
|
||
ArrayBufferReader.prototype.constructor = ArrayBufferReader;
|
||
|
||
function ArrayBufferWriter() {
|
||
var array, that = this;
|
||
|
||
function init(callback, onerror) {
|
||
array = new Uint8Array();
|
||
callback();
|
||
}
|
||
|
||
function writeUint8Array(arr, callback, onerror) {
|
||
var tmpArray = new Uint8Array(array.length + arr.length);
|
||
tmpArray.set(array);
|
||
tmpArray.set(arr, array.length);
|
||
array = tmpArray;
|
||
callback();
|
||
}
|
||
|
||
function getData(callback) {
|
||
callback(array.buffer);
|
||
}
|
||
|
||
that.init = init;
|
||
that.writeUint8Array = writeUint8Array;
|
||
that.getData = getData;
|
||
}
|
||
ArrayBufferWriter.prototype = new Writer();
|
||
ArrayBufferWriter.prototype.constructor = ArrayBufferWriter;
|
||
|
||
function FileWriter(fileEntry, contentType) {
|
||
var writer, that = this;
|
||
|
||
function init(callback, onerror) {
|
||
fileEntry.createWriter(function (fileWriter) {
|
||
writer = fileWriter;
|
||
callback();
|
||
}, onerror);
|
||
}
|
||
|
||
function writeUint8Array(array, callback, onerror) {
|
||
var blob = new Blob([appendABViewSupported ? array : array.buffer], {
|
||
type: contentType
|
||
});
|
||
writer.onwrite = function () {
|
||
writer.onwrite = null;
|
||
callback();
|
||
};
|
||
writer.onerror = onerror;
|
||
writer.write(blob);
|
||
}
|
||
|
||
function getData(callback) {
|
||
fileEntry.file(callback);
|
||
}
|
||
|
||
that.init = init;
|
||
that.writeUint8Array = writeUint8Array;
|
||
that.getData = getData;
|
||
}
|
||
FileWriter.prototype = new Writer();
|
||
FileWriter.prototype.constructor = FileWriter;
|
||
|
||
zip.FileWriter = FileWriter;
|
||
zip.HttpReader = HttpReader;
|
||
zip.HttpRangeReader = HttpRangeReader;
|
||
zip.ArrayBufferReader = ArrayBufferReader;
|
||
zip.ArrayBufferWriter = ArrayBufferWriter;
|
||
|
||
if (zip.fs) {
|
||
ZipDirectoryEntry = zip.fs.ZipDirectoryEntry;
|
||
ZipDirectoryEntry.prototype.addHttpContent = function (name, URL, useRangeHeader) {
|
||
function addChild(parent, name, params, directory) {
|
||
if (parent.directory) return directory ? new ZipDirectoryEntry(parent.fs, name, params, parent) : new zip.fs.ZipFileEntry(parent.fs, name, params, parent);
|
||
else throw "Parent entry is not a directory.";
|
||
}
|
||
|
||
return addChild(this, name, {
|
||
data: URL,
|
||
Reader: useRangeHeader ? HttpRangeReader : HttpReader
|
||
});
|
||
};
|
||
ZipDirectoryEntry.prototype.importHttpContent = function (URL, useRangeHeader, onend, onerror) {
|
||
this.importZip(useRangeHeader ? new HttpRangeReader(URL) : new HttpReader(URL), onend, onerror);
|
||
};
|
||
zip.fs.FS.prototype.importHttpContent = function (URL, useRangeHeader, onend, onerror) {
|
||
this.entries = [];
|
||
this.root = new ZipDirectoryEntry(this);
|
||
this.root.importHttpContent(URL, useRangeHeader, onend, onerror);
|
||
};
|
||
}
|
||
|
||
})();
|
||
|
||
provide(zip);
|
||
});
|