jszip.js 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474
  1. /**
  2. JSZip - A Javascript class for generating and reading zip files
  3. <http://stuartk.com/jszip>
  4. (c) 2009-2012 Stuart Knightley <stuart [at] stuartk.com>
  5. Dual licenced under the MIT license or GPLv3. See LICENSE.markdown.
  6. Usage:
  7. zip = new JSZip();
  8. zip.file("hello.txt", "Hello, World!").file("tempfile", "nothing");
  9. zip.folder("images").file("smile.gif", base64Data, {base64: true});
  10. zip.file("Xmas.txt", "Ho ho ho !", {date : new Date("December 25, 2007 00:00:01")});
  11. zip.remove("tempfile");
  12. base64zip = zip.generate();
  13. **/
  14. // We use strict, but it should not be placed outside of a function because
  15. // the environment is shared inside the browser.
  16. // "use strict";
  17. /**
  18. * Representation a of zip file in js
  19. * @constructor
  20. * @param {String=|ArrayBuffer=|Uint8Array=|Buffer=} data the data to load, if any (optional).
  21. * @param {Object=} options the options for creating this objects (optional).
  22. */
  23. var JSZip = function(data, options) {
  24. // object containing the files :
  25. // {
  26. // "folder/" : {...},
  27. // "folder/data.txt" : {...}
  28. // }
  29. this.files = {};
  30. // Where we are in the hierarchy
  31. this.root = "";
  32. if (data) {
  33. this.load(data, options);
  34. }
  35. };
  36. JSZip.signature = {
  37. LOCAL_FILE_HEADER : "\x50\x4b\x03\x04",
  38. CENTRAL_FILE_HEADER : "\x50\x4b\x01\x02",
  39. CENTRAL_DIRECTORY_END : "\x50\x4b\x05\x06",
  40. ZIP64_CENTRAL_DIRECTORY_LOCATOR : "\x50\x4b\x06\x07",
  41. ZIP64_CENTRAL_DIRECTORY_END : "\x50\x4b\x06\x06",
  42. DATA_DESCRIPTOR : "\x50\x4b\x07\x08"
  43. };
  44. // Default properties for a new file
  45. JSZip.defaults = {
  46. base64: false,
  47. binary: false,
  48. dir: false,
  49. date: null,
  50. compression: null
  51. };
  52. /*
  53. * List features that require a modern browser, and if the current browser support them.
  54. */
  55. JSZip.support = {
  56. // contains true if JSZip can read/generate ArrayBuffer, false otherwise.
  57. arraybuffer : (function(){
  58. return typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
  59. })(),
  60. // contains true if JSZip can read/generate nodejs Buffer, false otherwise.
  61. nodebuffer : (function(){
  62. return typeof Buffer !== "undefined";
  63. })(),
  64. // contains true if JSZip can read/generate Uint8Array, false otherwise.
  65. uint8array : (function(){
  66. return typeof Uint8Array !== "undefined";
  67. })(),
  68. // contains true if JSZip can read/generate Blob, false otherwise.
  69. blob : (function(){
  70. // the spec started with BlobBuilder then replaced it with a construtor for Blob.
  71. // Result : we have browsers that :
  72. // * know the BlobBuilder (but with prefix)
  73. // * know the Blob constructor
  74. // * know about Blob but not about how to build them
  75. // About the "=== 0" test : if given the wrong type, it may be converted to a string.
  76. // Instead of an empty content, we will get "[object Uint8Array]" for example.
  77. if (typeof ArrayBuffer === "undefined") {
  78. return false;
  79. }
  80. var buffer = new ArrayBuffer(0);
  81. try {
  82. return new Blob([buffer], { type: "application/zip" }).size === 0;
  83. }
  84. catch(e) {}
  85. try {
  86. var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
  87. var builder = new BlobBuilder();
  88. builder.append(buffer);
  89. return builder.getBlob('application/zip').size === 0;
  90. }
  91. catch(e) {}
  92. return false;
  93. })()
  94. };
  95. JSZip.prototype = (function () {
  96. var textEncoder, textDecoder;
  97. if (
  98. JSZip.support.uint8array &&
  99. typeof TextEncoder === "function" &&
  100. typeof TextDecoder === "function"
  101. ) {
  102. textEncoder = new TextEncoder("utf-8");
  103. textDecoder = new TextDecoder("utf-8");
  104. }
  105. /**
  106. * Returns the raw data of a ZipObject, decompress the content if necessary.
  107. * @param {ZipObject} file the file to use.
  108. * @return {String|ArrayBuffer|Uint8Array|Buffer} the data.
  109. */
  110. var getRawData = function (file) {
  111. if (file._data instanceof JSZip.CompressedObject) {
  112. file._data = file._data.getContent();
  113. file.options.binary = true;
  114. file.options.base64 = false;
  115. if (JSZip.utils.getTypeOf(file._data) === "uint8array") {
  116. var copy = file._data;
  117. // when reading an arraybuffer, the CompressedObject mechanism will keep it and subarray() a Uint8Array.
  118. // if we request a file in the same format, we might get the same Uint8Array or its ArrayBuffer (the original zip file).
  119. file._data = new Uint8Array(copy.length);
  120. // with an empty Uint8Array, Opera fails with a "Offset larger than array size"
  121. if (copy.length !== 0) {
  122. file._data.set(copy, 0);
  123. }
  124. }
  125. }
  126. return file._data;
  127. };
  128. /**
  129. * Returns the data of a ZipObject in a binary form. If the content is an unicode string, encode it.
  130. * @param {ZipObject} file the file to use.
  131. * @return {String|ArrayBuffer|Uint8Array|Buffer} the data.
  132. */
  133. var getBinaryData = function (file) {
  134. var result = getRawData(file), type = JSZip.utils.getTypeOf(result);
  135. if (type === "string") {
  136. if (!file.options.binary) {
  137. // unicode text !
  138. // unicode string => binary string is a painful process, check if we can avoid it.
  139. if (textEncoder) {
  140. return textEncoder.encode(result);
  141. }
  142. if (JSZip.support.nodebuffer) {
  143. return new Buffer(result, "utf-8");
  144. }
  145. }
  146. return file.asBinary();
  147. }
  148. return result;
  149. };
  150. /**
  151. * Transform this._data into a string.
  152. * @param {function} filter a function String -> String, applied if not null on the result.
  153. * @return {String} the string representing this._data.
  154. */
  155. var dataToString = function (asUTF8) {
  156. var result = getRawData(this);
  157. if (result === null || typeof result === "undefined") {
  158. return "";
  159. }
  160. // if the data is a base64 string, we decode it before checking the encoding !
  161. if (this.options.base64) {
  162. result = JSZip.base64.decode(result);
  163. }
  164. if (asUTF8 && this.options.binary) {
  165. // JSZip.prototype.utf8decode supports arrays as input
  166. // skip to array => string step, utf8decode will do it.
  167. result = JSZip.prototype.utf8decode(result);
  168. } else {
  169. // no utf8 transformation, do the array => string step.
  170. result = JSZip.utils.transformTo("string", result);
  171. }
  172. if (!asUTF8 && !this.options.binary) {
  173. result = JSZip.prototype.utf8encode(result);
  174. }
  175. return result;
  176. };
  177. /**
  178. * A simple object representing a file in the zip file.
  179. * @constructor
  180. * @param {string} name the name of the file
  181. * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data
  182. * @param {Object} options the options of the file
  183. */
  184. var ZipObject = function (name, data, options) {
  185. this.name = name;
  186. this._data = data;
  187. this.options = options;
  188. };
  189. ZipObject.prototype = {
  190. /**
  191. * Return the content as UTF8 string.
  192. * @return {string} the UTF8 string.
  193. */
  194. asText : function () {
  195. return dataToString.call(this, true);
  196. },
  197. /**
  198. * Returns the binary content.
  199. * @return {string} the content as binary.
  200. */
  201. asBinary : function () {
  202. return dataToString.call(this, false);
  203. },
  204. /**
  205. * Returns the content as a nodejs Buffer.
  206. * @return {Buffer} the content as a Buffer.
  207. */
  208. asNodeBuffer : function () {
  209. var result = getBinaryData(this);
  210. return JSZip.utils.transformTo("nodebuffer", result);
  211. },
  212. /**
  213. * Returns the content as an Uint8Array.
  214. * @return {Uint8Array} the content as an Uint8Array.
  215. */
  216. asUint8Array : function () {
  217. var result = getBinaryData(this);
  218. return JSZip.utils.transformTo("uint8array", result);
  219. },
  220. /**
  221. * Returns the content as an ArrayBuffer.
  222. * @return {ArrayBuffer} the content as an ArrayBufer.
  223. */
  224. asArrayBuffer : function () {
  225. return this.asUint8Array().buffer;
  226. }
  227. };
  228. /**
  229. * Transform an integer into a string in hexadecimal.
  230. * @private
  231. * @param {number} dec the number to convert.
  232. * @param {number} bytes the number of bytes to generate.
  233. * @returns {string} the result.
  234. */
  235. var decToHex = function(dec, bytes) {
  236. var hex = "", i;
  237. for(i = 0; i < bytes; i++) {
  238. hex += String.fromCharCode(dec&0xff);
  239. dec=dec>>>8;
  240. }
  241. return hex;
  242. };
  243. /**
  244. * Merge the objects passed as parameters into a new one.
  245. * @private
  246. * @param {...Object} var_args All objects to merge.
  247. * @return {Object} a new object with the data of the others.
  248. */
  249. var extend = function () {
  250. var result = {}, i, attr;
  251. for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
  252. for (attr in arguments[i]) {
  253. if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
  254. result[attr] = arguments[i][attr];
  255. }
  256. }
  257. }
  258. return result;
  259. };
  260. /**
  261. * Transforms the (incomplete) options from the user into the complete
  262. * set of options to create a file.
  263. * @private
  264. * @param {Object} o the options from the user.
  265. * @return {Object} the complete set of options.
  266. */
  267. var prepareFileAttrs = function (o) {
  268. o = o || {};
  269. /*jshint -W041 */
  270. if (o.base64 === true && o.binary == null) {
  271. o.binary = true;
  272. }
  273. /*jshint +W041 */
  274. o = extend(o, JSZip.defaults);
  275. o.date = o.date || new Date();
  276. if (o.compression !== null) o.compression = o.compression.toUpperCase();
  277. return o;
  278. };
  279. /**
  280. * Add a file in the current folder.
  281. * @private
  282. * @param {string} name the name of the file
  283. * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file
  284. * @param {Object} o the options of the file
  285. * @return {Object} the new file.
  286. */
  287. var fileAdd = function (name, data, o) {
  288. // be sure sub folders exist
  289. var parent = parentFolder(name), dataType = JSZip.utils.getTypeOf(data);
  290. if (parent) {
  291. folderAdd.call(this, parent);
  292. }
  293. o = prepareFileAttrs(o);
  294. if (o.dir || data === null || typeof data === "undefined") {
  295. o.base64 = false;
  296. o.binary = false;
  297. data = null;
  298. } else if (dataType === "string") {
  299. if (o.binary && !o.base64) {
  300. // optimizedBinaryString == true means that the file has already been filtered with a 0xFF mask
  301. if (o.optimizedBinaryString !== true) {
  302. // this is a string, not in a base64 format.
  303. // Be sure that this is a correct "binary string"
  304. data = JSZip.utils.string2binary(data);
  305. }
  306. }
  307. } else { // arraybuffer, uint8array, ...
  308. o.base64 = false;
  309. o.binary = true;
  310. if (!dataType && !(data instanceof JSZip.CompressedObject)) {
  311. throw new Error("The data of '" + name + "' is in an unsupported format !");
  312. }
  313. // special case : it's way easier to work with Uint8Array than with ArrayBuffer
  314. if (dataType === "arraybuffer") {
  315. data = JSZip.utils.transformTo("uint8array", data);
  316. }
  317. }
  318. var object = new ZipObject(name, data, o);
  319. this.files[name] = object;
  320. return object;
  321. };
  322. /**
  323. * Find the parent folder of the path.
  324. * @private
  325. * @param {string} path the path to use
  326. * @return {string} the parent folder, or ""
  327. */
  328. var parentFolder = function (path) {
  329. if (path.slice(-1) == '/') {
  330. path = path.substring(0, path.length - 1);
  331. }
  332. var lastSlash = path.lastIndexOf('/');
  333. return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
  334. };
  335. /**
  336. * Add a (sub) folder in the current folder.
  337. * @private
  338. * @param {string} name the folder's name
  339. * @return {Object} the new folder.
  340. */
  341. var folderAdd = function (name) {
  342. // Check the name ends with a /
  343. if (name.slice(-1) != "/") {
  344. name += "/"; // IE doesn't like substr(-1)
  345. }
  346. // Does this folder already exist?
  347. if (!this.files[name]) {
  348. fileAdd.call(this, name, null, {dir:true});
  349. }
  350. return this.files[name];
  351. };
  352. /**
  353. * Generate a JSZip.CompressedObject for a given zipOject.
  354. * @param {ZipObject} file the object to read.
  355. * @param {JSZip.compression} compression the compression to use.
  356. * @return {JSZip.CompressedObject} the compressed result.
  357. */
  358. var generateCompressedObjectFrom = function (file, compression) {
  359. var result = new JSZip.CompressedObject(), content;
  360. // the data has not been decompressed, we might reuse things !
  361. if (file._data instanceof JSZip.CompressedObject) {
  362. result.uncompressedSize = file._data.uncompressedSize;
  363. result.crc32 = file._data.crc32;
  364. if (result.uncompressedSize === 0 || file.options.dir) {
  365. compression = JSZip.compressions['STORE'];
  366. result.compressedContent = "";
  367. result.crc32 = 0;
  368. } else if (file._data.compressionMethod === compression.magic) {
  369. result.compressedContent = file._data.getCompressedContent();
  370. } else {
  371. content = file._data.getContent();
  372. // need to decompress / recompress
  373. result.compressedContent = compression.compress(JSZip.utils.transformTo(compression.compressInputType, content));
  374. }
  375. } else {
  376. // have uncompressed data
  377. content = getBinaryData(file);
  378. if (!content || content.length === 0 || file.options.dir) {
  379. compression = JSZip.compressions['STORE'];
  380. content = "";
  381. }
  382. result.uncompressedSize = content.length;
  383. result.crc32 = this.crc32(content);
  384. result.compressedContent = compression.compress(JSZip.utils.transformTo(compression.compressInputType, content));
  385. }
  386. result.compressedSize = result.compressedContent.length;
  387. result.compressionMethod = compression.magic;
  388. return result;
  389. };
  390. /**
  391. * Generate the various parts used in the construction of the final zip file.
  392. * @param {string} name the file name.
  393. * @param {ZipObject} file the file content.
  394. * @param {JSZip.CompressedObject} compressedObject the compressed object.
  395. * @param {number} offset the current offset from the start of the zip file.
  396. * @return {object} the zip parts.
  397. */
  398. var generateZipParts = function(name, file, compressedObject, offset) {
  399. var data = compressedObject.compressedContent,
  400. utfEncodedFileName = this.utf8encode(file.name),
  401. useUTF8 = utfEncodedFileName !== file.name,
  402. o = file.options,
  403. dosTime,
  404. dosDate;
  405. // date
  406. // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
  407. // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
  408. // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
  409. dosTime = o.date.getHours();
  410. dosTime = dosTime << 6;
  411. dosTime = dosTime | o.date.getMinutes();
  412. dosTime = dosTime << 5;
  413. dosTime = dosTime | o.date.getSeconds() / 2;
  414. dosDate = o.date.getFullYear() - 1980;
  415. dosDate = dosDate << 4;
  416. dosDate = dosDate | (o.date.getMonth() + 1);
  417. dosDate = dosDate << 5;
  418. dosDate = dosDate | o.date.getDate();
  419. var header = "";
  420. // version needed to extract
  421. header += "\x0A\x00";
  422. // general purpose bit flag
  423. // set bit 11 if utf8
  424. header += useUTF8 ? "\x00\x08" : "\x00\x00";
  425. // compression method
  426. header += compressedObject.compressionMethod;
  427. // last mod file time
  428. header += decToHex(dosTime, 2);
  429. // last mod file date
  430. header += decToHex(dosDate, 2);
  431. // crc-32
  432. header += decToHex(compressedObject.crc32, 4);
  433. // compressed size
  434. header += decToHex(compressedObject.compressedSize, 4);
  435. // uncompressed size
  436. header += decToHex(compressedObject.uncompressedSize, 4);
  437. // file name length
  438. header += decToHex(utfEncodedFileName.length, 2);
  439. // extra field length
  440. header += "\x00\x00";
  441. var fileRecord = JSZip.signature.LOCAL_FILE_HEADER + header + utfEncodedFileName;
  442. var dirRecord = JSZip.signature.CENTRAL_FILE_HEADER +
  443. // version made by (00: DOS)
  444. "\x14\x00" +
  445. // file header (common to file and central directory)
  446. header +
  447. // file comment length
  448. "\x00\x00" +
  449. // disk number start
  450. "\x00\x00" +
  451. // internal file attributes TODO
  452. "\x00\x00" +
  453. // external file attributes
  454. (file.options.dir===true?"\x10\x00\x00\x00":"\x00\x00\x00\x00")+
  455. // relative offset of local header
  456. decToHex(offset, 4) +
  457. // file name
  458. utfEncodedFileName;
  459. return {
  460. fileRecord : fileRecord,
  461. dirRecord : dirRecord,
  462. compressedObject : compressedObject
  463. };
  464. };
  465. /**
  466. * An object to write any content to a string.
  467. * @constructor
  468. */
  469. var StringWriter = function () {
  470. this.data = [];
  471. };
  472. StringWriter.prototype = {
  473. /**
  474. * Append any content to the current string.
  475. * @param {Object} input the content to add.
  476. */
  477. append : function (input) {
  478. input = JSZip.utils.transformTo("string", input);
  479. this.data.push(input);
  480. },
  481. /**
  482. * Finalize the construction an return the result.
  483. * @return {string} the generated string.
  484. */
  485. finalize : function () {
  486. return this.data.join("");
  487. }
  488. };
  489. /**
  490. * An object to write any content to an Uint8Array.
  491. * @constructor
  492. * @param {number} length The length of the array.
  493. */
  494. var Uint8ArrayWriter = function (length) {
  495. this.data = new Uint8Array(length);
  496. this.index = 0;
  497. };
  498. Uint8ArrayWriter.prototype = {
  499. /**
  500. * Append any content to the current array.
  501. * @param {Object} input the content to add.
  502. */
  503. append : function (input) {
  504. if (input.length !== 0) {
  505. // with an empty Uint8Array, Opera fails with a "Offset larger than array size"
  506. input = JSZip.utils.transformTo("uint8array", input);
  507. this.data.set(input, this.index);
  508. this.index += input.length;
  509. }
  510. },
  511. /**
  512. * Finalize the construction an return the result.
  513. * @return {Uint8Array} the generated array.
  514. */
  515. finalize : function () {
  516. return this.data;
  517. }
  518. };
  519. // return the actual prototype of JSZip
  520. return {
  521. /**
  522. * Read an existing zip and merge the data in the current JSZip object.
  523. * The implementation is in jszip-load.js, don't forget to include it.
  524. * @param {String|ArrayBuffer|Uint8Array|Buffer} stream The stream to load
  525. * @param {Object} options Options for loading the stream.
  526. * options.base64 : is the stream in base64 ? default : false
  527. * @return {JSZip} the current JSZip object
  528. */
  529. load : function (stream, options) {
  530. throw new Error("Load method is not defined. Is the file jszip-load.js included ?");
  531. },
  532. /**
  533. * Filter nested files/folders with the specified function.
  534. * @param {Function} search the predicate to use :
  535. * function (relativePath, file) {...}
  536. * It takes 2 arguments : the relative path and the file.
  537. * @return {Array} An array of matching elements.
  538. */
  539. filter : function (search) {
  540. var result = [], filename, relativePath, file, fileClone;
  541. for (filename in this.files) {
  542. if ( !this.files.hasOwnProperty(filename) ) { continue; }
  543. file = this.files[filename];
  544. // return a new object, don't let the user mess with our internal objects :)
  545. fileClone = new ZipObject(file.name, file._data, extend(file.options));
  546. relativePath = filename.slice(this.root.length, filename.length);
  547. if (filename.slice(0, this.root.length) === this.root && // the file is in the current root
  548. search(relativePath, fileClone)) { // and the file matches the function
  549. result.push(fileClone);
  550. }
  551. }
  552. return result;
  553. },
  554. /**
  555. * Add a file to the zip file, or search a file.
  556. * @param {string|RegExp} name The name of the file to add (if data is defined),
  557. * the name of the file to find (if no data) or a regex to match files.
  558. * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded
  559. * @param {Object} o File options
  560. * @return {JSZip|Object|Array} this JSZip object (when adding a file),
  561. * a file (when searching by string) or an array of files (when searching by regex).
  562. */
  563. file : function(name, data, o) {
  564. if (arguments.length === 1) {
  565. if (JSZip.utils.isRegExp(name)) {
  566. var regexp = name;
  567. return this.filter(function(relativePath, file) {
  568. return !file.options.dir && regexp.test(relativePath);
  569. });
  570. } else { // text
  571. return this.filter(function (relativePath, file) {
  572. return !file.options.dir && relativePath === name;
  573. })[0]||null;
  574. }
  575. } else { // more than one argument : we have data !
  576. name = this.root+name;
  577. fileAdd.call(this, name, data, o);
  578. }
  579. return this;
  580. },
  581. /**
  582. * Add a directory to the zip file, or search.
  583. * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders.
  584. * @return {JSZip} an object with the new directory as the root, or an array containing matching folders.
  585. */
  586. folder : function(arg) {
  587. if (!arg) {
  588. return this;
  589. }
  590. if (JSZip.utils.isRegExp(arg)) {
  591. return this.filter(function(relativePath, file) {
  592. return file.options.dir && arg.test(relativePath);
  593. });
  594. }
  595. // else, name is a new folder
  596. var name = this.root + arg;
  597. var newFolder = folderAdd.call(this, name);
  598. // Allow chaining by returning a new object with this folder as the root
  599. var ret = this.clone();
  600. ret.root = newFolder.name;
  601. return ret;
  602. },
  603. /**
  604. * Delete a file, or a directory and all sub-files, from the zip
  605. * @param {string} name the name of the file to delete
  606. * @return {JSZip} this JSZip object
  607. */
  608. remove : function(name) {
  609. name = this.root + name;
  610. var file = this.files[name];
  611. if (!file) {
  612. // Look for any folders
  613. if (name.slice(-1) != "/") {
  614. name += "/";
  615. }
  616. file = this.files[name];
  617. }
  618. if (file) {
  619. if (!file.options.dir) {
  620. // file
  621. delete this.files[name];
  622. } else {
  623. // folder
  624. var kids = this.filter(function (relativePath, file) {
  625. return file.name.slice(0, name.length) === name;
  626. });
  627. for (var i = 0; i < kids.length; i++) {
  628. delete this.files[kids[i].name];
  629. }
  630. }
  631. }
  632. return this;
  633. },
  634. /**
  635. * Generate the complete zip file
  636. * @param {Object} options the options to generate the zip file :
  637. * - base64, (deprecated, use type instead) true to generate base64.
  638. * - compression, "STORE" by default.
  639. * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
  640. * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file
  641. */
  642. generate : function(options) {
  643. options = extend(options || {}, {
  644. base64 : true,
  645. compression : "STORE",
  646. type : "base64"
  647. });
  648. JSZip.utils.checkSupport(options.type);
  649. var zipData = [], localDirLength = 0, centralDirLength = 0, writer, i;
  650. // first, generate all the zip parts.
  651. for (var name in this.files) {
  652. if ( !this.files.hasOwnProperty(name) ) { continue; }
  653. var file = this.files[name];
  654. var compressionName = file.options.compression || options.compression.toUpperCase();
  655. var compression = JSZip.compressions[compressionName];
  656. if (!compression) {
  657. throw new Error(compressionName + " is not a valid compression method !");
  658. }
  659. var compressedObject = generateCompressedObjectFrom.call(this, file, compression);
  660. var zipPart = generateZipParts.call(this, name, file, compressedObject, localDirLength);
  661. localDirLength += zipPart.fileRecord.length + compressedObject.compressedSize;
  662. centralDirLength += zipPart.dirRecord.length;
  663. zipData.push(zipPart);
  664. }
  665. var dirEnd = "";
  666. // end of central dir signature
  667. dirEnd = JSZip.signature.CENTRAL_DIRECTORY_END +
  668. // number of this disk
  669. "\x00\x00" +
  670. // number of the disk with the start of the central directory
  671. "\x00\x00" +
  672. // total number of entries in the central directory on this disk
  673. decToHex(zipData.length, 2) +
  674. // total number of entries in the central directory
  675. decToHex(zipData.length, 2) +
  676. // size of the central directory 4 bytes
  677. decToHex(centralDirLength, 4) +
  678. // offset of start of central directory with respect to the starting disk number
  679. decToHex(localDirLength, 4) +
  680. // .ZIP file comment length
  681. "\x00\x00";
  682. // we have all the parts (and the total length)
  683. // time to create a writer !
  684. switch(options.type.toLowerCase()) {
  685. case "uint8array" :
  686. case "arraybuffer" :
  687. case "blob" :
  688. case "nodebuffer" :
  689. writer = new Uint8ArrayWriter(localDirLength + centralDirLength + dirEnd.length);
  690. break;
  691. // case "base64" :
  692. // case "string" :
  693. default :
  694. writer = new StringWriter(localDirLength + centralDirLength + dirEnd.length);
  695. break;
  696. }
  697. for (i = 0; i < zipData.length; i++) {
  698. writer.append(zipData[i].fileRecord);
  699. writer.append(zipData[i].compressedObject.compressedContent);
  700. }
  701. for (i = 0; i < zipData.length; i++) {
  702. writer.append(zipData[i].dirRecord);
  703. }
  704. writer.append(dirEnd);
  705. var zip = writer.finalize();
  706. switch(options.type.toLowerCase()) {
  707. // case "zip is an Uint8Array"
  708. case "uint8array" :
  709. case "arraybuffer" :
  710. case "nodebuffer" :
  711. return JSZip.utils.transformTo(options.type.toLowerCase(), zip);
  712. case "blob" :
  713. return JSZip.utils.arrayBuffer2Blob(JSZip.utils.transformTo("arraybuffer", zip));
  714. // case "zip is a string"
  715. case "base64" :
  716. return (options.base64) ? JSZip.base64.encode(zip) : zip;
  717. default : // case "string" :
  718. return zip;
  719. }
  720. },
  721. /**
  722. *
  723. * Javascript crc32
  724. * http://www.webtoolkit.info/
  725. *
  726. */
  727. crc32 : function crc32(input, crc) {
  728. if (typeof input === "undefined" || !input.length) {
  729. return 0;
  730. }
  731. var isArray = JSZip.utils.getTypeOf(input) !== "string";
  732. var table = [
  733. 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
  734. 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
  735. 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
  736. 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
  737. 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
  738. 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
  739. 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
  740. 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
  741. 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
  742. 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
  743. 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
  744. 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
  745. 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
  746. 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
  747. 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
  748. 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
  749. 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
  750. 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
  751. 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
  752. 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
  753. 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
  754. 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
  755. 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
  756. 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
  757. 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
  758. 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
  759. 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
  760. 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
  761. 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
  762. 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
  763. 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
  764. 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
  765. 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
  766. 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
  767. 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
  768. 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
  769. 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
  770. 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
  771. 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
  772. 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
  773. 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
  774. 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
  775. 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
  776. 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
  777. 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
  778. 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
  779. 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
  780. 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
  781. 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
  782. 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
  783. 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
  784. 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
  785. 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
  786. 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
  787. 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
  788. 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
  789. 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
  790. 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
  791. 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
  792. 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
  793. 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
  794. 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
  795. 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
  796. 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
  797. ];
  798. if (typeof(crc) == "undefined") { crc = 0; }
  799. var x = 0;
  800. var y = 0;
  801. var byte = 0;
  802. crc = crc ^ (-1);
  803. for( var i = 0, iTop = input.length; i < iTop; i++ ) {
  804. byte = isArray ? input[i] : input.charCodeAt(i);
  805. y = ( crc ^ byte ) & 0xFF;
  806. x = table[y];
  807. crc = ( crc >>> 8 ) ^ x;
  808. }
  809. return crc ^ (-1);
  810. },
  811. // Inspired by http://my.opera.com/GreyWyvern/blog/show.dml/1725165
  812. clone : function() {
  813. var newObj = new JSZip();
  814. for (var i in this) {
  815. if (typeof this[i] !== "function") {
  816. newObj[i] = this[i];
  817. }
  818. }
  819. return newObj;
  820. },
  821. /**
  822. * http://www.webtoolkit.info/javascript-utf8.html
  823. */
  824. utf8encode : function (string) {
  825. // TextEncoder + Uint8Array to binary string is faster than checking every bytes on long strings.
  826. // http://jsperf.com/utf8encode-vs-textencoder
  827. // On short strings (file names for example), the TextEncoder API is (currently) slower.
  828. if (textEncoder) {
  829. var u8 = textEncoder.encode(string);
  830. return JSZip.utils.transformTo("string", u8);
  831. }
  832. if (JSZip.support.nodebuffer) {
  833. return JSZip.utils.transformTo("string", new Buffer(string, "utf-8"));
  834. }
  835. // array.join may be slower than string concatenation but generates less objects (less time spent garbage collecting).
  836. // See also http://jsperf.com/array-direct-assignment-vs-push/31
  837. var result = [], resIndex = 0;
  838. for (var n = 0; n < string.length; n++) {
  839. var c = string.charCodeAt(n);
  840. if (c < 128) {
  841. result[resIndex++] = String.fromCharCode(c);
  842. } else if ((c > 127) && (c < 2048)) {
  843. result[resIndex++] = String.fromCharCode((c >> 6) | 192);
  844. result[resIndex++] = String.fromCharCode((c & 63) | 128);
  845. } else {
  846. result[resIndex++] = String.fromCharCode((c >> 12) | 224);
  847. result[resIndex++] = String.fromCharCode(((c >> 6) & 63) | 128);
  848. result[resIndex++] = String.fromCharCode((c & 63) | 128);
  849. }
  850. }
  851. return result.join("");
  852. },
  853. /**
  854. * http://www.webtoolkit.info/javascript-utf8.html
  855. */
  856. utf8decode : function (input) {
  857. var result = [], resIndex = 0;
  858. var type = JSZip.utils.getTypeOf(input);
  859. var isArray = type !== "string";
  860. var i = 0;
  861. var c = 0, c1 = 0, c2 = 0, c3 = 0;
  862. // check if we can use the TextDecoder API
  863. // see http://encoding.spec.whatwg.org/#api
  864. if (textDecoder) {
  865. return textDecoder.decode(
  866. JSZip.utils.transformTo("uint8array", input)
  867. );
  868. }
  869. if (JSZip.support.nodebuffer) {
  870. return JSZip.utils.transformTo("nodebuffer", input).toString("utf-8");
  871. }
  872. while ( i < input.length ) {
  873. c = isArray ? input[i] : input.charCodeAt(i);
  874. if (c < 128) {
  875. result[resIndex++] = String.fromCharCode(c);
  876. i++;
  877. } else if ((c > 191) && (c < 224)) {
  878. c2 = isArray ? input[i+1] : input.charCodeAt(i+1);
  879. result[resIndex++] = String.fromCharCode(((c & 31) << 6) | (c2 & 63));
  880. i += 2;
  881. } else {
  882. c2 = isArray ? input[i+1] : input.charCodeAt(i+1);
  883. c3 = isArray ? input[i+2] : input.charCodeAt(i+2);
  884. result[resIndex++] = String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
  885. i += 3;
  886. }
  887. }
  888. return result.join("");
  889. }
  890. };
  891. }());
  892. /*
  893. * Compression methods
  894. * This object is filled in as follow :
  895. * name : {
  896. * magic // the 2 bytes indentifying the compression method
  897. * compress // function, take the uncompressed content and return it compressed.
  898. * uncompress // function, take the compressed content and return it uncompressed.
  899. * compressInputType // string, the type accepted by the compress method. null to accept everything.
  900. * uncompressInputType // string, the type accepted by the uncompress method. null to accept everything.
  901. * }
  902. *
  903. * STORE is the default compression method, so it's included in this file.
  904. * Other methods should go to separated files : the user wants modularity.
  905. */
  906. JSZip.compressions = {
  907. "STORE" : {
  908. magic : "\x00\x00",
  909. compress : function (content) {
  910. return content; // no compression
  911. },
  912. uncompress : function (content) {
  913. return content; // no compression
  914. },
  915. compressInputType : null,
  916. uncompressInputType : null
  917. }
  918. };
  919. (function () {
  920. JSZip.utils = {
  921. /**
  922. * Convert a string to a "binary string" : a string containing only char codes between 0 and 255.
  923. * @param {string} str the string to transform.
  924. * @return {String} the binary string.
  925. */
  926. string2binary : function (str) {
  927. var result = "";
  928. for (var i = 0; i < str.length; i++) {
  929. result += String.fromCharCode(str.charCodeAt(i) & 0xff);
  930. }
  931. return result;
  932. },
  933. /**
  934. * Create a Uint8Array from the string.
  935. * @param {string} str the string to transform.
  936. * @return {Uint8Array} the typed array.
  937. * @throws {Error} an Error if the browser doesn't support the requested feature.
  938. * @deprecated : use JSZip.utils.transformTo instead.
  939. */
  940. string2Uint8Array : function (str) {
  941. return JSZip.utils.transformTo("uint8array", str);
  942. },
  943. /**
  944. * Create a string from the Uint8Array.
  945. * @param {Uint8Array} array the array to transform.
  946. * @return {string} the string.
  947. * @throws {Error} an Error if the browser doesn't support the requested feature.
  948. * @deprecated : use JSZip.utils.transformTo instead.
  949. */
  950. uint8Array2String : function (array) {
  951. return JSZip.utils.transformTo("string", array);
  952. },
  953. /**
  954. * Create a blob from the given ArrayBuffer.
  955. * @param {ArrayBuffer} buffer the buffer to transform.
  956. * @return {Blob} the result.
  957. * @throws {Error} an Error if the browser doesn't support the requested feature.
  958. */
  959. arrayBuffer2Blob : function (buffer) {
  960. JSZip.utils.checkSupport("blob");
  961. try {
  962. // Blob constructor
  963. return new Blob([buffer], { type: "application/zip" });
  964. }
  965. catch(e) {}
  966. try {
  967. // deprecated, browser only, old way
  968. var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
  969. var builder = new BlobBuilder();
  970. builder.append(buffer);
  971. return builder.getBlob('application/zip');
  972. }
  973. catch(e) {}
  974. // well, fuck ?!
  975. throw new Error("Bug : can't construct the Blob.");
  976. },
  977. /**
  978. * Create a blob from the given string.
  979. * @param {string} str the string to transform.
  980. * @return {Blob} the result.
  981. * @throws {Error} an Error if the browser doesn't support the requested feature.
  982. */
  983. string2Blob : function (str) {
  984. var buffer = JSZip.utils.transformTo("arraybuffer", str);
  985. return JSZip.utils.arrayBuffer2Blob(buffer);
  986. }
  987. };
  988. /**
  989. * The identity function.
  990. * @param {Object} input the input.
  991. * @return {Object} the same input.
  992. */
  993. function identity(input) {
  994. return input;
  995. }
  996. /**
  997. * Fill in an array with a string.
  998. * @param {String} str the string to use.
  999. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated).
  1000. * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array.
  1001. */
  1002. function stringToArrayLike(str, array) {
  1003. for (var i = 0; i < str.length; ++i) {
  1004. array[i] = str.charCodeAt(i) & 0xFF;
  1005. }
  1006. return array;
  1007. }
  1008. /**
  1009. * Transform an array-like object to a string.
  1010. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
  1011. * @return {String} the result.
  1012. */
  1013. function arrayLikeToString(array) {
  1014. // Performances notes :
  1015. // --------------------
  1016. // String.fromCharCode.apply(null, array) is the fastest, see
  1017. // see http://jsperf.com/converting-a-uint8array-to-a-string/2
  1018. // but the stack is limited (and we can get huge arrays !).
  1019. //
  1020. // result += String.fromCharCode(array[i]); generate too many strings !
  1021. //
  1022. // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2
  1023. var chunk = 65536;
  1024. var result = [], len = array.length, type = JSZip.utils.getTypeOf(array), k = 0;
  1025. var canUseApply = true;
  1026. try {
  1027. switch(type) {
  1028. case "uint8array":
  1029. String.fromCharCode.apply(null, new Uint8Array(0));
  1030. break;
  1031. case "nodebuffer":
  1032. String.fromCharCode.apply(null, new Buffer(0));
  1033. break;
  1034. }
  1035. } catch(e) {
  1036. canUseApply = false;
  1037. }
  1038. // no apply : slow and painful algorithm
  1039. // default browser on android 4.*
  1040. if (!canUseApply) {
  1041. var resultStr = "";
  1042. for(var i = 0; i < array.length;i++) {
  1043. resultStr += String.fromCharCode(array[i]);
  1044. }
  1045. return resultStr;
  1046. }
  1047. while (k < len && chunk > 1) {
  1048. try {
  1049. if (type === "array" || type === "nodebuffer") {
  1050. result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len))));
  1051. } else {
  1052. result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len))));
  1053. }
  1054. k += chunk;
  1055. } catch (e) {
  1056. chunk = Math.floor(chunk / 2);
  1057. }
  1058. }
  1059. return result.join("");
  1060. }
  1061. /**
  1062. * Copy the data from an array-like to an other array-like.
  1063. * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array.
  1064. * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated.
  1065. * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array.
  1066. */
  1067. function arrayLikeToArrayLike(arrayFrom, arrayTo) {
  1068. for(var i = 0; i < arrayFrom.length; i++) {
  1069. arrayTo[i] = arrayFrom[i];
  1070. }
  1071. return arrayTo;
  1072. }
  1073. // a matrix containing functions to transform everything into everything.
  1074. var transform = {};
  1075. // string to ?
  1076. transform["string"] = {
  1077. "string" : identity,
  1078. "array" : function (input) {
  1079. return stringToArrayLike(input, new Array(input.length));
  1080. },
  1081. "arraybuffer" : function (input) {
  1082. return transform["string"]["uint8array"](input).buffer;
  1083. },
  1084. "uint8array" : function (input) {
  1085. return stringToArrayLike(input, new Uint8Array(input.length));
  1086. },
  1087. "nodebuffer" : function (input) {
  1088. return stringToArrayLike(input, new Buffer(input.length));
  1089. }
  1090. };
  1091. // array to ?
  1092. transform["array"] = {
  1093. "string" : arrayLikeToString,
  1094. "array" : identity,
  1095. "arraybuffer" : function (input) {
  1096. return (new Uint8Array(input)).buffer;
  1097. },
  1098. "uint8array" : function (input) {
  1099. return new Uint8Array(input);
  1100. },
  1101. "nodebuffer" : function (input) {
  1102. return new Buffer(input);
  1103. }
  1104. };
  1105. // arraybuffer to ?
  1106. transform["arraybuffer"] = {
  1107. "string" : function (input) {
  1108. return arrayLikeToString(new Uint8Array(input));
  1109. },
  1110. "array" : function (input) {
  1111. return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength));
  1112. },
  1113. "arraybuffer" : identity,
  1114. "uint8array" : function (input) {
  1115. return new Uint8Array(input);
  1116. },
  1117. "nodebuffer" : function (input) {
  1118. return new Buffer(new Uint8Array(input));
  1119. }
  1120. };
  1121. // uint8array to ?
  1122. transform["uint8array"] = {
  1123. "string" : arrayLikeToString,
  1124. "array" : function (input) {
  1125. return arrayLikeToArrayLike(input, new Array(input.length));
  1126. },
  1127. "arraybuffer" : function (input) {
  1128. return input.buffer;
  1129. },
  1130. "uint8array" : identity,
  1131. "nodebuffer" : function(input) {
  1132. return new Buffer(input);
  1133. }
  1134. };
  1135. // nodebuffer to ?
  1136. transform["nodebuffer"] = {
  1137. "string" : arrayLikeToString,
  1138. "array" : function (input) {
  1139. return arrayLikeToArrayLike(input, new Array(input.length));
  1140. },
  1141. "arraybuffer" : function (input) {
  1142. return transform["nodebuffer"]["uint8array"](input).buffer;
  1143. },
  1144. "uint8array" : function (input) {
  1145. return arrayLikeToArrayLike(input, new Uint8Array(input.length));
  1146. },
  1147. "nodebuffer" : identity
  1148. };
  1149. /**
  1150. * Transform an input into any type.
  1151. * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer.
  1152. * If no output type is specified, the unmodified input will be returned.
  1153. * @param {String} outputType the output type.
  1154. * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert.
  1155. * @throws {Error} an Error if the browser doesn't support the requested output type.
  1156. */
  1157. JSZip.utils.transformTo = function (outputType, input) {
  1158. if (!input) {
  1159. // undefined, null, etc
  1160. // an empty string won't harm.
  1161. input = "";
  1162. }
  1163. if (!outputType) {
  1164. return input;
  1165. }
  1166. JSZip.utils.checkSupport(outputType);
  1167. var inputType = JSZip.utils.getTypeOf(input);
  1168. var result = transform[inputType][outputType](input);
  1169. return result;
  1170. };
  1171. /**
  1172. * Return the type of the input.
  1173. * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer.
  1174. * @param {Object} input the input to identify.
  1175. * @return {String} the (lowercase) type of the input.
  1176. */
  1177. JSZip.utils.getTypeOf = function (input) {
  1178. if (typeof input === "string") {
  1179. return "string";
  1180. }
  1181. if (Object.prototype.toString.call(input) === "[object Array]") {
  1182. return "array";
  1183. }
  1184. if (JSZip.support.nodebuffer && Buffer.isBuffer(input)) {
  1185. return "nodebuffer";
  1186. }
  1187. if (JSZip.support.uint8array && input instanceof Uint8Array) {
  1188. return "uint8array";
  1189. }
  1190. if (JSZip.support.arraybuffer && input instanceof ArrayBuffer) {
  1191. return "arraybuffer";
  1192. }
  1193. };
  1194. /**
  1195. * Cross-window, cross-Node-context regular expression detection
  1196. * @param {Object} object Anything
  1197. * @return {Boolean} true if the object is a regular expression,
  1198. * false otherwise
  1199. */
  1200. JSZip.utils.isRegExp = function (object) {
  1201. return Object.prototype.toString.call(object) === "[object RegExp]";
  1202. };
  1203. /**
  1204. * Throw an exception if the type is not supported.
  1205. * @param {String} type the type to check.
  1206. * @throws {Error} an Error if the browser doesn't support the requested type.
  1207. */
  1208. JSZip.utils.checkSupport = function (type) {
  1209. var supported = true;
  1210. switch (type.toLowerCase()) {
  1211. case "uint8array":
  1212. supported = JSZip.support.uint8array;
  1213. break;
  1214. case "arraybuffer":
  1215. supported = JSZip.support.arraybuffer;
  1216. break;
  1217. case "nodebuffer":
  1218. supported = JSZip.support.nodebuffer;
  1219. break;
  1220. case "blob":
  1221. supported = JSZip.support.blob;
  1222. break;
  1223. }
  1224. if (!supported) {
  1225. throw new Error(type + " is not supported by this browser");
  1226. }
  1227. };
  1228. })();
  1229. (function (){
  1230. /**
  1231. * Represents an entry in the zip.
  1232. * The content may or may not be compressed.
  1233. * @constructor
  1234. */
  1235. JSZip.CompressedObject = function () {
  1236. this.compressedSize = 0;
  1237. this.uncompressedSize = 0;
  1238. this.crc32 = 0;
  1239. this.compressionMethod = null;
  1240. this.compressedContent = null;
  1241. };
  1242. JSZip.CompressedObject.prototype = {
  1243. /**
  1244. * Return the decompressed content in an unspecified format.
  1245. * The format will depend on the decompressor.
  1246. * @return {Object} the decompressed content.
  1247. */
  1248. getContent : function () {
  1249. return null; // see implementation
  1250. },
  1251. /**
  1252. * Return the compressed content in an unspecified format.
  1253. * The format will depend on the compressed conten source.
  1254. * @return {Object} the compressed content.
  1255. */
  1256. getCompressedContent : function () {
  1257. return null; // see implementation
  1258. }
  1259. };
  1260. })();
  1261. /**
  1262. *
  1263. * Base64 encode / decode
  1264. * http://www.webtoolkit.info/
  1265. *
  1266. * Hacked so that it doesn't utf8 en/decode everything
  1267. **/
  1268. JSZip.base64 = (function() {
  1269. // private property
  1270. var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  1271. return {
  1272. // public method for encoding
  1273. encode : function(input, utf8) {
  1274. var output = "";
  1275. var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
  1276. var i = 0;
  1277. while (i < input.length) {
  1278. chr1 = input.charCodeAt(i++);
  1279. chr2 = input.charCodeAt(i++);
  1280. chr3 = input.charCodeAt(i++);
  1281. enc1 = chr1 >> 2;
  1282. enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
  1283. enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
  1284. enc4 = chr3 & 63;
  1285. if (isNaN(chr2)) {
  1286. enc3 = enc4 = 64;
  1287. } else if (isNaN(chr3)) {
  1288. enc4 = 64;
  1289. }
  1290. output = output +
  1291. _keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
  1292. _keyStr.charAt(enc3) + _keyStr.charAt(enc4);
  1293. }
  1294. return output;
  1295. },
  1296. // public method for decoding
  1297. decode : function(input, utf8) {
  1298. var output = "";
  1299. var chr1, chr2, chr3;
  1300. var enc1, enc2, enc3, enc4;
  1301. var i = 0;
  1302. input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  1303. while (i < input.length) {
  1304. enc1 = _keyStr.indexOf(input.charAt(i++));
  1305. enc2 = _keyStr.indexOf(input.charAt(i++));
  1306. enc3 = _keyStr.indexOf(input.charAt(i++));
  1307. enc4 = _keyStr.indexOf(input.charAt(i++));
  1308. chr1 = (enc1 << 2) | (enc2 >> 4);
  1309. chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
  1310. chr3 = ((enc3 & 3) << 6) | enc4;
  1311. output = output + String.fromCharCode(chr1);
  1312. if (enc3 != 64) {
  1313. output = output + String.fromCharCode(chr2);
  1314. }
  1315. if (enc4 != 64) {
  1316. output = output + String.fromCharCode(chr3);
  1317. }
  1318. }
  1319. return output;
  1320. }
  1321. };
  1322. }());
  1323. // enforcing Stuk's coding style
  1324. // vim: set shiftwidth=3 softtabstop=3: