custominputfile.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. /*
  2. jQuery Custom Input File Plugin - version 1.0
  3. Copyright 2014, Ángel Espro, www.aesolucionesweb.com.ar,
  4. Licence : GNU General Public License
  5. Site: www.aesolucionesweb.com.ar/plugins/custom-input-file
  6. You must not remove these lines. Read gpl.txt for further legal information.
  7. */
  8. (function($){
  9. /*
  10. * CUSTOMFILE
  11. */
  12. // Constructor (CustomFile)
  13. CustomFile = function(elem, options){
  14. this.elem = $(elem);
  15. this.itemFileList = [];
  16. this.defaults = {
  17. type : 'all',
  18. allowed : 'all', //or array of allowed extensions. E.g. ["exe", "jpeg", "jpg"]
  19. notAllowed : [],//E.g. ["exe", "jpeg", "jpg"]
  20. addContainerAfter : $(elem), //jQuery Object. E.g $('#my-element')
  21. multiple : true,
  22. maxFiles : 5, //0 = no limits
  23. maxMB : 0, //0 = no limits
  24. maxKBperFile: 2048,
  25. fileWrapper : '<div class="cif-parent"></div>', //markup or jQuery object. E.g $('#my-element')
  26. filePicker : '<h3>Drop files here</h3><p>or click to select them</p><div class="cif-icon-picker"></div>',//markup or jQuery object. E.g $('#my-element')
  27. image : {
  28. crop : false,
  29. preview : {
  30. display: true,
  31. maxWidth: 300 //if cropSize were wider than maxWidth, only preview would be redimensioned (real crop area would not)
  32. },
  33. cropSize: [320,225], //[width, height]
  34. minSize : [0,0], //[width, height] 0 = no limits
  35. maxSize : [0,0] //[width, height] 0 = no limits
  36. },
  37. messages : {
  38. errorType : "File type not allowed",
  39. errorMaxMB : "Total weight exceeded",
  40. errorFileKB : "File too heavy",
  41. errorMaxFiles : "Maximum number of files reached",
  42. errorBigImage : "Image too big",
  43. errorSmallImage : "Image too small",
  44. errorOnReading : "App busy. Please, try again later.",
  45. errorMultipleDisable : "Drag & drop one file at a time."
  46. },
  47. popup : {
  48. active : true,
  49. autoclose : true,
  50. delay : 10000 //10sec
  51. },
  52. callbacks : {
  53. onComplete : function(app){
  54. //function fires when all files have already read;
  55. },
  56. beforeRead : function(file){
  57. //function fires every time a file is just to be read;
  58. //Return false if you want to stop app reading this file
  59. },
  60. onSuccess : function(item, callback){
  61. //function called every time a file has been successfuly read;
  62. //retur false for stop app reading next file. Then, you can execute callback for reading next file.
  63. //E.g.
  64. //item.serialize(); FunctionToSend(callback); return false;
  65. },
  66. onError : function(file, msg){
  67. //function called every time a file has been not successfuly read;
  68. },
  69. beforeRemove : function(item){
  70. //function called every time an item file is been removed;
  71. //Here you can handle file (item.file) before removing item
  72. },
  73. }
  74. }
  75. this.settings = $.extend(true, {}, this.defaults, options);
  76. this.status = 'stop';
  77. this.init();
  78. }
  79. //Methods (CustomFile)
  80. CustomFile.prototype = {
  81. init : function(){
  82. var attnm = this.elem.attr("name");
  83. if(attnm == '') var attnm = "inputfile";
  84. //properties:
  85. this.name = (attnm.indexOf("[]", attnm - 2) === -1)? attnm+"[]" : attnm;
  86. this.form = this.elem.parents('form');
  87. this.container = $('<div class="cif-file-container cif-container-'+this.settings.type+'-type">');
  88. //minSize for images
  89. var image = this.settings.image;
  90. image.minSize = (typeof(image.minSize) !== 'object' || image.minSize.length !== 2)? [0,0] : image.minSize;
  91. if(image.crop){
  92. var minSize = [];
  93. for(i=0;i<image.minSize.length;i++){
  94. var value = (image.minSize[i]>image.cropSize[i])? image.minSize[i] : image.cropSize[i];
  95. minSize.push(value);
  96. }
  97. image.minSize = minSize
  98. }
  99. //calling methods and creating instance of File Picker
  100. this.setFileWrapper();
  101. this.appendContainer();
  102. this.filePicker = new FilePicker(this);
  103. //store in $.customFile Object
  104. $.customFile.elements.push(this);
  105. },
  106. onSuccess : function(item, callback){
  107. this.itemFileList.push(item);
  108. if(this.settings.callbacks.onSuccess(item, callback) === false) return;
  109. callback();
  110. },
  111. onError : function(file, msg){
  112. this.settings.callbacks.onError(file, msg);
  113. var popupSet = this.settings.popup;
  114. if(popupSet.active)
  115. for(k=0;k<msg.length;k++){
  116. var fileExt = file.name.substr(file.name.lastIndexOf('.') + 1);
  117. var fileName = file.name.substr(0, file.name.lastIndexOf('.'));
  118. if(fileName.length > 42 ) var fileName = fileName.substr(0,40) + "...";
  119. msg[k] += ' ('+fileName+'.'+fileExt+')';
  120. $.customFile.popup.add( msg[k], popupSet.autoclose, popupSet.delay , 'error');
  121. }
  122. },
  123. onComplete : function(){
  124. this.status = "completed";
  125. if(this.settings.multiple){
  126. var response = this.checkMaxFiles()
  127. var popupSet = this.settings.popup;
  128. if(response && popupSet.active) $.customFile.popup.add( response, popupSet.autoclose, popupSet.delay , 'ok');//////////////////////////////////////////////////////////////////////////
  129. }else{
  130. if(this.itemFileList.length>1) this.itemFileList[0].destroy();
  131. }
  132. this.settings.callbacks.onComplete(this);
  133. },
  134. read : function(fileList, currentItem){
  135. var i = currentItem;
  136. if(i+1>fileList.length) this.status = 'completed';
  137. if(this.status === 'completed'){
  138. this.onComplete();
  139. return false;
  140. }
  141. var beforeRead = this.settings.callbacks.beforeRead(fileList[i]);
  142. if(beforeRead === false) return this.read(fileList, i+1);
  143. app = this;
  144. var response = app.checkMaxFiles(fileList[i]);
  145. if(typeof(response) === 'string'){
  146. this.onError(fileList[i],[response]);
  147. return this.read(fileList, i+1);
  148. }
  149. var msg = [];
  150. var checklist = ["checkFileKB", "checkFileType", "checkTotalMB"]
  151. for(j=0;j<checklist.length;j++){
  152. var response = app[checklist[j]](fileList[i]);
  153. if(response) msg.push(response)
  154. }
  155. if(msg.length>0){
  156. this.onError(fileList[i],msg);
  157. return this.read(fileList, i+1);
  158. }
  159. new FileItem(this, fileList, i);
  160. },
  161. appendContainer : function(){
  162. var sett = this.settings;
  163. if(sett.fileWrapper.parent().length != 0 && sett.appendAfter != this.elem){
  164. sett.addContainerAfter = sett.fileWrapper;
  165. }
  166. sett.addContainerAfter.after(this.container);
  167. },
  168. setFileWrapper : function(){
  169. //Edit name attr for each fileWrapper input
  170. var app = this;
  171. var fwr = this.settings.fileWrapper;
  172. if(typeof(fwr)==='string') this.settings.fileWrapper = $(fwr);
  173. this.settings.fileWrapper = $('<div>', {class: "cif-file-row"}).append(this.settings.fileWrapper);
  174. var fwr = this.settings.fileWrapper;
  175. fwr.find(':input').each(function(index){
  176. var $this = $(this);
  177. var attnm = $this.attr("name");
  178. if(!attnm) var attnm = app.name.substr(0, app.name.indexOf("[]", -2))+"-"+index;
  179. if(attnm.indexOf("[]", - 2) === -1){
  180. $this.attr("name",attnm+"[]");
  181. }
  182. });
  183. //prepare fileWrapper's children
  184. if(fwr.find('.cif-img').length==0 && this.settings.type == 'image'){
  185. var parent = fwr.find('.cif-parent');
  186. if(parent.length===0){
  187. var $img = fwr.find('img');
  188. var parent = $('<div>', {class: 'cif-parent'});
  189. parent.append($img);
  190. fwr.append(parent);
  191. }
  192. if(parent.find('img').length === 0) parent.append('<img>')
  193. parent.find('img').addClass("cif-img");
  194. }
  195. if(fwr.find('.cif-parent').length==0){
  196. fwr.prepend('<div class="cif-parent"></div>');
  197. }
  198. },
  199. //Cheking files...
  200. checkFileKB : function(file){
  201. if(file.size>this.settings.maxKBperFile*1024 && this.settings.maxKBperFile!=0){
  202. var msg = this.settings.messages.errorFileKB;
  203. }
  204. return msg;
  205. },
  206. checkFileType : function(file){
  207. var ext = file.name.substr(file.name.lastIndexOf('.') + 1);
  208. var ext = ext.toLowerCase();
  209. var imageCropAllowed = ["jpeg", "jpg", "png"]
  210. if((this.settings.type == 'image' && this.settings.image.crop && imageCropAllowed.indexOf(ext) == -1)
  211. || (this.settings.type == 'image' && !file.type.match(/image\//))
  212. || (this.settings.allowed != 'all' && this.settings.allowed.indexOf(ext) == -1)
  213. || (this.settings.notAllowed.indexOf(ext) != -1))
  214. {
  215. var msg = this.settings.messages.errorType;
  216. }
  217. return msg;
  218. },
  219. checkMaxFiles : function(file){
  220. if(this.settings.maxFiles<=this.itemFileList.length && this.settings.maxFiles){
  221. var msg = this.settings.messages.errorMaxFiles;
  222. this.filePicker.btn.addClass('inactive');
  223. }else{
  224. this.filePicker.btn.removeClass('inactive');
  225. }
  226. return msg;
  227. },
  228. checkTotalMB : function(file){
  229. var fileSize = (typeof(file)!=='undefined')? file.size : 0;
  230. var totalSize = 0;
  231. for(var obj in this.itemFileList){
  232. totalSize += this.itemFileList[obj].file.size;
  233. }
  234. if(fileSize+totalSize>this.settings.maxMB*1024*1024 && this.settings.maxMB!=0){
  235. var msg = this.settings.messages.errorMaxMB;
  236. }
  237. return msg;
  238. },
  239. checkImageSize : function(img, file){
  240. var stt = this.settings.image
  241. if((stt.minSize[0] && img.width<stt.minSize[0]) || (stt.minSize[1] && img.height<stt.minSize[1]) ){
  242. var msg = this.settings.messages.errorSmallImage;
  243. if(stt.minSize[0]) msg += ' Ancho mínimo:'+stt.minSize[0]+'px.';
  244. if(stt.minSize[1]) msg += ' Alto mínimo: '+stt.minSize[1]+'px.';
  245. }
  246. if((stt.maxSize[0] && img.width>stt.maxSize[0]) || (stt.maxSize[1] && img.height>stt.maxSize[1]) ){
  247. var msg = this.settings.messages.errorBigImage;
  248. if(stt.maxSize[0]) msg += ' Ancho máximo:'+stt.maxSize[0]+'px.';
  249. if(stt.maxSize[1]) msg += ' Alto máximo: '+stt.maxSize[1]+'px.';
  250. }
  251. return msg;
  252. },
  253. }
  254. /*
  255. * FOTOPICKER
  256. */
  257. // Constructor (FilePicker)
  258. FilePicker = function(app){
  259. this.btn = $('<div class="cif-file-picker"></div>').append(app.settings.filePicker);
  260. this.init(app);
  261. }
  262. // Methods (FilePicker)
  263. FilePicker.prototype = {
  264. init : function(app){
  265. var multiple = (app.settings.multiple || app.elem.attr("multiple") == "multiple")? 'multiple="multiple"' : ' ';
  266. this.inputHidden = $('<input type="file" '+multiple+'/>');
  267. app.elem.after(this.btn);
  268. var elem = app.elem.clone();
  269. app.elem.detach();
  270. app.elem = elem;
  271. //set className
  272. this.btn.addClass("cif-pkr-"+app.elem.attr("name"));
  273. var btn = this.btn;
  274. var inputHidden = this.inputHidden;
  275. //events
  276. inputHidden.change(function(){
  277. var popupSet = app.settings.popup;
  278. if(app.status == 'reading') return $.customFile.popup.add( app.settings.messages.errorOnReading, popupSet.autoclose, popupSet.delay , 'error');//return if app is reading
  279. //Read Files
  280. $.customFile.popup.close();
  281. fileList = $(this)[0].files;
  282. app.status = 'reading';
  283. app.read(fileList, 0);
  284. });
  285. btn.on({
  286. click : function(){
  287. if(!$(this).is('.inactive')) inputHidden.click();
  288. return false;
  289. },
  290. dragover: function(e){
  291. e = e || window.event;
  292. e.preventDefault();
  293. if($(this).is('.inactive')) e.dataTransfer.dropEffect = 'none';
  294. btn.addClass('dragover');
  295. return false;
  296. },
  297. dragleave: function(e){
  298. btn.removeClass('dragover');
  299. return false;
  300. },
  301. drop : function(e){
  302. e = window.event;
  303. e.preventDefault();
  304. btn.removeClass('dragover');
  305. var popupSet = app.settings.popup;
  306. if(app.status == 'reading') return $.customFile.popup.add( app.settings.messages.errorOnReading, popupSet.autoclose, popupSet.delay , 'error');//return if app is reading
  307. //Read Files.
  308. $.customFile.popup.close();
  309. var fileList = e.dataTransfer.files;
  310. if(fileList.length>1 && !app.settings.multiple) return $.customFile.popup.add( app.settings.messages.errorMultipleDisable, popupSet.autoclose, popupSet.delay , 'error');//return if app is reading
  311. app.status = 'reading';
  312. app.read(fileList, 0);
  313. }
  314. });
  315. },
  316. };
  317. /*
  318. * FILE
  319. */
  320. FileItem = function(app, fileList, currentItem){
  321. this.file = fileList[currentItem];
  322. this.app = app;
  323. this.fileList = fileList;
  324. this.currentItem = currentItem;
  325. this.init();
  326. }
  327. FileItem.prototype = {
  328. init : function(){
  329. this.jcropObj = null;
  330. this.node = this.app.settings.fileWrapper.clone();
  331. this.img = null;
  332. this.btnClose = $('<div class="cif-close" title="Remove"><i class="fas fa-times"></i></div>');
  333. this.btnClose.click(function(){
  334. fileObj.destroy();
  335. // AGREGADO para ocultar cuando no tiene archivos
  336. if ($('.arch').children('.cif-file-container').children('.cif-file-row').length > 0){
  337. $('.arch').show();
  338. }
  339. else {
  340. $('.arch').hide();
  341. }
  342. });
  343. this.fr = new FileReader;
  344. var fr = this.fr;
  345. var app = this.app;
  346. var fileObj = this;
  347. var fileList = this.fileList;
  348. var currentItem = this.currentItem;
  349. var callback = function(){
  350. app.onSuccess(fileObj, function(){
  351. app.read(fileList, currentItem+1);
  352. });
  353. // delete temp properties for clearer future use of current object
  354. delete fileObj.fr;
  355. delete fileObj.fileList;
  356. delete fileObj.currentItem;
  357. }
  358. fr.onload = function(){
  359. switch(app.settings.type){
  360. case "image" :
  361. fileObj.readImage(callback);
  362. break;
  363. default :
  364. fileObj.readAllTypes(callback);
  365. break;
  366. }
  367. }
  368. fr.readAsDataURL(this.file);
  369. },
  370. destroy : function(){
  371. this.app.settings.callbacks.beforeRemove(this);
  372. if(this.node) this.node.remove();
  373. var i = this.app.itemFileList.indexOf(this);
  374. this.app.itemFileList.splice(i, 1);
  375. this.app.checkMaxFiles();
  376. },
  377. serialize : function(){
  378. return $.customFile.serialize([{key: this.app.name, value: this.file}]);
  379. },
  380. readImage : function(callback){
  381. var fileObj = this;
  382. var fr = this.fr;
  383. var app = this.app;
  384. var imgNode = fileObj.node.find("img.cif-img");
  385. fileObj.img = new Image;
  386. fileObj.img.src = fr.result;
  387. fileObj.img.onload = function(){
  388. msg = app.checkImageSize(fileObj.img, fileObj.file);
  389. if(msg){
  390. app.onError(fileObj.file, [msg]);
  391. return app.read(fileObj.fileList, fileObj.currentItem+1);
  392. }
  393. imgNode.attr("src", fr.result);
  394. imgNode.parent().prepend(fileObj.btnClose);
  395. //Append node to container
  396. app.container.append(fileObj.node);
  397. if(app.settings.image.crop === true){
  398. fileObj.jcropObj = fileObj.initJcrop(app.settings.image, imgNode.parent(), fileObj.img);
  399. }
  400. //Callback
  401. callback();
  402. }
  403. },
  404. readAllTypes : function(callback){
  405. fileObj = this;
  406. var parent = fileObj.node.find('.cif-parent');
  407. var FileExt = fileObj.file.name.substr(fileObj.file.name.lastIndexOf('.') + 1);
  408. var FileName = fileObj.file.name.substr(0, fileObj.file.name.lastIndexOf('.'));
  409. if(FileName.length > 42 ) var FileName = FileName.substr(0,40) + "...";
  410. var fileSize = (fileObj.file.size < 102400)? (fileObj.file.size/1024).toFixed(2) : Math.round(fileObj.file.size/1024); //display two different formats, if is less than 100KB or heavier
  411. parent.append($('<div class="cif-all-type">'+FileName+'.'+FileExt+' <span class="cif-file-size">('+fileSize+'KB)</span><div>')).append(fileObj.btnClose);
  412. this.app.container.append(fileObj.node)
  413. callback();
  414. },
  415. initJcrop : function(options, parent, img){
  416. var jcrop_api,
  417. boundx,
  418. boundy;
  419. //if preview is turn on
  420. if(options.preview.display){
  421. prevMaxWidth = options.preview.maxWidth;
  422. prevSize = (options.cropSize[0]>prevMaxWidth)? [prevMaxWidth, options.cropSize[1]/options.cropSize[0]*prevMaxWidth] : options.cropSize;
  423. parent.append('<div class="preview-pane" style="width:'+prevSize[0]+'px;height:'+prevSize[1]+'px;"><div class="preview-container" style="width:'+prevSize[0]+'px;height:'+prevSize[1]+'px; overflow:hidden"><img src="'+img.src+'" class="jcrop-preview im-prv" /></div></div> '
  424. +'<input type="hidden" class="jcropx" name="x[]" /><input type="hidden" class="jcropy" name="y[]" /><input type="hidden" class="jcropw" name="w[]" /><input type="hidden" class="jcroph" name="h[]" />');
  425. parent.css("min-height", prevSize[1]+20+"px");
  426. }
  427. // Grab some information about the preview pane
  428. var $preview = parent.find('.preview-pane'),
  429. $pcnt = $preview.find('.preview-container'),
  430. $pimg = $preview.find('.preview-container img'),
  431. xsize = $pcnt.width(),
  432. ysize = $pcnt.height();
  433. api = parent.find('.cif-img').Jcrop({
  434. keySupport: false,
  435. onChange: updatePreview,
  436. onSelect: updatePreview,
  437. aspectRatio: options.cropSize[0]/options.cropSize[1],
  438. minSize : options.cropSize,
  439. trueSize: [img.width,img.height]
  440. },function(){
  441. // Use the API to get the real image size
  442. var bounds = this.getBounds();
  443. boundx = bounds[0];
  444. boundy = bounds[1];
  445. jcrop_api = this;
  446. jcrop_api.animateTo([0,0,options.cropSize[0]]);
  447. // Move the preview into the jcrop container for css positioning
  448. $preview.appendTo(jcrop_api.ui.holder);
  449. });
  450. function updatePreview(c){
  451. if (parseInt(c.w) > 0 && options.preview.display){
  452. var rx = xsize / c.w;
  453. var ry = ysize / c.h;
  454. $pimg.css({
  455. width: Math.round(rx * boundx) + 'px',
  456. height: Math.round(ry * boundy) + 'px',
  457. marginLeft: '-' + Math.round(rx * c.x) + 'px',
  458. marginTop: '-' + Math.round(ry * c.y) + 'px'
  459. });
  460. }
  461. updateCoords(c);
  462. };
  463. function updateCoords(c){
  464. parent.find('.jcropx').val(c.x);
  465. parent.find('.jcropy').val(c.y);
  466. parent.find('.jcropw').val(c.w);
  467. parent.find('.jcroph').val(c.h);
  468. }
  469. return jcrop_api;
  470. }
  471. }
  472. /*
  473. *
  474. *Public Static methods (CustomFile)
  475. *
  476. */
  477. $.customFile = {
  478. elements : [],
  479. /*
  480. * Get Elements: form, inputs or pseudoInputs file
  481. */
  482. getElements : function(selector){
  483. //it returns an array of object. Argument: ".myform, #myinput, textarea, input, name-of-my-custom-input-file"
  484. var elements = [];
  485. var selector = selector.split(",");
  486. var el = $.customFile.elements;
  487. for(k=0; k<selector.length; k++){
  488. selector[k] = selector[k].trim()
  489. for(i=0; i<el.length; i++){
  490. if(el[i].name === selector[k]+"[]" || el[i].name === selector[k])
  491. elements.push({type: "pseudoinput", obj: el[i]});
  492. if($(selector[k]).is('form')){
  493. $(selector[k]).each(function(){
  494. elements.push({type: "form", obj: $(this), pseudoChild : (el[i].form[0] === $(this)[0])});
  495. });
  496. }
  497. if($(selector[k]).is(':input')){
  498. $(selector[k]).not(':submit').each(function(){
  499. elements.push({type: "input", obj: $(this)});
  500. });
  501. }
  502. }
  503. }
  504. //Remove duplicates. Store duplicate elem in indexToRemove, and then copy no duplicate elems into a new array (var result)
  505. var indexToRemove = []
  506. for(i=0; i<elements.length; i++){
  507. if(indexToRemove.indexOf(i) !== -1) continue;
  508. for(j=0; j<elements.length; j++){
  509. if(j === i || indexToRemove.indexOf(j) !== -1) continue;
  510. switch(elements[i].type){
  511. case "form" :
  512. var el = elements[i].obj[0];
  513. if(el === elements[j].obj[0]
  514. ||(elements[j].type === "pseudoinput" && el == elements[j].obj.form[0])
  515. || (elements[j].type === "input" && el == elements[j].obj.parents('form')[0])
  516. ) {
  517. indexToRemove.push(j);
  518. }
  519. break;
  520. case "input" :
  521. var el = elements[i].obj[0];
  522. if(el === elements[j].obj[0])
  523. indexToRemove.push(j);
  524. break;
  525. case "pseudoinput" :
  526. var el = elements[i].obj.name;
  527. if(el === elements[j].obj.name || el+"[]" === elements[j].obj.name)
  528. indexToRemove.push(j);
  529. break;
  530. }
  531. }
  532. }
  533. var result =[];
  534. for(i=0;i<elements.length; i++){
  535. if(indexToRemove.indexOf(i) === -1) result.push(elements[i]);
  536. }
  537. return result;
  538. },
  539. /*
  540. *Serialize form, inputs or pseudoInputs file
  541. */
  542. serialize : function(elements){
  543. formData = null;
  544. if(typeof(elements) === 'object'){ // e.g. $.customFile.serialize([{key: "myFile", value: myFile},{key: "myInputText", value: "text to send"}]);
  545. formData = formData || new FormData();
  546. if(!elements.length) var elements = [elements]
  547. for(j=0;j<elements.length;j++){
  548. if(elements[j].hasOwnProperty("key") && elements[j].hasOwnProperty("value")){
  549. formData.append(elements[j].key, elements[j].value);
  550. }
  551. }
  552. }
  553. if(typeof(elements) === 'string'){ // e.g. $.customFile.serialize("#myInput, myPseudoInputFileName, etc"); or just $.customFile.serialize("#myForm");
  554. elements = this.getElements(elements);
  555. //elements = (arguments.length>0)? arguments : [$('form')]; //Default all forms
  556. for(j=0;j<elements.length;j++){
  557. formData = formData || new FormData();
  558. var elem = elements[j];
  559. switch(elem.type){
  560. case 'pseudoinput':
  561. $.each(elem.obj.itemFileList, function(index, element){
  562. formData.append(elem.obj.name, element.file);
  563. });
  564. break;
  565. case "form":
  566. //For each real input
  567. $.each(elem.obj.find(':input'), function(){
  568. if($(this).not(':submit'))
  569. formData.append($(this).attr("name"), $(this).val());
  570. });
  571. //For each Pseudoinput File
  572. var app = elem.obj.data("appCustomFile");
  573. if(typeof(app) == "undefined"){
  574. elem.obj.data("appCustomFile", []);
  575. }
  576. $.each(app, function(){
  577. appThis = this;
  578. $.each(appThis.itemFileList, function(index, element){
  579. formData.append(appThis.name, element.file);
  580. });
  581. });
  582. break;
  583. case "input" :
  584. formData.append(elem.obj.attr("name"), elem.obj.val());
  585. break;
  586. }
  587. }
  588. }
  589. return formData //return null if there are no elements to serialize
  590. },
  591. /*
  592. * Send form, input or file
  593. * E.g $.customFile.ajax('form') // $.customFile.ajax('form', {progress : false}) // $.customFile.ajax(myItem, {url : actionForm}) //
  594. */
  595. ajax : function(el, options){
  596. if(typeof(el) === 'string'){
  597. var element = this.getElements(el)[0];
  598. switch(element.type){
  599. case "form":
  600. var action = element.obj.attr("action");
  601. break;
  602. case "input":
  603. var action = element.obj.parents("form").attr("action");
  604. break;
  605. case "pseudoinput":
  606. var action = element.obj.form.attr("action");
  607. break;
  608. }
  609. var formData = $.customFile.serialize(el); //Serilize element
  610. }
  611. if(typeof(el) === 'object' && el instanceof FileItem){
  612. var formData = el.serialize(); //Serialize file
  613. var action = el.app.form.attr("action");
  614. }
  615. var defaults = {
  616. cache: false,
  617. contentType: false,
  618. data: formData,
  619. processData: false,
  620. url: action,
  621. type: 'POST',
  622. progressBar : {
  623. active : true,
  624. markup : '<div class="cf-progressbar-wr"><div class="cf-progressbar"><span width="0"></span></div></div>',
  625. appendTo : $('body'),
  626. removeAfterComplete : true,
  627. node : null
  628. },
  629. progress : function(e, total, position, percent){
  630. this.progressBar.node.find("span").width(percent+'%');
  631. },
  632. xhr: function(){
  633. var ax = this;
  634. var xhr = $.ajaxSettings.xhr() ;
  635. xhr.upload.onprogress = function(e){
  636. var e = e || window.event;
  637. var position = e.position || e.loaded;
  638. var total = e.totalSize || e.total;
  639. var percent = ((e.loaded/e.total)*100)+"";
  640. ax.progress(e, total, position, percent);
  641. } ;
  642. xhr.upload.onload = function(){
  643. ax.progressBar.node.find("span").width('100%');
  644. if(ax.progressBar.removeAfterComplete)
  645. ax.progressBar.node.fadeOut(function(){
  646. $(this).remove();
  647. });
  648. } ;
  649. return xhr ;
  650. },
  651. beforeSend : function(){},
  652. complete : function(){},
  653. success: function(xml){
  654. //console.log(xml);
  655. },
  656. }
  657. var settings = $.extend(true, {}, defaults, options);
  658. //append progressbar
  659. if(!settings.progressBar.active) settings.progress = function(){};
  660. settings.progressBar.node = $(settings.progressBar.markup);
  661. var settBefore = settings.beforeSend;
  662. if(settings.progressBar.active){
  663. settings.beforeSend = function(){
  664. settBefore();
  665. settings.progressBar.appendTo.append(settings.progressBar.node);
  666. };
  667. }
  668. //Call jQuery Ajax function
  669. $.ajax(settings);
  670. },
  671. /*
  672. * Validate. On Construction
  673. */
  674. validate : function(elements, options){
  675. //do some stuff with options, like validate email, telephone, not empty, callbacks,
  676. //and return object {elementNoPass: [{typeOfError: 'tel', elem : element}/*elements that no pass*/], elementPass: [/*elements that pass*/], pass: boolean}
  677. elements = this.getElements(elements);
  678. for(j=0;j<elements.length;j++){
  679. el = elements[j];
  680. switch(el.type){
  681. case "form" :
  682. //
  683. break;
  684. case "input" :
  685. //
  686. break;
  687. case "pseudoinput" :
  688. //
  689. break;
  690. }
  691. }
  692. //
  693. },
  694. popup : {
  695. /*wrapper : $('<div id="cif-msg-wr"><div class="cif-msg-close"><i class="fas fa-times"></i></div></div>'),*/
  696. wrapper: $('<div id="cif-msg-wr" class="alert alert-warning"><div class="cif-msg-close"><i class="fas fa-times"></i></div></div>'),
  697. open : function(){
  698. var popup = this;
  699. this.wrapper.find('.cif-msg-close').click(function(){
  700. popup.close()
  701. });
  702. $('body').append(popup.wrapper);
  703. },
  704. add : function(msg, autoclose, delay, type){
  705. //if(!autoclose) autoclose = true; //default
  706. if(!delay) delay = 3000; //default
  707. switch(type){
  708. case "error" :
  709. var icon = '<i class="fas fa-times text-danger mr-2"></i>';
  710. break;
  711. case "ok" :
  712. var icon = '<i class="fas fa-check text-success mr-2"></i>';
  713. break;
  714. default :
  715. var icon = '';
  716. }
  717. var popup = this;
  718. if($('body').find(popup.wrapper).length<1) popup.open();
  719. this.wrapper.append('<div class="cif-msg">'+icon+msg+'</div>');
  720. if(typeof(fftimeout)!== 'undefined') clearTimeout(fftimeout);
  721. if(autoclose)
  722. fftimeout = setTimeout(function(){
  723. popup.close();
  724. }, delay);
  725. },
  726. close : function(){
  727. this.wrapper.find(".cif-msg").remove();
  728. this.wrapper.detach();
  729. }
  730. }
  731. }
  732. //jQuery Plugin
  733. $.fn.customFile = function(options){
  734. return this.each(function(){
  735. var element = $(this);
  736. var tagName = element[0].tagName.toLowerCase();
  737. if(tagName == 'input'){
  738. // Call constructor
  739. var customFile = new CustomFile(this, options);
  740. var prop = customFile.form
  741. //Store plugin also in data form
  742. if(typeof(customFile.form.data("appCustomFile")) != "undefined"){
  743. var formData = customFile.form.data("appCustomFile");
  744. formData.push(customFile);
  745. }else{
  746. var formData = new Array(customFile);
  747. }
  748. customFile.form.data("appCustomFile", formData);
  749. }
  750. });
  751. };
  752. })(jQuery);