Le repo des sources pour le site web des JM2L
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 

705 строки
27 KiB

  1. /*
  2. * jQuery File Upload User Interface Plugin 9.6.0
  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://www.opensource.org/licenses/MIT
  10. */
  11. /* jshint nomen:false */
  12. /* global define, window */
  13. (function (factory) {
  14. 'use strict';
  15. if (typeof define === 'function' && define.amd) {
  16. // Register as an anonymous AMD module:
  17. define([
  18. 'jquery',
  19. 'tmpl',
  20. './jquery.fileupload-image',
  21. './jquery.fileupload-audio',
  22. './jquery.fileupload-video',
  23. './jquery.fileupload-validate'
  24. ], factory);
  25. } else {
  26. // Browser globals:
  27. factory(
  28. window.jQuery,
  29. window.tmpl
  30. );
  31. }
  32. }(function ($, tmpl) {
  33. 'use strict';
  34. $.blueimp.fileupload.prototype._specialOptions.push(
  35. 'filesContainer',
  36. 'uploadTemplateId',
  37. 'downloadTemplateId'
  38. );
  39. // The UI version extends the file upload widget
  40. // and adds complete user interface interaction:
  41. $.widget('blueimp.fileupload', $.blueimp.fileupload, {
  42. options: {
  43. // By default, files added to the widget are uploaded as soon
  44. // as the user clicks on the start buttons. To enable automatic
  45. // uploads, set the following option to true:
  46. autoUpload: false,
  47. // The ID of the upload template:
  48. uploadTemplateId: 'template-upload',
  49. // The ID of the download template:
  50. downloadTemplateId: 'template-download',
  51. // The container for the list of files. If undefined, it is set to
  52. // an element with class "files" inside of the widget element:
  53. filesContainer: undefined,
  54. // By default, files are appended to the files container.
  55. // Set the following option to true, to prepend files instead:
  56. prependFiles: false,
  57. // The expected data type of the upload response, sets the dataType
  58. // option of the $.ajax upload requests:
  59. dataType: 'json',
  60. // Error and info messages:
  61. messages: {
  62. unknownError: 'Unknown error'
  63. },
  64. // Function returning the current number of files,
  65. // used by the maxNumberOfFiles validation:
  66. getNumberOfFiles: function () {
  67. return this.filesContainer.children()
  68. .not('.processing').length;
  69. },
  70. // Callback to retrieve the list of files from the server response:
  71. getFilesFromResponse: function (data) {
  72. if (data.result && $.isArray(data.result.files)) {
  73. return data.result.files;
  74. }
  75. return [];
  76. },
  77. // The add callback is invoked as soon as files are added to the fileupload
  78. // widget (via file input selection, drag & drop or add API call).
  79. // See the basic file upload widget for more information:
  80. add: function (e, data) {
  81. if (e.isDefaultPrevented()) {
  82. return false;
  83. }
  84. var $this = $(this),
  85. that = $this.data('blueimp-fileupload') ||
  86. $this.data('fileupload'),
  87. options = that.options;
  88. data.context = that._renderUpload(data.files)
  89. .data('data', data)
  90. .addClass('processing');
  91. options.filesContainer[
  92. options.prependFiles ? 'prepend' : 'append'
  93. ](data.context);
  94. that._forceReflow(data.context);
  95. that._transition(data.context);
  96. data.process(function () {
  97. return $this.fileupload('process', data);
  98. }).always(function () {
  99. data.context.each(function (index) {
  100. $(this).find('.size').text(
  101. that._formatFileSize(data.files[index].size)
  102. );
  103. }).removeClass('processing');
  104. that._renderPreviews(data);
  105. }).done(function () {
  106. data.context.find('.start').prop('disabled', false);
  107. if ((that._trigger('added', e, data) !== false) &&
  108. (options.autoUpload || data.autoUpload) &&
  109. data.autoUpload !== false) {
  110. data.submit();
  111. }
  112. }).fail(function () {
  113. if (data.files.error) {
  114. data.context.each(function (index) {
  115. var error = data.files[index].error;
  116. if (error) {
  117. $(this).find('.error').text(error);
  118. }
  119. });
  120. }
  121. });
  122. },
  123. // Callback for the start of each file upload request:
  124. send: function (e, data) {
  125. if (e.isDefaultPrevented()) {
  126. return false;
  127. }
  128. var that = $(this).data('blueimp-fileupload') ||
  129. $(this).data('fileupload');
  130. if (data.context && data.dataType &&
  131. data.dataType.substr(0, 6) === 'iframe') {
  132. // Iframe Transport does not support progress events.
  133. // In lack of an indeterminate progress bar, we set
  134. // the progress to 100%, showing the full animated bar:
  135. data.context
  136. .find('.progress').addClass(
  137. !$.support.transition && 'progress-animated'
  138. )
  139. .attr('aria-valuenow', 100)
  140. .children().first().css(
  141. 'width',
  142. '100%'
  143. );
  144. }
  145. return that._trigger('sent', e, data);
  146. },
  147. // Callback for successful uploads:
  148. done: function (e, data) {
  149. if (e.isDefaultPrevented()) {
  150. return false;
  151. }
  152. var that = $(this).data('blueimp-fileupload') ||
  153. $(this).data('fileupload'),
  154. getFilesFromResponse = data.getFilesFromResponse ||
  155. that.options.getFilesFromResponse,
  156. files = getFilesFromResponse(data),
  157. template,
  158. deferred;
  159. if (data.context) {
  160. data.context.each(function (index) {
  161. var file = files[index] ||
  162. {error: 'Empty file upload result'};
  163. deferred = that._addFinishedDeferreds();
  164. that._transition($(this)).done(
  165. function () {
  166. var node = $(this);
  167. template = that._renderDownload([file])
  168. .replaceAll(node);
  169. that._forceReflow(template);
  170. that._transition(template).done(
  171. function () {
  172. data.context = $(this);
  173. that._trigger('completed', e, data);
  174. that._trigger('finished', e, data);
  175. deferred.resolve();
  176. }
  177. );
  178. }
  179. );
  180. });
  181. } else {
  182. template = that._renderDownload(files)[
  183. that.options.prependFiles ? 'prependTo' : 'appendTo'
  184. ](that.options.filesContainer);
  185. that._forceReflow(template);
  186. deferred = that._addFinishedDeferreds();
  187. that._transition(template).done(
  188. function () {
  189. data.context = $(this);
  190. that._trigger('completed', e, data);
  191. that._trigger('finished', e, data);
  192. deferred.resolve();
  193. }
  194. );
  195. }
  196. },
  197. // Callback for failed (abort or error) uploads:
  198. fail: function (e, data) {
  199. if (e.isDefaultPrevented()) {
  200. return false;
  201. }
  202. var that = $(this).data('blueimp-fileupload') ||
  203. $(this).data('fileupload'),
  204. template,
  205. deferred;
  206. if (data.context) {
  207. data.context.each(function (index) {
  208. if (data.errorThrown !== 'abort') {
  209. var file = data.files[index];
  210. file.error = file.error || data.errorThrown ||
  211. data.i18n('unknownError');
  212. deferred = that._addFinishedDeferreds();
  213. that._transition($(this)).done(
  214. function () {
  215. var node = $(this);
  216. template = that._renderDownload([file])
  217. .replaceAll(node);
  218. that._forceReflow(template);
  219. that._transition(template).done(
  220. function () {
  221. data.context = $(this);
  222. that._trigger('failed', e, data);
  223. that._trigger('finished', e, data);
  224. deferred.resolve();
  225. }
  226. );
  227. }
  228. );
  229. } else {
  230. deferred = that._addFinishedDeferreds();
  231. that._transition($(this)).done(
  232. function () {
  233. $(this).remove();
  234. that._trigger('failed', e, data);
  235. that._trigger('finished', e, data);
  236. deferred.resolve();
  237. }
  238. );
  239. }
  240. });
  241. } else if (data.errorThrown !== 'abort') {
  242. data.context = that._renderUpload(data.files)[
  243. that.options.prependFiles ? 'prependTo' : 'appendTo'
  244. ](that.options.filesContainer)
  245. .data('data', data);
  246. that._forceReflow(data.context);
  247. deferred = that._addFinishedDeferreds();
  248. that._transition(data.context).done(
  249. function () {
  250. data.context = $(this);
  251. that._trigger('failed', e, data);
  252. that._trigger('finished', e, data);
  253. deferred.resolve();
  254. }
  255. );
  256. } else {
  257. that._trigger('failed', e, data);
  258. that._trigger('finished', e, data);
  259. that._addFinishedDeferreds().resolve();
  260. }
  261. },
  262. // Callback for upload progress events:
  263. progress: function (e, data) {
  264. if (e.isDefaultPrevented()) {
  265. return false;
  266. }
  267. var progress = Math.floor(data.loaded / data.total * 100);
  268. if (data.context) {
  269. data.context.each(function () {
  270. $(this).find('.progress')
  271. .attr('aria-valuenow', progress)
  272. .children().first().css(
  273. 'width',
  274. progress + '%'
  275. );
  276. });
  277. }
  278. },
  279. // Callback for global upload progress events:
  280. progressall: function (e, data) {
  281. if (e.isDefaultPrevented()) {
  282. return false;
  283. }
  284. var $this = $(this),
  285. progress = Math.floor(data.loaded / data.total * 100),
  286. globalProgressNode = $this.find('.fileupload-progress'),
  287. extendedProgressNode = globalProgressNode
  288. .find('.progress-extended');
  289. if (extendedProgressNode.length) {
  290. extendedProgressNode.html(
  291. ($this.data('blueimp-fileupload') || $this.data('fileupload'))
  292. ._renderExtendedProgress(data)
  293. );
  294. }
  295. globalProgressNode
  296. .find('.progress')
  297. .attr('aria-valuenow', progress)
  298. .children().first().css(
  299. 'width',
  300. progress + '%'
  301. );
  302. },
  303. // Callback for uploads start, equivalent to the global ajaxStart event:
  304. start: function (e) {
  305. if (e.isDefaultPrevented()) {
  306. return false;
  307. }
  308. var that = $(this).data('blueimp-fileupload') ||
  309. $(this).data('fileupload');
  310. that._resetFinishedDeferreds();
  311. that._transition($(this).find('.fileupload-progress')).done(
  312. function () {
  313. that._trigger('started', e);
  314. }
  315. );
  316. },
  317. // Callback for uploads stop, equivalent to the global ajaxStop event:
  318. stop: function (e) {
  319. if (e.isDefaultPrevented()) {
  320. return false;
  321. }
  322. var that = $(this).data('blueimp-fileupload') ||
  323. $(this).data('fileupload'),
  324. deferred = that._addFinishedDeferreds();
  325. $.when.apply($, that._getFinishedDeferreds())
  326. .done(function () {
  327. that._trigger('stopped', e);
  328. });
  329. that._transition($(this).find('.fileupload-progress')).done(
  330. function () {
  331. $(this).find('.progress')
  332. .attr('aria-valuenow', '0')
  333. .children().first().css('width', '0%');
  334. $(this).find('.progress-extended').html(' ');
  335. deferred.resolve();
  336. }
  337. );
  338. },
  339. processstart: function (e) {
  340. if (e.isDefaultPrevented()) {
  341. return false;
  342. }
  343. $(this).addClass('fileupload-processing');
  344. },
  345. processstop: function (e) {
  346. if (e.isDefaultPrevented()) {
  347. return false;
  348. }
  349. $(this).removeClass('fileupload-processing');
  350. },
  351. // Callback for file deletion:
  352. destroy: function (e, data) {
  353. if (e.isDefaultPrevented()) {
  354. return false;
  355. }
  356. var that = $(this).data('blueimp-fileupload') ||
  357. $(this).data('fileupload'),
  358. removeNode = function () {
  359. that._transition(data.context).done(
  360. function () {
  361. $(this).remove();
  362. that._trigger('destroyed', e, data);
  363. }
  364. );
  365. };
  366. if (data.url) {
  367. data.dataType = data.dataType || that.options.dataType;
  368. $.ajax(data).done(removeNode).fail(function () {
  369. that._trigger('destroyfailed', e, data);
  370. });
  371. } else {
  372. removeNode();
  373. }
  374. }
  375. },
  376. _resetFinishedDeferreds: function () {
  377. this._finishedUploads = [];
  378. },
  379. _addFinishedDeferreds: function (deferred) {
  380. if (!deferred) {
  381. deferred = $.Deferred();
  382. }
  383. this._finishedUploads.push(deferred);
  384. return deferred;
  385. },
  386. _getFinishedDeferreds: function () {
  387. return this._finishedUploads;
  388. },
  389. // Link handler, that allows to download files
  390. // by drag & drop of the links to the desktop:
  391. _enableDragToDesktop: function () {
  392. var link = $(this),
  393. url = link.prop('href'),
  394. name = link.prop('download'),
  395. type = 'application/octet-stream';
  396. link.bind('dragstart', function (e) {
  397. try {
  398. e.originalEvent.dataTransfer.setData(
  399. 'DownloadURL',
  400. [type, name, url].join(':')
  401. );
  402. } catch (ignore) {}
  403. });
  404. },
  405. _formatFileSize: function (bytes) {
  406. if (typeof bytes !== 'number') {
  407. return '';
  408. }
  409. if (bytes >= 1000000000) {
  410. return (bytes / 1000000000).toFixed(2) + ' GB';
  411. }
  412. if (bytes >= 1000000) {
  413. return (bytes / 1000000).toFixed(2) + ' MB';
  414. }
  415. return (bytes / 1000).toFixed(2) + ' KB';
  416. },
  417. _formatBitrate: function (bits) {
  418. if (typeof bits !== 'number') {
  419. return '';
  420. }
  421. if (bits >= 1000000000) {
  422. return (bits / 1000000000).toFixed(2) + ' Gbit/s';
  423. }
  424. if (bits >= 1000000) {
  425. return (bits / 1000000).toFixed(2) + ' Mbit/s';
  426. }
  427. if (bits >= 1000) {
  428. return (bits / 1000).toFixed(2) + ' kbit/s';
  429. }
  430. return bits.toFixed(2) + ' bit/s';
  431. },
  432. _formatTime: function (seconds) {
  433. var date = new Date(seconds * 1000),
  434. days = Math.floor(seconds / 86400);
  435. days = days ? days + 'd ' : '';
  436. return days +
  437. ('0' + date.getUTCHours()).slice(-2) + ':' +
  438. ('0' + date.getUTCMinutes()).slice(-2) + ':' +
  439. ('0' + date.getUTCSeconds()).slice(-2);
  440. },
  441. _formatPercentage: function (floatValue) {
  442. return (floatValue * 100).toFixed(2) + ' %';
  443. },
  444. _renderExtendedProgress: function (data) {
  445. return this._formatBitrate(data.bitrate) + ' | ' +
  446. this._formatTime(
  447. (data.total - data.loaded) * 8 / data.bitrate
  448. ) + ' | ' +
  449. this._formatPercentage(
  450. data.loaded / data.total
  451. ) + ' | ' +
  452. this._formatFileSize(data.loaded) + ' / ' +
  453. this._formatFileSize(data.total);
  454. },
  455. _renderTemplate: function (func, files) {
  456. if (!func) {
  457. return $();
  458. }
  459. var result = func({
  460. files: files,
  461. formatFileSize: this._formatFileSize,
  462. options: this.options
  463. });
  464. if (result instanceof $) {
  465. return result;
  466. }
  467. return $(this.options.templatesContainer).html(result).children();
  468. },
  469. _renderPreviews: function (data) {
  470. data.context.find('.preview').each(function (index, elm) {
  471. $(elm).append(data.files[index].preview);
  472. });
  473. },
  474. _renderUpload: function (files) {
  475. return this._renderTemplate(
  476. this.options.uploadTemplate,
  477. files
  478. );
  479. },
  480. _renderDownload: function (files) {
  481. return this._renderTemplate(
  482. this.options.downloadTemplate,
  483. files
  484. ).find('a[download]').each(this._enableDragToDesktop).end();
  485. },
  486. _startHandler: function (e) {
  487. e.preventDefault();
  488. var button = $(e.currentTarget),
  489. template = button.closest('.template-upload'),
  490. data = template.data('data');
  491. button.prop('disabled', true);
  492. if (data && data.submit) {
  493. data.submit();
  494. }
  495. },
  496. _cancelHandler: function (e) {
  497. e.preventDefault();
  498. var template = $(e.currentTarget)
  499. .closest('.template-upload,.template-download'),
  500. data = template.data('data') || {};
  501. data.context = data.context || template;
  502. if (data.abort) {
  503. data.abort();
  504. } else {
  505. data.errorThrown = 'abort';
  506. this._trigger('fail', e, data);
  507. }
  508. },
  509. _deleteHandler: function (e) {
  510. e.preventDefault();
  511. var button = $(e.currentTarget);
  512. this._trigger('destroy', e, $.extend({
  513. context: button.closest('.template-download'),
  514. type: 'DELETE'
  515. }, button.data()));
  516. },
  517. _forceReflow: function (node) {
  518. return $.support.transition && node.length &&
  519. node[0].offsetWidth;
  520. },
  521. _transition: function (node) {
  522. var dfd = $.Deferred();
  523. if ($.support.transition && node.hasClass('fade') && node.is(':visible')) {
  524. node.bind(
  525. $.support.transition.end,
  526. function (e) {
  527. // Make sure we don't respond to other transitions events
  528. // in the container element, e.g. from button elements:
  529. if (e.target === node[0]) {
  530. node.unbind($.support.transition.end);
  531. dfd.resolveWith(node);
  532. }
  533. }
  534. ).toggleClass('in');
  535. } else {
  536. node.toggleClass('in');
  537. dfd.resolveWith(node);
  538. }
  539. return dfd;
  540. },
  541. _initButtonBarEventHandlers: function () {
  542. var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
  543. filesList = this.options.filesContainer;
  544. this._on(fileUploadButtonBar.find('.start'), {
  545. click: function (e) {
  546. e.preventDefault();
  547. filesList.find('.start').click();
  548. }
  549. });
  550. this._on(fileUploadButtonBar.find('.cancel'), {
  551. click: function (e) {
  552. e.preventDefault();
  553. filesList.find('.cancel').click();
  554. }
  555. });
  556. this._on(fileUploadButtonBar.find('.delete'), {
  557. click: function (e) {
  558. e.preventDefault();
  559. filesList.find('.toggle:checked')
  560. .closest('.template-download')
  561. .find('.delete').click();
  562. fileUploadButtonBar.find('.toggle')
  563. .prop('checked', false);
  564. }
  565. });
  566. this._on(fileUploadButtonBar.find('.toggle'), {
  567. change: function (e) {
  568. filesList.find('.toggle').prop(
  569. 'checked',
  570. $(e.currentTarget).is(':checked')
  571. );
  572. }
  573. });
  574. },
  575. _destroyButtonBarEventHandlers: function () {
  576. this._off(
  577. this.element.find('.fileupload-buttonbar')
  578. .find('.start, .cancel, .delete'),
  579. 'click'
  580. );
  581. this._off(
  582. this.element.find('.fileupload-buttonbar .toggle'),
  583. 'change.'
  584. );
  585. },
  586. _initEventHandlers: function () {
  587. this._super();
  588. this._on(this.options.filesContainer, {
  589. 'click .start': this._startHandler,
  590. 'click .cancel': this._cancelHandler,
  591. 'click .delete': this._deleteHandler
  592. });
  593. this._initButtonBarEventHandlers();
  594. },
  595. _destroyEventHandlers: function () {
  596. this._destroyButtonBarEventHandlers();
  597. this._off(this.options.filesContainer, 'click');
  598. this._super();
  599. },
  600. _enableFileInputButton: function () {
  601. this.element.find('.fileinput-button input')
  602. .prop('disabled', false)
  603. .parent().removeClass('disabled');
  604. },
  605. _disableFileInputButton: function () {
  606. this.element.find('.fileinput-button input')
  607. .prop('disabled', true)
  608. .parent().addClass('disabled');
  609. },
  610. _initTemplates: function () {
  611. var options = this.options;
  612. options.templatesContainer = this.document[0].createElement(
  613. options.filesContainer.prop('nodeName')
  614. );
  615. if (tmpl) {
  616. if (options.uploadTemplateId) {
  617. options.uploadTemplate = tmpl(options.uploadTemplateId);
  618. }
  619. if (options.downloadTemplateId) {
  620. options.downloadTemplate = tmpl(options.downloadTemplateId);
  621. }
  622. }
  623. },
  624. _initFilesContainer: function () {
  625. var options = this.options;
  626. if (options.filesContainer === undefined) {
  627. options.filesContainer = this.element.find('.files');
  628. } else if (!(options.filesContainer instanceof $)) {
  629. options.filesContainer = $(options.filesContainer);
  630. }
  631. },
  632. _initSpecialOptions: function () {
  633. this._super();
  634. this._initFilesContainer();
  635. this._initTemplates();
  636. },
  637. _create: function () {
  638. this._super();
  639. this._resetFinishedDeferreds();
  640. if (!$.support.fileInput) {
  641. this._disableFileInputButton();
  642. }
  643. },
  644. enable: function () {
  645. var wasDisabled = false;
  646. if (this.options.disabled) {
  647. wasDisabled = true;
  648. }
  649. this._super();
  650. if (wasDisabled) {
  651. this.element.find('input, button').prop('disabled', false);
  652. this._enableFileInputButton();
  653. }
  654. },
  655. disable: function () {
  656. if (!this.options.disabled) {
  657. this.element.find('input, button').prop('disabled', true);
  658. this._disableFileInputButton();
  659. }
  660. this._super();
  661. }
  662. });
  663. }));