utils.js 9.5 KB

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