utils.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. 'use strict';
  2. var support = require('./support');
  3. var compressions = require('./compressions');
  4. var nodeBuffer = require('./nodeBuffer');
  5. /**
  6. * Convert a string to a "binary string" : a string containing only char codes between 0 and 255.
  7. * @param {string} str the string to transform.
  8. * @return {String} the binary string.
  9. */
  10. exports.string2binary = function(str) {
  11. var result = "";
  12. for (var i = 0; i < str.length; i++) {
  13. result += String.fromCharCode(str.charCodeAt(i) & 0xff);
  14. }
  15. return result;
  16. };
  17. exports.arrayBuffer2Blob = function(buffer, mimeType) {
  18. exports.checkSupport("blob");
  19. mimeType = mimeType || 'application/zip';
  20. try {
  21. // Blob constructor
  22. return new Blob([buffer], {
  23. type: mimeType
  24. });
  25. }
  26. catch (e) {
  27. try {
  28. // deprecated, browser only, old way
  29. var Builder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;
  30. var builder = new Builder();
  31. builder.append(buffer);
  32. return builder.getBlob(mimeType);
  33. }
  34. catch (e) {
  35. // well, fuck ?!
  36. throw new Error("Bug : can't construct the Blob.");
  37. }
  38. }
  39. };
  40. /**
  41. * The identity function.
  42. * @param {Object} input the input.
  43. * @return {Object} the same input.
  44. */
  45. function identity(input) {
  46. return input;
  47. }
  48. /**
  49. * Fill in an array with a string.
  50. * @param {String} str the string to use.
  51. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated).
  52. * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array.
  53. */
  54. function stringToArrayLike(str, array) {
  55. for (var i = 0; i < str.length; ++i) {
  56. array[i] = str.charCodeAt(i) & 0xFF;
  57. }
  58. return array;
  59. }
  60. /**
  61. * Transform an array-like object to a string.
  62. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
  63. * @return {String} the result.
  64. */
  65. function arrayLikeToString(array) {
  66. // Performances notes :
  67. // --------------------
  68. // String.fromCharCode.apply(null, array) is the fastest, see
  69. // see http://jsperf.com/converting-a-uint8array-to-a-string/2
  70. // but the stack is limited (and we can get huge arrays !).
  71. //
  72. // result += String.fromCharCode(array[i]); generate too many strings !
  73. //
  74. // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2
  75. var chunk = 65536;
  76. var result = [],
  77. len = array.length,
  78. type = exports.getTypeOf(array),
  79. k = 0,
  80. canUseApply = true;
  81. try {
  82. switch(type) {
  83. case "uint8array":
  84. String.fromCharCode.apply(null, new Uint8Array(0));
  85. break;
  86. case "nodebuffer":
  87. String.fromCharCode.apply(null, nodeBuffer(0));
  88. break;
  89. }
  90. } catch(e) {
  91. canUseApply = false;
  92. }
  93. // no apply : slow and painful algorithm
  94. // default browser on android 4.*
  95. if (!canUseApply) {
  96. var resultStr = "";
  97. for(var i = 0; i < array.length;i++) {
  98. resultStr += String.fromCharCode(array[i]);
  99. }
  100. return resultStr;
  101. }
  102. while (k < len && chunk > 1) {
  103. try {
  104. if (type === "array" || type === "nodebuffer") {
  105. result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len))));
  106. }
  107. else {
  108. result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len))));
  109. }
  110. k += chunk;
  111. }
  112. catch (e) {
  113. chunk = Math.floor(chunk / 2);
  114. }
  115. }
  116. return result.join("");
  117. }
  118. exports.applyFromCharCode = arrayLikeToString;
  119. /**
  120. * Copy the data from an array-like to an other array-like.
  121. * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array.
  122. * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated.
  123. * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array.
  124. */
  125. function arrayLikeToArrayLike(arrayFrom, arrayTo) {
  126. for (var i = 0; i < arrayFrom.length; i++) {
  127. arrayTo[i] = arrayFrom[i];
  128. }
  129. return arrayTo;
  130. }
  131. // a matrix containing functions to transform everything into everything.
  132. var transform = {};
  133. // string to ?
  134. transform["string"] = {
  135. "string": identity,
  136. "array": function(input) {
  137. return stringToArrayLike(input, new Array(input.length));
  138. },
  139. "arraybuffer": function(input) {
  140. return transform["string"]["uint8array"](input).buffer;
  141. },
  142. "uint8array": function(input) {
  143. return stringToArrayLike(input, new Uint8Array(input.length));
  144. },
  145. "nodebuffer": function(input) {
  146. return stringToArrayLike(input, nodeBuffer(input.length));
  147. }
  148. };
  149. // array to ?
  150. transform["array"] = {
  151. "string": arrayLikeToString,
  152. "array": identity,
  153. "arraybuffer": function(input) {
  154. return (new Uint8Array(input)).buffer;
  155. },
  156. "uint8array": function(input) {
  157. return new Uint8Array(input);
  158. },
  159. "nodebuffer": function(input) {
  160. return nodeBuffer(input);
  161. }
  162. };
  163. // arraybuffer to ?
  164. transform["arraybuffer"] = {
  165. "string": function(input) {
  166. return arrayLikeToString(new Uint8Array(input));
  167. },
  168. "array": function(input) {
  169. return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength));
  170. },
  171. "arraybuffer": identity,
  172. "uint8array": function(input) {
  173. return new Uint8Array(input);
  174. },
  175. "nodebuffer": function(input) {
  176. return nodeBuffer(new Uint8Array(input));
  177. }
  178. };
  179. // uint8array to ?
  180. transform["uint8array"] = {
  181. "string": arrayLikeToString,
  182. "array": function(input) {
  183. return arrayLikeToArrayLike(input, new Array(input.length));
  184. },
  185. "arraybuffer": function(input) {
  186. return input.buffer;
  187. },
  188. "uint8array": identity,
  189. "nodebuffer": function(input) {
  190. return nodeBuffer(input);
  191. }
  192. };
  193. // nodebuffer to ?
  194. transform["nodebuffer"] = {
  195. "string": arrayLikeToString,
  196. "array": function(input) {
  197. return arrayLikeToArrayLike(input, new Array(input.length));
  198. },
  199. "arraybuffer": function(input) {
  200. return transform["nodebuffer"]["uint8array"](input).buffer;
  201. },
  202. "uint8array": function(input) {
  203. return arrayLikeToArrayLike(input, new Uint8Array(input.length));
  204. },
  205. "nodebuffer": identity
  206. };
  207. /**
  208. * Transform an input into any type.
  209. * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer.
  210. * If no output type is specified, the unmodified input will be returned.
  211. * @param {String} outputType the output type.
  212. * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert.
  213. * @throws {Error} an Error if the browser doesn't support the requested output type.
  214. */
  215. exports.transformTo = function(outputType, input) {
  216. if (!input) {
  217. // undefined, null, etc
  218. // an empty string won't harm.
  219. input = "";
  220. }
  221. if (!outputType) {
  222. return input;
  223. }
  224. exports.checkSupport(outputType);
  225. var inputType = exports.getTypeOf(input);
  226. var result = transform[inputType][outputType](input);
  227. return result;
  228. };
  229. /**
  230. * Return the type of the input.
  231. * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer.
  232. * @param {Object} input the input to identify.
  233. * @return {String} the (lowercase) type of the input.
  234. */
  235. exports.getTypeOf = function(input) {
  236. if (typeof input === "string") {
  237. return "string";
  238. }
  239. if (Object.prototype.toString.call(input) === "[object Array]") {
  240. return "array";
  241. }
  242. if (support.nodebuffer && nodeBuffer.test(input)) {
  243. return "nodebuffer";
  244. }
  245. if (support.uint8array && input instanceof Uint8Array) {
  246. return "uint8array";
  247. }
  248. if (support.arraybuffer && input instanceof ArrayBuffer) {
  249. return "arraybuffer";
  250. }
  251. };
  252. /**
  253. * Throw an exception if the type is not supported.
  254. * @param {String} type the type to check.
  255. * @throws {Error} an Error if the browser doesn't support the requested type.
  256. */
  257. exports.checkSupport = function(type) {
  258. var supported = support[type.toLowerCase()];
  259. if (!supported) {
  260. throw new Error(type + " is not supported by this browser");
  261. }
  262. };
  263. exports.MAX_VALUE_16BITS = 65535;
  264. exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1
  265. /**
  266. * Prettify a string read as binary.
  267. * @param {string} str the string to prettify.
  268. * @return {string} a pretty string.
  269. */
  270. exports.pretty = function(str) {
  271. var res = '',
  272. code, i;
  273. for (i = 0; i < (str || "").length; i++) {
  274. code = str.charCodeAt(i);
  275. res += '\\x' + (code < 16 ? "0" : "") + code.toString(16).toUpperCase();
  276. }
  277. return res;
  278. };
  279. /**
  280. * Find a compression registered in JSZip.
  281. * @param {string} compressionMethod the method magic to find.
  282. * @return {Object|null} the JSZip compression object, null if none found.
  283. */
  284. exports.findCompression = function(compressionMethod) {
  285. for (var method in compressions) {
  286. if (!compressions.hasOwnProperty(method)) {
  287. continue;
  288. }
  289. if (compressions[method].magic === compressionMethod) {
  290. return compressions[method];
  291. }
  292. }
  293. return null;
  294. };
  295. /**
  296. * Cross-window, cross-Node-context regular expression detection
  297. * @param {Object} object Anything
  298. * @return {Boolean} true if the object is a regular expression,
  299. * false otherwise
  300. */
  301. exports.isRegExp = function (object) {
  302. return Object.prototype.toString.call(object) === "[object RegExp]";
  303. };
  304. /**
  305. * Merge the objects passed as parameters into a new one.
  306. * @private
  307. * @param {...Object} var_args All objects to merge.
  308. * @return {Object} a new object with the data of the others.
  309. */
  310. exports.extend = function() {
  311. var result = {}, i, attr;
  312. for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
  313. for (attr in arguments[i]) {
  314. if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
  315. result[attr] = arguments[i][attr];
  316. }
  317. }
  318. }
  319. return result;
  320. };