PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/js/vendor/jquery.filedrop.js

https://gitlab.com/craftbynick/Circular
JavaScript | 484 lines | 359 code | 79 blank | 46 comment | 43 complexity | 1d5d64b95ca9ccfc04fa83adf269ec57 MD5 | raw file
  1. /*global jQuery:false, alert:false */
  2. /*
  3. * Default text - jQuery plugin for html5 dragging files from desktop to browser
  4. *
  5. * Author: Weixi Yen
  6. *
  7. * Email: [Firstname][Lastname]@gmail.com
  8. *
  9. * Copyright (c) 2010 Resopollution
  10. *
  11. * Licensed under the MIT license:
  12. * http://www.opensource.org/licenses/mit-license.php
  13. *
  14. * Project home:
  15. * http://www.github.com/weixiyen/jquery-filedrop
  16. *
  17. * Version: 0.1.0
  18. *
  19. * Features:
  20. * Allows sending of extra parameters with file.
  21. * Works with Firefox 3.6+
  22. * Future-compliant with HTML5 spec (will work with Webkit browsers and IE9)
  23. * Usage:
  24. * See README at project homepage
  25. *
  26. */
  27. ;(function($) {
  28. jQuery.event.props.push("dataTransfer");
  29. var default_opts = {
  30. fallback_id: '',
  31. url: '',
  32. refresh: 1000,
  33. paramname: 'userfile',
  34. allowedfiletypes:[],
  35. maxfiles: 25, // Ignored if queuefiles is set > 0
  36. maxfilesize: 1, // MB file size limit
  37. queuefiles: 0, // Max files before queueing (for large volume uploads)
  38. queuewait: 200, // Queue wait time if full
  39. data: {},
  40. headers: {},
  41. drop: empty,
  42. dragStart: empty,
  43. dragEnter: empty,
  44. dragOver: empty,
  45. dragLeave: empty,
  46. docEnter: empty,
  47. docOver: empty,
  48. docLeave: empty,
  49. beforeEach: empty,
  50. afterAll: empty,
  51. rename: empty,
  52. error: function(err, file, i) {
  53. alert(err);
  54. },
  55. uploadStarted: empty,
  56. uploadFinished: empty,
  57. progressUpdated: empty,
  58. globalProgressUpdated: empty,
  59. speedUpdated: empty
  60. },
  61. errors = ["BrowserNotSupported", "TooManyFiles", "FileTooLarge", "FileTypeNotAllowed"],
  62. doc_leave_timer, stop_loop = false,
  63. files_count = 0,
  64. files;
  65. $.fn.filedrop = function(options) {
  66. var opts = $.extend({}, default_opts, options),
  67. global_progress = [];
  68. this.on('drop', drop).on('dragstart', opts.dragStart).on('dragenter', dragEnter).on('dragover', dragOver).on('dragleave', dragLeave);
  69. $(document).on('drop', docDrop).on('dragenter', docEnter).on('dragover', docOver).on('dragleave', docLeave);
  70. $('#' + opts.fallback_id).change(function(e) {
  71. opts.drop(e);
  72. files = e.target.files;
  73. files_count = files.length;
  74. upload();
  75. });
  76. function drop(e) {
  77. opts.drop.call(this, e);
  78. files = e.dataTransfer.files;
  79. if (files === null || files === undefined || files.length === 0) {
  80. opts.error(errors[0]);
  81. return false;
  82. }
  83. files_count = files.length;
  84. upload();
  85. e.preventDefault();
  86. return false;
  87. }
  88. function getBuilder(filename, filedata, mime, boundary) {
  89. var dashdash = '--',
  90. crlf = '\r\n',
  91. builder = '';
  92. if (opts.data) {
  93. var params = $.param(opts.data).split(/&/);
  94. $.each(params, function() {
  95. var pair = this.split("=", 2),
  96. name = decodeURIComponent(pair[0]),
  97. val = decodeURIComponent(pair[1]);
  98. builder += dashdash;
  99. builder += boundary;
  100. builder += crlf;
  101. builder += 'Content-Disposition: form-data; name="' + name + '"';
  102. builder += crlf;
  103. builder += crlf;
  104. builder += val;
  105. builder += crlf;
  106. });
  107. }
  108. builder += dashdash;
  109. builder += boundary;
  110. builder += crlf;
  111. builder += 'Content-Disposition: form-data; name="' + opts.paramname + '"';
  112. builder += '; filename="' + filename + '"';
  113. builder += crlf;
  114. builder += 'Content-Type: ' + mime;
  115. builder += crlf;
  116. builder += crlf;
  117. builder += filedata;
  118. builder += crlf;
  119. builder += dashdash;
  120. builder += boundary;
  121. builder += dashdash;
  122. builder += crlf;
  123. return builder;
  124. }
  125. function progress(e) {
  126. if (e.lengthComputable) {
  127. var percentage = Math.round((e.loaded * 100) / e.total);
  128. if (this.currentProgress !== percentage) {
  129. this.currentProgress = percentage;
  130. opts.progressUpdated(this.index, this.file, this.currentProgress);
  131. global_progress[this.global_progress_index] = this.currentProgress;
  132. globalProgress();
  133. var elapsed = new Date().getTime();
  134. var diffTime = elapsed - this.currentStart;
  135. if (diffTime >= opts.refresh) {
  136. var diffData = e.loaded - this.startData;
  137. var speed = diffData / diffTime; // KB per second
  138. opts.speedUpdated(this.index, this.file, speed);
  139. this.startData = e.loaded;
  140. this.currentStart = elapsed;
  141. }
  142. }
  143. }
  144. }
  145. function globalProgress() {
  146. if (global_progress.length === 0) {
  147. return;
  148. }
  149. var total = 0, index;
  150. for (index in global_progress) {
  151. if(global_progress.hasOwnProperty(index)) {
  152. total = total + global_progress[index];
  153. }
  154. }
  155. opts.globalProgressUpdated(Math.round(total / global_progress.length));
  156. }
  157. // Respond to an upload
  158. function upload() {
  159. stop_loop = false;
  160. if (!files) {
  161. opts.error(errors[0]);
  162. return false;
  163. }
  164. if (opts.allowedfiletypes.push && opts.allowedfiletypes.length) {
  165. for(var fileIndex = files.length;fileIndex--;) {
  166. if(!files[fileIndex].type || $.inArray(files[fileIndex].type, opts.allowedfiletypes) < 0) {
  167. opts.error(errors[3], files[fileIndex]);
  168. return false;
  169. }
  170. }
  171. }
  172. var filesDone = 0,
  173. filesRejected = 0;
  174. if (files_count > opts.maxfiles && opts.queuefiles === 0) {
  175. opts.error(errors[1]);
  176. return false;
  177. }
  178. // Define queues to manage upload process
  179. var workQueue = [];
  180. var processingQueue = [];
  181. var doneQueue = [];
  182. // Add everything to the workQueue
  183. for (var i = 0; i < files_count; i++) {
  184. workQueue.push(i);
  185. }
  186. // Helper function to enable pause of processing to wait
  187. // for in process queue to complete
  188. var pause = function(timeout) {
  189. setTimeout(process, timeout);
  190. return;
  191. };
  192. // Process an upload, recursive
  193. var process = function() {
  194. var fileIndex;
  195. if (stop_loop) {
  196. return false;
  197. }
  198. // Check to see if are in queue mode
  199. if (opts.queuefiles > 0 && processingQueue.length >= opts.queuefiles) {
  200. return pause(opts.queuewait);
  201. } else {
  202. // Take first thing off work queue
  203. fileIndex = workQueue[0];
  204. workQueue.splice(0, 1);
  205. // Add to processing queue
  206. processingQueue.push(fileIndex);
  207. }
  208. try {
  209. if (beforeEach(files[fileIndex]) !== false) {
  210. if (fileIndex === files_count) {
  211. return;
  212. }
  213. var reader = new FileReader(),
  214. max_file_size = 1048576 * opts.maxfilesize;
  215. reader.index = fileIndex;
  216. if (files[fileIndex].size > max_file_size) {
  217. opts.error(errors[2], files[fileIndex], fileIndex);
  218. // Remove from queue
  219. processingQueue.forEach(function(value, key) {
  220. if (value === fileIndex) {
  221. processingQueue.splice(key, 1);
  222. }
  223. });
  224. filesRejected++;
  225. return true;
  226. }
  227. reader.onloadend = !opts.beforeSend ? send : function (e) {
  228. opts.beforeSend(files[fileIndex], fileIndex, function () { send(e); });
  229. };
  230. reader.readAsBinaryString(files[fileIndex]);
  231. } else {
  232. filesRejected++;
  233. }
  234. } catch (err) {
  235. // Remove from queue
  236. processingQueue.forEach(function(value, key) {
  237. if (value === fileIndex) {
  238. processingQueue.splice(key, 1);
  239. }
  240. });
  241. opts.error(errors[0]);
  242. return false;
  243. }
  244. // If we still have work to do,
  245. if (workQueue.length > 0) {
  246. process();
  247. }
  248. };
  249. var send = function(e) {
  250. var fileIndex = ((typeof(e.srcElement) === "undefined") ? e.target : e.srcElement).index;
  251. // Sometimes the index is not attached to the
  252. // event object. Find it by size. Hack for sure.
  253. if (e.target.index === undefined) {
  254. e.target.index = getIndexBySize(e.total);
  255. }
  256. var xhr = new XMLHttpRequest(),
  257. upload = xhr.upload,
  258. file = files[e.target.index],
  259. index = e.target.index,
  260. start_time = new Date().getTime(),
  261. boundary = '------multipartformboundary' + (new Date()).getTime(),
  262. global_progress_index = global_progress.length,
  263. builder,
  264. newName = rename(file.name),
  265. mime = file.type;
  266. if (opts.withCredentials) {
  267. xhr.withCredentials = opts.withCredentials;
  268. }
  269. if (typeof newName === "string") {
  270. builder = getBuilder(newName, e.target.result, mime, boundary);
  271. } else {
  272. builder = getBuilder(file.name, e.target.result, mime, boundary);
  273. }
  274. upload.index = index;
  275. upload.file = file;
  276. upload.downloadStartTime = start_time;
  277. upload.currentStart = start_time;
  278. upload.currentProgress = 0;
  279. upload.global_progress_index = global_progress_index;
  280. upload.startData = 0;
  281. upload.addEventListener("progress", progress, false);
  282. xhr.open("POST", opts.url, true);
  283. xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary);
  284. // Add headers
  285. $.each(opts.headers, function(k, v) {
  286. xhr.setRequestHeader(k, v);
  287. });
  288. xhr.sendAsBinary(builder);
  289. global_progress[global_progress_index] = 0;
  290. globalProgress();
  291. opts.uploadStarted(index, file, files_count);
  292. xhr.onload = function() {
  293. var serverResponse = null;
  294. if (xhr.responseText) {
  295. try {
  296. serverResponse = jQuery.parseJSON(xhr.responseText);
  297. }
  298. catch (e) {
  299. serverResponse = xhr.responseText;
  300. }
  301. }
  302. var now = new Date().getTime(),
  303. timeDiff = now - start_time,
  304. result = opts.uploadFinished(index, file, serverResponse, timeDiff, xhr);
  305. filesDone++;
  306. // Remove from processing queue
  307. processingQueue.forEach(function(value, key) {
  308. if (value === fileIndex) {
  309. processingQueue.splice(key, 1);
  310. }
  311. });
  312. // Add to donequeue
  313. doneQueue.push(fileIndex);
  314. // Make sure the global progress is updated
  315. global_progress[global_progress_index] = 100;
  316. globalProgress();
  317. if (filesDone === (files_count - filesRejected)) {
  318. afterAll();
  319. }
  320. if (result === false) {
  321. stop_loop = true;
  322. }
  323. // Pass any errors to the error option
  324. if (xhr.status < 200 && xhr.status > 299) {
  325. opts.error(xhr.statusText);
  326. }
  327. };
  328. };
  329. // Initiate the processing loop
  330. process();
  331. }
  332. function getIndexBySize(size) {
  333. for (var i = 0; i < files_count; i++) {
  334. if (files[i].size === size) {
  335. return i;
  336. }
  337. }
  338. return undefined;
  339. }
  340. function rename(name) {
  341. return opts.rename(name);
  342. }
  343. function beforeEach(file) {
  344. return opts.beforeEach(file);
  345. }
  346. function afterAll() {
  347. return opts.afterAll();
  348. }
  349. function dragEnter(e) {
  350. clearTimeout(doc_leave_timer);
  351. e.preventDefault();
  352. opts.dragEnter.call(this, e);
  353. }
  354. function dragOver(e) {
  355. clearTimeout(doc_leave_timer);
  356. e.preventDefault();
  357. opts.docOver.call(this, e);
  358. opts.dragOver.call(this, e);
  359. }
  360. function dragLeave(e) {
  361. clearTimeout(doc_leave_timer);
  362. opts.dragLeave.call(this, e);
  363. e.stopPropagation();
  364. }
  365. function docDrop(e) {
  366. e.preventDefault();
  367. opts.docLeave.call(this, e);
  368. return false;
  369. }
  370. function docEnter(e) {
  371. clearTimeout(doc_leave_timer);
  372. e.preventDefault();
  373. opts.docEnter.call(this, e);
  374. return false;
  375. }
  376. function docOver(e) {
  377. clearTimeout(doc_leave_timer);
  378. e.preventDefault();
  379. opts.docOver.call(this, e);
  380. return false;
  381. }
  382. function docLeave(e) {
  383. doc_leave_timer = setTimeout((function(_this) {
  384. return function() {
  385. opts.docLeave.call(_this, e);
  386. };
  387. })(this), 200);
  388. }
  389. return this;
  390. };
  391. function empty() {}
  392. try {
  393. if (XMLHttpRequest.prototype.sendAsBinary) {
  394. return;
  395. }
  396. XMLHttpRequest.prototype.sendAsBinary = function(datastr) {
  397. function byteValue(x) {
  398. return x.charCodeAt(0) & 0xff;
  399. }
  400. var ords = Array.prototype.map.call(datastr, byteValue);
  401. var ui8a = new Uint8Array(ords);
  402. this.send(ui8a.buffer);
  403. };
  404. } catch (e) {}
  405. })(jQuery);