ssi-upload.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883
  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. if (this.successfulUpload > 0) {
  341. if (!confirm("确认清除己上传的图片?")) return;
  342. else {
  343. clearCompleted(this);
  344. }
  345. } else {
  346. clearPending(this);
  347. clearCompleted(this);
  348. }
  349. break;
  350. }
  351. }
  352. var $uploadBtn = this.$element.find('#ssi-uploadBtn'),
  353. $clearBtn = this.$element.find('#ssi-clearBtn');
  354. this.currentListLength = getCurrentListLength(this);
  355. if (this.inProgress === 0) { //if no file are uploading right now
  356. this.totalProgress = [];
  357. }
  358. if ((this.currentListLength === 0)) { // if no items in the list
  359. $clearBtn.addClass('ssi-hidden');
  360. $uploadBtn.addClass('ssi-hidden');
  361. this.totalFilesLength = 0;
  362. if (!this.options.dropZone) {
  363. this.$element.find('.ssi-uploadBox').removeClass('ssi-uploadNoDropZone')
  364. }
  365. }
  366. $clearBtn.prop('disabled', true);
  367. $uploadBtn.prop('disabled', true);
  368. if (!this.options.preview) {
  369. setNamePreview(this);
  370. }
  371. };
  372. var setNamePreview = function (thisS) {//set the name preview according to the remaining elements in the list
  373. if (thisS.currentListLength > 1) {//if more than one element left
  374. thisS.$element.find('.ssi-namePreview').html(thisS.currentListLength + ' files'); // set the name preview to the length of the remaining items
  375. } else if (thisS.currentListLength === 1) {//if one left
  376. setLastElementName(thisS); // set the name of the element
  377. } else { //if no elements left
  378. thisS.$element.find('.ssi-uploadDetails').removeClass('ssi-uploadBoxOpened');
  379. thisS.$element.find('#ssi-fileList').empty();//empty list
  380. thisS.$element.find('.ssi-namePreview').empty();//empty the name preview
  381. }
  382. };
  383. Ssi_upload.prototype.uploadFiles = function () {// upload the pending files
  384. if (this.toUpload.length <1) {
  385. alert("没有新的图片需要上传!");
  386. return;
  387. }
  388. //if (!confirm("确认上传?")) return;
  389. if (this.toUpload.length > 0) {
  390. this.$element.find('#ssi-abortBtn').removeClass('ssi-hidden');
  391. this.$element.find('.ssi-removeBtn')
  392. .addClass('ssi-abortUpload')
  393. .removeClass('ssi-removeBtn')
  394. .children('span').removeClass('trash7 trash10 trash')
  395. .addClass((this.options.preview ? 'ban7w' : 'ban7'));//transform remove button to abort button
  396. var $uploadBtn = this.$element.find('#ssi-uploadBtn'),
  397. $clearBtn = this.$element.find('#ssi-clearBtn');
  398. $uploadBtn.prop("disabled", true);
  399. var thisS = this,
  400. formData = new FormData(),//set the form data
  401. i = this.totalFilesLength;
  402. if (this.totalFilesLength !== 0 && !this.options.preview) {
  403. setNamePreview(this);
  404. }
  405. this.inProgress += this.pending;// add pending to in progress
  406. this.totalFilesLength += this.pending;// this variable is to prevent id duplication during uploads
  407. //this.pending = 0;
  408. if (this.inProgress === this.currentListLength) {// disable the clear button if no items in list we can be remove
  409. $clearBtn.prop("disabled", true);
  410. }
  411. while (this.toUpload[i] === null) { // do it until you find a file
  412. i++;
  413. }
  414. formData.append('files[]', thisS.toUpload[i]);//append the first file to the form data
  415. $.each(this.options.data, function (key, value) {// append all extra data
  416. formData.append(key, value);
  417. });
  418. if (typeof this.options.beforeUpload === 'function') {
  419. try {
  420. this.options.beforeUpload();// execute the beforeUpload callback
  421. } catch (err) {
  422. console.log('There is an error in beforeUpload callback');
  423. console.log(err);
  424. thisS.abortAll();
  425. return;
  426. }
  427. }
  428. thisS.$element.find('input.ssi-uploadInput').trigger('beforeUpload.ssi-uploader');
  429. ajaxLoopRequest(formData, i);// make the request
  430. }
  431. //--------------start of ajax request-----------------------
  432. function ajaxLoopRequest(formData, ii) {
  433. var selector = 'table.ssi-imgToUploadTable';
  434. if (!thisS.options.preview) {
  435. selector = 'tr.ssi-toUploadTr'
  436. }
  437. var uploadBar = thisS.$element.find('#ssi-uploadProgress' + ii);//get the file's progress bar
  438. uploadBar.removeClass('ssi-hidden') //make it visible
  439. .parents(selector).removeClass('ssi-pending');
  440. var ajaxOptions = $.extend({}, {//store the request to the uploadList variable
  441. xhr: function () {
  442. var xhr = new window.XMLHttpRequest();
  443. xhr.upload.addEventListener('progress', function (e) {// add event listener to progress
  444. if (e.lengthComputable) {
  445. var percentComplete = (e.loaded / e.total) * 100;// calculate the progress
  446. if (uploadBar) {
  447. uploadBar.css({
  448. width: percentComplete + '%'//update the progress bar width according to the progress
  449. });
  450. }
  451. thisS.totalProgress[ii] = percentComplete;//store the progress to the array
  452. var sum = arraySum(thisS.totalProgress) / (thisS.inProgress + thisS.successfulUpload);//and calculate the overall progress
  453. if (!thisS.options.preview) {
  454. thisS.$element.find('#ssi-uploadProgressNoPreview')
  455. .removeClass('ssi-hidden')
  456. .css({
  457. width: sum + '%'
  458. });
  459. }
  460. $uploadBtn.find('#ssi-up_loading').html(Math.ceil(sum) + '%');// add to upload button the current overall progress percent number
  461. }
  462. }, false);
  463. return xhr;
  464. },
  465. async: true,
  466. beforeSend: function (xhr) {
  467. thisS.uploadList[ii] = xhr;
  468. $uploadBtn.find('#ssi-up_loading') //add spiner to uploadbutton
  469. .html('<i class="fa fa-spinner fa-pulse"></i>');
  470. if (typeof thisS.options.beforeEachUpload === 'function') {
  471. try {
  472. var msg = thisS.options.beforeEachUpload({// execute the beforeEachUpload callback and save the returned value
  473. name: thisS.toUpload[ii].name,//send some info of the file
  474. type: thisS.toUpload[ii].type,
  475. size: (thisS.toUpload[ii].size / 1024).toFixed(2)
  476. }, xhr);
  477. } catch (err) {
  478. console.log('There is an error in beforeEachUpload callback');
  479. console.log(err);
  480. thisS.abortAll();
  481. return;
  482. }
  483. }
  484. thisS.$element.find('input.ssi-uploadInput').trigger('beforeEachUpload.ssi-uploader');
  485. if (xhr.status === 0) {
  486. if (xhr.statusText === 'canceled') {//if user used beforeEachUpload to abort the request
  487. if (typeof msg === 'undefined') {//if no message
  488. msg = false; //because user have already aborted the request set to false or anything else except undefined to prevent to abort it again
  489. }
  490. thisS.abortedWithError++;// we have one error more
  491. thisS.abort(ii, msg);//call the abort function
  492. }
  493. }
  494. },
  495. type: 'POST',
  496. data: formData,
  497. cache: false,
  498. contentType: false,
  499. processData: false,
  500. url: thisS.options.url+"?idx="+ii,
  501. error: function (request, error) {
  502. if (error !== 'abort') {
  503. uploadBar.addClass('ssi-canceledProgressBar');
  504. var msg = thisS.language.error;
  505. thisS.abortedWithError++;//add one more error
  506. thisS.totalProgress.splice(ii, 1); //remove from progress array
  507. if (!thisS.options.preview) {
  508. msg = '<span class="exclamation7"></span>';
  509. }
  510. setElementMessage(thisS, ii, 'error', msg, thisS.language.serverError);
  511. thisS.totalProgress[ii] = '';
  512. thisS.inProgress--;
  513. $clearBtn.prop("disabled", false);
  514. if (typeof thisS.options.onEachUpload === 'function') {//execute the onEachUpload callback
  515. try {
  516. thisS.options.onEachUpload({//and return some info
  517. uploadStatus: 'error',
  518. name: thisS.toUpload[ii].name,
  519. size: (thisS.toUpload[ii].size / 1024).toFixed(2),
  520. type: thisS.toUpload[ii].type
  521. });
  522. } catch (err) {
  523. console.log('There is an error in onEachUpload callback');
  524. console.log(err);
  525. }
  526. }
  527. if (getCompleteStatus(thisS)) {//if no more elements in progress
  528. finishUpload(thisS);
  529. }
  530. console.log(arguments);//log the error
  531. console.log(" Ajax error: " + error);
  532. }
  533. }
  534. }, thisS.options.ajaxOptions);
  535. $.ajax(ajaxOptions).done(function (responseData, textStatus, jqXHR) {
  536. var msg, title = '', dataType = 'error', spanClass = 'exclamation', data;
  537. try {
  538. data = $.parseJSON(responseData);
  539. } catch (err) {
  540. data = responseData;
  541. }
  542. thisS.result = data;
  543. if (thisS.options.responseValidation) {
  544. var valData = thisS.options.responseValidation;
  545. if (typeof valData.validationKey === 'object' && valData.resultKey == 'validationKey') {
  546. if (data.hasOwnProperty(valData.validationKey.success)) {
  547. cb(true);
  548. } else {
  549. cb(false, data[valData.validationKey.error]);
  550. }
  551. } else {
  552. if (data[valData.validationKey] == valData.success) {
  553. cb(true);
  554. } else {
  555. cb(false, data[valData.resultKey]);
  556. }
  557. }
  558. } else {
  559. if (jqXHR.status == 200) {
  560. cb(true, data);
  561. } else {
  562. cb(false, data);
  563. }
  564. }
  565. function cb(result, data) {
  566. if (result) {//if response type is success
  567. //if(result.index("|"))
  568. if (data.res == "1") {
  569. dataType = 'success';
  570. msg = thisS.language.success;
  571. spanClass = 'check';
  572. title = data.msg;
  573. thisS.successfulUpload++;
  574. } else {
  575. uploadBar.addClass('ssi-canceledProgressBar');
  576. msg = "上传失败";
  577. title = data.msg;
  578. thisS.abortedWithError++;
  579. }
  580. //dataType = 'success';
  581. //msg = thisS.language.success;
  582. //spanClass = 'check';
  583. //thisS.successfulUpload++;// one more successful upload
  584. } else {
  585. uploadBar.addClass('ssi-canceledProgressBar');
  586. if (thisS.options.preview) {
  587. msg = thisS.language.error;
  588. }
  589. title = data;
  590. thisS.abortedWithError++;
  591. }
  592. }
  593. if (!thisS.options.preview) {
  594. msg = '<span class="' + spanClass + '7"></span>';
  595. }
  596. setElementMessage(thisS, ii, dataType, msg, title);
  597. if (typeof thisS.options.onEachUpload === 'function') {//execute the onEachUpload callback
  598. try {
  599. thisS.options.onEachUpload({//and return some info
  600. uploadStatus: dataType,
  601. idx:ii,
  602. name: thisS.toUpload[ii].name,
  603. size: (thisS.toUpload[ii].size / 1024).toFixed(2),
  604. type: thisS.toUpload[ii].type,
  605. result:thisS.result
  606. });
  607. } catch (err) {
  608. console.log('There is an error in onEachUpload callback');
  609. console.log(err);
  610. }
  611. }
  612. thisS.$element.find('input.ssi-uploadInput').trigger('onEachUpload.ssi-uploader');
  613. thisS.inProgress--;//one less in progress upload
  614. $clearBtn.prop("disabled", false);
  615. if (getCompleteStatus(thisS)) {//if no more files in progress
  616. finishUpload(thisS);
  617. }
  618. // thisS.totalProgress[ii]='';
  619. //thisS.uploadList[ii] = '';
  620. //thisS.toUpload[ii] = '';
  621. //thisS.imgNames[ii] = '';
  622. });
  623. //--------------end of ajax request-----------------------
  624. i = ii;
  625. i++;//go to the next element
  626. while (thisS.toUpload[i] === null) {// do it until you find a file
  627. i++;
  628. }
  629. if (i < thisS.toUpload.length) {// if more files exist start the next request
  630. formData = new FormData();
  631. $.each(thisS.options.data, function (key, value) {
  632. formData.append(key, value);
  633. });
  634. formData.append('files[]', thisS.toUpload[i]);
  635. ajaxLoopRequest(formData, i);
  636. }
  637. }
  638. };
  639. var setElementMessage = function (thisS, index, msgType, msg, title) {
  640. var className = '', elementSelector = 'table.ssi-imgToUploadTable', element;
  641. if (!thisS.options.preview) {
  642. className = 'ssi-noPreviewSubMessage';
  643. elementSelector = 'tr.ssi-toUploadTr';
  644. if (thisS.currentListLength === 1) {
  645. thisS.errors = title;
  646. }
  647. }
  648. element = thisS.$element.find(".ssi-abortUpload[data-delete='" + index + "']");
  649. element.parents(elementSelector).addClass('ssi-completed');
  650. element.after(getResultMessage(msgType, msg, title, className))
  651. .remove();
  652. };
  653. var getCompleteStatus = function (thisS) {//check if file are in progress
  654. return (thisS.inProgress === 0);
  655. };
  656. var getResultMessage = function (type, msg, title, classes) {//return a message label
  657. return '<span class="ssi-statusLabel ' + classes + ' ' + type + '" data-status="' + title + '">' + msg + '</span>';
  658. };
  659. var getCurrentListLength = function (thisS) { //get the list length
  660. return (thisS.inProgress + thisS.successfulUpload + thisS.aborted + thisS.abortedWithError + thisS.pending);
  661. };
  662. var setLastElementName = function (thisS) { //if one file in list get the last file's name and put it to the name preview
  663. var fileName = thisS.$element.find('#ssi-fileList').find('span').html();//find the only span left
  664. var ext = fileName.getExtension();//get the extension
  665. thisS.$element.find('.ssi-uploadDetails').removeClass('ssi-uploadBoxOpened');
  666. thisS.$element.find('.ssi-namePreview').html(cutFileName(fileName, ext, 15));//short the name and put it to the name preview
  667. };
  668. Ssi_upload.prototype.abort = function (index, title) {//abort a request
  669. if (typeof title === 'undefined') {//if no title
  670. this.uploadList[index].abort();// abort the element
  671. this.totalProgress[index] = '';
  672. title = 'Aborted';
  673. this.aborted++;// one more aborted file
  674. } else if (typeof title !== 'string') {//if not string that means that the request aborted with the beforeUpload callback and no message returned
  675. title = '';
  676. }
  677. //nothing of the above happened that means the user aborted the request with the beforeUpload callback and returned a message
  678. var msg = this.language.aborted;
  679. if (!this.options.preview) {
  680. msg = '<span class="ban7w"></span>';
  681. }
  682. setElementMessage(this, index, 'error', msg, title);
  683. if (this.$element.find('#ssi-uploadProgress' + index).find("ssi-hidden").length > 0)
  684. {
  685. this.$element.find('#ssi-uploadProgress' + index).find("ssi-hidden").removeClass('ssi-hidden').addClass('ssi-canceledProgressBar');
  686. }
  687. //this.$element.find('#ssi-uploadProgress' + index).removeClass('ssi-hidden').addClass('ssi-canceledProgressBar');
  688. this.toUpload[index] = undefined;
  689. this.uploadList[index] = undefined;
  690. this.imgNames[index] = undefined;
  691. this.$element.find('#ssi-clearBtn').prop("disabled", false);
  692. this.inProgress--;//one less in progress file
  693. if (getCompleteStatus(this)) {//if no more file in progress
  694. finishUpload(this);
  695. }
  696. };
  697. var finishUpload = function (thisS) {//when every uplaod ends
  698. thisS.$element.find('#ssi-abortBtn').addClass('ssi-hidden');
  699. if (!thisS.options.preview) {//display tha main message in the name preview
  700. var type = 'error', title = '', msg = '';
  701. if (thisS.abortedWithError > 0) { //if no errors
  702. if (thisS.totalFilesLength === 1) {// if only one file in the list
  703. title = thisS.errors; //display the error
  704. } else {//else display something more general message
  705. title = thisS.language.someErrorsOccurred
  706. }
  707. msg = '<span class="exclamation23"></span>';
  708. } else if (thisS.aborted > 0 && thisS.successfulUpload === 0) {//if all request aborted
  709. msg = '<span class="ban23"></span>';
  710. title = thisS.language.aborted;
  711. } else if (thisS.successfulUpload > 0) {// all request were successfull
  712. type = 'success';
  713. msg = '<span class="check23"></span>';
  714. title = thisS.language.sucUpload;
  715. }
  716. thisS.$element.find('.ssi-namePreview').append(getResultMessage(type, msg, title, 'ssi-noPreviewMessage'));//show the message in the name preview
  717. thisS.$element.find('#ssi-uploadProgressNoPreview') //remove main overall progress bar
  718. .removeAttr('styles')
  719. .addClass('ssi-hidden');
  720. }
  721. if (typeof thisS.options.onUpload === 'function') {
  722. try {
  723. thisS.options.onUpload(thisS);//execute the on Upload callback
  724. } catch (err) {
  725. console.log('There is an error in onUpload callback');
  726. console.log(err);
  727. }
  728. }
  729. thisS.$element.find('input.ssi-uploadInput').trigger('onUpload.ssi-uploader');
  730. var $uploadBtn = thisS.$element.find('#ssi-uploadBtn');
  731. thisS.$element.find('#ssi-clearBtn').prop("disabled", false);
  732. $uploadBtn.prop("disabled", false)
  733. .find('#ssi-up_loading')
  734. .empty();
  735. if (thisS.pending === 0) {
  736. $uploadBtn.addClass('ssi-hidden');
  737. thisS.toUpload = [];
  738. thisS.imgNames = [];
  739. thisS.totalFilesLength = 0;
  740. }
  741. thisS.toUpload = [];
  742. thisS.imgNames = [];
  743. thisS.totalFilesLength = 0;
  744. thisS.uploadList = [];
  745. thisS.totalProgress = [];
  746. thisS.currentListLength = getCurrentListLength(thisS);
  747. //thisS.successfulUpload = 0;
  748. thisS.aborted = 0;
  749. thisS.abortedWithError = 0;
  750. //thisS.inProgress = 0;
  751. //alert(thisS.successfulUpload);
  752. };
  753. $.fn.ssi_uploader = function (opts) {
  754. var defaults = {
  755. url: '',
  756. data: {},
  757. locale: 'en',
  758. preview: true,
  759. dropZone: true,
  760. maxNumberOfFiles: '',
  761. responseValidation: false,
  762. maxFileSize: 2,
  763. ajaxOptions: {},
  764. onUpload: function () {
  765. },
  766. onEachUpload: function () {
  767. },
  768. beforeUpload: function () {
  769. },
  770. beforeEachUpload: function () {
  771. },
  772. allowed: ['jpg', 'jpeg', 'png', 'bmp', 'gif'],
  773. errorHandler: {
  774. method: function (msg) {
  775. alert(msg);
  776. },
  777. success: 'success',
  778. error: 'error'
  779. }
  780. };
  781. var options = $.extend(true, defaults, opts);
  782. return this.each(function () {
  783. var $element = $(this);
  784. if ($element.is('input[type="file"]')) {
  785. if ($element.data('ssi_upload')) return;
  786. var ssi_upload = new Ssi_upload(this, options);
  787. $element.data('ssi_upload', ssi_upload);
  788. } else {
  789. console.log('The targeted element is not file input.')
  790. }
  791. });
  792. };
  793. //functions
  794. String.prototype.replaceText = function () {//replace $ with variables
  795. var args = Array.apply(null, arguments);
  796. var text = this;
  797. for (var i = 0; i < args.length; i++) {
  798. text = text.replace('$' + (i + 1), args[i])
  799. }
  800. return text;
  801. };
  802. String.prototype.getExtension = function () {//returns the extension of a path or file
  803. return this.split('.').pop().toLowerCase();
  804. };
  805. var cutFileName = function (word, ext, maxLength) {//shorten the name
  806. if (typeof ext === 'undefined') ext = '';
  807. if (typeof maxLength === 'undefined') maxLength = 10;
  808. var min = 4;
  809. if (maxLength < min) return;
  810. var extLength = ext.length;
  811. var wordLength = word.length;
  812. if ((wordLength - 2) > maxLength) {
  813. word = word.substring(0, maxLength);
  814. var wl = word.length - extLength;
  815. word = word.substring(0, wl);
  816. return word + '...' + ext;
  817. } else return word;
  818. };
  819. var arraySum = function (arr) {//return the sum of an array
  820. var sum = 0;
  821. for (var i = 0; i < arr.length; i++) {
  822. if (typeof arr[i] === 'number')
  823. sum += arr[i];
  824. }
  825. return sum;
  826. };
  827. var locale = {
  828. en: {
  829. success: '成功',
  830. sucUpload: '上传成功',
  831. chooseFiles: '选择图片',
  832. uploadFailed: '上传失败',
  833. serverError: '内部服务器错误',
  834. error: '错误',
  835. abort: '中止',
  836. aborted: '失败',
  837. files: '文件',
  838. upload: '上传',
  839. clear: '清除',
  840. drag: '选择图片时按住ctrl键可以多选;也可以拖动图片到当前位置,选中后要点击\"上传\"(最多上传6张,单张不超过5MB)',
  841. sizeError: '$1 图片的大小不能超过 $2',// $1=file name ,$2=max ie( example.jpg has has exceed the size limit of 2mb)
  842. extError: '$1 扩展名的文件不可以上传',//$1=file extension ie(exe files are not supported)
  843. someErrorsOccurred: '发生错误!'
  844. }
  845. };
  846. }));