jquery.fileupload.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  1. /*
  2. * jQuery File Upload Plugin 4.5.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 XMLHttpRequestUpload, File, FileReader, FormData, ProgressEvent, unescape, jQuery, upload */
  13. (function ($) {
  14. 'use strict';
  15. var defaultNamespace = 'file_upload',
  16. undef = 'undefined',
  17. func = 'function',
  18. FileUpload,
  19. methods,
  20. MultiLoader = function (callBack, numOrList) {
  21. var loaded = 0,
  22. list = [];
  23. if (numOrList) {
  24. if (numOrList.length) {
  25. list = numOrList;
  26. } else {
  27. list[numOrList - 1] = null;
  28. }
  29. }
  30. this.complete = function () {
  31. loaded += 1;
  32. if (loaded === list.length) {
  33. callBack(list);
  34. loaded = 0;
  35. list = [];
  36. }
  37. };
  38. this.push = function (item) {
  39. list.push(item);
  40. };
  41. this.getList = function () {
  42. return list;
  43. };
  44. },
  45. SequenceHandler = function () {
  46. var sequence = [];
  47. this.push = function (callBack) {
  48. sequence.push(callBack);
  49. if (sequence.length === 1) {
  50. callBack();
  51. }
  52. };
  53. this.next = function () {
  54. sequence.shift();
  55. if (sequence.length) {
  56. sequence[0]();
  57. }
  58. };
  59. };
  60. FileUpload = function (container) {
  61. var fileUpload = this,
  62. uploadForm,
  63. fileInput,
  64. settings = {
  65. namespace: defaultNamespace,
  66. uploadFormFilter: function (index) {
  67. return true;
  68. },
  69. fileInputFilter: function (index) {
  70. return true;
  71. },
  72. cssClass: defaultNamespace,
  73. dragDropSupport: true,
  74. dropZone: container,
  75. url: function (form) {
  76. return form.attr('action');
  77. },
  78. method: function (form) {
  79. return form.attr('method');
  80. },
  81. fieldName: function (input) {
  82. return input.attr('name');
  83. },
  84. formData: function (form) {
  85. return form.serializeArray();
  86. },
  87. requestHeaders: null,
  88. multipart: true,
  89. multiFileRequest: false,
  90. withCredentials: false,
  91. forceIframeUpload: false,
  92. sequentialUploads: false,
  93. maxChunkSize: null,
  94. maxFileReaderSize: 50000000,
  95. replaceFileInput: true
  96. },
  97. documentListeners = {},
  98. dropZoneListeners = {},
  99. protocolRegExp = /^http(s)?:\/\//,
  100. optionsReference,
  101. multiLoader = new MultiLoader(function (list) {
  102. if (typeof settings.onLoadAll === func) {
  103. settings.onLoadAll(list);
  104. }
  105. }),
  106. sequenceHandler = new SequenceHandler(),
  107. completeNext = function () {
  108. multiLoader.complete();
  109. sequenceHandler.next();
  110. },
  111. isXHRUploadCapable = function () {
  112. return typeof XMLHttpRequest !== undef && typeof XMLHttpRequestUpload !== undef &&
  113. typeof File !== undef && (!settings.multipart || typeof FormData !== undef ||
  114. (typeof FileReader !== undef && typeof XMLHttpRequest.prototype.sendAsBinary === func));
  115. },
  116. initEventHandlers = function () {
  117. if (settings.dragDropSupport) {
  118. if (typeof settings.onDocumentDragEnter === func) {
  119. documentListeners['dragenter.' + settings.namespace] = function (e) {
  120. settings.onDocumentDragEnter(e);
  121. };
  122. }
  123. if (typeof settings.onDocumentDragLeave === func) {
  124. documentListeners['dragleave.' + settings.namespace] = function (e) {
  125. settings.onDocumentDragLeave(e);
  126. };
  127. }
  128. documentListeners['dragover.' + settings.namespace] = fileUpload.onDocumentDragOver;
  129. documentListeners['drop.' + settings.namespace] = fileUpload.onDocumentDrop;
  130. $(document).bind(documentListeners);
  131. if (typeof settings.onDragEnter === func) {
  132. dropZoneListeners['dragenter.' + settings.namespace] = function (e) {
  133. settings.onDragEnter(e);
  134. };
  135. }
  136. if (typeof settings.onDragLeave === func) {
  137. dropZoneListeners['dragleave.' + settings.namespace] = function (e) {
  138. settings.onDragLeave(e);
  139. };
  140. }
  141. dropZoneListeners['dragover.' + settings.namespace] = fileUpload.onDragOver;
  142. dropZoneListeners['drop.' + settings.namespace] = fileUpload.onDrop;
  143. settings.dropZone.bind(dropZoneListeners);
  144. }
  145. fileInput.bind('change.' + settings.namespace, fileUpload.onChange);
  146. },
  147. removeEventHandlers = function () {
  148. $.each(documentListeners, function (key, value) {
  149. $(document).unbind(key, value);
  150. });
  151. $.each(dropZoneListeners, function (key, value) {
  152. settings.dropZone.unbind(key, value);
  153. });
  154. fileInput.unbind('change.' + settings.namespace);
  155. },
  156. isChunkedUpload = function (settings) {
  157. return typeof settings.uploadedBytes !== undef;
  158. },
  159. createProgressEvent = function (lengthComputable, loaded, total) {
  160. var event;
  161. if (typeof document.createEvent === func && typeof ProgressEvent !== undef) {
  162. event = document.createEvent('ProgressEvent');
  163. event.initProgressEvent(
  164. 'progress',
  165. false,
  166. false,
  167. lengthComputable,
  168. loaded,
  169. total
  170. );
  171. } else {
  172. event = {
  173. lengthComputable: true,
  174. loaded: loaded,
  175. total: total
  176. };
  177. }
  178. return event;
  179. },
  180. getProgressTotal = function (files, index, settings) {
  181. var i,
  182. total;
  183. if (typeof settings.progressTotal === undef) {
  184. if (files[index]) {
  185. total = files[index].size;
  186. settings.progressTotal = total ? total : 1;
  187. } else {
  188. total = 0;
  189. for (i = 0; i < files.length; i += 1) {
  190. total += files[i].size;
  191. }
  192. settings.progressTotal = total;
  193. }
  194. }
  195. return settings.progressTotal;
  196. },
  197. handleGlobalProgress = function (event, files, index, xhr, settings) {
  198. var progressEvent,
  199. loaderList,
  200. globalLoaded = 0,
  201. globalTotal = 0;
  202. if (event.lengthComputable && typeof settings.onProgressAll === func) {
  203. settings.progressLoaded = parseInt(
  204. event.loaded / event.total * getProgressTotal(files, index, settings),
  205. 10
  206. );
  207. loaderList = multiLoader.getList();
  208. $.each(loaderList, function (index, item) {
  209. // item is an array with [files, index, xhr, settings]
  210. globalLoaded += item[3].progressLoaded || 0;
  211. globalTotal += getProgressTotal(item[0], item[1], item[3]);
  212. });
  213. progressEvent = createProgressEvent(
  214. true,
  215. globalLoaded,
  216. globalTotal
  217. );
  218. settings.onProgressAll(progressEvent, loaderList);
  219. }
  220. },
  221. handleLoadEvent = function (event, files, index, xhr, settings) {
  222. var progressEvent;
  223. if (isChunkedUpload(settings)) {
  224. settings.uploadedBytes += settings.chunkSize;
  225. progressEvent = createProgressEvent(
  226. true,
  227. settings.uploadedBytes,
  228. files[index].size
  229. );
  230. if (typeof settings.onProgress === func) {
  231. settings.onProgress(progressEvent, files, index, xhr, settings);
  232. }
  233. handleGlobalProgress(progressEvent, files, index, xhr, settings);
  234. if (settings.uploadedBytes < files[index].size) {
  235. if (typeof settings.resumeUpload === func) {
  236. settings.resumeUpload(
  237. event,
  238. files,
  239. index,
  240. xhr,
  241. settings,
  242. function () {
  243. upload(event, files, index, xhr, settings, true);
  244. }
  245. );
  246. } else {
  247. upload(event, files, index, xhr, settings, true);
  248. }
  249. return;
  250. }
  251. }
  252. settings.progressLoaded = getProgressTotal(files, index, settings);
  253. if (typeof settings.onLoad === func) {
  254. settings.onLoad(event, files, index, xhr, settings);
  255. }
  256. completeNext();
  257. },
  258. handleProgressEvent = function (event, files, index, xhr, settings) {
  259. var progressEvent = event;
  260. if (isChunkedUpload(settings) && event.lengthComputable) {
  261. progressEvent = createProgressEvent(
  262. true,
  263. settings.uploadedBytes + parseInt(event.loaded / event.total * settings.chunkSize, 10),
  264. files[index].size
  265. );
  266. }
  267. if (typeof settings.onProgress === func) {
  268. settings.onProgress(progressEvent, files, index, xhr, settings);
  269. }
  270. handleGlobalProgress(progressEvent, files, index, xhr, settings);
  271. },
  272. initUploadEventHandlers = function (files, index, xhr, settings) {
  273. if (xhr.upload) {
  274. xhr.upload.onprogress = function (e) {
  275. handleProgressEvent(e, files, index, xhr, settings);
  276. };
  277. }
  278. xhr.onload = function (e) {
  279. handleLoadEvent(e, files, index, xhr, settings);
  280. };
  281. xhr.onabort = function (e) {
  282. settings.progressTotal = settings.progressLoaded;
  283. if (typeof settings.onAbort === func) {
  284. settings.onAbort(e, files, index, xhr, settings);
  285. }
  286. completeNext();
  287. };
  288. xhr.onerror = function (e) {
  289. settings.progressTotal = settings.progressLoaded;
  290. if (typeof settings.onError === func) {
  291. settings.onError(e, files, index, xhr, settings);
  292. }
  293. completeNext();
  294. };
  295. },
  296. getUrl = function (settings) {
  297. if (typeof settings.url === func) {
  298. return settings.url(settings.uploadForm || uploadForm);
  299. }
  300. return settings.url;
  301. },
  302. getMethod = function (settings) {
  303. if (typeof settings.method === func) {
  304. return settings.method(settings.uploadForm || uploadForm);
  305. }
  306. return settings.method;
  307. },
  308. getFieldName = function (settings) {
  309. if (typeof settings.fieldName === func) {
  310. return settings.fieldName(settings.fileInput || fileInput);
  311. }
  312. return settings.fieldName;
  313. },
  314. getFormData = function (settings) {
  315. var formData;
  316. if (typeof settings.formData === func) {
  317. return settings.formData(settings.uploadForm || uploadForm);
  318. } else if ($.isArray(settings.formData)) {
  319. return settings.formData;
  320. } else if (settings.formData) {
  321. formData = [];
  322. $.each(settings.formData, function (name, value) {
  323. formData.push({name: name, value: value});
  324. });
  325. return formData;
  326. }
  327. return [];
  328. },
  329. isSameDomain = function (url) {
  330. if (protocolRegExp.test(url)) {
  331. var host = location.host,
  332. indexStart = location.protocol.length + 2,
  333. index = url.indexOf(host, indexStart),
  334. pathIndex = index + host.length;
  335. if ((index === indexStart || index === url.indexOf('@', indexStart) + 1) &&
  336. (url.length === pathIndex || $.inArray(url.charAt(pathIndex), ['/', '?', '#']) !== -1)) {
  337. return true;
  338. }
  339. return false;
  340. }
  341. return true;
  342. },
  343. initUploadRequest = function (files, index, xhr, settings) {
  344. var file = files[index],
  345. url = getUrl(settings),
  346. sameDomain = isSameDomain(url);
  347. xhr.open(getMethod(settings), url, true);
  348. if (sameDomain) {
  349. xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
  350. if (!settings.multipart || isChunkedUpload(settings)) {
  351. xhr.setRequestHeader('X-File-Name', file.name);
  352. xhr.setRequestHeader('X-File-Type', file.type);
  353. xhr.setRequestHeader('X-File-Size', file.size);
  354. if (!isChunkedUpload(settings)) {
  355. xhr.setRequestHeader('Content-Type', file.type);
  356. } else if (!settings.multipart) {
  357. xhr.setRequestHeader('Content-Type', 'application/octet-stream');
  358. }
  359. }
  360. } else if (settings.withCredentials) {
  361. xhr.withCredentials = true;
  362. }
  363. if ($.isArray(settings.requestHeaders)) {
  364. $.each(settings.requestHeaders, function (index, header) {
  365. xhr.setRequestHeader(header.name, header.value);
  366. });
  367. } else if (settings.requestHeaders) {
  368. $.each(settings.requestHeaders, function (name, value) {
  369. xhr.setRequestHeader(name, value);
  370. });
  371. }
  372. },
  373. formDataUpload = function (files, xhr, settings) {
  374. var formData = new FormData(),
  375. i;
  376. $.each(getFormData(settings), function (index, field) {
  377. formData.append(field.name, field.value);
  378. });
  379. for (i = 0; i < files.length; i += 1) {
  380. formData.append(getFieldName(settings), files[i]);
  381. }
  382. xhr.send(formData);
  383. },
  384. loadFileContent = function (file, callBack) {
  385. file.reader = new FileReader();
  386. file.reader.onload = callBack;
  387. file.reader.readAsBinaryString(file);
  388. },
  389. utf8encode = function (str) {
  390. return unescape(encodeURIComponent(str));
  391. },
  392. buildMultiPartFormData = function (boundary, files, filesFieldName, fields) {
  393. var doubleDash = '--',
  394. crlf = '\r\n',
  395. formData = '',
  396. buffer = [];
  397. $.each(fields, function (index, field) {
  398. formData += doubleDash + boundary + crlf +
  399. 'Content-Disposition: form-data; name="' +
  400. utf8encode(field.name) +
  401. '"' + crlf + crlf +
  402. utf8encode(field.value) + crlf;
  403. });
  404. $.each(files, function (index, file) {
  405. formData += doubleDash + boundary + crlf +
  406. 'Content-Disposition: form-data; name="' +
  407. utf8encode(filesFieldName) +
  408. '"; filename="' + utf8encode(file.name) + '"' + crlf +
  409. 'Content-Type: ' + utf8encode(file.type) + crlf + crlf;
  410. buffer.push(formData);
  411. buffer.push(file.reader.result);
  412. delete file.reader;
  413. formData = crlf;
  414. });
  415. formData += doubleDash + boundary + doubleDash + crlf;
  416. buffer.push(formData);
  417. return buffer.join('');
  418. },
  419. fileReaderUpload = function (files, xhr, settings) {
  420. var boundary = '----MultiPartFormBoundary' + (new Date()).getTime(),
  421. loader,
  422. i;
  423. xhr.setRequestHeader('Content-Type', 'multipart/form-data; boundary=' + boundary);
  424. loader = new MultiLoader(function () {
  425. xhr.sendAsBinary(buildMultiPartFormData(
  426. boundary,
  427. files,
  428. getFieldName(settings),
  429. getFormData(settings)
  430. ));
  431. }, files.length);
  432. for (i = 0; i < files.length; i += 1) {
  433. loadFileContent(files[i], loader.complete);
  434. }
  435. },
  436. getBlob = function (file, settings) {
  437. var blob,
  438. ub = settings.uploadedBytes,
  439. mcs = settings.maxChunkSize;
  440. if (file && typeof file.slice === func && (ub || (mcs && mcs < file.size))) {
  441. settings.uploadedBytes = ub = ub || 0;
  442. blob = file.slice(ub, mcs || file.size - ub);
  443. settings.chunkSize = blob.size;
  444. return blob;
  445. }
  446. return file;
  447. },
  448. upload = function (event, files, index, xhr, settings, nextChunk) {
  449. var send;
  450. send = function () {
  451. if (!nextChunk) {
  452. if (typeof settings.onSend === func &&
  453. settings.onSend(event, files, index, xhr, settings) === false) {
  454. completeNext();
  455. return;
  456. }
  457. }
  458. var blob = getBlob(files[index], settings),
  459. filesToUpload;
  460. initUploadEventHandlers(files, index, xhr, settings);
  461. initUploadRequest(files, index, xhr, settings);
  462. if (!settings.multipart) {
  463. if (xhr.upload) {
  464. xhr.send(blob);
  465. } else {
  466. $.error('Browser does not support XHR file uploads');
  467. }
  468. } else {
  469. filesToUpload = (typeof index === 'number') ? [blob] : files;
  470. if (typeof FormData !== undef) {
  471. formDataUpload(filesToUpload, xhr, settings);
  472. } else if (typeof FileReader !== undef && typeof xhr.sendAsBinary === func) {
  473. fileReaderUpload(filesToUpload, xhr, settings);
  474. } else {
  475. $.error('Browser does not support multipart/form-data XHR file uploads');
  476. }
  477. }
  478. };
  479. if (!nextChunk) {
  480. multiLoader.push(Array.prototype.slice.call(arguments, 1));
  481. if (settings.sequentialUploads) {
  482. sequenceHandler.push(send);
  483. return;
  484. }
  485. }
  486. send();
  487. },
  488. handleUpload = function (event, files, input, form, index) {
  489. var xhr = new XMLHttpRequest(),
  490. uploadSettings = $.extend({}, settings);
  491. uploadSettings.fileInput = input;
  492. uploadSettings.uploadForm = form;
  493. if (typeof uploadSettings.initUpload === func) {
  494. uploadSettings.initUpload(
  495. event,
  496. files,
  497. index,
  498. xhr,
  499. uploadSettings,
  500. function () {
  501. upload(event, files, index, xhr, uploadSettings);
  502. }
  503. );
  504. } else {
  505. upload(event, files, index, xhr, uploadSettings);
  506. }
  507. },
  508. handleLegacyGlobalProgress = function (event, files, index, iframe, settings) {
  509. var total = 0,
  510. progressEvent;
  511. if (typeof index === undef) {
  512. $.each(files, function (index, file) {
  513. total += file.size ? file.size : 1;
  514. });
  515. } else {
  516. total = files[index].size ? files[index].size : 1;
  517. }
  518. progressEvent = createProgressEvent(true, total, total);
  519. settings.progressLoaded = total;
  520. handleGlobalProgress(progressEvent, files, index, iframe, settings);
  521. },
  522. legacyUploadFormDataInit = function (input, form, settings) {
  523. var formData = getFormData(settings);
  524. form.find(':input').not(':disabled')
  525. .attr('disabled', true)
  526. .addClass(settings.namespace + '_disabled');
  527. $.each(formData, function (index, field) {
  528. $('<input type="hidden"/>')
  529. .attr('name', field.name)
  530. .val(field.value)
  531. .addClass(settings.namespace + '_form_data')
  532. .appendTo(form);
  533. });
  534. input
  535. .attr('name', getFieldName(settings))
  536. .appendTo(form);
  537. },
  538. legacyUploadFormDataReset = function (input, form, settings) {
  539. input.detach();
  540. form.find('.' + settings.namespace + '_disabled')
  541. .removeAttr('disabled')
  542. .removeClass(settings.namespace + '_disabled');
  543. form.find('.' + settings.namespace + '_form_data').remove();
  544. },
  545. legacyUpload = function (event, files, input, form, iframe, settings, index) {
  546. var send;
  547. send = function () {
  548. if (typeof settings.onSend === func && settings.onSend(event, files, index, iframe, settings) === false) {
  549. completeNext();
  550. return;
  551. }
  552. var originalAttributes = {
  553. 'action': form.attr('action'),
  554. 'method': form.attr('method'),
  555. 'target': form.attr('target'),
  556. 'enctype': form.attr('enctype')
  557. };
  558. iframe
  559. .unbind('abort')
  560. .bind('abort', function (e) {
  561. iframe.readyState = 0;
  562. // javascript:false as iframe src prevents warning popups on HTTPS in IE6
  563. // concat is used here to prevent the "Script URL" JSLint error:
  564. iframe.unbind('load').attr('src', 'javascript'.concat(':false;'));
  565. handleLegacyGlobalProgress(e, files, index, iframe, settings);
  566. if (typeof settings.onAbort === func) {
  567. settings.onAbort(e, files, index, iframe, settings);
  568. }
  569. completeNext();
  570. })
  571. .unbind('load')
  572. .bind('load', function (e) {
  573. iframe.readyState = 4;
  574. handleLegacyGlobalProgress(e, files, index, iframe, settings);
  575. if (typeof settings.onLoad === func) {
  576. settings.onLoad(e, files, index, iframe, settings);
  577. }
  578. // Fix for IE endless progress bar activity bug
  579. // (happens on form submits to iframe targets):
  580. $('<iframe src="javascript:false;" style="display:none;"></iframe>')
  581. .appendTo(form).remove();
  582. completeNext();
  583. });
  584. form
  585. .attr('action', getUrl(settings))
  586. .attr('method', getMethod(settings))
  587. .attr('target', iframe.attr('name'))
  588. .attr('enctype', 'multipart/form-data');
  589. legacyUploadFormDataInit(input, form, settings);
  590. iframe.readyState = 2;
  591. form.get(0).submit();
  592. legacyUploadFormDataReset(input, form, settings);
  593. $.each(originalAttributes, function (name, value) {
  594. if (value) {
  595. form.attr(name, value);
  596. } else {
  597. form.removeAttr(name);
  598. }
  599. });
  600. };
  601. multiLoader.push([files, index, iframe, settings]);
  602. if (settings.sequentialUploads) {
  603. sequenceHandler.push(send);
  604. } else {
  605. send();
  606. }
  607. },
  608. normalizeFile = function (index, file) {
  609. if (typeof file.name === undef && typeof file.size === undef) {
  610. file.name = file.fileName;
  611. file.size = file.fileSize;
  612. }
  613. },
  614. handleLegacyUpload = function (event, input, form, index) {
  615. if (!(event && input && form)) {
  616. $.error('Iframe based File Upload requires a file input change event');
  617. return;
  618. }
  619. // javascript:false as iframe src prevents warning popups on HTTPS in IE6:
  620. var iframe = $('<iframe src="javascript:false;" style="display:none;" name="iframe_' +
  621. settings.namespace + '_' + (new Date()).getTime() + '"></iframe>'),
  622. uploadSettings = $.extend({}, settings),
  623. files = event.target && event.target.files;
  624. files = files ? Array.prototype.slice.call(files, 0) : [{name: input.val(), type: null, size: null}];
  625. $.each(files, normalizeFile);
  626. index = files.length === 1 ? 0 : index;
  627. uploadSettings.fileInput = input;
  628. uploadSettings.uploadForm = form;
  629. iframe.readyState = 0;
  630. iframe.abort = function () {
  631. iframe.trigger('abort');
  632. };
  633. iframe.bind('load', function () {
  634. iframe.unbind('load');
  635. if (typeof uploadSettings.initUpload === func) {
  636. uploadSettings.initUpload(
  637. event,
  638. files,
  639. index,
  640. iframe,
  641. uploadSettings,
  642. function () {
  643. legacyUpload(event, files, input, form, iframe, uploadSettings, index);
  644. }
  645. );
  646. } else {
  647. legacyUpload(event, files, input, form, iframe, uploadSettings, index);
  648. }
  649. }).appendTo(form);
  650. },
  651. canHandleXHRUploadSize = function (files) {
  652. var bytes = 0,
  653. totalBytes = 0,
  654. i;
  655. if (settings.multipart && typeof FormData === undef) {
  656. for (i = 0; i < files.length; i += 1) {
  657. bytes = files[i].size;
  658. if (bytes > settings.maxFileReaderSize) {
  659. return false;
  660. }
  661. totalBytes += bytes;
  662. }
  663. if (settings.multiFileRequest && totalBytes > settings.maxFileReaderSize) {
  664. return false;
  665. }
  666. }
  667. return true;
  668. },
  669. handleFiles = function (event, files, input, form) {
  670. if (!canHandleXHRUploadSize(files)) {
  671. handleLegacyUpload(event, input, form);
  672. return;
  673. }
  674. var i;
  675. files = Array.prototype.slice.call(files, 0);
  676. $.each(files, normalizeFile);
  677. if (settings.multiFileRequest && settings.multipart && files.length) {
  678. handleUpload(event, files, input, form);
  679. } else {
  680. for (i = 0; i < files.length; i += 1) {
  681. handleUpload(event, files, input, form, i);
  682. }
  683. }
  684. },
  685. initUploadForm = function () {
  686. uploadForm = (container.is('form') ? container : container.find('form'))
  687. .filter(settings.uploadFormFilter);
  688. },
  689. initFileInput = function () {
  690. fileInput = (uploadForm.length ? uploadForm : container).find('input:file')
  691. .filter(settings.fileInputFilter);
  692. },
  693. replaceFileInput = function (input) {
  694. var inputClone = input.clone(true);
  695. $('<form/>').append(inputClone).get(0).reset();
  696. input.after(inputClone).detach();
  697. initFileInput();
  698. };
  699. this.onDocumentDragOver = function (e) {
  700. if (typeof settings.onDocumentDragOver === func &&
  701. settings.onDocumentDragOver(e) === false) {
  702. return false;
  703. }
  704. e.preventDefault();
  705. };
  706. this.onDocumentDrop = function (e) {
  707. if (typeof settings.onDocumentDrop === func &&
  708. settings.onDocumentDrop(e) === false) {
  709. return false;
  710. }
  711. e.preventDefault();
  712. };
  713. this.onDragOver = function (e) {
  714. if (typeof settings.onDragOver === func &&
  715. settings.onDragOver(e) === false) {
  716. return false;
  717. }
  718. var dataTransfer = e.originalEvent.dataTransfer;
  719. if (dataTransfer && dataTransfer.files) {
  720. dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy';
  721. e.preventDefault();
  722. }
  723. };
  724. this.onDrop = function (e) {
  725. if (typeof settings.onDrop === func &&
  726. settings.onDrop(e) === false) {
  727. return false;
  728. }
  729. var dataTransfer = e.originalEvent.dataTransfer;
  730. if (dataTransfer && dataTransfer.files && isXHRUploadCapable()) {
  731. handleFiles(e, dataTransfer.files);
  732. }
  733. e.preventDefault();
  734. };
  735. this.onChange = function (e) {
  736. if (typeof settings.onChange === func &&
  737. settings.onChange(e) === false) {
  738. return false;
  739. }
  740. var input = $(e.target),
  741. form = $(e.target.form);
  742. if (form.length === 1) {
  743. if (settings.replaceFileInput) {
  744. input.data(defaultNamespace + '_form', form);
  745. replaceFileInput(input);
  746. }
  747. } else {
  748. form = input.data(defaultNamespace + '_form');
  749. }
  750. if (!settings.forceIframeUpload && e.target.files && isXHRUploadCapable()) {
  751. handleFiles(e, e.target.files, input, form);
  752. } else {
  753. handleLegacyUpload(e, input, form);
  754. }
  755. };
  756. this.init = function (options) {
  757. if (options) {
  758. $.extend(settings, options);
  759. optionsReference = options;
  760. }
  761. initUploadForm();
  762. initFileInput();
  763. if (container.data(settings.namespace)) {
  764. $.error('FileUpload with namespace "' + settings.namespace + '" already assigned to this element');
  765. return;
  766. }
  767. container
  768. .data(settings.namespace, fileUpload)
  769. .addClass(settings.cssClass);
  770. settings.dropZone.not(container).addClass(settings.cssClass);
  771. initEventHandlers();
  772. if (typeof settings.init === func) {
  773. settings.init();
  774. }
  775. };
  776. this.options = function (options) {
  777. var oldCssClass,
  778. oldDropZone,
  779. uploadFormFilterUpdate,
  780. fileInputFilterUpdate;
  781. if (typeof options === undef) {
  782. return $.extend({}, settings);
  783. }
  784. if (optionsReference) {
  785. $.extend(optionsReference, options);
  786. }
  787. removeEventHandlers();
  788. $.each(options, function (name, value) {
  789. switch (name) {
  790. case 'namespace':
  791. $.error('The FileUpload namespace cannot be updated.');
  792. return;
  793. case 'uploadFormFilter':
  794. uploadFormFilterUpdate = true;
  795. fileInputFilterUpdate = true;
  796. break;
  797. case 'fileInputFilter':
  798. fileInputFilterUpdate = true;
  799. break;
  800. case 'cssClass':
  801. oldCssClass = settings.cssClass;
  802. break;
  803. case 'dropZone':
  804. oldDropZone = settings.dropZone;
  805. break;
  806. }
  807. settings[name] = value;
  808. });
  809. if (uploadFormFilterUpdate) {
  810. initUploadForm();
  811. }
  812. if (fileInputFilterUpdate) {
  813. initFileInput();
  814. }
  815. if (typeof oldCssClass !== undef) {
  816. container
  817. .removeClass(oldCssClass)
  818. .addClass(settings.cssClass);
  819. (oldDropZone ? oldDropZone : settings.dropZone).not(container)
  820. .removeClass(oldCssClass);
  821. settings.dropZone.not(container).addClass(settings.cssClass);
  822. } else if (oldDropZone) {
  823. oldDropZone.not(container).removeClass(settings.cssClass);
  824. settings.dropZone.not(container).addClass(settings.cssClass);
  825. }
  826. initEventHandlers();
  827. };
  828. this.option = function (name, value) {
  829. var options;
  830. if (typeof value === undef) {
  831. return settings[name];
  832. }
  833. options = {};
  834. options[name] = value;
  835. fileUpload.options(options);
  836. };
  837. this.destroy = function () {
  838. if (typeof settings.destroy === func) {
  839. settings.destroy();
  840. }
  841. removeEventHandlers();
  842. container
  843. .removeData(settings.namespace)
  844. .removeClass(settings.cssClass);
  845. settings.dropZone.not(container).removeClass(settings.cssClass);
  846. };
  847. this.upload = function (files) {
  848. if (typeof files.length === undef) {
  849. files = [files];
  850. }
  851. handleFiles(null, files);
  852. };
  853. };
  854. methods = {
  855. init : function (options) {
  856. return this.each(function () {
  857. (new FileUpload($(this))).init(options);
  858. });
  859. },
  860. option: function (option, value, namespace) {
  861. namespace = namespace ? namespace : defaultNamespace;
  862. var fileUpload = $(this).data(namespace);
  863. if (fileUpload) {
  864. if (!option) {
  865. return fileUpload.options();
  866. } else if (typeof option === 'string' && typeof value === undef) {
  867. return fileUpload.option(option);
  868. }
  869. } else {
  870. $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
  871. }
  872. return this.each(function () {
  873. var fu = $(this).data(namespace);
  874. if (fu) {
  875. if (typeof option === 'string') {
  876. fu.option(option, value);
  877. } else {
  878. fu.options(option);
  879. }
  880. } else {
  881. $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
  882. }
  883. });
  884. },
  885. destroy: function (namespace) {
  886. namespace = namespace ? namespace : defaultNamespace;
  887. return this.each(function () {
  888. var fileUpload = $(this).data(namespace);
  889. if (fileUpload) {
  890. fileUpload.destroy();
  891. } else {
  892. $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
  893. }
  894. });
  895. },
  896. upload: function (files, namespace) {
  897. namespace = namespace ? namespace : defaultNamespace;
  898. return this.each(function () {
  899. var fileUpload = $(this).data(namespace);
  900. if (fileUpload) {
  901. fileUpload.upload(files);
  902. } else {
  903. $.error('No FileUpload with namespace "' + namespace + '" assigned to this element');
  904. }
  905. });
  906. }
  907. };
  908. $.fn.fileUpload = function (method) {
  909. if (methods[method]) {
  910. return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  911. } else if (typeof method === 'object' || !method) {
  912. return methods.init.apply(this, arguments);
  913. } else {
  914. $.error('Method "' + method + '" does not exist on jQuery.fileUpload');
  915. }
  916. };
  917. }(jQuery));