plugins.js 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818
  1. // Avoid `console` errors in browsers that lack a console.
  2. (function() {
  3. var method;
  4. var noop = function () {};
  5. var methods = [
  6. 'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
  7. 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
  8. 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
  9. 'timeline', 'timelineEnd', 'timeStamp', 'trace', 'warn'
  10. ];
  11. var length = methods.length;
  12. var console = (window.console = window.console || {});
  13. while (length--) {
  14. method = methods[length];
  15. // Only stub undefined methods.
  16. if (!console[method]) {
  17. console[method] = noop;
  18. }
  19. }
  20. }());
  21. //Affiche un message 'message' de type 'type' pendant 'timeout' secondes and
  22. $.message = function (type,message,timeout,addClass){
  23. message = message.replace(/<\/?script|iframe|object[^>]*>/gim,'');
  24. var types = {
  25. error: 'ERREUR',
  26. warning: 'ATTENTION',
  27. info: 'INFORMATION',
  28. }
  29. var data = {
  30. type: type,
  31. content: message,
  32. timeout: timeout,
  33. progress: true,
  34. addClass:addClass
  35. };
  36. if(types[type]) data.title = types[type];
  37. $.toast(data);
  38. }
  39. //appelle un hook js
  40. $.callHook = function (hook,args){
  41. if(!window.hooks) window.hooks = {};
  42. if(!window.hooks[hook]) window.hooks[hook] = [];
  43. for(var k in window.hooks[hook]){
  44. var method = window.hooks[hook][k];
  45. method(args);
  46. }
  47. }
  48. //Définis un hook js
  49. $.addHook = function (hook,callback){
  50. if(!window.hooks) window.hooks = {};
  51. if(!window.hooks[hook]) window.hooks[hook] = [];
  52. window.hooks[hook].push(callback);
  53. }
  54. //Permet les notifications types toast sans dépendance de librairie/css/html
  55. $.toast = function (options) {
  56. var o = $.extend({
  57. title: null,
  58. content: '',
  59. type: 'info',
  60. timeout: 3000,
  61. addClass: ''
  62. }, options);
  63. var toastFunctions = {
  64. hideToast: function(toast, timeout){
  65. setTimeout(function(){
  66. toast.addClass('hidden');
  67. setTimeout(function(){
  68. toast.remove();
  69. },100);
  70. }, timeout);
  71. },
  72. updateProgress: function(){
  73. var percentage = ((progress.hideEta - (new Date().getTime())) / progress.maxHideTime) * 100;
  74. progressBar.width(percentage + '%');
  75. }
  76. };
  77. var progress = {
  78. toastInterval: null,
  79. hideEta: null,
  80. maxHideTime: null
  81. };
  82. if($('.toastContainer').length == 0) $('body').append('<div class="toastContainer"></div>');
  83. var popupContainer = $('.toastContainer');
  84. var popup = $('<div class="noPrint toast toast-'+o.type+' '+o.addClass+' media hidden"><i class="toastIcon align-self-start fa"></i><div class="toastContent media-body"><div class="toastTitle"><span class="m-auto d-inline-block"></span><i class="toastRemove fas fa-times hidden" onclick="var toast = $(this).closest(\'div.toast.media\'); toast.addClass(\'hidden\');setTimeout(function(){toast.remove();},100);";></i></div><div class="toastMessage"></div></div></div>');
  85. popupContainer.append(popup);
  86. if(o.title) $('.toastTitle > span', popup).html(o.title);
  87. $('.toastMessage', popup).html(o.content);
  88. //Apparition du toast
  89. setTimeout(function(){ popup.removeClass('hidden'); }, 50);
  90. var progressBar = $('<div class="toastProgress"></div>');
  91. clearTimeout(progress.toastInterval);
  92. //Cache le toast en fonction d'un timeout ou non
  93. if(o.timeout > 0) {
  94. if(o.progress) {
  95. toastInterval = setTimeout(function(){toastFunctions.hideToast(popup);}, o.timeout);
  96. progress.maxHideTime = parseFloat(o.timeout);
  97. progress.hideEta = new Date().getTime() + progress.maxHideTime;
  98. popup.append(progressBar);
  99. progress.toastInterval = setInterval(toastFunctions.updateProgress, 10);
  100. }
  101. //Au terme du timeout, on cache le toast
  102. toastFunctions.hideToast(popup, o.timeout);
  103. } else {
  104. popup.find('.toastRemove').removeClass('hidden');
  105. }
  106. }
  107. //Permet l'utilisation du json dans les local storage
  108. $.storage = function(key,value){
  109. var json = localStorage.getItem(key);
  110. json = !json ? {} : JSON.parse(json);
  111. if(!value) return json;
  112. localStorage.setItem(key,JSON.stringify(value));
  113. }
  114. $.page = function(element){
  115. var path = window.location.pathname.split('/') ;
  116. path = path[path.length-1];
  117. path = path.replace('.php','');
  118. return path;
  119. }
  120. $.getForm = function(element){
  121. return $(element).getForm();
  122. }
  123. $.setForm = function(element,data){
  124. var o = {};
  125. var obj = $(element);
  126. $('input,select,textarea',obj).each(function(i,element){
  127. var input = $(element);
  128. var id = element.id!=null && element.id!="" ? element.id : '';
  129. if(!id && input.attr('data-id')) id = input.attr('data-id');
  130. if(!id || data[id]==null) return;
  131. if(input.attr("type")=='checkbox' || input.attr("type")=='radio'){
  132. input.prop("checked",data[id]==1 || data[id]=='true' ?true:false);
  133. } else {
  134. input.val(data[id]);
  135. }
  136. });
  137. return o;
  138. }
  139. //Gestion des tableaux pour le formData
  140. $.form_data = function(formData, key,values){
  141. var hasFile = false;
  142. if(values instanceof File) hasFile = true;
  143. if(typeof values == 'object' && !(values instanceof File)){
  144. for(subkey in values)
  145. if($.form_data(formData,key + '[' + subkey + ']',values[subkey])) hasFile = true;
  146. }else{
  147. formData.append(key, values);
  148. }
  149. return hasFile;
  150. }
  151. $.action = function(data,success,error,progress) {
  152. var formData = new FormData();
  153. var defaultSuccess = data.defaultSuccess != undefined ? data.defaultSuccess : true;
  154. var async = data.async != undefined ? data.async : true ;
  155. // if(data.defaultSuccess !== undefined) delete data.defaultSuccess;
  156. //hasFile determine si un fichier uploadé se trouve dans le tableau des data
  157. // Si c'est le cas, il envois les données en multipart, sinon il les envoie en json classique.
  158. var hasFile = false;
  159. $.each(data, function(key, value){
  160. if($.form_data(formData,key, value) == true) hasFile = true;
  161. });
  162. var request = {
  163. url : 'action.php',
  164. method : 'POST',
  165. async :async,
  166. success: function(response){
  167. $('.btn-preloader').removeClass('btn-preloader');
  168. //Debug des erreur php non catchées
  169. if(window.location.host=='127.0.0.1'){
  170. var debugRegex = /Erreur : <\/strong><span>(.*)- <small style="opacity:0.5;">(.*\.php) L([0-9]*)/gm;
  171. while ((m = debugRegex.exec(response)) !== null) {
  172. if (m.index === debugRegex.lastIndex) debugRegex.lastIndex++;
  173. var path = m[2].split('/');
  174. path = path.slice(-3).join('/');
  175. var filePath = m[2].replace(/\//ig,'\\');
  176. var link = 'w2d://'+btoa(JSON.stringify({action:'alias',alias:'sublime',arguments:filePath+'":'+m[3]}));
  177. var html = '<a style="color:inherit;text-decoration:none;" href="'+link+'">'+path+'<br><strong>L'+m[3]+'</strong><br>'+m[1]+'<br/><small>('+data.action+')</small></a>';
  178. $.message('warning',html,0);
  179. }
  180. }
  181. if(response && !response.error){
  182. if(success!=null)success(response);
  183. } else {
  184. if(response.errorCode && response.errorCode=='401') core_logout();
  185. var errorMessage = '';
  186. if(response.file){
  187. var filePath = response.file.replace(/\//ig,'\\');
  188. var link = 'w2d://'+btoa(JSON.stringify({action:'alias',alias:'sublime',arguments:filePath+'":'+response.line}));
  189. errorMessage += '<a style="color:inherit;text-decoration:none;" href="'+link+'">'+response.file.replace('/var/www/html/','')+'<br><strong>L'+response.line+'</strong></a>';
  190. }
  191. errorMessage += "<br>"+response.error;
  192. if(response.trace) errorMessage += "<i class='fab fa-stack-overflow d-block pointer m-1' title=\"Voir la trace complête\" onclick=\"$(this).next('div').toggleClass('hidden')\"></i><div class='hidden' style='font-size:10px;max-height:200px;overflow:auto;text-align:left;'>"+response.trace+"</div>";
  193. if(defaultSuccess) $.message('error',errorMessage,0);
  194. if(error!=null) error(response);
  195. }
  196. },
  197. error : function(response){
  198. $('.btn-preloader').removeClass('btn-preloader');
  199. if(response.status == 200 && $.localhost() ){
  200. $('body').append('<div class="debugFrame" style="box-shadow: 0px 0px 10px rgba(0,0,0,0.5);z-index: 30001;padding: 5px;position: fixed;left: 0;top: 0;width: 40%;min-height: 100%;background: rgba(242,245,255, 0.9);overflow-y: auto;height: 100%; word-break: break-word;"><h5 class="text-center">ACTION DEBUG</h5> <i style="position:absolute; right:10px; top:10px;font-size:1.5em;" onclick="$(this).parent().remove()" class="fas fa-times pointer"></i>'+response.responseText+'</div>');
  201. } else {
  202. if(response.readyState == 0 && error==null) return;
  203. if(error!=null){
  204. if(response.errorCode && response.errorCode=='401') core_logout();
  205. error(response);
  206. }else{
  207. $.message('error','Erreur indefinie, merci de contacter un administrateur',0);
  208. }
  209. }
  210. },
  211. xhr: function() {
  212. var xhr = new window.XMLHttpRequest();
  213. if(data.downloadResponse) xhr.responseType = 'blob';
  214. xhr.upload.addEventListener("progress", function(evt){
  215. if (evt.lengthComputable) {
  216. var percentComplete = (evt.loaded / evt.total) * 100;
  217. percentComplete = Math.round(percentComplete * 100) / 100;
  218. if(progress) progress(percentComplete,'upload');
  219. }
  220. }, false);
  221. xhr.addEventListener("progress", function(evt){
  222. if (evt.lengthComputable) {
  223. var percentComplete = evt.loaded / evt.total;
  224. if(progress) progress(percentComplete,'download');
  225. }
  226. }, false);
  227. xhr.addEventListener('readystatechange', function(e) {
  228. if(xhr.readyState == 4 && xhr.status == 200) {
  229. if(data.downloadResponse){
  230. var disposition = xhr.getResponseHeader('content-disposition');
  231. var matches = /"([^"]*)"/.exec(disposition);
  232. var filename = (matches != null && matches[1] ? matches[1] : 'file');
  233. var blob = new Blob([xhr.response], { type: xhr.response.type });
  234. var link = document.createElement('a');
  235. link.href = window.URL.createObjectURL(blob);
  236. link.download = filename;
  237. $('body').append(link);
  238. link.click();
  239. link.remove();
  240. window.URL.revokeObjectURL(link);
  241. }
  242. }
  243. });
  244. return xhr;
  245. }
  246. };
  247. if(!hasFile){
  248. request.data = data;
  249. }else{
  250. request.data = formData;
  251. request.processData = false;
  252. request.contentType = false;
  253. }
  254. $.ajax(request);
  255. }
  256. $.localhost = function(){
  257. return (document.location.hostname=='127.0.0.1' || document.location.hostname=='localhost');
  258. }
  259. $.hashData = function(name){
  260. var page = window.location.hash.substring(1);
  261. page += "&"+window.location.search.substring(1);
  262. data = {};
  263. if(page!='' && page!= null){
  264. options = page.split('&');
  265. var data = {};
  266. for(var key in options){
  267. infos = options[key].split('=');
  268. data[infos[0]] = infos[1];
  269. }
  270. }
  271. if(name == null) return data;
  272. if(typeof name === "object"){
  273. data = name;
  274. hashstring = '';
  275. for(var key in data)
  276. hashstring+= "&"+key+"="+data[key];
  277. hashstring = hashstring.substring(1);
  278. window.location.hash = hashstring;
  279. return;
  280. }
  281. return typeof data[name] == "undefined" ? '':data[name];
  282. }
  283. $.urlParam = function (name,value,forceValue) {
  284. var parameters = window.location.href.match(/[\\?&]([^&#]*)=([^&#]*)/g);
  285. var data = {};
  286. for (var key in parameters) {
  287. var couple = parameters[key].substring(1, parameters[key].length).split('=');
  288. data[couple[0]] = couple[1];
  289. }
  290. if(name == null) return data;
  291. if (value == null && !forceValue)
  292. return data[name] ? data[name] : null;
  293. if (value !== false) data[name] = ''+value;
  294. var url = '?';
  295. for (var key in data) {
  296. if (value == false && !forceValue && key == name) continue;
  297. url += key + '=' + data[key]+'&';
  298. }
  299. window.history.pushState('', document.title, url.substring(0, url.length-1));
  300. }
  301. $.fn.extend({
  302. fromJson : function(data){
  303. $.setForm(this,data);
  304. return $(this);
  305. },
  306. toJson : function(){
  307. return $.getForm(this);
  308. },
  309. hasAttr : function(attr){
  310. if(this.length==0) return false;
  311. return this.get(0).hasAttribute(attr)
  312. },
  313. fitText: function(ratio, options) {
  314. var compressor = ratio || 1,
  315. settings = $.extend({
  316. 'minFontSize' : Number.NEGATIVE_INFINITY,
  317. 'maxFontSize' : Number.POSITIVE_INFINITY
  318. }, options);
  319. return this.each(function(){
  320. var element = $(this);
  321. var resizer = function() {
  322. element.css('font-size', Math.max(Math.min(element.width() / (compressor*10), parseFloat(settings.maxFontSize)), parseFloat(settings.minFontSize)));
  323. };
  324. resizer();
  325. $(window).on('resize.fittext orientationchange.fittext', resizer);
  326. });
  327. },
  328. getForm: function(){
  329. var o = {};
  330. var obj = $(this);
  331. for(var key in obj.data()){
  332. if(key!="action" && key != "id") continue;
  333. o[key] = obj.attr('data-'+key);
  334. }
  335. $('input,select,textarea',obj).each(function(i,element){
  336. var element = $(element);
  337. var id = element.attr('id');
  338. if(!id) id = element.attr('data-id');
  339. if(!id && element.attr("type")=='radio') id = element.attr('name');
  340. if(!id) return;
  341. if(element.attr("type")=='checkbox'){
  342. o[id] = (element.is(':checked')?1:0);
  343. }else if(element.attr("type")=='radio' && element.is(':checked')){
  344. o[id] = element.val();
  345. } else if(element.attr("type")=='file'){
  346. if(!element[0].files.length) return;
  347. if(element[0].files.length == 1){
  348. o[id] = element[0].files[0];
  349. } else {
  350. //Attention, le passage de multiples fichiers
  351. //entraine une modification de la structure du
  352. //tableau $_FILES dans le php (voir fonction normalize_php_files())
  353. o[id] = {};
  354. for(var i=0; i<element[0].files.length; ++i)
  355. o[id][i] = element[0].files[i];
  356. }
  357. } else {
  358. //permet le set d emultiples valeurs sur un seul input
  359. if(element.attr('data-multiple-values') == 'true'){
  360. try{
  361. o[id] = element.val()!= '' ? JSON.parse(element.val()) : [];
  362. }catch(e){
  363. o[id] = [];
  364. }
  365. }else{
  366. o[id] = element.val();
  367. }
  368. }
  369. });
  370. return o;
  371. },
  372. upload: function (options) {
  373. //var options = $.extend(defaults, options);
  374. return this.each(function () {
  375. var o = options;
  376. var droppedFiles = false;
  377. var div = $(this);
  378. var data = div.data();
  379. data.html =!div.attr('data-label') ? '': div.attr('data-label');
  380. data.label = data.html;
  381. div.html('');
  382. var o = $.extend(o, data);
  383. if(o.readonly == false){
  384. var form = $('<form class="box" method="post" action="' + o.action + '" enctype="multipart/form-data"><input type="file" name="'+(o.fileName?o.fileName:div.attr('id'))+'[]" multiple /></form>');
  385. div.append(form);
  386. var input = form.find('input[type="file"]');
  387. var zone = $('<div class="pointer text-center">' + o.label + '</div>');
  388. div.append(zone);
  389. //test if dnd is enabled n browser
  390. var div = document.createElement('div');
  391. var dragAndDropEnabled = (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window;
  392. //set elements styles
  393. input.attr('style', "width: 0.1px;height: 0.1px;opacity: 0;overflow: hidden; position: absolute;z-index: -1");
  394. //set events
  395. zone.on('click', function (e) {
  396. form.find('input[type="file"]').trigger('click');
  397. e.preventDefault();
  398. e.stopPropagation();
  399. })
  400. .on('drag dragstart dragend dragover dragenter dragleave drop', function (e) {
  401. e.preventDefault();
  402. e.stopPropagation();
  403. })
  404. .on('dragover dragenter', function () {
  405. if (o.hover) form.addClass(o.hover);
  406. })
  407. .on('dragleave dragend drop', function () {
  408. if (o.hover) form.removeClass(o.hover);
  409. })
  410. .on('drop', function (e) {
  411. droppedFiles = e.originalEvent.dataTransfer.files;
  412. form.trigger('submit');
  413. });
  414. input.on('change', function (e) {
  415. form.trigger('submit');
  416. });
  417. form.on('submit', function (e) {
  418. e.preventDefault();
  419. if (o.start){
  420. var result = o.start();
  421. if(result == -1) return;
  422. }
  423. if (dragAndDropEnabled) {
  424. var ajaxData = new FormData();
  425. if (droppedFiles) {
  426. $.each(droppedFiles, function (i, file) {
  427. var ext = file.name.split('.');
  428. ext = ext.pop();
  429. if(o.allowed && $.inArray(ext.toLowerCase(),o.allowed.split(','))===-1){
  430. $.message('error','Extension fichier '+ext+' non permise (autorisé:'+o.allowed+')',0);
  431. isProcessing = false;
  432. return;
  433. }
  434. if(o.size && file.size > o.size){
  435. $.message('error','Taille fichier '+file.size+' octets trop grande (autorisé:'+o.size+' octets)',0);
  436. isProcessing = false;
  437. return;
  438. }
  439. ajaxData.append(input.attr('name'), file);
  440. });
  441. }else{
  442. ajaxData = new FormData(form.get(0));
  443. for(var key in $('input',form).get(0).files){
  444. var file = $('input',form).get(0).files[key];
  445. if(file.name==null || typeof file !='object') continue;
  446. var ext = file.name.split('.');
  447. ext = ext.pop();
  448. if(o.allowed && $.inArray(ext.toLowerCase(),o.allowed.split(','))===-1){
  449. $.message('error','Extension fichier '+ext+' non permise (autorisé:'+o.allowed+')',0);
  450. $('input',form).val();
  451. isProcessing = false;
  452. return;
  453. }
  454. if(o.size && file.size > o.size){
  455. $.message('error','Taille fichier '+file.size+' octets trop grande (autorisé:'+o.size+' octets)',0);
  456. $('input',form).val();
  457. isProcessing = false;
  458. return;
  459. }
  460. }
  461. }
  462. if (o.addData){
  463. var addionnalData = o.addData();
  464. for(var k in addionnalData)
  465. ajaxData.append(k, addionnalData[k]);
  466. }
  467. droppedFiles = null;
  468. $.ajax({
  469. url: form.attr('action'),
  470. type: form.attr('method'),
  471. data: ajaxData,
  472. dataType: 'json',
  473. cache: false,
  474. contentType: false,
  475. processData: false,
  476. complete: function (data) {
  477. if (o.complete) o.complete(data.responseJSON);
  478. },
  479. success: function (data) {
  480. if (o.success) o.success(data);
  481. },
  482. error: function (data) {
  483. if (o.error) o.error(data);
  484. }
  485. });
  486. } else {
  487. var iframeName = 'uploadiframe' + new Date().getTime();
  488. iframe = $('<iframe name="' + iframeName + '" style="display: none;"></iframe>');
  489. $('body').append(iframe);
  490. form.attr('target', iframeName);
  491. iframe.one('load', function () {
  492. var data = JSON.parse(iframe.contents().find('body').text());
  493. if (!data.success) alert(data.error);
  494. form.removeAttr('target');
  495. iframe.remove();
  496. if (o.complete) o.complete();
  497. });
  498. }
  499. });
  500. }
  501. });
  502. },
  503. shiftCheckboxes: function() {
  504. var instance = this;
  505. this.on('click', function(e) {
  506. var checkbox = $(e.target);
  507. if(e.shiftKey && instance.last) {
  508. var from = instance.index(instance.last);
  509. var to = instance.index(checkbox);
  510. instance.slice(Math.min(from, to), Math.max(from, to) + 1)
  511. .filter(':not(:disabled)')
  512. .prop('checked', checkbox.prop('checked'))
  513. .trigger('change');
  514. }
  515. instance.last = checkbox;
  516. });
  517. },
  518. clear: function(){
  519. return this.each(function() {
  520. var obj = $(this);
  521. obj.find('input,textarea').not(':input[type=checkbox], :input[type=radio]').val('')
  522. obj.find('input').prop('checked',false).prop('selected',false);
  523. obj.find('select').prop('selectedIndex',0);
  524. obj.find('*[contenteditable="true"]').text('');
  525. });
  526. },
  527. //Rends le formulaire et ses inputs en lecture seule uniquement ou pas en fonction du booleen donné
  528. readonly: function(enableReadOnly){
  529. return this.each(function() {
  530. var obj = $(this);
  531. if(enableReadOnly){
  532. obj.find('input,textarea,select').attr('readonly','readonly');
  533. obj.find('textarea[data-type="wysiwyg"]').trumbowyg('disable');
  534. obj.find('input[data-type],select[data-type],textarea[data-type]').each(function(){
  535. var input = $(this);
  536. var component = input.data("data-component");
  537. if(input.attr('data-type') == 'wysiwyg'){
  538. component = input.data('trumbowyg').$box;
  539. input.data('trumbowyg').$ed.attr('contenteditable',false);
  540. }
  541. if(component) component.attr('readonly','readonly');
  542. });
  543. }else{
  544. obj.find('input,textarea,select').removeAttr('readonly');
  545. obj.find('textarea[data-type="wysiwyg"]').trumbowyg('enable');
  546. obj.find('input[data-type],select[data-type],textarea[data-type]').each(function(){
  547. var input = $(this);
  548. var component = input.data("data-component");
  549. if(input.attr('data-type') == 'wysiwyg'){
  550. component = input.data('trumbowyg').$box;
  551. input.data('trumbowyg').$ed.attr('contenteditable',true);
  552. }
  553. if(component) component.removeAttr('readonly');
  554. });
  555. }
  556. });
  557. },
  558. //@TODO:
  559. // - Voir pour fusionner avec $.fill
  560. // - Voir pour respecter la sémantique (addLine supprime les anciennes lignes...)
  561. addLine: function (rows){
  562. return this.each(function() {
  563. var obj = $(this);
  564. var model = null;
  565. var container = null;
  566. if(obj.prop("tagName") == 'UL'){
  567. container = obj;
  568. model = container.find('li:first-child');
  569. container.find('li:visible').remove();
  570. }else if(obj.prop("tagName") == 'TABLE'){
  571. container = obj.find('tbody');
  572. model = container.find('tr:first-child');
  573. container.find('tr:visible').remove();
  574. }else{
  575. container = obj;
  576. childName = container.children().get(0).nodeName;
  577. model = container.find(childName+':first-child');
  578. container.find(childName+':visible:not(.nofill)').remove();
  579. }
  580. var tpl = model.get(0).outerHTML;
  581. //fix jquery backslahes break
  582. tpl = tpl.replace(/{{##/g,'{{/').replace(/{{\/(.*)}}=""/g,'{{/$1}}');
  583. //fix images url not found on template
  584. tpl = tpl.replace(/(<img\s[^>]*\s)(data-src)/g,'$1src');
  585. for(var key in rows){
  586. var line = $(Mustache.render(tpl,rows[key]));
  587. container.append(line);
  588. line.removeClass('hidden');
  589. }
  590. });
  591. },
  592. //Retourne un node jquery visible à partir d'un
  593. //modèle de template invisible + données mustache
  594. template: function(data){
  595. return $(Mustache.render($(this).get(0).outerHTML,data)).removeClass('hidden');
  596. },
  597. //Permet le resize d'un panel vertical
  598. panelResize : function(options){
  599. var obj = $(this);
  600. var o = $.extend({
  601. handlerClass : 'panel-resize-handler',
  602. hoverClass : 'panel-resize-handler-hover',
  603. handlerWidth : 10,
  604. direction : 'right',
  605. minWidth : 30,
  606. maxWidth : 700,
  607. update : function(data){}
  608. }, options);
  609. var handlerId = 'handler-'+generate_uuid(10);
  610. var handler = $('<div id="'+handlerId+'" class="'+o.handlerClass+'" style="cursor:col-resize;opacity:0.1;position:fixed;z-index:1000;height:'+obj.outerHeight()+'px;width:'+o.handlerWidth+'px;"></div>');
  611. var timeout = null;
  612. var left;
  613. if(o.direction == 'right'){
  614. left = obj.offset().left + obj.outerWidth() - (o.handlerWidth/2);
  615. }else if (o.direction == 'left'){
  616. left = obj.offset().left - (o.handlerWidth/2);
  617. }
  618. handler.css({
  619. left : left,
  620. top : obj.offset().top
  621. });
  622. handler.hover(function(){
  623. clearTimeout(timeout);
  624. handler.addClass(o.hoverClass);
  625. },function(){
  626. timeout = setTimeout(function(){
  627. handler.removeClass(o.hoverClass);
  628. },300);
  629. });
  630. if(!window.handlerActive) window.handlerActive = {};
  631. if(!window.handlerPosition) window.handlerPosition = {};
  632. handler.mousedown(function(){
  633. window.handlerPosition[handlerId] = handler.offset().left;
  634. window.handlerPanelWidth = obj.outerWidth();
  635. window.handlerActive[handlerId] = true;
  636. });
  637. $(document).mouseup(function(){
  638. window.handlerActive[handlerId] = false;
  639. var left;
  640. if(o.direction == 'right'){
  641. left = obj.offset().left + obj.outerWidth() - (o.handlerWidth/2);
  642. }else if (o.direction == 'left'){
  643. left = obj.offset().left-(o.handlerWidth/2);
  644. }
  645. handler.css({left : left});
  646. o.update({
  647. element : obj,
  648. handler : handler,
  649. width : obj.outerWidth()
  650. });
  651. });
  652. $(document).mousemove(function( event ) {
  653. if(!window.handlerActive[handlerId]) return;
  654. var newPosition = (event.pageX-2);
  655. var negativeWidth = window.handlerPosition[handlerId] - newPosition;
  656. var newWidth = window.handlerPanelWidth- (negativeWidth * (o.direction== 'right'?1:-1) );
  657. if(newWidth < o.minWidth || newWidth <1 || newWidth > o.maxWidth ){
  658. return;
  659. }
  660. handler.css({
  661. left : newPosition+'px'
  662. });
  663. obj.css({
  664. width : newWidth+'px',
  665. maxWidth : newWidth+'px'
  666. });
  667. });
  668. obj.append(handler);
  669. return handlerId;
  670. },
  671. fill: function (option,callback,progress){
  672. return this.each(function() {
  673. var obj = $(this);
  674. var model = null;
  675. var container = null;
  676. option = $.extend({
  677. differential : false,
  678. showing : function(item,key,data){
  679. //permet la personnalisation de l'apparition des lignes (removeClass('hidden') par defaut)
  680. item.removeClass('hidden');
  681. }
  682. },option);
  683. var preloader = null;
  684. if(option.preloader){
  685. var preloader = $(option.preloader);
  686. preloader.css('position','absolute');
  687. preloader.css('left',(obj.offset().left+obj.width()/2)+'px');
  688. preloader.css('top',(obj.offset().top+30)+'px');
  689. $('body').append(preloader);
  690. }
  691. var paginationPreferenceUid = ($.urlParam('module')?$.urlParam('module'):'main')+'-'+($.urlParam('page')?$.urlParam('page') :'main')+'-'+obj.attr('id');
  692. var paginationPreference = $.storage('pagination');
  693. if(paginationPreference[paginationPreferenceUid]){
  694. //si une valeur est présente dans le localstorage on la prend
  695. option.itemPerPage = paginationPreference[paginationPreferenceUid];
  696. }else{
  697. //si pas de valeur en local storage mais une valeur par defaut, on la prend
  698. var paginationPreference = $('[data-type="pagination-preference"][data-table="#'+obj.attr('id')+'"]');
  699. if(paginationPreference.length >0) option.itemPerPage = paginationPreference.attr('data-value');
  700. }
  701. //Permet l'affichage d'un élément HTML si aucun résultat ne ressort
  702. if(option.empty) $(option.empty+':not(.hidden)').remove();
  703. if(obj.prop("tagName") == 'UL'){
  704. container = obj;
  705. model = container.find('li:first-child');
  706. if(!option.export && !option.differential) container.children('li:not(.hidden)').remove();
  707. } else if(obj.prop("tagName") == 'TABLE'){
  708. container = $('>tbody',obj);
  709. model = container.find('tr:first-child');
  710. if(!option.export && !option.differential) container.children('tr:not(.hidden)').remove();
  711. /** début colonnes dynamiques **/
  712. var slug = obj.attr('data-slug');
  713. if(slug){
  714. //Refresh du dropdown de colonnes
  715. var dynamic_table_refresh_menu = function(table){
  716. var picker = $('.dynamic-table-picker .dropdown-menu',table);
  717. picker.html('');
  718. var columns = dynamic_table_columns(table,true);
  719. for(var i in columns.deleted){
  720. var th = columns.deleted[i];
  721. if(!th.hasClass('hidden')) continue;
  722. picker.append('<div class="dropdown-item px-2 pointer" data-slug="'+th.attr('data-available')+'" title="Ajouter la colonne"><i class="text-muted fas fa-sm fa-angle-right mr-1"></i>'+th.text()+'</div>');
  723. };
  724. if(!picker.html().length) picker.append('<div class="dropdown-item px-2 text-muted user-select-none">Aucune colonne disponible</div>');
  725. };
  726. //Enregistrement de l'affichage des colonnes
  727. var dynamic_table_save = function(table,callback){
  728. var columns = dynamic_table_columns(table);
  729. if($('#loginHeader').attr('data-connected') == ''){
  730. var localColumns = $.storage('component.tablesearch.columns');
  731. localColumns[table.attr('data-slug')] = columns.added;
  732. $.storage('component.tablesearch.columns',localColumns);
  733. if(callback) callback();
  734. }else{
  735. $.action({
  736. action : 'core_table_colum_save',
  737. slug : table.attr('data-slug'),
  738. added : columns.added
  739. },function(){
  740. if(callback) callback();
  741. });
  742. }
  743. }
  744. //Récupération de l'état des colonnes affichées/masquées
  745. var dynamic_table_columns = function(table,getElement){
  746. table = $(table);
  747. var columns = {added:[],deleted:[]};
  748. $('th[data-available]',table).each(function(){
  749. var th = $(this);
  750. var thSlug = th.attr('data-available');
  751. if(th.hasClass('hidden')) {
  752. columns.deleted.push(!getElement ? thSlug : th);
  753. }else{
  754. columns.added.push(!getElement ? thSlug : th);
  755. }
  756. });
  757. return columns;
  758. }
  759. option.columns = dynamic_table_columns(obj);
  760. var plusButton = $('.dynamic-table-picker',obj);
  761. if(!plusButton.length){
  762. plusButton = $('<div class="dropdown dynamic-table-picker">\
  763. <div data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" class="text-right">\
  764. <i class="fas fa-plus text-success dynamic-table-add" title="Ajouter une colonne" data-tooltip></i></div>\
  765. <div class="dropdown-menu dropdown-menu-right"></div></div>');
  766. $('thead tr:eq(0) th:last-child',obj).append(plusButton);
  767. $('thead tr:eq(0) th[data-available]',obj).append('<i class="fas fa-times text-danger dynamic-table-delete ml-2" title="Supprimer la colonne" data-tooltip></i>');
  768. //Gestion events
  769. $('.dynamic-table-delete',obj).click(function(){
  770. element = $(this).closest('th');
  771. var th = $('>thead th[data-available="'+element.attr('data-available')+'"]',obj);
  772. th.addClass('hidden');
  773. $('> tbody > tr',obj).each(function(){
  774. $('> td:eq('+th.index()+')',this).addClass('hidden');
  775. });
  776. dynamic_table_refresh_menu(obj);
  777. dynamic_table_save(obj);
  778. });
  779. $(plusButton).off('click').on('click','.dropdown-item[data-slug]',function(){
  780. element = $(this);
  781. var th = $('thead th[data-available="'+element.attr('data-slug')+'"]',obj);
  782. th.removeClass('hidden');
  783. $('tbody tr',obj).each(function(){
  784. $('td:eq('+th.index()+')',this).removeClass('hidden');
  785. });
  786. dynamic_table_refresh_menu(obj);
  787. dynamic_table_save(obj,function(){
  788. if(obj.attr('data-column-search')) window[obj.attr('data-column-search')]();
  789. });
  790. });
  791. }
  792. var addDynamicColumns = function(added){
  793. $('th[data-available]',obj).each(function(){
  794. var th = $(this);
  795. var td = $('tbody tr td:eq('+th.index()+')',obj);
  796. if(added.indexOf(th.attr('data-available')) !== -1) {
  797. th.removeClass('hidden');
  798. td.removeClass('hidden');
  799. return;
  800. }
  801. th.addClass('hidden');
  802. td.addClass('hidden');
  803. });
  804. dynamic_table_refresh_menu(obj);
  805. }
  806. if($('#loginHeader').attr('data-connected') == ''){
  807. var localColumns = $.storage('component.tablesearch.columns');
  808. var added = localColumns[slug] ? localColumns[slug] : [];
  809. addDynamicColumns(added);
  810. }else{
  811. $.action({
  812. action : 'core_table_colum_search',
  813. async : false,
  814. },function(preferences){
  815. plusButton.addClass('btn-preloader');
  816. var added = preferences[slug] ? preferences[slug] : [];
  817. addDynamicColumns(added);
  818. });
  819. }
  820. }
  821. /** fin colonnes dynamiques **/
  822. } else if(obj.prop("tagName") == "SELECT"){
  823. container = obj;
  824. model = container.find('option[value*="{{"]');
  825. if(model.length==0) model = $('<option class="hidden" value="{{value}}">{{label}}</option>');
  826. if(!option.export && !option.differential) container.find('option:not([value*="{{"])').remove();
  827. } else{
  828. container = obj;
  829. childName = container.children().get(0).nodeName;
  830. model = container.find(childName+':first-child');
  831. if(!option.export && !option.differential) container.find(childName+':visible:not(.nofill)').remove();
  832. }
  833. var tpl = model.get(0).outerHTML;
  834. //fix jquery backslashes break
  835. tpl = tpl.replace(/{{##/g,'{{/').replace(/{{\/(.*)}}=""/g,'{{/$1}}');
  836. //fix images url not found on template
  837. tpl = tpl.replace(/(<img\s[^>]*\s?)(data-src)/g,'$1src');
  838. var pagination =
  839. obj.attr('data-pagination')!=null && $(obj.attr('data-pagination')).length ?
  840. $(obj.attr('data-pagination')) :
  841. (obj.nextAll('.pagination').length ?
  842. obj.nextAll('.pagination') :
  843. obj.parent().nextAll('.pagination')
  844. );
  845. if(pagination.length!=0) {
  846. var activePage = $('li.active', pagination);
  847. if($.urlParam('p')==null || $.urlParam('p') <= 0 || activePage.length > 0)
  848. option.page = activePage.attr('data-value');
  849. else
  850. option.page = parseInt($.urlParam('p') - 1);
  851. }
  852. tpl = tpl.replace(/(data-style)/g, 'style');
  853. if(option.export) option.downloadResponse = true;
  854. //on clone l'objet option pour ne transmettre que des datas utiles
  855. data = $.extend({},option);
  856. delete data.showing;
  857. delete data.templating;
  858. $.action(data,function(r){
  859. //On ne gere la pagination et l'affichage tableau que si on est pas en mode export
  860. if(!option.export){
  861. if(option.differential){
  862. //suppression des élements dom qui ne sont plus en db
  863. $('>[data-id]:visible',container).each(function(i,line){
  864. var line = $(line);
  865. toDelete = true;
  866. for(var key in r.rows){
  867. if(line.attr('data-id') == r.rows[key].id){
  868. toDelete = false;
  869. break;
  870. }
  871. }
  872. if(toDelete) line.remove();
  873. });
  874. }
  875. //var activeIds = [];
  876. for(var key in r.rows){
  877. var line;
  878. var data = r.rows[key];
  879. var lineTpl = tpl;
  880. if(option.templating) lineTpl = option.templating(data,line,tpl);
  881. if(!option.differential){
  882. line = $(Mustache.render(lineTpl,data));
  883. container.append(line);
  884. option.showing(line,key,data);
  885. }else{
  886. //activeIds.push(data.id);
  887. var existing = $('> [data-id="'+data.id+'"]',container);
  888. //existe en data et pas dans le dom : ajout
  889. if(existing.length == 0){
  890. line = $(Mustache.render(lineTpl,data));
  891. line.attr('data-update-tag',data.updated);
  892. if(key==0){
  893. container.append(line);
  894. }else{
  895. var previousIndex = key-1;
  896. var previous = $('>[data-id]:visible',container).eq(previousIndex);
  897. previous.after(line);
  898. }
  899. option.showing(line,key,data);
  900. }else{
  901. //existe en data et dans le dom et pas de modification : on passe au suivant
  902. if(existing.attr('data-update-tag') == data.updated){
  903. continue;
  904. //existe en data et dans le dom mais a été modifié : on remplace
  905. }else{
  906. line = $(Mustache.render(lineTpl,data));
  907. line.attr('data-update-tag',data.updated);
  908. existing.after(line);
  909. existing.remove();
  910. option.showing(line,key,data);
  911. }
  912. }
  913. }
  914. }
  915. if(!r.rows || !r.rows.length && option.empty) obj.after($(option.empty).removeClass('hidden'));
  916. if(option.differential){
  917. var existingDom = [];
  918. for(var key in r.rows){
  919. var data = r.rows[key];
  920. var line = $('>[data-id="'+r.rows[key].id+'"]',container);
  921. var index = line.index() -1;
  922. if(key != index){
  923. var prevIndex = (key-1);
  924. if(prevIndex<0){
  925. container.prepend(line.detach());
  926. }else{
  927. var prevElement = $('>[data-id]:visible()',container).eq(prevIndex);
  928. prevElement.after(line.detach());
  929. }
  930. }
  931. }
  932. }
  933. if(r.pagination){
  934. $('.page-item-previous,.page-item-next').remove();
  935. r.pagination.pages = Math.ceil(r.pagination.pages);
  936. var previewNumber = pagination.attr('data-range') != null && pagination.attr('data-range').length ? parseInt(pagination.attr('data-range')) : 5;
  937. previewNumber = previewNumber < 2 ? 2 : previewNumber;
  938. //Possible values: simple, numbers, full
  939. var controls = pagination.attr('data-control') != null && pagination.attr('data-control').length ? pagination.attr('data-control') : "full";
  940. var template = pagination.find('li:not(:visible)');
  941. if(template.length>0 ){
  942. template = template.get(0).outerHTML;
  943. $('li:not(:eq(0))',pagination).remove();
  944. var current = parseInt(r.pagination.current);
  945. var previousPages = current-previewNumber;
  946. var nextPages = current+previewNumber;
  947. var dropDownTpl = '<div data-toggle="dropdown"><i class="fas fa-ellipsis-h pointer"></i></div><div class="dropdown-menu pagination-select">{{#choices}}<span class="dropdown-item">{{.}}</span>{{/choices}}</div>';
  948. for(i=0;i<r.pagination.pages;i++){
  949. var li = $(Mustache.render(template,{value:i,label:i+1}));
  950. if(
  951. (previousPages < i && i < nextPages)
  952. || i==0 || i==r.pagination.pages-1
  953. ){
  954. li.removeClass('hidden');
  955. if(i==current) li.addClass('active');
  956. }else{
  957. if(i==1 || i==r.pagination.pages-2){
  958. var dotli = li.clone()
  959. var start = i==1 ? 2: current+previewNumber+1;
  960. var end = i==1 ? (current+1)-previewNumber : r.pagination.pages-1;
  961. var choices = [];
  962. for(u=start; u<end+1;u++)
  963. choices.push(u);
  964. rightDropDown = $(Mustache.render(dropDownTpl,{choices:choices}));
  965. dotli.removeClass('hidden')
  966. .attr('title','Voir les pages '+(start)+' à '+(end))
  967. .removeAttr('onclick')
  968. .css('position','relative')
  969. .addClass('page-link').html(rightDropDown);
  970. pagination.append(dotli);
  971. }
  972. }
  973. pagination.append(li);
  974. }
  975. if($('li.active',pagination).length) $.urlParam('p', parseInt($('li.active',pagination).attr('data-value')) + 1);
  976. if(current!=0){
  977. var prev = $('<li class="page-item page-item-previous"><span class="page-link" ><span><i class="fas fa-angle-left"></i></span></span></li>');
  978. pagination.prepend(prev);
  979. prev.click(function(e){
  980. if ($(e.target).closest('span.page-link').hasClass('disabled')) return;
  981. pagination.find('li[data-value="'+(current-1)+'"]').trigger('click');
  982. });
  983. }
  984. if(current<r.pagination.pages-1){
  985. var next = $('<li class="page-item page-item-next"><span class="page-link" ><span><i class="fas fa-angle-right"></i></span></span></li>');
  986. pagination.append(next);
  987. next.click(function(e){
  988. if ($(e.target).closest('span.page-link').hasClass('disabled')) return;
  989. pagination.find('li[data-value="'+(current+1)+'"]').trigger('click');
  990. });
  991. }
  992. //Affiche uniquement les flèches précédent / suivant de contrôle de la pagination
  993. if(controls == "simple") pagination.find('li[data-value]:not(.hidden)').addClass('hidden');
  994. //Affiche uniquement les n° de page de contrôle de la pagination
  995. if(controls == "numbers") pagination.find('li:not([data-value]):not(.hidden)').addClass('hidden');
  996. var visiblePageCount = pagination.find('.page-item:not(.hidden)').length;
  997. if(visiblePageCount>1){
  998. pagination.find('.page-item:not(.hidden):first .page-link').css('border-radius','0.25rem 0 0 0.25rem');
  999. pagination.find('.page-item:not(.hidden):last .page-link').css('border-radius','0 0.25rem 0.25rem 0');
  1000. }else{
  1001. pagination.find('.page-item:not(.hidden):first .page-link').css('border-radius','0.25rem 0.25rem 0.25rem 0.25rem');
  1002. }
  1003. $('.pagination-select span',pagination).click(function(){
  1004. var selectedPage = (parseInt($(this).text())-1);
  1005. pagination.find('li[data-value="'+selectedPage+'"]').trigger('click');
  1006. });
  1007. }
  1008. }
  1009. }
  1010. if(preloader) preloader.remove();
  1011. if(callback!=null)callback(r);
  1012. },null,function(percent,type){
  1013. if(progress) progress(percent,type);
  1014. });
  1015. });
  1016. },
  1017. enter: function (option){
  1018. return this.each(function() {
  1019. var obj = $(this);
  1020. obj.keydown(function(event){
  1021. if(event.keyCode == 13){
  1022. option();
  1023. return false;
  1024. }
  1025. });
  1026. });
  1027. },
  1028. sortable_table: function (option){
  1029. if(option=='get'){
  1030. var obj = $(this);
  1031. var response = {};
  1032. obj.find('thead th[data-sortable]').each(function(i,th){
  1033. var th = $(th);
  1034. if(th.attr('data-sort') && th.attr('data-sort')!=""){
  1035. response = {
  1036. sort : th.attr('data-sort'),
  1037. sortable : th.attr('data-sortable')
  1038. };
  1039. }
  1040. });
  1041. return response;
  1042. }
  1043. return this.each(function() {
  1044. var obj = $(this);
  1045. obj.find('thead th[data-sortable]').click(function(e){
  1046. if($(e.target).hasClass('dynamic-table-delete')) return;
  1047. var th = $(this);
  1048. var data = th.data();
  1049. if(!data.sort || data.sort==''){
  1050. data.sort = 'asc'
  1051. }else if(data.sort == 'asc'){
  1052. data.sort ='desc'
  1053. }else{
  1054. data.sort = '';
  1055. }
  1056. obj.find('thead th').removeClass('sort-asc').removeClass('sort-desc').removeAttr('data-sort');
  1057. if(data.sort!='') th.addClass('sort-'+data.sort);
  1058. th.attr('data-sort',data.sort);
  1059. if(option.onSort) option.onSort();
  1060. });
  1061. });
  1062. },
  1063. autocomplete: function(o){
  1064. return this.each(function() {
  1065. var obj = $(this);
  1066. if(o == 'off') return obj.typeahead('destroy');
  1067. obj.attr('autocomplete','new-password');
  1068. var option = obj.data();
  1069. option = $.extend(option,o);
  1070. if(!option.delay) option.delay = 1;
  1071. var timeout = null;
  1072. obj.typeahead({
  1073. items: (o.items) ? o.items : 5,
  1074. minLength: o.suggest ? 0 : (o.minLength ? o.minLength : 2) ,
  1075. fitToElement: false,
  1076. autoSelect : false,
  1077. selectOnBlur : false,
  1078. displayText : function(item){
  1079. return (o.skin) ? o.skin(item,obj) : item.name || item;
  1080. },
  1081. source: function(keyword, response){
  1082. if (timeout) clearTimeout(timeout) ;
  1083. timeout = setTimeout(function() {
  1084. if(o.onKeypress) o.onKeypress(obj);
  1085. if(o.dynamicData) o.data = $.extend(o.data,o.dynamicData());
  1086. if(o.data) o.data.limit = (o.items) ? o.items : 5;
  1087. $.action({
  1088. action: option.action,
  1089. keyword: obj.val(),
  1090. data: o.data
  1091. },function(r){
  1092. if(r.rows != null) response(r.rows);
  1093. });
  1094. },option.delay);
  1095. },
  1096. highlighter: function(item){
  1097. if(o.highlight) {
  1098. return o.highlight(item)
  1099. }
  1100. else {
  1101. var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
  1102. return item.replace(new RegExp('('+query+')', 'ig'), function($1, match){
  1103. return '<strong>'+match+'</strong>';
  1104. });
  1105. }
  1106. },
  1107. matcher: function(r){
  1108. if(obj.val() == r.name && o.selectEqual!==false)
  1109. if(o.onClick)
  1110. o.onClick(r,obj);
  1111. return '<div>'+r.name+'</div>';
  1112. },
  1113. afterSelect: function(item) {
  1114. obj.data('selected',true);
  1115. if(o.onClick) o.onClick(item,obj);
  1116. obj.trigger("change");
  1117. }
  1118. }).blur(function(){
  1119. if(o.onBlur) o.onBlur(obj);
  1120. });
  1121. if(o.suggest){
  1122. obj.off("click").on("click", function () {
  1123. if(typeof o.suggest === 'function' && !o.suggest()) return;
  1124. ev = $.Event("keydown");
  1125. ev.keyCode = ev.which = 40;
  1126. $(this).trigger(ev);
  1127. return true
  1128. });
  1129. }
  1130. obj.data('typeahead').next = function (event) {
  1131. var active = this.$menu.find('.active').removeClass('active');
  1132. var next = active.next();
  1133. if (!next.length) {
  1134. next = $(this.$menu.find($(this.options.item || this.theme.item).prop('tagName'))[0]);
  1135. }
  1136. while (next.hasClass('divider') || next.hasClass('dropdown-header')) {
  1137. next = next.next();
  1138. }
  1139. next.addClass('active');
  1140. var newVal = this.updater(next.data('value'));
  1141. this.$element.val(newVal.name || newVal.label || newVal);
  1142. };
  1143. obj.data('typeahead').prev = function (event) {
  1144. var active = this.$menu.find('.active').removeClass('active');
  1145. var prev = active.prev();
  1146. if (!prev.length){
  1147. prev = this.$menu.find($(this.options.item || this.theme.item).prop('tagName')).last();
  1148. }
  1149. while (prev.hasClass('divider') || prev.hasClass('dropdown-header')) {
  1150. prev = prev.prev();
  1151. }
  1152. prev.addClass('active');
  1153. var newVal = this.updater(prev.data('value'));
  1154. if (this.changeInputOnMove) this.$element.val(newVal.name || newVal.label || newVal);
  1155. };
  1156. });
  1157. },
  1158. location: function (options){
  1159. return this.each(function() {
  1160. var obj = $(this);
  1161. var o = $.extend({},options);
  1162. obj.off('keyup').on('keyup', function(event) {
  1163. if(!$(this).val().length) return;
  1164. });
  1165. obj.autocomplete({
  1166. action: 'core_location_search',
  1167. delay : 300,
  1168. force: (o.force ? o.force : false),
  1169. items: (o.items ? o.items : 8),
  1170. data: {
  1171. maxresults: (o.items ? o.items : 8),
  1172. language: (o.language ? o.language : "fr"),
  1173. country: (o.country ? o.country : ""),
  1174. },
  1175. skin : function(item){
  1176. var html = '<small class="text-muted"><i class="fas fa-map-marker-alt d-inline-block mr-2 text-muted"></i>';
  1177. var re = new RegExp(obj.val(),"gi");
  1178. name = item.label.replace(re, function (x) {
  1179. return '<strong class="text-primary">'+x+'</strong>';
  1180. });
  1181. html += name+'</small>';
  1182. return html;
  1183. },
  1184. highlight : function(item){
  1185. return item;
  1186. },
  1187. onClick : function(selected,element){
  1188. obj.val(selected.label);
  1189. if(!is_empty_obj(selected.address)) {
  1190. var infos = {
  1191. number: selected.address.houseNumber ? selected.address.houseNumber : '',
  1192. street: selected.address.street ? selected.address.street : '',
  1193. district: selected.address.district ? selected.address.district : '',
  1194. city: selected.address.city ? selected.address.city : '',
  1195. county: selected.address.county ? selected.address.county : '',
  1196. state: selected.address.state ? selected.address.state : '',
  1197. country: selected.address.country ? selected.address.country : '',
  1198. zip: selected.address.postalCode ? selected.address.postalCode : '',
  1199. };
  1200. infos.address = (infos.street && infos.street.length && infos.number && infos.number.length) ? infos.number+' '+infos.street : (infos.street ? infos.street : '');
  1201. obj.removeData();
  1202. obj.data(infos);
  1203. if(o.select) o.select(infos);
  1204. if(o.geocode) o.geocode(selected);
  1205. }
  1206. },
  1207. onBlur : function(selected,element){
  1208. },
  1209. onCancel : function(element){
  1210. obj.val('');
  1211. }
  1212. });
  1213. });
  1214. },
  1215. colorInput: function (options) {
  1216. return this.each(function () {
  1217. var o = $.extend({
  1218. choices : [
  1219. '#34495e','#9b59b6','#3498db','#2ecc71','#1abc9c',
  1220. '#95a5a6','#ecf0f1','#e74c3c','#e67e22','#f1c40f',
  1221. '#d35400','#55E6C1','#EAB543','#F8EFBA','#FD7272',
  1222. '#3B3B98','#B33771','#6c5ce7','#0984e3','#00cec9',
  1223. '#fd79a8','#d63031','#a29bfe','#55efc4'
  1224. ]
  1225. },options);
  1226. var input = $(this);
  1227. input.hide();
  1228. var component = input.data('data-component');
  1229. if(component){
  1230. //reload
  1231. }else{
  1232. var component = $('<div class="component-color d-flex form-control" title="Ouvrir la palette"><div class="component-color-thumb"></div><div class="component-color-picker hidden"></div><input type="color" class="component-color-palette"></div>');
  1233. //load
  1234. input.data('data-component',component);
  1235. input.after(component);
  1236. component.append(input.detach());
  1237. init_tooltips(component);
  1238. }
  1239. var picker = $('.component-color-picker',component);
  1240. var palette = $('.component-color-palette',component);
  1241. var thumb = $('.component-color-thumb',component);
  1242. thumb.css('backgroundColor',input.val());
  1243. var html = '<ul>';
  1244. for(var k in o.choices){
  1245. html += '<li><div class="color-choice" data-color="'+o.choices[k]+'" style="background-color:'+o.choices[k]+'"></div></li>';
  1246. }
  1247. html += '<li><div class="other-color" title="Autre couleur"><i class="fas fa-palette"></i></div></li>';
  1248. html += '</ul>';
  1249. picker.html(html);
  1250. $('.other-color',component).click( function(){
  1251. palette.click();
  1252. });
  1253. palette.change(function(){
  1254. input.val(palette.val()).change();
  1255. picker.addClass('hidden');
  1256. });
  1257. input.change(function(){
  1258. thumb.css('backgroundColor',input.val());
  1259. });
  1260. component.click(function(e){
  1261. e.stopPropagation();
  1262. if(input.is('[readonly]') || input.is('[disabled]')) return;
  1263. if($(e.target).closest('.component-color-picker').length || $(e.target).closest('.component-color-palette').length) return;
  1264. picker.removeClass('active');
  1265. setTimeout(function(){
  1266. picker.removeClass('hidden');
  1267. setTimeout(function(){picker.addClass('active')},50);
  1268. },150);
  1269. });
  1270. $('.color-choice',picker).click(function(e){
  1271. e.stopPropagation();
  1272. picker.removeClass('active');
  1273. setTimeout(function(){picker.addClass('hidden')},200);
  1274. input.val($(this).attr('data-color')).change();
  1275. });
  1276. $(document).click(function() {
  1277. picker.removeClass('active');
  1278. setTimeout(function(){picker.addClass('hidden')},200);
  1279. });
  1280. if(input.hasAttr('required')) component.attr('required','');
  1281. if(input.hasAttr('readonly')) component.attr('readonly','');
  1282. });
  1283. },
  1284. date: function (options){
  1285. return this.each(function(){
  1286. var obj = $(this);
  1287. obj.on('paste', function(e){
  1288. is_valid_date(e.originalEvent.clipboardData.getData('Text')) ? obj.val("") : e.preventDefault();
  1289. });
  1290. //Jours ouvrés
  1291. var minDate = options.beginDate;
  1292. var beforeShowDay = '';
  1293. if(options.workdays){
  1294. var minDate = new Date();
  1295. var daysToAdd = [0, 0, 2, 2, 2, 2, 1];
  1296. minDate.setDate(minDate.getDate()+parseInt(options.beginDate)+daysToAdd[minDate.getDay()]);
  1297. beforeShowDay = $.datepicker.noWeekends;
  1298. }
  1299. obj.datepicker({
  1300. dateFormat: options.dateFormat,
  1301. dayNames: ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"],
  1302. dayNamesMin: ["Di", "Lu", "Ma", "Me", "Je", "Ve", "Sa"],
  1303. dayNamesShort: ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"],
  1304. monthNames: ["Janvier","Février","Mars","Avril","Mai","Juin","Juillet","Aout","Septembre","Octobre","Novembre","Décembre"],
  1305. monthNamesShort: ["Jan","Fév","Mars","Avr","Mai","Juin","Juil","Aout","Sept","Oct","Nov","Déc"],
  1306. firstDay: 1,
  1307. minDate: minDate,
  1308. beforeShowDay: beforeShowDay,
  1309. maxDate: options.endDate,
  1310. changeMonth: true,
  1311. yearRange: "-100:+30",
  1312. changeYear: true,
  1313. onSelect: function(dateText, inst){
  1314. obj.trigger("blur");
  1315. obj.trigger("change");
  1316. },
  1317. beforeShow: function(){
  1318. if(obj.is('[readonly]')) return false;
  1319. }
  1320. }).keypress(function(event){
  1321. if(event.key=='/') event.preventDefault();
  1322. var length = obj.val().length;
  1323. if(length == 2 || length == 5) obj.val(obj.val()+'/');
  1324. }).blur(function(event){
  1325. obj.removeClass('border border-danger');
  1326. if(obj.val()=='') return;
  1327. var segments = obj.val().split('/');
  1328. if(segments.length!=3) return;
  1329. if(segments[0] > 31 || segments[1] > 12) obj.addClass('border border-danger');
  1330. }).attr('maxlength','10');
  1331. obj.attr('placeholder',options.placeholder);
  1332. obj.attr('pattern',"^(\\d{2}(?:\\d{2})?)\/(\\d{2})\/(\\d{2}(?:\\d{2})?)$");
  1333. obj.attr('title','Format '+options.dateFormat);
  1334. });
  1335. },
  1336. phone: function (options){
  1337. return this.each(function(){
  1338. var obj = $(this);
  1339. options = $.extend({
  1340. regex: '^[0-9]{10}$',
  1341. emptyField: false,
  1342. spacing: true,
  1343. typeOnly: '[0-9\+\s]',
  1344. invalidClass: 'invalid-value',
  1345. },options);
  1346. if(options.typeOnly){
  1347. obj.keydown(function(event){
  1348. if((event.ctrlKey && ['v','a','c','x'].indexOf(event.key) !== -1) || ['Backspace','ArrowLeft','ArrowRight',' '].indexOf(event.key) !== -1) return true;
  1349. var regexType = RegExp(options.typeOnly, 'g');
  1350. return regexType.test(event.key);
  1351. });
  1352. }
  1353. obj.blur(function(event){
  1354. var value = obj.val()
  1355. .trim()
  1356. .replace(/\s/ig,'')
  1357. .replace(/\+33/ig,'0');
  1358. if(value=='') return obj.removeClass(options.invalidClass);;
  1359. var regex = RegExp(options.regex, 'g');
  1360. if(!regex.test(value)){
  1361. obj.addClass(options.invalidClass);
  1362. if(options.emptyField) value = '';
  1363. } else {
  1364. if(options.spacing) value = value.match(/.{1,2}/g).join(' ');
  1365. obj.val(value);
  1366. obj.removeClass(options.invalidClass);
  1367. }
  1368. if(obj.data('blur')) eval(obj.data('blur'));
  1369. });
  1370. });
  1371. },
  1372. hour: function (options){
  1373. return this.each(function() {
  1374. var obj = $(this);
  1375. obj.on('paste', function(e){
  1376. is_valid_hour(e.originalEvent.clipboardData.getData('Text')) ? obj.val("") : e.preventDefault();
  1377. });
  1378. obj.timepicker({
  1379. scrollDefault: 'now',
  1380. timeFormat: options.timeFormat,
  1381. step: options.step,
  1382. onSelect: function(timeText, inst) {
  1383. obj.trigger("blur");
  1384. obj.trigger("change");
  1385. }
  1386. }).keydown(function(e) {
  1387. //Si c'est pas un chiffre ou :
  1388. if(!input_number_control(e)) return false;
  1389. //Si on efface et qu'on est après les deux points, on les supprime et le chiffre qui précède aussi
  1390. if(e.key=='Backspace' && obj.val().substr(obj.val().length-1)==':'){
  1391. e.preventDefault();
  1392. obj.val(obj.val().substring(0, obj.val().length-2));
  1393. //Premier digit avant les deux points supérieur à 2, on ajoute un 0 devant
  1394. } else if((obj.val().length==0 || get_selected_text(obj).length == obj.val().length) && e.key>2) {
  1395. e.preventDefault();
  1396. obj.val('0'+e.key+':');
  1397. //Gestion des deux points entre heures et minutes
  1398. } else if(obj.val().length==1 && e.key.match(/[0-9:]/)) {
  1399. e.preventDefault();
  1400. obj.val(obj.val()+e.key+':');
  1401. //Si le 1er digit après les deux points est supérieur à 5, on bloque
  1402. } else if(obj.val().length==3 && e.key>5 && get_selected_text(obj).length<2) {
  1403. e.preventDefault();
  1404. }
  1405. }).blur(function(event){
  1406. obj.removeClass('border border-danger')
  1407. if(obj.val()=='') return;
  1408. var segments = obj.val().split(':');
  1409. if(!segments[1]){
  1410. obj.val(obj.val()+'00');
  1411. segments[1] = '00';
  1412. }
  1413. if(segments.length!=2)return;
  1414. if(segments[0] > 23 || segments[1] > 59) obj.addClass('border border-danger');
  1415. }).attr('maxlength','5');
  1416. obj.attr("placeholder",options.placeholder);
  1417. obj.attr('title',"Format hh:mm");
  1418. });
  1419. },
  1420. raiseNumber: function(from,to,time) {
  1421. return this.each(function(){
  1422. var obj = $(this);
  1423. obj.text(from);
  1424. time = time!=null ? time : 100;
  1425. var ratio = (from<=to) ? to/time : from/time;
  1426. var increment = Math.round(ratio)<1 ? 1 : Math.round(ratio);
  1427. increment = (from<=to) ? increment : increment*-1;
  1428. var updateCount = function(obj, increment){
  1429. var count = parseInt(obj.text());
  1430. if((from<=to && count<to) || (from>to && count>=to)) {
  1431. obj.text(count + increment);
  1432. setTimeout(function(){updateCount(obj,increment);}, 1);
  1433. } else {
  1434. obj.text(to);
  1435. }
  1436. };
  1437. updateCount(obj,increment);
  1438. });
  1439. },
  1440. component_autocomplete : function(slug,options){
  1441. if(!slug) $.message('error','Slug non définis pour un composant autocomplete');
  1442. var options = $.extend({
  1443. edit : slug+'_by_uid',
  1444. autocomplete : slug+'_autocomplete',
  1445. force : true,
  1446. nullChars : ['0'],
  1447. data : function(){
  1448. return {};
  1449. },
  1450. editData : function(){
  1451. return {};
  1452. }
  1453. },options);
  1454. return this.each(function(){
  1455. var input = $(this);
  1456. var container;
  1457. if(!input.data("data-component")){
  1458. container = $('<input class="'+input.attr('class')+' data-type-'+slug.replace(/_/ig,'-')+'" type="text" '+(input.attr('placeholder')!=null?'placeholder="'+input.attr('placeholder')+'"':'')+'>');
  1459. input.before(container);
  1460. input.addClass('component-raw-value hidden'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant
  1461. input.data("data-component", container);
  1462. if(input.attr("required")) container.attr("required","");
  1463. if(input.attr("disabled")) container.attr("disabled","");
  1464. if(input.attr("readonly")) container.attr("readonly","");
  1465. } else {
  1466. container = input.data("data-component");
  1467. }
  1468. if(!options.skin){
  1469. options.skin = function(item){
  1470. var html = '';
  1471. var re = new RegExp(container.val(),"gi");
  1472. name = item.label.replace(re, function (x) {
  1473. return '<strong>'+x+'</strong>';
  1474. });
  1475. html += '<div class="'+slug+'-infos"><span>'+name+'</span>';
  1476. html += '<div class="clear"></div>';
  1477. return html;
  1478. }
  1479. }
  1480. if(!options.onClick){
  1481. options.onClick = function(selected,element){
  1482. input.val(selected.id);
  1483. container.val(selected.label);
  1484. input.trigger('click').trigger('change');
  1485. }
  1486. }
  1487. if(!options.onLoad){
  1488. options.onLoad = function(component,item){
  1489. component.container.val(item.label);
  1490. }
  1491. }
  1492. //Gestion des champs déja remplis au chargement de la page
  1493. if(input.val() !='' && options.nullChars.indexOf(input.val())==-1){
  1494. var uid = input.val();
  1495. if(!window.componentQueue[slug]) window.componentQueue[slug] = {timeout : null,components : [],uids :{} };
  1496. clearTimeout(window.componentQueue[slug].timeout);
  1497. var uids = uid.split(',');
  1498. for(var k in uids)
  1499. window.componentQueue[slug].uids[uids[k]] = 1;
  1500. window.componentQueue[slug].components.push({
  1501. input : input,
  1502. container : container,
  1503. values : uids
  1504. });
  1505. container.val('Chargement...');
  1506. window.componentQueue[slug].timeout = setTimeout(function(){
  1507. $.action({
  1508. action : options.edit,
  1509. data : options.editData(slug),
  1510. items : Object.keys(window.componentQueue[slug].uids)
  1511. },function(r){
  1512. if(window.componentQueue[slug]){
  1513. for(var key in window.componentQueue[slug].components){
  1514. var component = window.componentQueue[slug].components[key];
  1515. input = window.componentQueue[slug].components[key].input;
  1516. for(var i in component.values){
  1517. var value = component.values[i];
  1518. if(!r.items[value]) continue;
  1519. options.onLoad(component,r.items[value]);
  1520. //component.container.val(r.items[value].label);
  1521. }
  1522. }
  1523. delete window.componentQueue[slug];
  1524. }
  1525. });
  1526. },50);
  1527. }
  1528. container.keyup(function(e){
  1529. if (e.ctrlKey ||
  1530. e.key == "Control" ||
  1531. e.key == "ArrowUp" ||
  1532. e.key == "ArrowDown" ||
  1533. e.key == "ArrowLeft" ||
  1534. e.key == "ArrowRight" ||
  1535. e.key == "Shift" ||
  1536. e.key == "CapsLock" ||
  1537. e.key == "Tab" ||
  1538. e.key == "Alt" ||
  1539. e.key == "Home" ||
  1540. e.key == "End")
  1541. return;
  1542. input.val('');
  1543. });
  1544. //autocompletion
  1545. container.autocomplete({
  1546. action : options.autocomplete,
  1547. suggest : function(){ return !input.is('[readonly]') && !input.is('[disabled]')},
  1548. dynamicData : options.data,
  1549. skin : options.skin,
  1550. highlight : function(item){
  1551. return item;
  1552. },
  1553. onClick : function(selected,element){
  1554. container.val(selected.label);
  1555. options.onClick(selected,element);
  1556. },
  1557. onBlur : function(selected){
  1558. if((input.attr('data-force')!='false' || options.force) && input.val() == ''){
  1559. container.val('');
  1560. input.trigger('click').trigger('change');
  1561. }
  1562. input.trigger('blur');
  1563. }
  1564. });
  1565. });
  1566. },
  1567. //Chargement des images en dynamique au scroll
  1568. //Par défaut check sur scroll de window sinon sur celui du parent indiqué
  1569. lazyLoad : function(parent, callback) {
  1570. var obj = this;
  1571. var parent = parent!=null ? $(parent) : $(window);
  1572. this.lazyload = function(elem, parent, callback){
  1573. var viewportHeight = parent.height();
  1574. if(!elem.filter(':not([data-lazy=""])').length) return;
  1575. elem.filter(':not([data-lazy=""])').each(function() {
  1576. var element = $(this);
  1577. var elemOffsetTop = element.offset().top;
  1578. //Si parent est $(window) on récupère la valeur du scrollTop, sinon on prend la valeur du offsetTop du parent
  1579. var viewportOffset = viewportHeight + ((parent.get(0)===parent.get(0).window) ? parent.scrollTop() : parent.offset().top);
  1580. //Check si l'image se situe (en px) à 1/3 de sa taille ou moins du bas du viewport du parent pour le requêter
  1581. if(elemOffsetTop - element.height()/3 < viewportOffset && element.attr('data-lazy')){
  1582. var srcImg = element.attr('data-lazy');
  1583. element.removeAttr('data-lazy');
  1584. //Check pour ajouter attribut src (tag IMG) ou mettre en background-image (tag autre)
  1585. element.is('img') ? element.attr({src: srcImg}) : element.css({"background-image": 'url('+srcImg+')'});
  1586. if(callback!=null) {
  1587. elementEvent = !element.is('img') ? $('<img/>').attr('src', srcImg) : element;
  1588. elementEvent.one("load", function() {
  1589. if(!element.is('img')) $(this).remove();
  1590. callback(element);
  1591. }).each(function() {
  1592. if(this.complete) $(this).trigger('load');
  1593. });
  1594. }
  1595. }
  1596. });
  1597. }
  1598. //Initialisation
  1599. this.lazyload(obj,parent,callback);
  1600. //Appel qd parent scroll
  1601. parent.off('scroll').on('scroll', function(){
  1602. if(window.scrollTimer) clearTimeout(window.scrollTimer);
  1603. //Ajout timeout pour éviter de trigger à
  1604. //chaque scroll, on attend que le user stabilise
  1605. window.scrollTimer = setTimeout(function(){
  1606. obj.lazyload(obj,parent,callback);
  1607. }, 25);
  1608. });
  1609. //Appel sur resize
  1610. $(window).resize(function(){
  1611. if(window.scrollTimer) clearTimeout(window.scrollTimer);
  1612. window.scrollTimer = setTimeout(function(){
  1613. obj.lazyload(obj,parent,callback);
  1614. }, 25);
  1615. });
  1616. }
  1617. });