ssi-upload.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. /*
  2. * error:an teleiwsei to upload kai iparxei ena sti lista pou den exei patithei
  3. * */
  4. (function (root, factory) {
  5. //@author http://ifandelse.com/its-not-hard-making-your-library-support-amd-and-commonjs/
  6. if (typeof module === "object" && module.exports) {
  7. module.exports = factory(require("jquery"));
  8. } else {
  9. factory(root.jQuery);
  10. }
  11. }(this, function ($) {
  12. var Ssi_upload = function (element, options) {
  13. this.options = options;
  14. this.$element = '';
  15. this.language = locale[this.options.locale];
  16. this.uploadList = [];
  17. this.totalProgress = [];
  18. this.toUpload = [];
  19. this.imgNames = [];
  20. this.totalFilesLength = 0;
  21. this.successfulUpload = 0;
  22. this.aborted = 0;
  23. this.abortedWithError = 0;
  24. this.pending = 0;
  25. this.inProgress = 0;
  26. this.currentListLength = 0;
  27. this.result = '';
  28. this.init(element);
  29. };
  30. Ssi_upload.prototype.init = function (element) {
  31. $(element).addClass('ssi-uploadInput')
  32. .after(this.$element = $('<div class="ssi-uploader">'));
  33. var $chooseBtn = $('' +
  34. '<span class="ssi-InputLabel">' +
  35. '<button class="ssi-button success">' + this.language.chooseFiles + '</button>' +
  36. '</span>').append(element);
  37. var $uploadBtn = $('<button id="ssi-uploadBtn" class="ssi-button success ssi-hidden" >' +
  38. '<span class="ssi-btnIn">' + this.language.upload + '&nbsp;</span>' +
  39. '<div id="ssi-up_loading" class="ssi-btnIn"></div></button>');
  40. var $clearBtn = $('<button id="ssi-clearBtn" class="ssi-hidden ssi-button info" >' + this.language.clear +
  41. '</button>');
  42. var $abortBtn = $('<button id="ssi-abortBtn" class="ssi-button error ssi-cancelAll ssi-hidden" ><span class="inBtn">' + this.language.abort + ' </span></button>');
  43. this.$element.append($('<div class="ssi-buttonWrapper">').append($chooseBtn, $abortBtn, $uploadBtn, $clearBtn));
  44. var $uploadBox;
  45. if (!this.options.preview) {
  46. this.$element.addClass('ssi-uploaderNP');
  47. var $fileList = $('<table id="ssi-fileList" class="ssi-fileList"></table>');
  48. var $namePreview = $('<span class="ssi-namePreview"></span>');
  49. var $mainBox = $('<div id="ssi-uploadFiles" class="ssi-tooltip ssi-uploadFiles ' + (this.options.dropZone ? 'ssi-dropZone' : '') + '"><div id="ssi-uploadProgressNoPreview" class="ssi-uploadProgressNoPreview"></div></div>')
  50. .append($namePreview);
  51. var $uploadDetails = $('<div class="ssi-uploadDetails"></div>').append($fileList);
  52. $uploadBox = $('<div class="ssi-uploadBoxWrapper ssi-uploadBox"></div>').append($mainBox, $uploadDetails);
  53. this.$element.prepend($uploadBox);
  54. } else {
  55. $uploadBox = $('<div id="ssi-previewBox" class="ssi-uploadBox ssi-previewBox ' + (this.options.dropZone ? 'ssi-dropZonePreview ssi-dropZone"><div id="ssi-DropZoneBack">' + this.language.drag + '</div>' : '">') + '</div>');
  56. this.$element.append($uploadBox);
  57. }
  58. var thisS = this;
  59. var $input = $chooseBtn.find(".ssi-uploadInput");
  60. $chooseBtn.find('button').click(function () {
  61. $input.trigger('click');
  62. });
  63. $input.on('change', function () { //choose files
  64. thisS.toUploadFiles(this.files);
  65. $input.val('');
  66. });
  67. //drag n drop
  68. if (thisS.options.dropZone) {
  69. $uploadBox.on("drop", function (e) {
  70. e.preventDefault();
  71. $uploadBox.removeClass("ssi-dragOver");
  72. var files = e.originalEvent.dataTransfer.files;
  73. thisS.toUploadFiles(files);
  74. });
  75. $uploadBox.on("dragover", function (e) {
  76. e.preventDefault();
  77. $uploadBox.addClass("ssi-dragOver");
  78. return false;
  79. });
  80. $uploadBox.on("dragleave", function (e) {
  81. e.preventDefault();
  82. $uploadBox.removeClass("ssi-dragOver");
  83. return false;
  84. });
  85. }
  86. if (!thisS.options.preview) {
  87. $mainBox.click(function () {
  88. if (thisS.currentListLength > 1)
  89. $uploadDetails.toggleClass('ssi-uploadBoxOpened');
  90. })
  91. }
  92. $clearBtn.click(function () { //choose files completed and pending files
  93. upArr = new Array();
  94. thisS.clear();
  95. });
  96. var $tooltip;
  97. $uploadBox.on('mouseenter', '.ssi-statusLabel', function (e) { //the tooltip
  98. var $eventTarget = $(e.currentTarget);
  99. var title = $eventTarget.attr('data-status');
  100. if (!title || title === '') {
  101. return;
  102. }
  103. var tooltipHeight = '35';
  104. if ($eventTarget.hasClass('ssi-noPreviewSubMessage')) {
  105. tooltipHeight = 0;
  106. }
  107. $tooltip = $('<div class="ssi-infoTooltip">'
  108. + title +
  109. '</div>')
  110. .appendTo(thisS.$element)
  111. .css({ top: $eventTarget.position().top - tooltipHeight, left: $eventTarget.position().left - 5 })
  112. .fadeIn('slow');
  113. });
  114. $uploadBox.on('mouseleave', '.ssi-statusLabel', function () {
  115. if ($tooltip)
  116. $tooltip.remove();
  117. });
  118. $uploadBox.on('click', '.ssi-removeBtn', function (e) { //remove the file from list
  119. var $currentTarget = $(e.currentTarget);
  120. var index = $currentTarget.data('delete'); //get file's index
  121. thisS.pending--; //reduce pending number by 1
  122. thisS.currentListLength--; //reduce current list length by 1
  123. if (thisS.pending === 0) {
  124. $uploadBtn.prop('disabled', true); //if there is no more files disable upload button
  125. }
  126. if (thisS.options.preview) { //if preview is true
  127. $currentTarget.parents('table.ssi-imgToUploadTable').remove(); //remove table
  128. } else {
  129. var target = $currentTarget.parents('tr.ssi-toUploadTr'); //find the tr of file
  130. $namePreview.html((thisS.currentListLength) + ' files'); //set the main name to the remaining files
  131. target.prev().remove();// remove empty tr (using id for margin between rows)
  132. target.remove();// remove the file
  133. if (thisS.currentListLength === 1) { //if only one file left in the list
  134. setLastElementName(thisS); //set main preview to display the name
  135. }
  136. }
  137. thisS.toUpload[index] = null; //set the file's obj to null (we don't splice it because we need to keep the same indexes)
  138. thisS.imgNames[index] = null; //set the file's name to null
  139. thisS.toUpload.splice(index, 1);
  140. thisS.imgNames.splice(index, 1);
  141. if (thisS.currentListLength === 0) { // if no more files in the list
  142. if (!thisS.options.dropZone) { // if drag and drop is disabled
  143. $uploadBox.removeClass('ssi-uploadNoDropZone');
  144. }
  145. $clearBtn.addClass('ssi-hidden');
  146. $uploadBtn.addClass('ssi-hidden');
  147. }
  148. });
  149. $uploadBox.on('click', '.ssi-abortUpload', function (e) {//abort one element
  150. var $eventTarget = $(e.currentTarget);
  151. var index = $eventTarget.data('delete');// get the element id
  152. thisS.abort(index); // abort request
  153. });
  154. //----------------------------UPLOADFILES------------------------------------
  155. $uploadBtn.click(function () {// upload the files
  156. thisS.uploadFiles();
  157. });
  158. $abortBtn.click(function () { // abort all requests
  159. thisS.abortAll();
  160. });
  161. };
  162. Ssi_upload.prototype.abortAll = function () {
  163. for (var i = 0; i < this.uploadList.length; i++) { //all element in the list
  164. if (typeof this.uploadList[i] === 'object') {// check if not deleted
  165. this.abort(i)
  166. }
  167. }
  168. };
  169. Ssi_upload.prototype.toUploadFiles = function (files) {
  170. if (typeof this.options.maxNumberOfFiles === 'number') {
  171. if ((this.inProgress + this.pending) >= this.options.maxNumberOfFiles) {// if in progress files + pending files are more than the number that we have define as max number of files pre download
  172. return;//don't do anything
  173. }
  174. }
  175. var thisS = this,
  176. j = 0,
  177. length,
  178. imgContent = '',
  179. $uploadBtn = this.$element.find('#ssi-uploadBtn'),
  180. $clearBtn = this.$element.find('#ssi-clearBtn'),
  181. $fileList = this.$element.find('#ssi-fileList'),
  182. $uploadBox = this.$element.find('.ssi-uploadBox'),
  183. imgs = [];
  184. //if ((this.inProgress === 0 && this.pending === 0)) { //if no file are pending or are in progress
  185. // this.clear(); //clear the list
  186. //}
  187. var extErrors = [], sizeErrors = [], errorMessage = '';
  188. var toUploadLength, filesLength = length = toUploadLength = files.length;
  189. if (typeof this.options.maxNumberOfFiles === 'number') {//check if requested files agree with our arguments
  190. if (filesLength > this.options.maxNumberOfFiles - (this.inProgress + this.pending)) { //if requested files is more than we need
  191. filesLength = toUploadLength = this.options.maxNumberOfFiles - (this.inProgress + this.pending); // set variable to the number of files we need
  192. }
  193. }
  194. //
  195. for (var i = 0; i < filesLength; i++) {
  196. var file = files[i],
  197. ext = file.name.getExtension();// get file's extension
  198. if ($.inArray(ext, this.options.allowed) === -1) { // if requested file not allowed
  199. if (length > filesLength) {//there are more file we dont pick
  200. filesLength++;//the add 1 more loop
  201. } else {
  202. toUploadLength--;
  203. }
  204. if ($.inArray(ext, extErrors) === -1) {//if we see first time this extension
  205. extErrors.push(ext); //push it to extErrors variable
  206. }
  207. } else if ((file.size * Math.pow(10, -6)).toFixed(2) > this.options.maxFileSize) {//if file size is more than we ask
  208. if (length > filesLength) {
  209. filesLength++;
  210. } else {
  211. toUploadLength--;
  212. }
  213. sizeErrors.push(cutFileName(file.name, ext, 15));//register a size error
  214. } else if ($.inArray(file.name, this.imgNames) === -1) {// if the file is not already in the list
  215. $uploadBtn.prop("disabled", false);
  216. setupReader(file);
  217. this.pending++; // we have one more file that is pending to be uploaded
  218. this.currentListLength++;// we have one more file in the list
  219. } else {
  220. if (length > filesLength) {
  221. filesLength++;
  222. } else {
  223. toUploadLength--;
  224. }
  225. }
  226. }
  227. var extErrorsLength = extErrors.length, sizeErrorsLength = sizeErrors.length;
  228. if (extErrorsLength + sizeErrorsLength > 0) { // in the end expose all errors
  229. if (extErrorsLength > 0) {
  230. errorMessage = this.language.extError.replaceText(extErrors.toString().replace(/,/g, ', '));
  231. }
  232. if (sizeErrorsLength > 0) {
  233. errorMessage += this.language.sizeError.replaceText(sizeErrors.toString().replace(/,/g, ', '), this.options.maxFileSize + 'mb');
  234. }
  235. this.options.errorHandler.method(errorMessage, this.options.errorHandler.error);
  236. }
  237. function setupReader() {
  238. var index = thisS.totalFilesLength + thisS.pending;
  239. if (index === 0) {//do it only the first time
  240. if (thisS.options.preview) {
  241. if (!thisS.options.dropZone) {
  242. $uploadBox.addClass('ssi-uploadNoDropZone')
  243. }
  244. }
  245. $uploadBtn.removeClass('ssi-hidden');
  246. $clearBtn.removeClass('ssi-hidden');
  247. }
  248. $clearBtn.prop('disabled', false);
  249. thisS.toUpload[index] = file;
  250. var filename = file.name;
  251. var ext = filename.getExtension(); //get file's extension
  252. thisS.imgNames[index] = filename; //register file's name
  253. if (thisS.options.preview) {
  254. var getTemplate = function (content) {
  255. return '<table class="ssi-imgToUploadTable ssi-pending">' +
  256. '<tr><td class="ssi-upImgTd">' + content + '</td></tr>' +
  257. '<tr><td><div id="ssi-uploadProgress' + index + '" class="ssi-hidden ssi-uploadProgress"></div></td></tr>' +
  258. '<tr><td><button data-delete="' + index + '" class=" ssi-button error ssi-removeBtn"><span class="trash10 trash"></span></button></td></tr>' +
  259. '<tr><td>' + cutFileName(filename, ext, 15) + '</td></tr></table>'
  260. };
  261. var fileType = file.type.split('/');
  262. if (fileType[0] == 'image') {
  263. $uploadBtn.prop("disabled", true);
  264. $clearBtn.prop("disabled", true);
  265. var fileReader = new FileReader();
  266. fileReader.onload = function () {
  267. imgContent += getTemplate('<img class="ssi-imgToUpload" src=""/><i class="fa-spin fa fa-spinner fa-pulse"></i>'); // set the files element without the img
  268. imgs[index] = fileReader.result;
  269. j++;
  270. if (toUploadLength === j) {// if all elements are in place lets load images
  271. $uploadBox.append(imgContent);
  272. $uploadBtn.prop("disabled", false);
  273. $clearBtn.prop("disabled", false);
  274. setTimeout(function () {
  275. setImg();//and load the images
  276. }, 10);
  277. imgContent = '';
  278. toUploadLength = [];
  279. }
  280. };
  281. fileReader.readAsDataURL(file);
  282. } else {
  283. imgs[index] = null;
  284. $uploadBox.append(getTemplate('<div class="document-item" href="test.mov" filetype="' + ext + '"><span class = "fileCorner"></span></div>'));
  285. j++;
  286. }
  287. } else {
  288. thisS.$element.find('.ssi-namePreview').html((index === 0 ? cutFileName(filename, ext, 13) : (thisS.currentListLength + 1) + ' ' + thisS.language.files));//set name preview
  289. $fileList.append('<tr class="ssi-space"><td></td></tr>' +//append files element to dom
  290. '<tr class="ssi-toUploadTr ssi-pending"><td><div id="ssi-uploadProgress' + index + '" class="ssi-hidden ssi-uploadProgress ssi-uploadProgressNoPre"></div>' +
  291. '<span>' + cutFileName(filename, ext, 20) + '</span></td>' +
  292. '<td><a data-delete="' + index + '" class="ssi-button ssi-removeBtn ssi-removeBtnNP"><span class="trash7 trash"></span></a></td></tr>');
  293. }
  294. var setImg = function () {//load the images
  295. for (var i = 0; i < imgs.length; i++) {
  296. if (imgs[i] !== null) {
  297. $uploadBox.find("#ssi-uploadProgress" + i).parents('table.ssi-imgToUploadTable')
  298. .find('.ssi-imgToUpload')
  299. .attr('src', imgs[i]) //set src of the image
  300. .next().remove();//remove the spinner
  301. imgs[i] = null;
  302. }
  303. }
  304. imgs = [];
  305. };
  306. }
  307. };
  308. var clearCompleted = function (thisS) {//clear all completed files
  309. var $completed = thisS.$element.find('.ssi-completed');
  310. thisS.successfulUpload = 0;
  311. thisS.aborted = 0;
  312. thisS.abortedWithError = 0;
  313. if (!thisS.options.preview) $completed.prev('tr').remove();
  314. $completed.remove();
  315. };
  316. var clearPending = function (thisS) {//clear all pending files.
  317. var $pending = thisS.$element.find('.ssi-pending');
  318. var length = thisS.imgNames.length;
  319. for (var i = 0; i < length; i++) {
  320. if (thisS.imgNames[i] === null) {
  321. thisS.toUpload.splice(i, 1);
  322. thisS.imgNames.splice(i, 1);
  323. }
  324. }
  325. thisS.toUpload.splice(-thisS.pending, thisS.pending);
  326. thisS.imgNames.splice(-thisS.pending, thisS.pending);
  327. thisS.pending = 0;
  328. if (!thisS.options.preview) $pending.prev('tr').remove();
  329. $pending.remove();
  330. };
  331. Ssi_upload.prototype.clear = function (action) {//clean the list of all non in progress files
  332. switch (action) {
  333. case 'pending':
  334. clearPending(this);
  335. break;
  336. case 'completed':
  337. clearCompleted(this);
  338. break;
  339. default:
  340. clearPending(this);
  341. clearCompleted(this);
  342. }
  343. var $uploadBtn = this.$element.find('#ssi-uploadBtn'),
  344. $clearBtn = this.$element.find('#ssi-clearBtn');
  345. this.currentListLength = getCurrentListLength(this);
  346. if (this.inProgress === 0) { //if no file are uploading right now
  347. this.totalProgress = [];
  348. }
  349. if ((this.currentListLength === 0)) { // if no items in the list
  350. $clearBtn.addClass('ssi-hidden');
  351. $uploadBtn.addClass('ssi-hidden');
  352. this.$element.find('#ssi-fileNumber').addClass('ssi-hidden');
  353. this.totalFilesLength = 0;
  354. if (!this.options.dropZone) {
  355. this.$element.find('.ssi-uploadBox').removeClass('ssi-uploadNoDropZone')
  356. }
  357. }
  358. $clearBtn.prop('disabled', true);
  359. $uploadBtn.prop('disabled', true);
  360. if (!this.options.preview) {
  361. setNamePreview(this);
  362. }
  363. };
  364. var setNamePreview = function (thisS) {//set the name preview according to the remaining elements in the list
  365. if (thisS.currentListLength > 1) {//if more than one element left
  366. thisS.$element.find('.ssi-namePreview').html(thisS.currentListLength + ' files'); // set the name preview to the length of the remaining items
  367. } else if (thisS.currentListLength === 1) {//if one left
  368. setLastElementName(thisS); // set the name of the element
  369. } else { //if no elements left
  370. thisS.$element.find('.ssi-uploadDetails').removeClass('ssi-uploadBoxOpened');
  371. thisS.$element.find('#ssi-fileList').empty();//empty list
  372. thisS.$element.find('.ssi-namePreview').empty();//empty the name preview
  373. }
  374. };
  375. Ssi_upload.prototype.uploadFiles = function () {// upload the pending files
  376. if (this.toUpload.length <1) {
  377. alert("没有新的图片需要上传!");
  378. return;
  379. }
  380. if (!confirm("确认上传?")) return;
  381. if (this.toUpload.length > 0) {
  382. this.$element.find('#ssi-abortBtn').removeClass('ssi-hidden');
  383. this.$element.find('.ssi-removeBtn')
  384. .addClass('ssi-abortUpload')
  385. .removeClass('ssi-removeBtn')
  386. .children('span').removeClass('trash7 trash10 trash')
  387. .addClass((this.options.preview ? 'ban7w' : 'ban7'));//transform remove button to abort button
  388. var $uploadBtn = this.$element.find('#ssi-uploadBtn'),
  389. $clearBtn = this.$element.find('#ssi-clearBtn');
  390. $uploadBtn.prop("disabled", true);
  391. var thisS = this,
  392. formData = new FormData(),//set the form data
  393. i = this.totalFilesLength;
  394. if (this.totalFilesLength !== 0 && !this.options.preview) {
  395. setNamePreview(this);
  396. }
  397. this.inProgress += this.pending;// add pending to in progress
  398. this.totalFilesLength += this.pending;// this variable is to prevent id duplication during uploads
  399. //this.pending = 0;
  400. if (this.inProgress === this.currentListLength) {// disable the clear button if no items in list we can be remove
  401. $clearBtn.prop("disabled", true);
  402. }
  403. while (this.toUpload[i] === null) { // do it until you find a file
  404. i++;
  405. }
  406. formData.append('files[]', thisS.toUpload[i]);//append the first file to the form data
  407. $.each(this.options.data, function (key, value) {// append all extra data
  408. formData.append(key, value);
  409. });
  410. if (typeof this.options.beforeUpload === 'function') {
  411. try {
  412. this.options.beforeUpload();// execute the beforeUpload callback
  413. } catch (err) {
  414. console.log('There is an error in beforeUpload callback');
  415. console.log(err);
  416. thisS.abortAll();
  417. return;
  418. }
  419. }
  420. thisS.$element.find('input.ssi-uploadInput').trigger('beforeUpload.ssi-uploader');
  421. ajaxLoopRequest(formData, i);// make the request
  422. }
  423. //--------------start of ajax request-----------------------
  424. function ajaxLoopRequest(formData, ii) {
  425. var selector = 'table.ssi-imgToUploadTable';
  426. if (!thisS.options.preview) {
  427. selector = 'tr.ssi-toUploadTr'
  428. }
  429. var uploadBar = thisS.$element.find('#ssi-uploadProgress' + ii);//get the file's progress bar
  430. uploadBar.removeClass('ssi-hidden') //make it visible
  431. .parents(selector).removeClass('ssi-pending');
  432. var ajaxOptions = $.extend({}, {//store the request to the uploadList variable
  433. xhr: function () {
  434. var xhr = new window.XMLHttpRequest();
  435. xhr.upload.addEventListener('progress', function (e) {// add event listener to progress
  436. if (e.lengthComputable) {
  437. var percentComplete = (e.loaded / e.total) * 100;// calculate the progress
  438. if (uploadBar) {
  439. uploadBar.css({
  440. width: percentComplete + '%'//update the progress bar width according to the progress
  441. });
  442. }
  443. thisS.totalProgress[ii] = percentComplete;//store the progress to the array
  444. var sum = arraySum(thisS.totalProgress) / (thisS.inProgress + thisS.successfulUpload);//and calculate the overall progress
  445. if (!thisS.options.preview) {
  446. thisS.$element.find('#ssi-uploadProgressNoPreview')
  447. .removeClass('ssi-hidden')
  448. .css({
  449. width: sum + '%'
  450. });
  451. }
  452. $uploadBtn.find('#ssi-up_loading').html(Math.ceil(sum) + '%');// add to upload button the current overall progress percent number
  453. }
  454. }, false);
  455. return xhr;
  456. },
  457. async: true,
  458. beforeSend: function (xhr) {
  459. thisS.uploadList[ii] = xhr;
  460. $uploadBtn.find('#ssi-up_loading') //add spiner to uploadbutton
  461. .html('<i class="fa fa-spinner fa-pulse"></i>');
  462. if (typeof thisS.options.beforeEachUpload === 'function') {
  463. try {
  464. var msg = thisS.options.beforeEachUpload({// execute the beforeEachUpload callback and save the returned value
  465. name: thisS.toUpload[ii].name,//send some info of the file
  466. type: thisS.toUpload[ii].type,
  467. size: (thisS.toUpload[ii].size / 1024).toFixed(2)
  468. }, xhr);
  469. } catch (err) {
  470. console.log('There is an error in beforeEachUpload callback');
  471. console.log(err);
  472. thisS.abortAll();
  473. return;
  474. }
  475. }
  476. thisS.$element.find('input.ssi-uploadInput').trigger('beforeEachUpload.ssi-uploader');
  477. if (xhr.status === 0) {
  478. if (xhr.statusText === 'canceled') {//if user used beforeEachUpload to abort the request
  479. if (typeof msg === 'undefined') {//if no message
  480. msg = false; //because user have already aborted the request set to false or anything else except undefined to prevent to abort it again
  481. }
  482. thisS.abortedWithError++;// we have one error more
  483. thisS.abort(ii, msg);//call the abort function
  484. }
  485. }
  486. },
  487. type: 'POST',
  488. data: formData,
  489. cache: false,
  490. contentType: false,
  491. processData: false,
  492. url: thisS.options.url+"&idx="+ii,
  493. error: function (request, error) {
  494. if (error !== 'abort') {
  495. uploadBar.addClass('ssi-canceledProgressBar');
  496. var msg = thisS.language.error;
  497. thisS.abortedWithError++;//add one more error
  498. thisS.totalProgress.splice(ii, 1); //remove from progress array
  499. if (!thisS.options.preview) {
  500. msg = '<span class="exclamation7"></span>';
  501. }
  502. setElementMessage(thisS, ii, 'error', msg, thisS.language.serverError);
  503. thisS.totalProgress[ii] = '';
  504. thisS.inProgress--;
  505. $clearBtn.prop("disabled", false);
  506. if (typeof thisS.options.onEachUpload === 'function') {//execute the onEachUpload callback
  507. try {
  508. thisS.options.onEachUpload({//and return some info
  509. uploadStatus: 'error',
  510. name: thisS.toUpload[ii].name,
  511. size: (thisS.toUpload[ii].size / 1024).toFixed(2),
  512. type: thisS.toUpload[ii].type
  513. });
  514. } catch (err) {
  515. console.log('There is an error in onEachUpload callback');
  516. console.log(err);
  517. }
  518. }
  519. if (getCompleteStatus(thisS)) {//if no more elements in progress
  520. finishUpload(thisS);
  521. }
  522. console.log(arguments);//log the error
  523. console.log(" Ajax error: " + error);
  524. }
  525. }
  526. }, thisS.options.ajaxOptions);
  527. $.ajax(ajaxOptions).done(function (responseData, textStatus, jqXHR) {
  528. var msg, title = '', dataType = 'error', spanClass = 'exclamation', data;
  529. try {
  530. data = $.parseJSON(responseData);
  531. } catch (err) {
  532. data = responseData;
  533. }
  534. thisS.result = data;
  535. if (thisS.options.responseValidation) {
  536. var valData = thisS.options.responseValidation;
  537. if (typeof valData.validationKey === 'object' && valData.resultKey == 'validationKey') {
  538. if (data.hasOwnProperty(valData.validationKey.success)) {
  539. cb(true);
  540. } else {
  541. cb(false, data[valData.validationKey.error]);
  542. }
  543. } else {
  544. if (data[valData.validationKey] == valData.success) {
  545. cb(true);
  546. } else {
  547. cb(false, data[valData.resultKey]);
  548. }
  549. }
  550. } else {
  551. if (jqXHR.status == 200) {
  552. cb(true, data);
  553. } else {
  554. cb(false, data);
  555. }
  556. }
  557. function cb(result, data) {
  558. if (result) {//if response type is success
  559. //if(result.index("|"))
  560. if (data.res == "1") {
  561. dataType = 'success';
  562. msg = thisS.language.success;
  563. spanClass = 'check';
  564. title = data.msg;
  565. thisS.successfulUpload++;
  566. } else {
  567. uploadBar.addClass('ssi-canceledProgressBar');
  568. msg = "上传失败";
  569. title = data.msg;
  570. thisS.abortedWithError++;
  571. }
  572. //dataType = 'success';
  573. //msg = thisS.language.success;
  574. //spanClass = 'check';
  575. //thisS.successfulUpload++;// one more successful upload
  576. } else {
  577. uploadBar.addClass('ssi-canceledProgressBar');
  578. if (thisS.options.preview) {
  579. msg = thisS.language.error;
  580. }
  581. title = data;
  582. thisS.abortedWithError++;
  583. }
  584. }
  585. if (!thisS.options.preview) {
  586. msg = '<span class="' + spanClass + '7"></span>';
  587. }
  588. setElementMessage(thisS, ii, dataType, msg, title);
  589. if (typeof thisS.options.onEachUpload === 'function') {//execute the onEachUpload callback
  590. try {
  591. thisS.options.onEachUpload({//and return some info
  592. uploadStatus: dataType,
  593. idx:ii,
  594. name: thisS.toUpload[ii].name,
  595. size: (thisS.toUpload[ii].size / 1024).toFixed(2),
  596. type: thisS.toUpload[ii].type,
  597. result:thisS.result
  598. });
  599. } catch (err) {
  600. console.log('There is an error in onEachUpload callback');
  601. console.log(err);
  602. }
  603. }
  604. thisS.$element.find('input.ssi-uploadInput').trigger('onEachUpload.ssi-uploader');
  605. thisS.inProgress--;//one less in progress upload
  606. $clearBtn.prop("disabled", false);
  607. if (getCompleteStatus(thisS)) {//if no more files in progress
  608. finishUpload(thisS);
  609. }
  610. // thisS.totalProgress[ii]='';
  611. //thisS.uploadList[ii] = '';
  612. //thisS.toUpload[ii] = '';
  613. //thisS.imgNames[ii] = '';
  614. });
  615. //--------------end of ajax request-----------------------
  616. i = ii;
  617. i++;//go to the next element
  618. while (thisS.toUpload[i] === null) {// do it until you find a file
  619. i++;
  620. }
  621. if (i < thisS.toUpload.length) {// if more files exist start the next request
  622. formData = new FormData();
  623. $.each(thisS.options.data, function (key, value) {
  624. formData.append(key, value);
  625. });
  626. formData.append('files[]', thisS.toUpload[i]);
  627. ajaxLoopRequest(formData, i);
  628. }
  629. }
  630. };
  631. var setElementMessage = function (thisS, index, msgType, msg, title) {
  632. var className = '', elementSelector = 'table.ssi-imgToUploadTable', element;
  633. if (!thisS.options.preview) {
  634. className = 'ssi-noPreviewSubMessage';
  635. elementSelector = 'tr.ssi-toUploadTr';
  636. if (thisS.currentListLength === 1) {
  637. thisS.errors = title;
  638. }
  639. }
  640. element = thisS.$element.find(".ssi-abortUpload[data-delete='" + index + "']");
  641. element.parents(elementSelector).addClass('ssi-completed');
  642. element.after(getResultMessage(msgType, msg, title, className))
  643. .remove();
  644. };
  645. var getCompleteStatus = function (thisS) {//check if file are in progress
  646. return (thisS.inProgress === 0);
  647. };
  648. var getResultMessage = function (type, msg, title, classes) {//return a message label
  649. return '<span class="ssi-statusLabel ' + classes + ' ' + type + '" data-status="' + title + '">' + msg + '</span>';
  650. };
  651. var getCurrentListLength = function (thisS) { //get the list length
  652. return (thisS.inProgress + thisS.successfulUpload + thisS.aborted + thisS.abortedWithError + thisS.pending);
  653. };
  654. var setLastElementName = function (thisS) { //if one file in list get the last file's name and put it to the name preview
  655. var fileName = thisS.$element.find('#ssi-fileList').find('span').html();//find the only span left
  656. if (fileName != undefined) {
  657. var ext = fileName.getExtension();//get the extension
  658. thisS.$element.find('.ssi-uploadDetails').removeClass('ssi-uploadBoxOpened');
  659. thisS.$element.find('.ssi-namePreview').html(cutFileName(fileName, ext, 15));//short the name and put it to the name preview
  660. }
  661. };
  662. Ssi_upload.prototype.abort = function (index, title) {//abort a request
  663. if (typeof title === 'undefined') {//if no title
  664. this.uploadList[index].abort();// abort the element
  665. this.totalProgress[index] = '';
  666. title = 'Aborted';
  667. this.aborted++;// one more aborted file
  668. } else if (typeof title !== 'string') {//if not string that means that the request aborted with the beforeUpload callback and no message returned
  669. title = '';
  670. }
  671. //nothing of the above happened that means the user aborted the request with the beforeUpload callback and returned a message
  672. var msg = this.language.aborted;
  673. if (!this.options.preview) {
  674. msg = '<span class="ban7w"></span>';
  675. }
  676. setElementMessage(this, index, 'error', msg, title);
  677. if (this.$element.find('#ssi-uploadProgress' + index).find("ssi-hidden").length > 0)
  678. {
  679. this.$element.find('#ssi-uploadProgress' + index).find("ssi-hidden").removeClass('ssi-hidden').addClass('ssi-canceledProgressBar');
  680. }
  681. //this.$element.find('#ssi-uploadProgress' + index).removeClass('ssi-hidden').addClass('ssi-canceledProgressBar');
  682. this.toUpload[index] = undefined;
  683. this.uploadList[index] = undefined;
  684. this.imgNames[index] = undefined;
  685. this.$element.find('#ssi-clearBtn').prop("disabled", false);
  686. this.inProgress--;//one less in progress file
  687. if (getCompleteStatus(this)) {//if no more file in progress
  688. finishUpload(this);
  689. }
  690. };
  691. var finishUpload = function (thisS) {//when every uplaod ends
  692. thisS.$element.find('#ssi-abortBtn').addClass('ssi-hidden');
  693. if (!thisS.options.preview) {//display tha main message in the name preview
  694. var type = 'error', title = '', msg = '';
  695. if (thisS.abortedWithError > 0) { //if no errors
  696. if (thisS.totalFilesLength === 1) {// if only one file in the list
  697. title = thisS.errors; //display the error
  698. } else {//else display something more general message
  699. title = thisS.language.someErrorsOccurred
  700. }
  701. msg = '<span class="exclamation23"></span>';
  702. } else if (thisS.aborted > 0 && thisS.successfulUpload === 0) {//if all request aborted
  703. msg = '<span class="ban23"></span>';
  704. title = thisS.language.aborted;
  705. } else if (thisS.successfulUpload > 0) {// all request were successfull
  706. type = 'success';
  707. msg = '<span class="check23"></span>';
  708. title = thisS.language.sucUpload;
  709. }
  710. thisS.$element.find('.ssi-namePreview').append(getResultMessage(type, msg, title, 'ssi-noPreviewMessage'));//show the message in the name preview
  711. thisS.$element.find('#ssi-uploadProgressNoPreview') //remove main overall progress bar
  712. .removeAttr('styles')
  713. .addClass('ssi-hidden');
  714. }
  715. if (typeof thisS.options.onUpload === 'function') {
  716. try {
  717. thisS.options.onUpload(thisS);//execute the on Upload callback
  718. } catch (err) {
  719. console.log('There is an error in onUpload callback');
  720. console.log(err);
  721. }
  722. }
  723. thisS.$element.find('input.ssi-uploadInput').trigger('onUpload.ssi-uploader');
  724. var $uploadBtn = thisS.$element.find('#ssi-uploadBtn');
  725. thisS.$element.find('#ssi-clearBtn').prop("disabled", false);
  726. $uploadBtn.prop("disabled", false)
  727. .find('#ssi-up_loading')
  728. .empty();
  729. if (thisS.pending === 0) {
  730. $uploadBtn.addClass('ssi-hidden');
  731. thisS.toUpload = [];
  732. thisS.imgNames = [];
  733. thisS.totalFilesLength = 0;
  734. }
  735. thisS.toUpload = [];
  736. thisS.imgNames = [];
  737. thisS.totalFilesLength = 0;
  738. thisS.uploadList = [];
  739. thisS.totalProgress = [];
  740. thisS.currentListLength = getCurrentListLength(thisS);
  741. //thisS.successfulUpload = 0;
  742. thisS.aborted = 0;
  743. thisS.abortedWithError = 0;
  744. //thisS.inProgress = 0;
  745. //alert(thisS.successfulUpload);
  746. };
  747. $.fn.ssi_uploader = function (opts) {
  748. var defaults = {
  749. url: '',
  750. data: {},
  751. locale: 'en',
  752. preview: true,
  753. dropZone: true,
  754. maxNumberOfFiles: '',
  755. responseValidation: false,
  756. maxFileSize: 2,
  757. ajaxOptions: {},
  758. onUpload: function () {
  759. },
  760. onEachUpload: function () {
  761. },
  762. beforeUpload: function () {
  763. },
  764. beforeEachUpload: function () {
  765. },
  766. allowed: ['jpg', 'jpeg', 'png', 'bmp','pdf', 'gif'],
  767. errorHandler: {
  768. method: function (msg) {
  769. alert(msg);
  770. },
  771. success: 'success',
  772. error: 'error'
  773. }
  774. };
  775. var options = $.extend(true, defaults, opts);
  776. return this.each(function () {
  777. var $element = $(this);
  778. if ($element.is('input[type="file"]')) {
  779. if ($element.data('ssi_upload')) return;
  780. var ssi_upload = new Ssi_upload(this, options);
  781. $element.data('ssi_upload', ssi_upload);
  782. } else {
  783. console.log('The targeted element is not file input.')
  784. }
  785. });
  786. };
  787. //functions
  788. String.prototype.replaceText = function () {//replace $ with variables
  789. var args = Array.apply(null, arguments);
  790. var text = this;
  791. for (var i = 0; i < args.length; i++) {
  792. text = text.replace('$' + (i + 1), args[i])
  793. }
  794. return text;
  795. };
  796. String.prototype.getExtension = function () {//returns the extension of a path or file
  797. return this.split('.').pop().toLowerCase();
  798. };
  799. var cutFileName = function (word, ext, maxLength) {//shorten the name
  800. if (typeof ext === 'undefined') ext = '';
  801. if (typeof maxLength === 'undefined') maxLength = 10;
  802. var min = 4;
  803. if (maxLength < min) return;
  804. var extLength = ext.length;
  805. var wordLength = word.length;
  806. if ((wordLength - 2) > maxLength) {
  807. word = word.substring(0, maxLength);
  808. var wl = word.length - extLength;
  809. word = word.substring(0, wl);
  810. return word + '...' + ext;
  811. } else return word;
  812. };
  813. var arraySum = function (arr) {//return the sum of an array
  814. var sum = 0;
  815. for (var i = 0; i < arr.length; i++) {
  816. if (typeof arr[i] === 'number')
  817. sum += arr[i];
  818. }
  819. return sum;
  820. };
  821. var locale = {
  822. en: {
  823. success: '成功',
  824. sucUpload: '上传成功',
  825. chooseFiles: '选择图片',
  826. uploadFailed: '上传失败',
  827. serverError: '内部服务器错误',
  828. error: '错误',
  829. abort: '中止',
  830. aborted: '失败',
  831. files: '文件',
  832. upload: '上传',
  833. clear: '清除',
  834. drag: '选择图片时按住ctrl键可以多选;也可以拖动图片到当前位置,选中后要点击\"上传\"(最多上传6张,单张不超过5MB)',
  835. sizeError: '$1 图片的大小不能超过 $2',// $1=file name ,$2=max ie( example.jpg has has exceed the size limit of 2mb)
  836. extError: '$1 扩展名的文件不可以上传',//$1=file extension ie(exe files are not supported)
  837. someErrorsOccurred: '发生错误!'
  838. }
  839. };
  840. }));