jquery.fileupload-ui.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. /*
  2. * jQuery File Upload User Interface Plugin 4.4.1
  3. * https://github.com/blueimp/jQuery-File-Upload
  4. *
  5. * Copyright 2010, Sebastian Tschan
  6. * https://blueimp.net
  7. *
  8. * Licensed under the MIT license:
  9. * http://creativecommons.org/licenses/MIT/
  10. */
  11. /*jslint browser: true, unparam: true */
  12. /*global jQuery, FileReader, URL, webkitURL */
  13. (function ($) {
  14. 'use strict';
  15. var undef = 'undefined',
  16. func = 'function',
  17. UploadHandler,
  18. methods,
  19. MultiLoader = function (callBack) {
  20. var loaded = 0,
  21. list = [];
  22. this.complete = function () {
  23. loaded += 1;
  24. if (loaded === list.length + 1) {
  25. // list.length * onComplete + 1 * onLoadAll
  26. callBack(list);
  27. loaded = 0;
  28. list = [];
  29. }
  30. };
  31. this.push = function (item) {
  32. list.push(item);
  33. };
  34. this.getList = function () {
  35. return list;
  36. };
  37. };
  38. UploadHandler = function (container, options) {
  39. var uploadHandler = this,
  40. dragOverTimeout,
  41. isDropZoneEnlarged,
  42. multiLoader = new MultiLoader(function (list) {
  43. uploadHandler.hideProgressBarAll(function () {
  44. uploadHandler.resetProgressBarAll();
  45. if (typeof uploadHandler.onCompleteAll === func) {
  46. uploadHandler.onCompleteAll(list);
  47. }
  48. });
  49. }),
  50. getUploadTable = function (handler) {
  51. return typeof handler.uploadTable === func ?
  52. handler.uploadTable(handler) : handler.uploadTable;
  53. },
  54. getDownloadTable = function (handler) {
  55. return typeof handler.downloadTable === func ?
  56. handler.downloadTable(handler) : handler.downloadTable;
  57. };
  58. this.requestHeaders = {'Accept': 'application/json, text/javascript, */*; q=0.01'};
  59. this.dropZone = container;
  60. this.imageTypes = /^image\/(gif|jpeg|png)$/;
  61. this.previewMaxWidth = this.previewMaxHeight = 80;
  62. this.previewLoadDelay = 100;
  63. this.previewAsCanvas = true;
  64. this.previewSelector = '.file_upload_preview';
  65. this.progressSelector = '.file_upload_progress div';
  66. this.cancelSelector = '.file_upload_cancel button';
  67. this.cssClassSmall = 'file_upload_small';
  68. this.cssClassLarge = 'file_upload_large';
  69. this.cssClassHighlight = 'file_upload_highlight';
  70. this.dropEffect = 'highlight';
  71. this.uploadTable = this.downloadTable = null;
  72. this.buildUploadRow = this.buildDownloadRow = null;
  73. this.progressAllNode = null;
  74. this.loadImage = function (file, callBack, maxWidth, maxHeight, imageTypes, noCanvas) {
  75. var img,
  76. scaleImage,
  77. urlAPI,
  78. fileReader;
  79. if (imageTypes && !imageTypes.test(file.type)) {
  80. return null;
  81. }
  82. scaleImage = function (img) {
  83. var canvas = document.createElement('canvas'),
  84. scale = Math.min(
  85. (maxWidth || img.width) / img.width,
  86. (maxHeight || img.height) / img.height
  87. );
  88. if (scale > 1) {
  89. scale = 1;
  90. }
  91. img.width = parseInt(img.width * scale, 10);
  92. img.height = parseInt(img.height * scale, 10);
  93. if (noCanvas || typeof canvas.getContext !== func) {
  94. return img;
  95. }
  96. canvas.width = img.width;
  97. canvas.height = img.height;
  98. canvas.getContext('2d').drawImage(img, 0, 0, img.width, img.height);
  99. return canvas;
  100. };
  101. img = document.createElement('img');
  102. urlAPI = typeof URL !== undef ? URL : typeof webkitURL !== undef ? webkitURL : null;
  103. if (urlAPI && typeof urlAPI.createObjectURL === func) {
  104. img.onload = function () {
  105. urlAPI.revokeObjectURL(this.src);
  106. callBack(scaleImage(img));
  107. };
  108. img.src = urlAPI.createObjectURL(file);
  109. } else if (typeof FileReader !== undef &&
  110. typeof FileReader.prototype.readAsDataURL === func) {
  111. img.onload = function () {
  112. callBack(scaleImage(img));
  113. };
  114. fileReader = new FileReader();
  115. fileReader.onload = function (e) {
  116. img.src = e.target.result;
  117. };
  118. fileReader.readAsDataURL(file);
  119. } else {
  120. callBack(null);
  121. }
  122. };
  123. this.addNode = function (parentNode, node, callBack) {
  124. if (parentNode && parentNode.length && node && node.length) {
  125. node.css('display', 'none').appendTo(parentNode).fadeIn(function () {
  126. if (typeof callBack === func) {
  127. try {
  128. callBack();
  129. } catch (e) {
  130. // Fix endless exception loop:
  131. node.stop();
  132. throw e;
  133. }
  134. }
  135. });
  136. } else if (typeof callBack === func) {
  137. callBack();
  138. }
  139. };
  140. this.removeNode = function (node, callBack) {
  141. if (node && node.length) {
  142. node.fadeOut(function () {
  143. node.remove();
  144. if (typeof callBack === func) {
  145. try {
  146. callBack();
  147. } catch (e) {
  148. // Fix endless exception loop:
  149. node.stop();
  150. throw e;
  151. }
  152. }
  153. });
  154. } else if (typeof callBack === func) {
  155. callBack();
  156. }
  157. };
  158. this.replaceNode = function (oldNode, newNode, callBack) {
  159. if (!(newNode && newNode.length)) {
  160. return uploadHandler.removeNode(oldNode, callBack);
  161. }
  162. if (oldNode && oldNode.length) {
  163. oldNode.fadeOut(function () {
  164. newNode.css('display', 'none');
  165. oldNode.replaceWith(newNode);
  166. newNode.fadeIn(function () {
  167. if (typeof callBack === func) {
  168. try {
  169. callBack();
  170. } catch (e) {
  171. // Fix endless exception loop:
  172. oldNode.stop();
  173. newNode.stop();
  174. throw e;
  175. }
  176. }
  177. });
  178. });
  179. } else if (typeof callBack === func) {
  180. callBack();
  181. }
  182. };
  183. this.resetProgressBarAll = function () {
  184. if (uploadHandler.progressbarAll) {
  185. uploadHandler.progressbarAll.progressbar(
  186. 'value',
  187. 0
  188. );
  189. }
  190. };
  191. this.hideProgressBarAll = function (callBack) {
  192. if (uploadHandler.progressbarAll && !$(getUploadTable(uploadHandler))
  193. .find(uploadHandler.progressSelector + ':visible:first').length) {
  194. uploadHandler.progressbarAll.fadeOut(callBack);
  195. } else if (typeof callBack === func) {
  196. callBack();
  197. }
  198. };
  199. this.onAbort = function (event, files, index, xhr, handler) {
  200. handler.removeNode(handler.uploadRow, handler.hideProgressBarAll);
  201. };
  202. this.cancelUpload = function (event, files, index, xhr, handler) {
  203. var readyState = xhr.readyState;
  204. xhr.abort();
  205. // If readyState is below 2, abort() has no effect:
  206. if (typeof readyState !== 'number' || readyState < 2) {
  207. handler.onAbort(event, files, index, xhr, handler);
  208. }
  209. };
  210. this.initProgressBar = function (node, value) {
  211. if (!node || !node.length) {
  212. return null;
  213. }
  214. if (typeof node.progressbar === func) {
  215. return node.progressbar({
  216. value: value
  217. });
  218. } else {
  219. node.addClass('progressbar')
  220. .append($('<div/>').css('width', value + '%'))
  221. .progressbar = function (key, value) {
  222. return this.each(function () {
  223. if (key === 'destroy') {
  224. $(this).removeClass('progressbar').empty();
  225. } else {
  226. $(this).children().css('width', value + '%');
  227. }
  228. });
  229. };
  230. return node;
  231. }
  232. };
  233. this.destroyProgressBar = function (node) {
  234. if (!node || !node.length) {
  235. return null;
  236. }
  237. return node.progressbar('destroy');
  238. };
  239. this.initUploadProgress = function (xhr, handler) {
  240. if (!xhr.upload && handler.progressbar) {
  241. handler.progressbar.progressbar(
  242. 'value',
  243. 100 // indeterminate progress displayed by a full animated progress bar
  244. );
  245. }
  246. };
  247. this.initUploadProgressAll = function () {
  248. if (uploadHandler.progressbarAll && uploadHandler.progressbarAll.is(':hidden')) {
  249. uploadHandler.progressbarAll.fadeIn();
  250. }
  251. };
  252. this.onSend = function (event, files, index, xhr, handler) {
  253. handler.initUploadProgress(xhr, handler);
  254. };
  255. this.onProgress = function (event, files, index, xhr, handler) {
  256. if (handler.progressbar && event.lengthComputable) {
  257. handler.progressbar.progressbar(
  258. 'value',
  259. parseInt(event.loaded / event.total * 100, 10)
  260. );
  261. }
  262. };
  263. this.onProgressAll = function (event, list) {
  264. if (uploadHandler.progressbarAll && event.lengthComputable) {
  265. uploadHandler.progressbarAll.progressbar(
  266. 'value',
  267. parseInt(event.loaded / event.total * 100, 10)
  268. );
  269. }
  270. };
  271. this.onLoadAll = function (list) {
  272. multiLoader.complete();
  273. };
  274. this.initProgressBarAll = function () {
  275. if (!uploadHandler.progressbarAll) {
  276. uploadHandler.progressbarAll = uploadHandler.initProgressBar(
  277. (typeof uploadHandler.progressAllNode === func ?
  278. uploadHandler.progressAllNode(uploadHandler) : uploadHandler.progressAllNode),
  279. 0
  280. );
  281. }
  282. };
  283. this.destroyProgressBarAll = function () {
  284. uploadHandler.destroyProgressBar(uploadHandler.progressbarAll);
  285. };
  286. this.loadPreviewImage = function (files, index, handler) {
  287. index = index || 0;
  288. handler.uploadRow.find(handler.previewSelector).each(function () {
  289. var previewNode = $(this),
  290. file = files[index];
  291. setTimeout(function () {
  292. handler.loadImage(
  293. file,
  294. function (img) {
  295. handler.addNode(
  296. previewNode,
  297. $(img)
  298. );
  299. },
  300. handler.previewMaxWidth,
  301. handler.previewMaxHeight,
  302. handler.imageTypes,
  303. !handler.previewAsCanvas
  304. );
  305. }, handler.previewLoadDelay);
  306. index += 1;
  307. });
  308. };
  309. this.initUploadRow = function (event, files, index, xhr, handler) {
  310. var uploadRow = handler.uploadRow = (typeof handler.buildUploadRow === func ?
  311. handler.buildUploadRow(files, index, handler) : null);
  312. if (uploadRow) {
  313. handler.progressbar = handler.initProgressBar(
  314. uploadRow.find(handler.progressSelector),
  315. 0
  316. );
  317. uploadRow.find(handler.cancelSelector).click(function (e) {
  318. handler.cancelUpload(e, files, index, xhr, handler);
  319. e.preventDefault();
  320. });
  321. handler.loadPreviewImage(files, index, handler);
  322. }
  323. };
  324. this.initUpload = function (event, files, index, xhr, handler, callBack) {
  325. handler.initUploadRow(event, files, index, xhr, handler);
  326. handler.addNode(
  327. getUploadTable(handler),
  328. handler.uploadRow,
  329. function () {
  330. if (typeof handler.beforeSend === func) {
  331. handler.beforeSend(event, files, index, xhr, handler, callBack);
  332. } else {
  333. callBack();
  334. }
  335. }
  336. );
  337. handler.initUploadProgressAll();
  338. };
  339. this.parseResponse = function (xhr, handler) {
  340. if (typeof xhr.responseText !== undef) {
  341. return $.parseJSON(xhr.responseText);
  342. } else {
  343. // Instead of an XHR object, an iframe is used for legacy browsers:
  344. return $.parseJSON(xhr.contents().text());
  345. }
  346. };
  347. this.initDownloadRow = function (event, files, index, xhr, handler) {
  348. var json, downloadRow;
  349. try {
  350. json = handler.response = handler.parseResponse(xhr, handler);
  351. } catch (e) {
  352. if (typeof handler.onError === func) {
  353. handler.originalEvent = event;
  354. handler.onError(e, files, index, xhr, handler);
  355. } else {
  356. throw e;
  357. }
  358. }
  359. downloadRow = handler.downloadRow = (typeof handler.buildDownloadRow === func ?
  360. handler.buildDownloadRow(json, handler) : null);
  361. };
  362. this.onLoad = function (event, files, index, xhr, handler) {
  363. var uploadTable = getUploadTable(handler),
  364. downloadTable = getDownloadTable(handler),
  365. callBack = function () {
  366. if (typeof handler.onComplete === func) {
  367. handler.onComplete(event, files, index, xhr, handler);
  368. }
  369. multiLoader.complete();
  370. };
  371. multiLoader.push(Array.prototype.slice.call(arguments, 1));
  372. handler.initDownloadRow(event, files, index, xhr, handler);
  373. if (uploadTable && handler.uploadRow && handler.uploadRow.length &&
  374. (!downloadTable || uploadTable.get(0) === downloadTable.get(0))) {
  375. handler.replaceNode(handler.uploadRow, handler.downloadRow, callBack);
  376. } else {
  377. handler.removeNode(handler.uploadRow, function () {
  378. handler.addNode(
  379. downloadTable,
  380. handler.downloadRow,
  381. callBack
  382. );
  383. });
  384. }
  385. };
  386. this.dropZoneEnlarge = function () {
  387. if (!isDropZoneEnlarged) {
  388. if (typeof uploadHandler.dropZone.switchClass === func) {
  389. uploadHandler.dropZone.switchClass(
  390. uploadHandler.cssClassSmall,
  391. uploadHandler.cssClassLarge
  392. );
  393. } else {
  394. uploadHandler.dropZone.addClass(uploadHandler.cssClassLarge);
  395. uploadHandler.dropZone.removeClass(uploadHandler.cssClassSmall);
  396. }
  397. isDropZoneEnlarged = true;
  398. }
  399. };
  400. this.dropZoneReduce = function () {
  401. if (typeof uploadHandler.dropZone.switchClass === func) {
  402. uploadHandler.dropZone.switchClass(
  403. uploadHandler.cssClassLarge,
  404. uploadHandler.cssClassSmall
  405. );
  406. } else {
  407. uploadHandler.dropZone.addClass(uploadHandler.cssClassSmall);
  408. uploadHandler.dropZone.removeClass(uploadHandler.cssClassLarge);
  409. }
  410. isDropZoneEnlarged = false;
  411. };
  412. this.onDocumentDragEnter = function (event) {
  413. uploadHandler.dropZoneEnlarge();
  414. };
  415. this.onDocumentDragOver = function (event) {
  416. if (dragOverTimeout) {
  417. clearTimeout(dragOverTimeout);
  418. }
  419. dragOverTimeout = setTimeout(function () {
  420. uploadHandler.dropZoneReduce();
  421. }, 200);
  422. };
  423. this.onDragEnter = this.onDragLeave = function (event) {
  424. uploadHandler.dropZone.toggleClass(uploadHandler.cssClassHighlight);
  425. };
  426. this.onDrop = function (event) {
  427. if (dragOverTimeout) {
  428. clearTimeout(dragOverTimeout);
  429. }
  430. if (uploadHandler.dropEffect && typeof uploadHandler.dropZone.effect === func) {
  431. uploadHandler.dropZone.effect(uploadHandler.dropEffect, function () {
  432. uploadHandler.dropZone.removeClass(uploadHandler.cssClassHighlight);
  433. uploadHandler.dropZoneReduce();
  434. });
  435. } else {
  436. uploadHandler.dropZone.removeClass(uploadHandler.cssClassHighlight);
  437. uploadHandler.dropZoneReduce();
  438. }
  439. };
  440. this.init = function () {
  441. uploadHandler.initProgressBarAll();
  442. if (typeof uploadHandler.initExtended === func) {
  443. uploadHandler.initExtended();
  444. }
  445. };
  446. this.destroy = function () {
  447. if (typeof uploadHandler.destroyExtended === func) {
  448. uploadHandler.destroyExtended();
  449. }
  450. uploadHandler.destroyProgressBarAll();
  451. };
  452. $.extend(this, options);
  453. };
  454. methods = {
  455. init : function (options) {
  456. return this.each(function () {
  457. $(this).fileUpload(new UploadHandler($(this), options));
  458. });
  459. },
  460. option: function (option, value, namespace) {
  461. if (!option || (typeof option === 'string' && typeof value === undef)) {
  462. return $(this).fileUpload('option', option, value, namespace);
  463. }
  464. return this.each(function () {
  465. $(this).fileUpload('option', option, value, namespace);
  466. });
  467. },
  468. destroy : function (namespace) {
  469. return this.each(function () {
  470. $(this).fileUpload('destroy', namespace);
  471. });
  472. },
  473. upload: function (files, namespace) {
  474. return this.each(function () {
  475. $(this).fileUpload('upload', files, namespace);
  476. });
  477. }
  478. };
  479. $.fn.fileUploadUI = function (method) {
  480. if (methods[method]) {
  481. return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  482. } else if (typeof method === 'object' || !method) {
  483. return methods.init.apply(this, arguments);
  484. } else {
  485. $.error('Method "' + method + '" does not exist on jQuery.fileUploadUI');
  486. }
  487. };
  488. }(jQuery));