main.js 92 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710
  1. var isProcessing;
  2. //Permet aux composant de mutualiser leurs appels
  3. var componentQueue = {};
  4. const is_mobile = (function(a){
  5. var check = false;
  6. if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))
  7. check = true;
  8. return check;
  9. })(navigator.userAgent||navigator.vendor||window.opera);
  10. $(document).ready(function(){
  11. if($.urlParam('title')!=null){
  12. window.parent.document.title = decodeURI($.urlParam('title'));
  13. }
  14. var page = $.page();
  15. page = page == '' ? 'index' : page;
  16. var init = 'init_'+page;
  17. init = init.replace(/[^a-z_0-9]/g,'_');
  18. init_components();
  19. if($.urlParam('module')==null){
  20. if(window[init]!=null) window[init]($.urlParam());
  21. } else {
  22. var mod = $.urlParam('module').replace(/[^a-z_0-9]/g,'_');
  23. var init = 'init_plugin_'+mod;
  24. if(window[init]!=null) window[init]($.urlParam());
  25. }
  26. //SHOW HTTP ERROR/NOTICE
  27. if ($.urlParam('error') != null) {
  28. $.message('error', decodeURIComponent($.urlParam('error')), 0);
  29. $.urlParam('error', false);
  30. }
  31. if ($.urlParam('info') != null) {
  32. $.message('info', decodeURIComponent($.urlParam('info')));
  33. $.urlParam('info', false);
  34. }
  35. if ($.urlParam('success') != null) {
  36. $.message('success', decodeURIComponent($.urlParam('success')));
  37. $.urlParam('success', false);
  38. }
  39. //Icône menu mobile
  40. $('#mainMenu > button').on('click', function(e){
  41. $('.menu').toggleClass('open');
  42. });
  43. $('.navbar-toggler').on('click', function(e){
  44. if($(e.target).closest('#mainMenu').length) return;
  45. $('#navbarCollapse').collapse('hide');
  46. $('.menu').removeClass('open');
  47. });
  48. //Positionnement nom de l'utilisateur
  49. if($(document).width() <= 767){
  50. var userFullname = $('#loginHeader > .user-dropdown-menu .user-fullname').detach();
  51. $('#loginHeader > .user-dropdown-menu > button.dropdown-toggle').append(userFullname);
  52. }
  53. });
  54. //Changement positionnement nom de l'utilisateur
  55. //connecté au redimensionnement de la fenêtre
  56. $(window).resize(function(event) {
  57. if($(document).width() > 767){
  58. var userFullname = $('#loginHeader > .user-dropdown-menu > button.dropdown-toggle > .user-fullname').detach();
  59. $('#loginHeader > .user-dropdown-menu > .dropdown-menu').prepend(userFullname);
  60. }
  61. if($(document).width() <= 767){
  62. var userFullname = $('#loginHeader > .user-dropdown-menu .user-fullname').detach();
  63. $('#loginHeader > .user-dropdown-menu > button.dropdown-toggle').append(userFullname);
  64. }
  65. });
  66. /* COMPOSANT */
  67. function init_components(selector){
  68. var selected = selector ? $('[data-type]',selector) : '[data-type]';
  69. $(selected).each(function(i,input){
  70. var input = $(input);
  71. switch($(input).attr('data-type')){
  72. /**
  73. * data-format : Supporte les formats dd/mm/yyyy ou yyyy/mm/dd avec séparateur "/" ou "-"
  74. * data-begin : date de début de sélection par rapport à aujourd'hui(peut être en nb de jour, en objet Date ou en string de type dateFormat ou en date relative [+1m +7d]==>Début à 1 mois et 7 jours)
  75. * data-end : date de début de sélection par rapport à aujourd'hui(peut être en nb de jour, en objet Date ou en string de type dateFormat ou en date relative [+1m +7d]==>Début à 1 mois et 7 jours)
  76. */
  77. case 'date':
  78. //Les séparateurs doivent être similaires entre les éléments de la chaîne
  79. var dateFormatRegex = /^(dd|yy)([\/|-])(mm)\2(dd|yy)$/;
  80. var dateFormat = input.attr('data-format') != undefined && input.attr('data-format').match(dateFormatRegex) ? input.attr('data-format').match(dateFormatRegex)[0] : "dd/mm/yy";
  81. var begin = input.attr('data-begin');
  82. begin = (begin != undefined && (begin.match(/-?\d+/) || begin.match(/^(\d{2}(?:\d{2})?)\/(\d{2})\/(\d{2}(?:\d{2})?)$/))) ? begin : null ;
  83. var end = input.attr('data-end');
  84. end = (end != undefined && (end.match(/-?\d+/) || end.match(/^(\d{2}(?:\d{2})?)[-|\/](\d{2})[-|\/](\d{2}(?:\d{2})?)$/))) ? end : null ;
  85. input.date({
  86. placeholder: input.attr('placeholder') != undefined ? input.attr('placeholder') : "jj/mm/aaaa",
  87. dateFormat: dateFormat,
  88. beginDate: begin,
  89. endDate: end
  90. });
  91. break;
  92. /**
  93. * data-format : Supporte les formats dd/mm/yyyy ou yyyy/mm/dd avec séparateur "/" ou "-"
  94. * data-step : L'intervalle entre 2 valeurs en minutes
  95. */
  96. case 'hour':
  97. var timeFormatRegex = /^(H):(i)$/;
  98. var timeFormat = input.attr('data-format') != undefined && input.attr('data-format').match(timeFormatRegex) ? input.attr('data-format').match(timeFormatRegex)[0] : "H:i";
  99. var step = input.attr('data-step');
  100. input.hour({
  101. placeholder: input.attr('placeholder') != undefined ? input.attr('placeholder') : "hh:mm",
  102. timeFormat: timeFormat,
  103. step: is_numeric(step) && step>0 ? step : 1
  104. });
  105. break;
  106. case 'image':
  107. if(input.closest('.type-image-bloc').length!=0) break;
  108. input.wrap( "<div class='type-image-bloc'></div>");
  109. var src = ($(input).attr('value')!='') ? $(input).attr('value') : 'img/default-image.png';
  110. src += src.indexOf('?')!=-1 ? '&' : '?';
  111. src += 't='+(Math.random()*1000);
  112. var thumbnail = $('<img src="'+src+'" >');
  113. var deleteBtn = !input.attr('data-delete') ? '' : '<div class="btn btn-delete-img noPrint" onclick="'+input.attr('data-delete')+'"><i class="fas fa-times"></i></div>';
  114. input.before(thumbnail);
  115. if(thumbnail.attr('src').indexOf('default-') === -1) thumbnail.before(deleteBtn);
  116. input.addClass('noPrint');
  117. input.change(function(){
  118. var reader = new FileReader();
  119. reader.onload = function (e) {
  120. thumbnail.attr('src', e.target.result);
  121. thumbnail.before(deleteBtn);
  122. }
  123. reader.readAsDataURL(input.get(0).files[0]);
  124. });
  125. break;
  126. //Selection de tag liés a un dictionnary
  127. case 'tag-list':
  128. var container;
  129. var picker;
  130. var pickerLi;
  131. if(!input.data("data-component")){
  132. container = $('<div class="'+input.attr('class')+' data-type-tag-list"><ul><li class="tag-picker-li"><input type="text"></li></ul></div>');
  133. input.before(container);
  134. input.data("data-component", container);
  135. if(input.attr("required")) container.attr("required","");
  136. } else {
  137. container = input.data("data-component");
  138. }
  139. picker = container.find('input:eq(0)');
  140. pickerLi = container.find('ul li.tag-picker-li');
  141. container.find('.tag-picker-tag').remove();
  142. pickerLi.removeClass('hidden');
  143. var pickerFunctions = {
  144. //Récuperation des valeurs sélectionnées (objet et id) en fonction des tags visuels présents
  145. getValues : function( container){
  146. var tags = container.find('ul .tag-picker-tag');
  147. var values = {object:[],id:[]};
  148. tags.each(function(i,element){
  149. if($(element).attr('data-id') == '') return;
  150. var object = $(element).data();
  151. values['object'].push(object);
  152. values['id'].push(object.id);
  153. });
  154. return values;
  155. },
  156. //Ajout d'un tag visuel et mise à jour de l'input brut en fonction de l'objet fournis
  157. addTag : function( container,tag){
  158. pickerLi = container.find('ul li.tag-picker-li');
  159. if( container.find('li[data-id="'+tag.id+'"]').length>0) return;
  160. var tag = $('<li class="tag-picker-tag" data-slug="'+tag.slug+'" data-id="'+tag.id+'"><div>'+tag.name+'<i class="fa fa-times"></i></div></li>');
  161. pickerLi.before(tag);
  162. var values = pickerFunctions.getValues( container);
  163. input.val(values['id'].join(','));
  164. input.data('values',values['object']);
  165. if((values['id'].length) == 1 && input.attr('data-multiple') == null){
  166. pickerLi.addClass('hidden');
  167. }else{
  168. pickerLi.removeClass('hidden');
  169. }
  170. tag.find('i').click(function(){
  171. $(this).closest('.tag-picker-tag').remove();
  172. var values = pickerFunctions.getValues( container);
  173. input.val(values['id'].join(','));
  174. input.data('values',values['object']);
  175. if((values['id'].length-1) ==1 && input.attr('data-multiple') == null){
  176. pickerLi.addClass('hidden');
  177. }else{
  178. pickerLi.removeClass('hidden');
  179. }
  180. });
  181. }
  182. }
  183. //Gestion des champs déja remplis au chargement de la page
  184. if(input.val() !=''){
  185. var id = input.val();
  186. container.find('ul').append('<li class="tag-picker-loader"><i class="fas fa-spinner fa-pulse"></i> Chargement</li>');
  187. picker.addClass('hidden');
  188. $.action({
  189. action : 'tag_list_by_id',
  190. id : id
  191. },function(r){
  192. container.find('.tag-picker-loader').remove();
  193. picker.removeClass('hidden');
  194. for(var key in r.tags)
  195. pickerFunctions.addTag( container,r.tags[key]);
  196. });
  197. }
  198. //Sélectionne l'input d'auto-completion ou que l'on clique dans le composant
  199. container.find('ul').click(function(e){
  200. picker.focus();
  201. e.stopPropagation();
  202. });
  203. //Selectionne l'item dropdown actif lors de l'appuis sur entré
  204. picker.keyup(function(e){
  205. if(e.keyCode!=13 || input.is('[readonly]')) return;
  206. var active = $('.tag-picker-li .dropdown-menu .active');
  207. if(active.length==0) return;
  208. active.trigger('click');
  209. });
  210. //aucompletion sur le nom des tags
  211. picker.autocomplete({
  212. action : 'tag_list_autocomplete',
  213. suggest : true,
  214. items : input.attr('data-items'),
  215. data : {
  216. parent : input.attr('data-slug')
  217. },
  218. skin : function(item){
  219. var html = '';
  220. var re = new RegExp(picker.val(),"gi");
  221. name = item.name.replace(re, function (x) {
  222. return '<strong>'+x+'</strong>';
  223. });
  224. html += '<div class="tag-infos"><span>'+name+'</span>';
  225. html += '<div class="clear"></div>';
  226. return html;
  227. },
  228. highlight : function(item){
  229. return item;
  230. },
  231. onClick : function(selected,element){
  232. picker.val('');
  233. selected.label = selected.name;
  234. pickerFunctions.addTag(container,selected);
  235. input.trigger('click').trigger('change');
  236. },
  237. onBlur : function(selected){
  238. if(input.attr('data-force')!='false' && input.val() == '') picker.val('');
  239. }
  240. });
  241. break;
  242. //composant permettant les listes sous
  243. //form de dropdown avec icones et couleurs
  244. case 'dropdown-select':
  245. if(!input.is(':visible')) return;
  246. var container;
  247. if(!input.data("data-component")){
  248. container = $('<div class="'+input.attr('class')+' btn-group data-type-dropdown-select"> \
  249. <button title="'+input.attr('title')+'" type="button" class="btn btn-small btn-secondary border-0 dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button> \
  250. <div class="dropdown-menu w-100 text-center"></div> \
  251. </div>');
  252. input.before(container);
  253. input.data("data-component", container);
  254. if(input.attr("required")) container.attr("required","");
  255. var html = '';
  256. input.find('option').each(function(i,element){
  257. var option = $(element);
  258. icon = option.attr("data-icon") ? '<i class="'+option.attr("data-icon")+'"></i> ': '';
  259. html+='<a class="dropdown-item pointer" data-value="'+option.val()+'" style="'+option.attr("style")+'" >'+icon+option.html()+'</a>';
  260. });
  261. container.find('.dropdown-menu').append(html);
  262. $('.dropdown-menu a',container).click(function(){
  263. if(input.attr("readonly") == 'readonly') return;
  264. var item = $(this);
  265. $('.dropdown-menu a',container).removeClass('active');
  266. //Utilisation background-color pour fonctionner sous FF
  267. container.find('.dropdown-toggle').html(item.html()).css('background-color',item.css('background-color'));
  268. item.addClass('active');
  269. input.val(item.attr('data-value')).trigger('change');
  270. });
  271. } else {
  272. container = input.data("data-component");
  273. }
  274. input.addClass('hidden');
  275. var state = input.attr('data-value') != null ? input.attr('data-value') : input.val();
  276. if(state == '') return;
  277. var item = $('.dropdown-menu a[data-value="'+state+'"]', container);
  278. //Utilisation background-color pour fonctionner sous FF
  279. container.find('.dropdown-toggle').html(item.html()).css('background-color',item.css('background-color'));
  280. item.addClass('active');
  281. input.val(item.attr('data-value')).trigger('change');
  282. break;
  283. case 'user':
  284. var userContainer;
  285. var userPicker;
  286. var pickerLi;
  287. if(!input.data("data-component")){
  288. userContainer = $('<div class="'+input.attr('class')+' data-type-user"><ul><li class="user-picker-li"><input type="text" '+(input.attr('placeholder')!==undefined?'placeholder="'+input.attr('placeholder'):'')+'"></li></ul></div>');
  289. input.before(userContainer);
  290. input.data("data-component", userContainer);
  291. if(input.attr("required")) userContainer.attr("required","");
  292. if(input.attr("disabled")) userContainer.attr("disabled","");
  293. } else {
  294. userContainer = input.data("data-component");
  295. }
  296. userPicker = userContainer.find('input:eq(0)');
  297. pickerLi = userContainer.find('ul li.user-picker-li');
  298. userContainer.find('.user-picker-tag').remove();
  299. pickerLi.removeClass('hidden');
  300. var pickerFunctions = {
  301. //Récuperation des valeurs sélectionnées (objet et uid) en fonction des tags visuels présents
  302. getValues : function(userContainer){
  303. var tags = userContainer.find('ul .user-picker-tag');
  304. var values = {object:[],uid:[]};
  305. tags.each(function(i,element){
  306. if($(element).attr('data-uid') == '') return;
  307. var object = $(element).data();
  308. values['object'].push(object);
  309. values['uid'].push(object.uid);
  310. });
  311. return values;
  312. },
  313. //Ajout d'un tag visuel et mise à jour de l'input brut en fonction de l'objet user fournis
  314. addTag : function(userContainer,user){
  315. pickerLi = userContainer.find('ul li.user-picker-li');
  316. if(userContainer.find('li[data-uid="'+user.uid+'"]').length>0) return;
  317. var closeBtn = userContainer.attr('disabled') || userContainer.attr('readonly') ? '' : '<i class="fa fa-times"></i>';
  318. var tag = $('<li class="user-picker-tag" data-entity="'+user.type+'" data-fullname="'+user.fullname+'" data-uid="'+user.uid+'"><div title="'+(user.type=='rank'?'Rang':'Utilisateur')+'">'+user.fullname+closeBtn+'</div></li>');
  319. pickerLi.before(tag);
  320. var values = pickerFunctions.getValues(userContainer);
  321. input.val(values['uid'].join(','));
  322. input.data('values',values['object']);
  323. if((values['uid'].length) == 1 && input.attr('data-multiple') == null){
  324. pickerLi.addClass('hidden');
  325. }else{
  326. pickerLi.removeClass('hidden');
  327. }
  328. tag.find('i').click(function(){
  329. if(userContainer.attr('disabled') || userContainer.attr('readonly')) return;
  330. $(this).closest('.user-picker-tag').remove();
  331. var values = pickerFunctions.getValues(userContainer);
  332. input.val(values['uid'].join(','));
  333. input.data('values',values['object']);
  334. if((values['uid'].length-1) ==1 && input.attr('data-multiple') == null){
  335. pickerLi.addClass('hidden');
  336. }else{
  337. pickerLi.removeClass('hidden');
  338. }
  339. });
  340. if(userContainer.find('ul li.user-picker-tag'))
  341. userContainer.find('ul li.user-picker-li input').removeAttr('placeholder');
  342. }
  343. }
  344. //Gestion des champs déja remplis au chargement de la page
  345. if(input.val() !=''){
  346. var uid = input.val();
  347. if(!window.componentQueue.user) window.componentQueue.user = {timeout : null,components : [],uids :{} };
  348. clearTimeout(window.componentQueue.user.timeout);
  349. var uids = uid.split(',');
  350. for(var k in uids){
  351. window.componentQueue.user.uids[uids[k]] = 1;
  352. }
  353. window.componentQueue.user.components.push({
  354. input : input,
  355. picker : userPicker,
  356. container : userContainer,
  357. values : uids
  358. });
  359. userContainer.find('ul').append('<li class="user-picker-loader"><i class="fas fa-spinner fa-pulse"></i> Chargement</li>');
  360. userPicker.addClass('hidden');
  361. window.componentQueue.user.timeout = setTimeout(function(){
  362. $.action({
  363. action : 'user_by_uid',
  364. uids : Object.keys(window.componentQueue.user.uids)
  365. },function(r){
  366. for(var key in window.componentQueue.user.components){
  367. var component = window.componentQueue.user.components[key];
  368. component.container.find('.user-picker-loader').remove();
  369. component.picker.removeClass('hidden');
  370. for(var i in component.values){
  371. var value = component.values[i];
  372. if(!r.users[value]) continue;
  373. pickerFunctions.addTag(component.container,r.users[value]);
  374. }
  375. }
  376. delete window.componentQueue.user;
  377. });
  378. },50);
  379. // var login = input.val();
  380. // userContainer.find('ul').append('<li class="user-picker-loader"><i class="fas fa-spinner fa-pulse"></i> Chargement</li>');
  381. // userPicker.addClass('hidden');
  382. // $.action({
  383. // action : 'user_by_uid',
  384. // login : login
  385. // },function(r){
  386. // userContainer.find('.user-picker-loader').remove();
  387. // userPicker.removeClass('hidden');
  388. // for(var key in r.users)
  389. // pickerFunctions.addTag(userContainer,r.users[key]);
  390. // });
  391. }
  392. //Sélectionne l'input d'auto-completion ou que l'on clique dans le composant
  393. userContainer.find('ul').click(function(e){
  394. if(input.attr("readonly") == "readonly") return;
  395. userPicker.focus();
  396. e.stopPropagation();
  397. });
  398. //Selectionne l'item dropdown actif lors de l'appui sur entrée
  399. userPicker.keyup(function(e){
  400. if(e.keyCode!=13 || input.is('[readonly]')) return;
  401. if(input.attr("readonly") == "readonly") return;
  402. var active = $('.user-picker-li .dropdown-menu .active');
  403. if(active.length==0) return;
  404. active.trigger('click').trigger('change');
  405. });
  406. var types = ['user'];
  407. if(input.data('types') && input.data('types')!='')
  408. types = input.data('types').split(',');
  409. //aucompletion sur le nom des users / rangs
  410. userPicker.autocomplete({
  411. action : 'user_autocomplete',
  412. data : {
  413. types : types
  414. },
  415. skin : function(item){
  416. var html = '';
  417. var re = new RegExp(userPicker.val(),"gi");
  418. name = item.name.replace(re, function (x) {
  419. return '<strong>'+x+'</strong>';
  420. });
  421. if(item.type=='user'){
  422. if(item.avatar) html += '<div class="user-logo"><img src="'+item.avatar+'" class="avatar-mini avatar-rounded"></div>';
  423. html += '<div class="user-infos"><span>'+name+'</span> <small class="text-muted">- Utilisateur (@'+item.id+')</small>';
  424. html += item.function ? '<br/><small>'+item.function+'</small></div>' : '</div>';
  425. html += '<div class="clear"></div>';
  426. }else{
  427. html += '<div class="rank-logo"><i class="far fa-address-card"></i></div>';
  428. html += '<div class="rank-infos"><span>'+name+'</span> <small class="text-muted">- Rang</small>';
  429. html += item.description ? '<br/><small>'+item.description+'</small></div>' : '</div>';
  430. html += '<div class="clear"></div>';
  431. }
  432. return html;
  433. },
  434. highlight : function(item){
  435. return item;
  436. },
  437. onClick : function(selected,element){
  438. userPicker.val('');
  439. selected.fullname = selected.name;
  440. selected.uid = selected.id;
  441. pickerFunctions.addTag(userContainer,selected);
  442. input.trigger('click').trigger('change');
  443. },
  444. onBlur : function(selected){
  445. if(input.attr('data-force')!='false' && input.val() == '') userPicker.val('');
  446. }
  447. });
  448. break;
  449. /**
  450. * data-labels : tableau des libellés entre double quotes eg : ["Libellé 1","Libellé 2"]
  451. * data-values : taleau des valeurs eg : [12,13]
  452. * data-colors : taleau des couleurs entre double quotes eg : ["#cecece","#222222"]
  453. */
  454. case 'doughnut':
  455. var data = input.data();
  456. var myChart = new Chart(input.get(0).getContext('2d'), {
  457. type: 'doughnut',
  458. data: {
  459. labels: data.labels,
  460. datasets: [{
  461. data: data.values,
  462. backgroundColor: data.colors
  463. }]
  464. },
  465. options: {
  466. cutoutPercentage:80
  467. }
  468. });
  469. break;
  470. /**
  471. * data-labels : tableau des libellés entre double quotes eg : ["Libellé1","Libellé 2"]
  472. * data-values : taleau des valeurs eg : [12,13]
  473. * data-colors : taleau des couleurs entre double quotes eg : ["#cecece","#222222"]
  474. */
  475. case 'bar':
  476. var data = input.data();
  477. var myChart = new Chart(input.get(0).getContext('2d'), {
  478. type: 'bar',
  479. data: {
  480. labels: data.labels,
  481. datasets: [{
  482. label : input.html(),
  483. data: data.values,
  484. backgroundColor: data.colors
  485. }]
  486. },
  487. options: {
  488. scales: {
  489. yAxes: [{
  490. ticks: {
  491. beginAtZero:true
  492. }
  493. }]
  494. },
  495. legend: {
  496. display: false
  497. }
  498. }
  499. });
  500. break;
  501. /**
  502. * data-labels : tableau des libellés entre double quotes eg : ["Libellé1","Libellé 2"]
  503. * data-values : taleau des valeurs eg : [12,13]
  504. * data-color : couleurs eg : #cecece
  505. */
  506. case 'line':
  507. var data = input.data();
  508. var myChart = new Chart(input.get(0).getContext('2d'), {
  509. type: 'line',
  510. data: {
  511. labels: data.labels,
  512. datasets: [{
  513. label : input.html(),
  514. data: data.values,
  515. borderColor: [data.color]
  516. }]
  517. },
  518. options: {
  519. scales: {
  520. yAxes: [{
  521. ticks: {
  522. beginAtZero:true
  523. }
  524. }]
  525. }
  526. }
  527. });
  528. break;
  529. /**
  530. * data-input-city : le champ d'id "#votre-id" à remplir avec la ville
  531. * data-input-latitude : le champ d'id "#votre-id" à remplir avec la latitude
  532. * data-input-longitude : le champ d'id "#votre-id" à remplir avec la longitude
  533. * data-input-country : le champ d'id "#votre-id" à remplir avec le pays
  534. * data-input-code : le champ d'id "#votre-id" à remplir avec le code postal
  535. * data-input-street : le champ d'id "#votre-id" à remplir avec la rue
  536. * data-input-number : le champ d'id "#votre-id" à remplir avec le n°
  537. */
  538. case 'location':
  539. input.location({
  540. select :function(location){
  541. var attributes = {};
  542. for(var key in location){
  543. attributes['data-'+key] = location[key];
  544. if(input.attr('data-input-'+key)!='') $(input.attr('data-input-'+key)).val(location[key]).text(location[key]);
  545. }
  546. input.attr(attributes);
  547. }
  548. });
  549. break;
  550. /**
  551. * data-depth : nb de profondeur de liste (ex: 2, affichera 2 select au maximum), 1 par défaut
  552. * data-slug : le slug de la liste mère à afficher, listes primaires par défaut
  553. * data-value : la valeur de l'entité à récup en base
  554. * data-disable-label : cache le label de sous-liste si mentionné
  555. * data-hierarchy : si mentionné à false, ne récupère pas
  556. * data-parent-id : l'id de la liste parente associée
  557. */
  558. case 'dictionnary':
  559. var slug = input.attr('data-slug') ? input.attr('data-slug') : "";
  560. var parentId = input.attr('data-parent-id') && input.attr('data-parent-id').length ? input.attr('data-parent-id') : "";
  561. if (!slug.match(/^[a-z\d\-_]+$/i) && parentId == '') return;
  562. $.action({
  563. action : 'load_dictionnary_component',
  564. slug: slug,
  565. parentId : parentId,
  566. hierarchy : input.attr('data-hierarchy') == 'false' ? 0 : 1,
  567. value: input.attr('data-value')
  568. },function(r){
  569. var children = r.content.childs ? r.content.childs : r.content;
  570. input.attr('onchange', 'get_sub_dictionnary(this, "'+input.attr('name')+'",'+1+');');
  571. input.append('<option value=""> - </option>');
  572. $.each(children, function (index, value){
  573. if (value.selected) {
  574. input.append('<option value="'+value.id+'" data-parent="'+value.parent+'" data-sublabel="'+value.sublistlabel+'" selected>'+value.label+'</option>');
  575. get_selected_values(input, value);
  576. }
  577. else
  578. input.append('<option value="'+value.id+'" data-parent="'+value.parent+'" data-sublabel="'+value.sublistlabel+'">'+value.label+'</option>');
  579. });
  580. });
  581. break;
  582. /**
  583. * data-label : le label affiché dans la zone
  584. * data-delete : méthode de suppression de doc de l'entité
  585. * data-save : méthode de sauvegarde de doc de l'entité (si mentionné, save automatique)
  586. * data-readonly: Empeche l'ajout/suppression de documents
  587. * data-allowed : les extensions de fichier acceptées
  588. */
  589. case 'dropzone':
  590. if(input.find('form').length != 0 || input.find('ul>li').length) break;
  591. if(!input.attr('data-action')) input.attr('data-action','action.php?action=upload_temporary_file');
  592. var readonly = input.attr('data-readonly') == "true" ? true : false;
  593. if(!input.get(0).hasAttribute('id')) input.attr('id',generate_uuid(10));
  594. var customTpl = input.find('> *:not(:visible)');
  595. var customActions = '';
  596. if(customTpl && customTpl.length){
  597. $.each(customTpl, function(i, action){
  598. if(i>2) return;
  599. var valCalc = readonly ? i*28+3 : i*28+25;
  600. customActions += $(action).removeClass('hidden').css('right', valCalc).get(0).outerHTML;
  601. });
  602. }
  603. var preview = '<li data-path="{{path}}">';
  604. preview += input.get(0).hasAttribute('data-preview') ? '<img style="margin: 0 5px;float: left;max-height:100px;max-width:80px;" src="{{url}}"/>' : '<i class="fa {{icon}}"></i>';
  605. preview += ' <a {{#url}}href="{{url}}"{{/url}} target="_blank" title="{{name}}">{{name}}{{lastModification}}</a> '+customActions+' <i class="fas fa-times pointer '+(input.attr('data-delete')?'':'hidden')+'" onclick="{{#temporary}}dropzone_delete_file(this);{{/temporary}}{{^temporary}}'+input.attr('data-delete')+'(this){{/temporary}}"></i><div class="clear"></div></li>';
  606. var valueFiles = input.html()!='' && is_json_string(input.text()) ? JSON.parse(input.text()) : [];
  607. input.html('');
  608. var allowed = input.attr('data-allowed');
  609. if(allowed) allowed = allowed.split(',');
  610. var save = input.attr('data-save');
  611. var size = input.attr('data-max-size');
  612. input.upload({
  613. allowed : allowed,
  614. size : size == '' ? 0 : size,
  615. readonly: readonly,
  616. start: function(){
  617. preload.removeClass('hidden');
  618. },
  619. success: function(response){
  620. if(response.previews.length && response.previews[0].name) {
  621. var inputTemp = $('#'+input.attr('id')+'_temporary');
  622. var currVal = inputTemp.val().length ? JSON.parse(inputTemp.val()) : [];
  623. for(var i in response.previews){
  624. files.append(Mustache.render(preview,response.previews[i]));
  625. currVal.push(response.previews[i]);
  626. }
  627. inputTemp.val(JSON.stringify(currVal));
  628. if(save)
  629. window[save](response.previews);
  630. }
  631. preload.addClass('hidden');
  632. },
  633. complete: function(){
  634. preload.addClass('hidden');
  635. }
  636. });
  637. var preload = $('<div class="preload progress-bar progress-bar-striped progress-bar-animated"></div>');
  638. input.append(preload);
  639. var files = $('<ul></ul>');
  640. input.append(files);
  641. var filesValues = $('<input type="hidden" name="'+input.attr('id')+'_temporary" id="'+input.attr('id')+'_temporary">');
  642. input.append(filesValues);
  643. for(var i in valueFiles) {
  644. files.append(Mustache.render(preview,valueFiles[i]));
  645. if(readonly) files.find('li > i.fa-times').remove();
  646. }
  647. if(!valueFiles.length && readonly)
  648. input.append('<div>Aucun document</div>');
  649. break;
  650. /**
  651. * data-simple : Si "true" alors interface avec moins de boutons
  652. */
  653. case 'wysiwyg':
  654. var buttons = [
  655. ['strong', 'em', 'underline','del'],
  656. ['foreColor', 'backColor'],
  657. ['undo', 'redo'], // Only supported in Blink browsers
  658. ['formatting'],
  659. ['superscript', 'subscript'],
  660. ['link'],
  661. ['insertImage'],
  662. ['table'],
  663. ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'],
  664. ['unorderedList', 'orderedList'],
  665. ['horizontalRule'],
  666. ['removeformat'],
  667. ['fullscreen'],
  668. ['viewHTML']
  669. ]
  670. if(input.attr('data-simple') == 'true'){
  671. buttons = [
  672. ['strong', 'em', 'underline','del'],
  673. ['foreColor', 'backColor'],
  674. ['undo', 'redo'], // Only supported in Blink browsers
  675. ['formatting'],
  676. ['link'],
  677. ['unorderedList', 'orderedList'],
  678. ['insertImage']
  679. ]
  680. }
  681. var defaultOptions = {
  682. btns: buttons,
  683. lang: 'fr',
  684. autogrow: true,
  685. semantic: false
  686. };
  687. var data = input.data();
  688. var options = $.extend(defaultOptions,data);
  689. input.trumbowyg(options);
  690. if(input.attr('required')) $(input).closest('div.trumbowyg-box').attr('required', true);
  691. break;
  692. /**
  693. * data-join : Spécifie la liaison par défaut des filtres (and | or) si rien n'est spéficié ou que l'attrbute n'existe pas,
  694. * un select apparait pour que l'utilisateur puisse choisir,
  695. * data-slug : Si spécifié, la recherche devient enregistrable pour une réutilisation ultérieure,
  696. * data-only-advanced : Si l'attribut est présent, cache la recherche simple et ouvre par defaut la recherche avancée
  697. * data-autosearch (default: true) : si définit a false, ne lancera pas la fonction data-function automatiquement en fin de chargement du composant
  698. */
  699. case 'filter':
  700. if(input.next('.filter-box:visible').length!=0) break;
  701. input.addClass('hidden');
  702. var data = input.data();
  703. var box = $('.filter-box:not(:visible)').clone();
  704. input.after(box);
  705. box.removeClass('hidden');
  706. if($('option',input).length == 0)
  707. box.find('.advanced-button-search').addClass('hidden');
  708. box.attr('data-join',data.join);
  709. if(data.join!='' && data.join!=null)
  710. box.find('.filter-join').addClass('hidden');
  711. var options = '<option value=""> - Choix filtre - </option>';
  712. $('option',input).each(function(i,element){
  713. options += element.outerHTML;
  714. });
  715. $('.filter-column').append(options);
  716. box.on('keyup', '.filter-value, .filter-keyword', function(event){
  717. if(event.keyCode == 13) filter_search();
  718. });
  719. if(data.onlyAdvanced!=null){
  720. $('.simple-search').addClass('hidden');
  721. box.find('.advanced-search').removeClass('hidden');
  722. }
  723. if(data.onlyAdvanced==null && $('option',input).length != 0)
  724. $('.simple-search div.btn.filter-button-search:visible').html('<i class="fas fa-search"></i>');
  725. var filters = $.urlParam('filters');
  726. if(data.slug && data.slug!=''){
  727. box.find('.advanced-search-save').removeClass('hidden');
  728. box.find('.advanced-search-action-separator').removeClass('hidden');
  729. //On ne charge les filtres enregistrés que si pas de filtres dans la barre d'adresse (la barre d'adresse prévaut)
  730. if(!filters|| filters==''){
  731. $.action({
  732. action : 'filter_load',
  733. slug : data.slug,
  734. },function(response){
  735. if(response.filters.advanced && response.filters.advanced.length > 0) {
  736. box.find('.advanced-search').removeClass('hidden');
  737. for(var i=response.filters.advanced.length-1; i!=-1; i--){
  738. var filter = response.filters.advanced[i];
  739. filter_add(box.find('.filterRow:visible:eq(0)'),filter);
  740. }
  741. }
  742. if(response.filters.custom && !is_empty_obj(response.filters.custom)) {
  743. box.find('.advanced-search').removeClass('hidden');
  744. for(var key in response.filters.custom){
  745. var filter = response.filters.custom[key];
  746. filter_add(box.find('.filterRow:visible:eq(0)'),filter);
  747. }
  748. }
  749. box.find('.filterRow:visible:eq(0)').remove();
  750. //Problème:
  751. //Quand on load une recherche enregistrée avec un
  752. //composant de type dictionnary, le autosearch qui lance
  753. //la fonction de recherche n'attend pas que l'intégralité
  754. //des filtres soient settés et chargés et donc il manque
  755. //une info lors de la recherche
  756. //--> besoin d'un timeout ?
  757. if(data.function && (!data.hasOwnProperty('autosearch') || data.autosearch==true))
  758. // setTimeout(function(){window[data.function]();}, 500);
  759. window[data.function]();
  760. });
  761. }
  762. }
  763. if(filters && filters!=''){
  764. filters = JSON.parse(atob(filters));
  765. input.filters(filters);
  766. if(filters.advanced && filters.advanced.length>0){
  767. box.find('.advanced-search').removeClass('hidden');
  768. box.find('.filterRow:visible:eq(0)').remove();
  769. }
  770. }
  771. break;
  772. /**
  773. * data-toggle-event : Pour le moment, que "hover", de base au click
  774. * data-show-strength: Si indiqué, affiche la barre de force du mot de passe renseigné dans l'input
  775. * data-generator : Si indiqué, affiche une icône pour géénrer un mdp sécurisé
  776. */
  777. case 'password':
  778. if(input.closest('.password-field').length) return;
  779. input.attr('type', 'password');
  780. input.wrap(function() {return '<div class="password-field"></div>';});
  781. var container = input.parent('.password-field');
  782. container.append('<i class="fas fa-eye-slash noPrint password-toggler"></i>');
  783. container.find('i.password-toggler').attr((input.attr('data-toggle-event') == 'hover')?{
  784. 'onmouseover': 'toggle_password(this);',
  785. 'onmouseleave': 'toggle_password(this);'
  786. }:{'onclick': 'toggle_password(this);'});
  787. if(input.attr('data-generator') !== undefined) {
  788. container.append('<i class="fas fa-shield-alt noPrint password-generator"></i>')
  789. container.find('i.password-generator').on('click', function(){
  790. if(input.val()!='' && !confirm('Attention, un mot de passe est déjà défini.\nVoulez-vous quand même en générer un nouveau ?')) return;
  791. //regex norme ANSSI
  792. var strong = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[=/\()%ยง!@#$%^&*])(?=.{12,})');
  793. do {
  794. password = generate_password();
  795. } while(!strong.test(password))
  796. input.val(password);
  797. if(input.attr('data-show-strength') !== undefined)
  798. check_password_strength(input);
  799. });
  800. function generate_password(){
  801. var password = "";
  802. var charset = get_characters_set();
  803. var length = Math.ceil(parseFloat(78) * Math.log(2) / Math.log(charset.length));
  804. for (var i=0; i<length; ++i)
  805. password += charset[get_random_int(charset.length)];
  806. return password;
  807. }
  808. }
  809. if(input.attr('data-show-strength') !== undefined) {
  810. input.parent('.password-field').append('<div class="strength-lines"><div class="line"></div><div class="line"></div><div class="line"></div></div>');
  811. // check_password_strength($('input[data-type="password"][data-show-strength]'));
  812. // Strength validation on keyup-event
  813. input.on("keyup mouseup", function(e) {
  814. check_password_strength($(this));
  815. });
  816. //Check password strength
  817. function check_password_strength(input) {
  818. var value = $(input).val();
  819. var container = $(input).closest('.password-field');
  820. $(".line", container).removeClass("strength-low strength-medium strength-hard").addClass("strength-transparent");
  821. if(!value.length) return;
  822. var strongRegex = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[=/\()%ยง!@#$%^&*])(?=.{12,})'),
  823. mediumRegex = new RegExp('^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})');
  824. if(strongRegex.test(value)) {
  825. $(".line", container).removeClass("strength-transparent").addClass("strength-hard");
  826. } else if (mediumRegex.test(value)) {
  827. $(".line:not(:last-of-type)", container).removeClass("strength-transparent").addClass("strength-medium");
  828. } else {
  829. $(".line:nth-child(1)", container).removeClass("strength-transparent").addClass("strength-low");
  830. }
  831. }
  832. }
  833. break;
  834. case 'icon':
  835. input.addClass('hidden');
  836. input.next('.component-icon').remove();
  837. var data = {
  838. value : input.val(),
  839. choices : []
  840. };
  841. var icons = $.fontAwesome();
  842. var line = [];
  843. for(var key in icons){
  844. if(line.length==15){
  845. data.choices.push(line);
  846. line = [];
  847. }
  848. line.push(icons[key].icon);
  849. }
  850. var selector = $(Mustache.render($('.component-icon.hidden').get(0).outerHTML,data));
  851. selector.removeClass('hidden');
  852. input.after(selector);
  853. selector.on('show.bs.dropdown', function () {
  854. setTimeout(function(){selector.find('input.form-control').focus();},0);
  855. })
  856. selector.find('.dropdown-icon-item').click(function(){
  857. selector.find('.dropdown-icon-item').removeClass('hidden');
  858. selector.find('input.form-control').val('');
  859. var icon = $(this).attr('data-icon');
  860. input.val(icon);
  861. selector.find('.dropdown-toggle i').attr('class',icon);
  862. input.trigger('change');
  863. });
  864. selector.find('input.form-control').keyup(function(){
  865. var value = $(this).val();
  866. $('.dropdown-icon-item i',selector).each(function(i,iconElement){
  867. iconElement = $(iconElement);
  868. parent = iconElement.parent();
  869. iconElement.attr('class').indexOf(value)!=-1 ? parent.removeClass('hidden') : parent.addClass('hidden');
  870. });
  871. /*var icon = $(this).attr('data-icon');
  872. input.val(icon);
  873. selector.find('.dropdown-toggle i').attr('class',icon);*/
  874. });
  875. break;
  876. break;
  877. /**
  878. * data-title (*) : Le titre du modal
  879. * data-warning : Message affiché dans le tooltip au survol
  880. * data-loaded (*): fonction callback appelée auprès le chargement du modal (bien pour setter des actions custom sur les boutons dans le modal par exemple)
  881. * data-params : Les paramètres utilisés pour l'appel du callback
  882. * data-url (*) : L'URL vers le contenu du modal qui sera ajouté au modal préexistant dans le footer
  883. * data-precall : fonction appelée avant le chargement du modal (méthode de check sur l'UI par exemple)
  884. * Pour customiser l'icône du quickform, il suffit de placer le contenu que l'on veut dans la div de data-type="quickform"
  885. */
  886. case 'quickform':
  887. if(input.find('span').length) return;
  888. if(input.attr('disabled')) return;
  889. var qfUrl = input.attr('data-url');
  890. var cbLoaded = input.attr('data-loaded');
  891. var preCb = input.attr('data-precall') ? input.attr('data-precall') : '';
  892. var warningMsg = input.attr('data-warning') ? input.attr('data-warning') : '';
  893. var icon = !input.find('i').length ? $('<i class="fas fa-plus-square"></i>') : input.find('i').detach();
  894. var checker = '<span class="tooltip-warning" title="'+warningMsg+'">'+icon.get(0).outerHTML+input.html()+'</span>';
  895. input.html(checker);
  896. var title = input.attr('data-title');
  897. var params = input.attr('data-params') ? input.attr('data-params').split(',') : [];
  898. $(document).ready(function(e){
  899. if(warningMsg.length)
  900. $('span.tooltip-warning').tooltip({
  901. track: true,
  902. tooltipClass: 'quickform-tooltip',
  903. });
  904. input.on('click', function(e){
  905. if(preCb.length && !window[preCb]()) return;
  906. $.ajax({
  907. type: 'GET',
  908. url: qfUrl,
  909. async: true,
  910. success : function(modal){
  911. $('#quickform-modal-label').text(title);
  912. $('.quickform-modal .modal-footer > div:first-of-type').nextAll().remove();
  913. }
  914. }).done(function(modalBody){
  915. var modal = $('.quickform-modal');
  916. var qfBody = $('.modal-body', modal)
  917. qfBody.html('').append(modalBody);
  918. reset_quickform_modal();
  919. if(cbLoaded) window[cbLoaded].apply(null,params);
  920. modal.modal('show');
  921. init_components($('#quickform-modal'));
  922. });
  923. });
  924. });
  925. break;
  926. /**
  927. * data-class : Classe custom à ajouter au conteneur du l'input
  928. */
  929. case 'checkbox':
  930. //3 cas possibles :
  931. // - input sans rien, data-type="checkbox" --> on fait tout
  932. // - input avec coquille sans data-uuid, --> on génère simplement un data-uuid et maj valeur
  933. // - input avec coquille et data-uuid --> on maj simplement sa valeur
  934. if(input.get(0).hasAttribute('data-uuid')) return;
  935. if(input.attr('type') != 'checkbox') input.attr('type', 'checkbox');
  936. if(!input.closest('label.check-component').length) {
  937. var labelBox = $('<label class="check-component"></label>');
  938. var checkbox = $('<div class="box"></div>');
  939. var id = input.attr('id');
  940. var title = input.attr('title');
  941. var customClass = input.attr('data-class');
  942. if(id) labelBox.attr('for', id);
  943. if(title) labelBox.attr('title', title);
  944. if(customClass) labelBox.addClass(customClass);
  945. if(input.prop('disabled') == true) labelBox.attr('disabled', true);
  946. input.removeAttr('title data-class').wrap(labelBox);
  947. input.after(checkbox);
  948. }
  949. var uuid = generate_uuid(15);
  950. input.attr('data-uuid', uuid);
  951. if(input.get(0).hasAttribute('id')) return;
  952. input.closest('label.check-component').off('click','div.box').on('click','div.box',function(e){
  953. var checkboxInput = $('input[data-uuid="'+uuid+'"]');
  954. checkboxInput.prop('checked', checkboxInput.prop('checked'));
  955. });
  956. break;
  957. /**
  958. * data-label : Libellé affiché à côté de l'input radio
  959. * name (*) : Le nom du groupe dont fait partie l'input radio
  960. */
  961. case 'radio':
  962. if(input.get(0).hasAttribute('data-uuid')) return;
  963. if(input.attr('type') != 'radio') input.attr('type', 'radio');
  964. var uuid = generate_uuid(15);
  965. var id = input.get(0).hasAttribute('id')? input.attr('id') : '';
  966. var title = input.get(0).hasAttribute('title') ? input.attr('title') : '';
  967. var label = input.attr('data-label');
  968. var labelBox = $('<label data-uid="'+uuid+'"></label>');
  969. labelBox.attr({
  970. 'for': id,
  971. 'title': title
  972. });
  973. if(input.prop('disabled') == true) labelBox.attr('disabled', true);
  974. input.addClass('radio-component').removeAttr('title data-label').attr('data-uuid', uuid);
  975. input.after(labelBox);
  976. if(label) labelBox.after('<label for="'+id+'">'+label+'</label>');
  977. if(input.attr('id') && input.attr('id').length) return;
  978. labelBox.click(function(e){
  979. var radioInput = $('input[data-uuid="'+uuid+'"]');
  980. if(radioInput.prop('disabled') != true) radioInput.prop('checked', true);
  981. });
  982. break;
  983. /**
  984. * data-action : l'action php pour récupérer l'UI de la card
  985. * data-parameters : paramètres à passer avec l'action json encodés
  986. */
  987. case 'card':
  988. if(!input.is(':visible')) return;
  989. var action = input.attr('data-action');
  990. if(!action) {
  991. console.log('CARD COMPONENT: Need "data-action" to get card content');
  992. return;
  993. }
  994. var showDelay = input.attr('data-show-delay') ? input.attr('data-show-delay') : 0;
  995. var hideDelay = input.attr('data-hide-delay') ? input.attr('data-hide-delay') : 600;
  996. var parameters = input.attr('data-parameters');
  997. var data = parameters ? JSON.parse(parameters) : {};
  998. data.action = action;
  999. input.addClass('card-component-container');
  1000. $(document).ready(function(){
  1001. var timeout;
  1002. input.mouseenter(function(event){
  1003. var e = event.target || event.relatedTarget;
  1004. if (e.parentNode != this && e != this) return;
  1005. event.stopImmediatePropagation();
  1006. event.stopPropagation();
  1007. $('*[data-type="card"]').find('.card-component')
  1008. .addClass('card-component-hide')
  1009. .removeClass('card-component-hover')
  1010. .one('webkitAnimationEnd oanimationend msAnimationEnd animationend',
  1011. function(e) {
  1012. $(this).closest('.wrapper').remove();
  1013. });
  1014. timeout = setTimeout(function(){
  1015. var currInput = input;
  1016. if(!currInput.find('.card-component').length) {
  1017. $.action(data, function(r){
  1018. if(!r.content) return;
  1019. //On utilise un wrapper pour gérer les overflows "out of the box"
  1020. var wrapper = $('<div class="wrapper"></div>');
  1021. var card = $(r.content);
  1022. var leftOffset = currInput.offset().left;
  1023. var topOffset = currInput.offset().top;
  1024. currInput.append(card);
  1025. var cardWidth = card.outerWidth();
  1026. var position = {};
  1027. card.wrap(wrapper);
  1028. card.addClass('card-component card-component-hover');
  1029. var wrapper = card.closest('div.wrapper');
  1030. if($('body').width() < (leftOffset + cardWidth))
  1031. position['right'] = cardWidth;
  1032. else
  1033. position['left'] = -currInput.outerWidth();
  1034. var cardHeight = card.outerHeight();
  1035. if($('body').height() < (topOffset + cardHeight))
  1036. position['top'] = -cardHeight;
  1037. currInput.find('.wrapper').css(position);
  1038. });
  1039. } else {
  1040. var card = $(currInput.find('.card-component'));
  1041. card.addClass('card-component-hover').removeClass('card-component-hide');
  1042. }
  1043. clearTimeout(currInput.data('tOutbox'));
  1044. }, showDelay);
  1045. });
  1046. input.add(input.find('.card-component')).mouseleave(function(e){
  1047. e.stopImmediatePropagation();
  1048. e.stopPropagation();
  1049. var input = $(this),
  1050. card = input.find('.card-component'),
  1051. tOutbox = setTimeout(function(){
  1052. if(!$('.card-component:hover').length) {
  1053. card.addClass('card-component-hide').removeClass('card-component-hover');
  1054. card.one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) {
  1055. $(this).closest('.wrapper').remove();
  1056. });
  1057. }
  1058. }, hideDelay);
  1059. //Clear du timeout d'apparition
  1060. clearTimeout(timeout);
  1061. //Set du l'id de timeout, permet de clear ce trigger si la souris revient sur l'input
  1062. input.data('tOutbox', tOutbox);
  1063. });
  1064. });
  1065. break;
  1066. /**
  1067. * data-dictionnary : Le slug du dictionnary à utiliser
  1068. */
  1069. case 'dictionnary-table':
  1070. if(input.hasClass('component-dictionnary-table')) return;
  1071. if(!input.attr('data-dictionnary') || !input.attr('data-dictionnary').length) return;
  1072. var tpl = $('.component-dictionnary-table').get(0).outerHTML;
  1073. $.action({
  1074. action: 'load_dictionnary_component',
  1075. slug : input.attr('data-dictionnary')
  1076. },function(r){
  1077. var list = $(Mustache.render(tpl,{label:'{{label}}', slug:'{{slug}}', id:'{{id}}',parent:r.content}));
  1078. input.append(list);
  1079. list.removeClass('hidden');
  1080. dictionnary_table_refresh(list);
  1081. //Save
  1082. $(input).on('click','thead .btn-success',function(){
  1083. var line = $(this).closest('tr');
  1084. var data = {
  1085. action: 'dictionnary_table_save',
  1086. label: line.find('input.list-label').val(),
  1087. id: list.attr('data-id'),
  1088. list: list.attr('data-dictionnary')
  1089. };
  1090. if(list.find('input.list-slug')) data.slug = $('input.list-slug', list).val();
  1091. $.action(data, function(r){
  1092. list.attr('data-id', '');
  1093. line.find('input').val('');
  1094. $.message('success','Enregistré');
  1095. dictionnary_table_refresh(list);
  1096. });
  1097. });
  1098. //Suppression
  1099. $(input).on('click','tbody tr .btn-danger',function(){
  1100. if(!confirm('Êtes-vous sûr de vouloir supprimer cet élément de liste ?')) return;
  1101. var line = $(this).closest('tr');
  1102. $.action({
  1103. action: 'delete_dictionnary',
  1104. id: line.attr('data-id')
  1105. },function(r){
  1106. line.remove();
  1107. line.closest('.edit-field').val('');
  1108. $.message('info', 'Élément de liste supprimé');
  1109. });
  1110. });
  1111. //Édition
  1112. $(input).on('click','tbody tr .btn-edit',function(){
  1113. var line = $(this).closest('tr');
  1114. $.action({
  1115. action: 'edit_dictionnary',
  1116. id: line.attr('data-id')
  1117. },function(r){
  1118. list.find('input.list-label').val(r.label);
  1119. if(list.find('input.list-slug')) $('input.list-slug', list).val(r.slug);
  1120. list.attr('data-id',r.id);
  1121. });
  1122. });
  1123. });
  1124. break;
  1125. //Permet aux plugins d'ajouter leurs composants
  1126. //via la fonction init_components_nomcomposant(input);
  1127. default:
  1128. var type = input.attr('data-type').replace(/[^a-z0-9]/i,'_');
  1129. if(window['init_components_'+type] !=null) window['init_components_'+type](input);
  1130. break;
  1131. }
  1132. });
  1133. }
  1134. /** PRELOADER **/
  1135. var detached;
  1136. function preloader(parent, mode){
  1137. var mode = typeof mode !== "undefined" ? mode : 'add';
  1138. if(['toggle', 'add', 'delete'].indexOf(mode) === -1) return;
  1139. var parent = $(parent);
  1140. var loader = $('<div class="loader-container text-center"><div class="loader"><div class="inner one"></div><div class="inner two"></div><div class="inner three"></div></div></div>');
  1141. switch (mode) {
  1142. case 'add':
  1143. parent.html(loader);
  1144. break;
  1145. case 'delete':
  1146. parent.find('div.loader-container').remove();
  1147. break;
  1148. case 'toggle':
  1149. if(parent.find('div.loader-container').length) {
  1150. parent.find('div.loader-container').remove();
  1151. parent.append(detached);
  1152. } else {
  1153. detached = parent.find('>*').detach();
  1154. parent.html(loader);
  1155. }
  1156. break;
  1157. default:
  1158. break;
  1159. }
  1160. }
  1161. /** BACK TO TOP **/
  1162. $('#toTheTop').click(function() {
  1163. scroll_top(150);
  1164. });
  1165. // Affichage du bouton dès lors où l'on a scrollé
  1166. $(window).on('ready scroll', function(){
  1167. scrollPos = $(document).scrollTop();
  1168. if (scrollPos >= 100) {
  1169. $('#toTheTop').css('display','flex').animate({opacity:1}, 100);
  1170. } else {
  1171. $('#toTheTop').stop().animate({opacity:0}, 100);
  1172. $('#toTheTop').removeAttr('style');
  1173. }
  1174. });
  1175. //Permet de scroller en haut de page
  1176. function scroll_top(time){
  1177. $('html,body').animate({scrollTop:0},time);
  1178. }
  1179. /** CONTRÔLES DATE ET HEURE **/
  1180. function is_valid_date(string){
  1181. var format = /^(?:(?:31(\/|-|\.)(?:0?[13578]|1[02]|(?:Jan|Mar|May|Jul|Aug|Oct|Dec)))\1|(?:(?:29|30)(\/|-|\.)(?:0?[1,3-9]|1[0-2]|(?:Jan|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))\2))(?:(?:1[6-9]|[2-9]\d)?\d{2})$|^(?:29(\/|-|\.)(?:0?2|(?:Feb))\3(?:(?:(?:1[6-9]|[2-9]\d)?(?:0[48]|[2468][048]|[13579][26])|(?:(?:16|[2468][048]|[3579][26])00))))$|^(?:0?[1-9]|1\d|2[0-8])(\/|-|\.)(?:(?:0?[1-9]|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep))|(?:1[0-2]|(?:Oct|Nov|Dec)))\4(?:(?:1[6-9]|[2-9]\d)?\d{2})$/;
  1182. return format.test(string);
  1183. }
  1184. function is_valid_hour(string){
  1185. var format = /^(?:\d|[01]\d|2[0-3]):[0-5]\d$/;
  1186. return format.test(string);
  1187. }
  1188. //SHOW/HIDE PASSWORD
  1189. function toggle_password(element){
  1190. if ($(element).hasClass('fa-eye-slash')) {
  1191. $(element).removeClass('fa-eye-slash').addClass('fa-eye');
  1192. $(element).prev('input').attr('type', 'text');
  1193. } else {
  1194. $(element).removeClass('fa-eye').addClass('fa-eye-slash');
  1195. $(element).prev('input').attr('type', 'password');
  1196. }
  1197. }
  1198. /** QUICKFORM **/
  1199. function reset_quickform_modal(){
  1200. $('.modal form').attr('data-id','');
  1201. $('input, textarea', '.modal').each(function(i, v){
  1202. if($(v).attr('type') == 'checkbox')
  1203. $(v).prop('checked', false);
  1204. else
  1205. $(v).val('');
  1206. });
  1207. }
  1208. /** CORE **/
  1209. //LOGIN
  1210. $(document).ready(function(){
  1211. $('#loginForm').on('keyup', 'input', function(e){
  1212. if(e.keyCode == 13) core_login();
  1213. });
  1214. });
  1215. function core_login(parent){
  1216. if(isProcessing) return;
  1217. var parent = parent!=undefined ? parent : '#loginRequest';
  1218. var data = $('#loginForm').toJson();
  1219. var loginBtn = $('#login-button');
  1220. isProcessing = true;
  1221. preloader(parent, 'toggle');
  1222. $('.loader-container').append('<small>Connexion en cours</small>');
  1223. loginBtn.html('<i class="fas fa-spinner fa-pulse"></i> Connexion');
  1224. $.action(data, function(r){
  1225. isProcessing = false;
  1226. if(r.redirect) window.location = r.redirect;
  1227. }, function(r){
  1228. isProcessing = false;
  1229. $('#password').val('');
  1230. $('.user-dropdown-menu').trigger('click');
  1231. loginBtn.html("Se connecter");
  1232. preloader(parent, 'toggle');
  1233. });
  1234. }
  1235. /** FILTER **/
  1236. $(document).ready(function(){
  1237. // RESET KEYWORD
  1238. $("input.filter-keyword").on('keyup',function(){
  1239. var posx = (this.offsetWidth + this.offsetLeft - 20);
  1240. var props = $(this).val()!='' ? {'left':posx,'opacity':'1'} : {'left':posx+30,'opacity':'0'}
  1241. $('#search-clear').css(props);
  1242. });
  1243. $("#search-clear").click(function(){
  1244. $(".filter-keyword").val('');
  1245. $(this).attr('style', '');
  1246. $(".filter-keyword").focus();
  1247. var searchCallback = $('#filters').attr('data-function');
  1248. if(searchCallback) window[searchCallback]();
  1249. });
  1250. });
  1251. //TOGGLE FILTERS SEARCH
  1252. function switch_advanced_filter(element){
  1253. $(element).closest('.filter-box').find('.advanced-search').toggleClass('hidden');
  1254. }
  1255. // SEARCH
  1256. function filter_search(box){
  1257. box = !box ? $('.filter-box:eq(0)'): box;
  1258. var component = box.prev('[data-type="filter"]');
  1259. var data = component.data();
  1260. var filters = component.filters();
  1261. if(filters.advanced.length>0 || filters.keyword!=''){
  1262. filters = JSON.stringify(filters);
  1263. $.urlParam('filters',btoa(filters));
  1264. }else{
  1265. $.urlParam('filters','');
  1266. }
  1267. window[data.function]();
  1268. if(data.callback) window[data.callback]();
  1269. }
  1270. function filter_clean(element){
  1271. var box = $(element).closest('.filter-box');
  1272. $.urlParam('filters','');
  1273. $('.filterRow:visible:not(:eq(0))',box).remove();
  1274. $('.filterRow:visible .filter-column',box).prop('selectedIndex',0).trigger('change');
  1275. }
  1276. function filter_save(element){
  1277. var input = $(element).closest('.filter-box').prev();
  1278. var data = input.filters();
  1279. $.action({
  1280. action : 'filter_save',
  1281. slug : input.attr('data-slug'),
  1282. filters : data
  1283. },function(response){
  1284. $.message('info',response.message);
  1285. });
  1286. }
  1287. // ADD
  1288. function filter_add(element,data){
  1289. var box = $(element).closest('.filter-box');
  1290. var line = $(element).closest('.filterRow');
  1291. var newline = line.clone();
  1292. newline.find('input').val('');
  1293. newline.find('input:eq(3)').replaceWith('<div readonly="readonly" class="form-control">Sélectionnez la colonne de tri</div>');
  1294. newline.find('.filter-values').removeClass('hidden');
  1295. newline.find('.filter-value-block').remove();
  1296. line.after(newline);
  1297. if(box.attr('data-join') == null || box.attr('data-join') == ''){
  1298. box.find('.filter-join:not(:eq(0))').removeClass('invisible');
  1299. } else {
  1300. box.find('.filter-join:not(:eq(0))').val(box.attr('data-join'));
  1301. }
  1302. init_components(newline);
  1303. if(!data) return;
  1304. $('.filter-column',newline).val(data.column).trigger('change');
  1305. $('.filter-operator',newline).val(data.operator).trigger('change');
  1306. if(!Array.isArray(data.value)) data.value = [data.value];
  1307. //Exception pour les composant dictionnary multi niveau
  1308. $('.filter-value[data-type="dictionnary"]',newline).attr('data-value',data.value.slice(-1)[0] );
  1309. for(var i in data.value){
  1310. $('.filter-value:eq('+i+')',newline).val(data.value[i]).trigger('change');
  1311. }
  1312. init_components(newline);
  1313. }
  1314. // DELETE
  1315. function filter_delete(element){
  1316. var line = $(element).closest('.filterRow');
  1317. if($('.filterRow:visible').length>1){
  1318. line.remove();
  1319. }else{
  1320. line.find('input').val('');
  1321. line.find('select').prop('selectedIndex',0);
  1322. line.find('.filter-value-block').remove();
  1323. line.find('.filter-join').addClass('invisible');
  1324. line.find('div.filter-values').removeClass('hidden');
  1325. }
  1326. }
  1327. //SET COMPARATOR
  1328. function filter_set_comparator(element){
  1329. var selected = $('option:selected',element);
  1330. var line = $(element).closest('.filterRow');
  1331. filter_refresh_values(line);
  1332. if(selected.attr('data-values')){
  1333. if(selected.attr('data-values')==0){
  1334. $('.filter-value:eq(0)',line).remove();
  1335. }else if(selected.attr('data-values')>1){
  1336. for(var i=0;i<selected.attr('data-values')-1;i++){
  1337. var cloneValue = $('.filter-value:eq(0)',line).clone();
  1338. cloneValue.addClass('ml-1').removeClass('hasDatepicker').removeAttr('id');
  1339. $('.filter-value-block',line).append(cloneValue);
  1340. init_components(line);
  1341. }
  1342. }
  1343. }
  1344. init_components(line);
  1345. }
  1346. // SET
  1347. function filter_set_column(element){
  1348. var line = $(element).closest('.filterRow');
  1349. $('.filter-operator,.filter-value',line).remove();
  1350. filter_refresh_operator(line);
  1351. filter_refresh_values(line);
  1352. init_components(line);
  1353. }
  1354. function filter_refresh_operator(line){
  1355. var valueZone = line.find('.filter-values');
  1356. var filterColumn = line.find('.filter-column');
  1357. var operatorColumn = line.find('.filter-operator');
  1358. var option = $('option:selected',filterColumn);
  1359. var data = option.data();
  1360. // Si aucun filtre n'est selctionné, on remove les champs suivants
  1361. if(!option.val().length) {
  1362. line.find('input').val('');
  1363. line.find('select').prop('selectedIndex',0);
  1364. line.find('.filter-value-block').remove();
  1365. line.find('.filter-join').addClass('invisible');
  1366. valueZone.removeClass('hidden');
  1367. return;
  1368. }
  1369. data.value = filterColumn.val();
  1370. //supression de lancien block operateur + valeur
  1371. line.find('.filter-value-block').remove();
  1372. //ajout du bloc operateur / valeur
  1373. var templateOperator = '<div class="filter-value-block">'+$('.filter-value-block[data-value-type="'+data.filterType+'"]:hidden .filter-operator').get(0).outerHTML.replace('data-template','data-type')+"</div>";
  1374. filterOperator = $(templateOperator);
  1375. valueZone.addClass('hidden').after(filterOperator);
  1376. line.find('.filter-value-block').css('display','inline-block');
  1377. }
  1378. function filter_refresh_values(line){
  1379. var filterColumn = line.find('.filter-column');
  1380. var currOption = $('option:selected',filterColumn);
  1381. var data = currOption.data();
  1382. if(is_empty_obj(data)) return;
  1383. //data.value = filterColumn.val();
  1384. //supression de l'ancien block operateur + valeur
  1385. line.find('.filter-value-block .filter-value').remove();
  1386. //ajout du bloc operateur / valeur
  1387. var templateValue = $('.filter-value-block[data-value-type="'+data.filterType+'"]:hidden .filter-value').get(0).outerHTML.replace('data-template','data-type');
  1388. filterValue = $(Mustache.render(templateValue,data));
  1389. //Si le type de champ est une liste on la remplit avec le datasource
  1390. if(data.filterSource){
  1391. var source = JSON.parse(currOption.attr('data-filter-source'));
  1392. var options = '<option value=""> - </option>';
  1393. for (var k in source)
  1394. options += '<option value="'+k+'">'+source[k]+'</option>';
  1395. filterValue.append(options);
  1396. }
  1397. filterValue.removeClass('hasDatepicker').removeAttr('id');
  1398. $('.filter-operator',line).after(filterValue);
  1399. }
  1400. // INIT - INDEX
  1401. function init_index(){
  1402. }
  1403. function init_setting(parameter){
  1404. switch(parameter.section){
  1405. case 'plugin':
  1406. search_plugin();
  1407. $('.section-plugin').on('change', 'input.toggle', function(){
  1408. var input = $(this);
  1409. var button = input.closest('.activator');
  1410. var label = $('> label', button).detach();
  1411. var value = input.prop('checked');
  1412. if(!value) {
  1413. if(!confirm("Êtes-vous sûr de vouloir désactiver ce plugin ?\nCela entraînera la suppression de toutes les données associées.")) {
  1414. input.prop('checked',!value);
  1415. button.prepend(label);
  1416. return;
  1417. }
  1418. button.removeClass('btn-danger').addClass('btn-info').text('Activer').prepend(label);
  1419. } else {
  1420. button.addClass('btn-danger').removeClass('btn-info').text('Désactiver').prepend(label);
  1421. }
  1422. $.action({
  1423. action : 'change_plugin_state',
  1424. plugin : input.closest('li').attr('data-id'),
  1425. state: value ? 1 : 0
  1426. }, function(r){
  1427. }, function(r){
  1428. if(button.hasClass('btn-danger'))
  1429. button.removeClass('btn-danger').addClass('btn-info').text('Activer').prepend(label);
  1430. else if(button.hasClass('btn-info'))
  1431. button.removeClass('btn-info').addClass('btn-danger').text('Désactiver').prepend(label);
  1432. input.prop('checked',!value);
  1433. });
  1434. });
  1435. break;
  1436. case 'user':
  1437. search_user();
  1438. break;
  1439. case 'firm':
  1440. search_firm();
  1441. break;
  1442. case 'firmPlugin':
  1443. search_firm_plugin('update_checkboxes');
  1444. break;
  1445. case 'userfirmrank':
  1446. search_userfirmrank();
  1447. break;
  1448. case 'log':
  1449. $('#logs').sortable_table({
  1450. onSort : search_log
  1451. });
  1452. break;
  1453. case 'rank':
  1454. search_rank();
  1455. break;
  1456. case 'dictionnary':
  1457. search_dictionnary();
  1458. break;
  1459. case 'right':
  1460. search_right('update_checkboxes');
  1461. break;
  1462. default:
  1463. if(parameter.section!= null){
  1464. var section = parameter.section.replace(/[^a-z_0-9]/g,'_');
  1465. var init = 'init_setting_'+section;
  1466. if(window[init]!=null) window[init]($.urlParam());
  1467. }
  1468. break;
  1469. }
  1470. }
  1471. /** RIGHT **/
  1472. function right_switch(element){
  1473. $(element).closest('tr').find('input').trigger('click');
  1474. }
  1475. /** FORM **/
  1476. function send_form(element){
  1477. var form = $(element).closest('[data-form]');
  1478. var data = $.getForm(form);
  1479. var data = {};
  1480. for(var key in form.data()){
  1481. if(key!="action" && key != "id") continue;
  1482. data[key] = form.attr('data-'+key);
  1483. }
  1484. $('input,select,textarea',form).each(function(i,element){
  1485. element = $(element);
  1486. if(element.attr('data-id')!=null && element.attr('data-id')!=""){
  1487. if(element.attr("type")=='checkbox' || element.attr("type")=='radio'){
  1488. data[element.attr('data-id')] = (element.is(':checked')?1:0);
  1489. }else{
  1490. data[element.attr('data-id')] = element.val();
  1491. }
  1492. }
  1493. });
  1494. data.action = 'send_form';
  1495. $.action(data,function(r){
  1496. });
  1497. }
  1498. /** LOG **/
  1499. // SEARCH
  1500. function search_log(callback){
  1501. $('#logs').fill({
  1502. action:'search_log',
  1503. filters : $('#filters').filters(),
  1504. sort : $('#logs').sortable_table('get')
  1505. },function(){
  1506. if(callback!=null) callback();
  1507. });
  1508. }
  1509. /** USER FIRM RANK**/
  1510. // SEARCH
  1511. function search_userfirmrank(callback){
  1512. $('#userfirmranks').fill({firm : $('#firm').val(),action:'search_userfirmrank'},function(){
  1513. if(callback!=null) callback();
  1514. });
  1515. }
  1516. // SAVE
  1517. function save_userfirmrank(){
  1518. var data = $.getForm('#userfirmrankForm');
  1519. data.id = $('#userfirmrankForm').attr('data-id');
  1520. $.action(data,function(r){
  1521. if(typeof r.success !== 'undefined'){
  1522. var messageSuccess = 'Utilisateur ajouté pour '+(r.success.length > 1 ? 'les ' : 'l\'')+'établissement'+(r.success.length > 1 ? 's : ' : ' : ')+r.success
  1523. $.message('success',messageSuccess,0);
  1524. }
  1525. $('#userfirmrankForm').attr('data-id','');
  1526. $('#userfirmrankForm input').val('');
  1527. $('#firm').val($("#firm option:first").val());
  1528. $('#rank').val($("#rank option:first").val());
  1529. search_userfirmrank();
  1530. });
  1531. }
  1532. // EDIT
  1533. function edit_userfirmrank(element){
  1534. var line = $(element).closest('tr');
  1535. $.action({
  1536. action:'edit_userfirmrank',
  1537. id:line.attr('data-id')
  1538. },function(r){
  1539. $.setForm('#userfirmrankForm',r);
  1540. $('#firm').change();
  1541. init_components($('#userfirmrankForm'));
  1542. $('#userfirmrankForm').attr('data-id',r.id);
  1543. });
  1544. }
  1545. // DELETE
  1546. function delete_userfirmrank(element){
  1547. if(!confirm('Êtes vous sûr de vouloir supprimer ce lien Établissement / Utilisateur / Rang ?')) return;
  1548. var line = $(element).closest('tr');
  1549. $.action({
  1550. action : 'delete_userfirmrank',
  1551. id : line.attr('data-id')
  1552. },function(r){
  1553. $.message('info', 'Lien Établissement / Utilisateur / Rang supprimé.')
  1554. line.remove();
  1555. });
  1556. }
  1557. /** FIRM **/
  1558. // SEARCH
  1559. function search_firm(callback){
  1560. $('#firms').fill({action:'search_firm'},function(){
  1561. if(callback!=null) callback();
  1562. });
  1563. }
  1564. // EDIT
  1565. function edit_firm(element){
  1566. var line = $(element).closest('tr');
  1567. window.location = 'firm.php?id='+line.attr('data-id');
  1568. }
  1569. // DELETE
  1570. function delete_firm(element){
  1571. if(!confirm('Êtes vous sûr de vouloir supprimer cet établissement ?')) return;
  1572. var line = $(element).closest('tr');
  1573. $.action({
  1574. action : 'delete_firm',
  1575. id : line.attr('data-id')
  1576. },function(r){
  1577. $.message('info','Établissement supprimé');
  1578. line.remove();
  1579. });
  1580. }
  1581. /** FIRM PLUGINS **/
  1582. // SEARCH
  1583. function search_right(callback){
  1584. $('#ranks').fill({
  1585. action: 'search_right',
  1586. rank: $('#rank').attr('data-rank'),
  1587. firm: $('#firm').val()
  1588. },function(r){
  1589. if(callback!=null) update_checkboxes(r);
  1590. });
  1591. }
  1592. // TOGGLE RIGHT
  1593. function toggle_right(element){
  1594. var line = $(element).closest('tr');
  1595. $.action({
  1596. action:'toggle_right',
  1597. rank:$('#rank').attr('data-rank'),
  1598. firm:$('#firm').val(),
  1599. section:line.attr('data-section'),
  1600. right:$(element).attr('data-right'),
  1601. state:$(element).prop('checked')?1:0
  1602. });
  1603. }
  1604. // SEARCH
  1605. function search_firm_plugin(callback){
  1606. $('#firmplugins').fill({
  1607. action:'search_firm_plugin',
  1608. firm:$('#firm').val()
  1609. },function(r){
  1610. if(callback!=null) update_checkboxes(r);
  1611. });
  1612. }
  1613. // ENABLE/DISABLE
  1614. function toggle_firm_plugin(element){
  1615. var line = $(element).closest('tr');
  1616. $.action({
  1617. action:'toggle_firm_plugin',
  1618. firm :$('#firm').val(),
  1619. state : $(element).prop('checked')?1:0,
  1620. plugin: line.attr('data-id')
  1621. });
  1622. }
  1623. function update_checkboxes(response){
  1624. $('input[data-result="0"]').prop('checked',false);
  1625. $('input[data-result="1"]').prop('checked',true);
  1626. $('input[data-result="2"]').prop('indeterminate',true);
  1627. }
  1628. // SUPPRESSION LOGO ÉTABLISSEMENT
  1629. function firm_logo_delete(element){
  1630. if(!confirm('Êtes vous sûr de vouloir supprimer l\'image ?')) return;
  1631. var imageComposer = $(element).parent().find("input[data-type='image']");
  1632. $.action({
  1633. action: 'firm_logo_delete',
  1634. id: $('#firm').val()
  1635. }, function(r){
  1636. imageComposer.wrap('<form>').closest('form').get(0).reset();
  1637. imageComposer.unwrap();
  1638. $(element).next('img').attr('src', $(imageComposer).attr('data-default-src'));
  1639. $(element).remove();
  1640. });
  1641. }
  1642. /** USER **/
  1643. // SEARCH
  1644. function search_user(callback){
  1645. $('#users').fill({action:'search_user'},function(){
  1646. if(callback!=null) callback();
  1647. });
  1648. }
  1649. // SAVE
  1650. function save_user(){
  1651. var data = $.getForm('#userFormAdmin');
  1652. data.id = $('#userFormAdmin').attr('data-id');
  1653. $.action(data,function(r){
  1654. $.message('success','Utilisateur enregistré');
  1655. $('#login').removeAttr('readonly');
  1656. $('#userFormAdmin input').val('');
  1657. $('#userFormAdmin').attr('data-id','');
  1658. search_user();
  1659. });
  1660. }
  1661. // EDIT
  1662. function edit_user(element){
  1663. var line = $(element).closest('tr');
  1664. $.action({
  1665. action:'edit_user',
  1666. login:line.attr('data-user')
  1667. },function(r){
  1668. $.setForm('#userFormAdmin',r);
  1669. $('#userFormAdmin').attr('data-id',r.id);
  1670. $('#login').attr('readonly', true);
  1671. init_components('#userFormAdmin');
  1672. });
  1673. }
  1674. // DELETE
  1675. function delete_user(element){
  1676. if(!confirm('Êtes vous sûr de vouloir supprimer cet utilisateur ?')) return;
  1677. var line = $(element).closest('tr');
  1678. $.action({
  1679. action : 'delete_user',
  1680. login : line.attr('data-user')
  1681. },function(r){
  1682. $.message('info','Utilisateur supprimé');
  1683. $('#userFormAdmin').attr('data-id','');
  1684. reset_inputs($('#userFormAdmin'));
  1685. $('#login').removeAttr('readonly');
  1686. line.remove();
  1687. });
  1688. }
  1689. /* ACCOUNT **/
  1690. function account_lost_password (){
  1691. $.action({
  1692. action: 'account_lost_password',
  1693. mail : $('#mail').val()
  1694. }, function(r){
  1695. $.message('success','Confirmation envoyée par e-mail');
  1696. });
  1697. }
  1698. function account_save(element){
  1699. var data = $('#user-form').toJson();
  1700. data.login= $('#login').val();
  1701. data.avatar = $('#avatar')[0].files[0];
  1702. data.action = 'account_save';
  1703. $.action(data, function(r){
  1704. $.message('success','Enregistré');
  1705. if(r.warning) $.message('warning',r.warning);
  1706. });
  1707. }
  1708. // SUPPRIME AVATAR USER
  1709. function account_avatar_delete(element){
  1710. if(!confirm('Êtes vous sûr de vouloir supprimer l\'image ?')) return;
  1711. var imageComposer = $(element).parent().find("input[data-type='image']");
  1712. $.action({
  1713. action: 'account_avatar_delete',
  1714. login: $('#login').val()
  1715. }, function(r){
  1716. imageComposer.wrap('<form>').closest('form').get(0).reset();
  1717. imageComposer.unwrap();
  1718. $(element).next('img').attr('src', $(imageComposer).attr('data-default-src'));
  1719. $(element).remove();
  1720. });
  1721. }
  1722. /** RANKS **/
  1723. // SEARCH
  1724. function search_rank(callback){
  1725. $('#ranks').fill({action:'search_rank'},function(){
  1726. if(callback!=null) callback();
  1727. });
  1728. }
  1729. // SAVE
  1730. function save_rank(){
  1731. var data = $.getForm('#rankForm');
  1732. data.id = $('#rankForm').attr('data-id');
  1733. $.action(data,function(r){
  1734. $.message('success','Rang enregistré');
  1735. $('#rankForm input').val('');
  1736. $('#rankForm').attr('data-id','');
  1737. search_rank();
  1738. });
  1739. }
  1740. // EDIT
  1741. function edit_rank(element){
  1742. var line = $(element).closest('tr');
  1743. $.action({action:'edit_rank',id:line.attr('data-id')},function(r){
  1744. $.setForm('#rankForm',r);
  1745. $('#rankForm').attr('data-id',r.id);
  1746. });
  1747. }
  1748. // DELETE
  1749. function delete_rank(element){
  1750. if(!confirm('Êtes vous sûr de vouloir supprimer ce rang ?')) return;
  1751. var line = $(element).closest('tr');
  1752. $.action({
  1753. action : 'delete_rank',
  1754. id : line.attr('data-id')
  1755. },function(r){
  1756. $.message('info','Rang supprimé');
  1757. line.remove();
  1758. });
  1759. }
  1760. /** DICTIONNARY **/
  1761. // SEARCH
  1762. function search_dictionnary(callback){
  1763. var parentValue = $('#parent').val();
  1764. parentValue != "" ? $('#prev-button').removeClass('hidden') : $('#prev-button').addClass('hidden');
  1765. $('#dictionnaries').fill({
  1766. action:'search_dictionnary',
  1767. parent : parentValue
  1768. },function(r){
  1769. reset_inputs($('#dictionnaryForm'), false, true);
  1770. if(callback!=null) callback();
  1771. var tpl = $('#parent').find('option[value="{{id}}"]');
  1772. if(!tpl.parent('span').length) tpl.wrap('<span>').addClass('hidden');
  1773. });
  1774. }
  1775. // SAVE
  1776. function save_dictionnary(){
  1777. var data = $.getForm('#dictionnaryForm');
  1778. data.id = $('#dictionnaryForm').attr('data-id');
  1779. data.parent = $('#parent').val();
  1780. $.action(data,function(r){
  1781. $.message('success','Liste enregistrée');
  1782. $('#dictionnaryForm input').val('');
  1783. $('#dictionnaryForm').attr('data-id','');
  1784. search_dictionnary();
  1785. });
  1786. }
  1787. // EDIT
  1788. function edit_dictionnary(element){
  1789. var line = $(element).closest('tr');
  1790. $.action({
  1791. action: 'edit_dictionnary',
  1792. id: line.attr('data-id')
  1793. },function(r){
  1794. $.setForm('#dictionnaryForm',r);
  1795. $('#dictionnaryForm').attr('data-id',r.id);
  1796. });
  1797. }
  1798. // DELETE
  1799. function delete_dictionnary(element){
  1800. if(!confirm('Êtes-vous sûr de vouloir supprimer cette liste ?')) return;
  1801. var line = $(element).closest('tr');
  1802. $.action({
  1803. action: 'delete_dictionnary',
  1804. id: line.attr('data-id')
  1805. },function(r){
  1806. line.remove();
  1807. $.message('info','Liste supprimée');
  1808. });
  1809. }
  1810. // Remplissage de la liste (select) --> Dans les settings
  1811. function get_dictionnary_items(elem, elemToFill){
  1812. var parent = $(elem).closest('tr');
  1813. var id = $(parent).attr('data-id');
  1814. var parentId = $(parent).attr('data-parent');
  1815. $(elemToFill).fill({
  1816. action:'search_dictionnary',
  1817. parent : parentId.toString()
  1818. },function(){
  1819. $(elemToFill).val(id).change();
  1820. });
  1821. }
  1822. // Ajout de sous-liste --> Dans les settings
  1823. function add_sub_dictionnary(elem){
  1824. reset_inputs($('#dictionnaryForm'), false, true);
  1825. var parent = $(elem).closest('tr');
  1826. var id = $(parent).attr('data-id');
  1827. var value = parent.find(".itemLabel").text();
  1828. if ($("#parent option[value='"+id+"']").length > 0)
  1829. $('#parent').val(id).change();
  1830. else {
  1831. get_dictionnary_items(elem, "#parent");
  1832. $('code').addClass('hidden');
  1833. }
  1834. $('#prev-button').removeClass('hidden');
  1835. }
  1836. // Récupération éléments de la liste précédente --> Dans les settings
  1837. function previous_list_dictionnary(elem){
  1838. var selected = $('#parent > option:selected').val();
  1839. reset_inputs($('#dictionnaryForm'), false, true);
  1840. $.action({
  1841. action: 'get_parent_dictionnary',
  1842. selected: selected
  1843. }, function(r){
  1844. var data = r.rows[0];
  1845. $('#parent option[value!="{{id}}"]').remove();
  1846. if (data.parentId == "") $('#prev-button').addClass('hidden');
  1847. if (data.parents[0].parent == "0") {
  1848. $('#parent').append("<option value='' selected>-</option>");
  1849. $('code').removeClass('hidden');
  1850. }
  1851. $.each(data.parents, function (index, value){
  1852. if (value.id == data.parentId)
  1853. $('#parent').append("<option selected value='"+value.id+"'>"+value.label+"</option>");
  1854. else
  1855. $('#parent').append("<option value='"+value.id+"'>"+value.label+"</option>");
  1856. });
  1857. search_dictionnary();
  1858. });
  1859. }
  1860. // Récupération des sous-listes pour les champ de type "dictionnary" --> Où le module est appelé
  1861. function get_sub_dictionnary(elem, name, currentDepth){
  1862. var input = $(elem);
  1863. var selectValue = input.val();
  1864. var optSubLabel = $('option:selected', elem).attr('data-sublabel');
  1865. var depth = (input.attr('data-depth') && input.attr('data-depth') != "") ? input.attr('data-depth') : "1";
  1866. var fieldName = name == '' ? input.attr('name') : name;
  1867. currentDepth -= currentDepth != 1 ? input.nextAll('select').length : 0;
  1868. if(input.closest('span.dictionnary-container').length != 0){
  1869. var id = input.nextAll().last().attr('id');
  1870. input.attr('id', id).nextAll().remove();
  1871. }
  1872. if (selectValue.length && currentDepth < depth) {
  1873. input.removeAttr('name');
  1874. currentDepth += 1;
  1875. $.action({
  1876. action: 'search_dictionnary',
  1877. parent: selectValue
  1878. }, function(r){
  1879. var option = '';
  1880. if(input.closest('span.dictionnary-container').length == 0) input.wrap('<span class="dictionnary-container"></span>');
  1881. var newSelect = clone_input_dictionnary(input, selectValue, fieldName);
  1882. var currentDepth = input.closest('span.dictionnary-container').children('select').length;
  1883. if (input.attr('data-disable-label') != "" && optSubLabel != "" && optSubLabel != "null") input.after('<label class="label-select">'+optSubLabel+'</label>');
  1884. newSelect.attr('onchange', 'get_sub_dictionnary(this, "'+fieldName+'", '+currentDepth+');');
  1885. newSelect.append('<option value=""> - </option>');
  1886. $.each(r.rows, function(index, value){
  1887. option += '<option value="'+value.id+'" data-parent="'+value.parent+'" data-sublabel="'+value.sublistlabel+'">'+value.label+'</option>';
  1888. });
  1889. newSelect.append(option);
  1890. });
  1891. } else if (!selectValue.length) {
  1892. input.attr('name', fieldName);
  1893. } else if (currentDepth == depth && input.attr('data-filter-type-value') && input.attr('data-filter-type-value').length) {
  1894. data = input.data();
  1895. input.closest('.filter-value-block').next('.filter-value-block[data-value-type="'+data.filterTypeValue+'"]').remove();
  1896. var template = $('.filter-value-block[data-value-type="'+data.filterTypeValue+'"]:hidden').get(0).outerHTML;
  1897. filterValue = $(Mustache.render(template,data));
  1898. filterValue.css('display','inline-block');
  1899. filterValue.find('input').removeClass('hasDatepicker').removeAttr('id');
  1900. input.closest('.filter-value-block').after(filterValue);
  1901. }
  1902. }
  1903. // Récupération des sous-listes depuis l'id du plus petit enfant --> Où le module est appelé
  1904. function get_selected_values(elem, select){
  1905. var input = $(elem);
  1906. if(input.closest('span.dictionnary-container').length == 0) input.wrap('<span class="dictionnary-container"></span>');
  1907. var fieldName = input.attr('name');
  1908. var currentDepth = 1+input.closest('span.dictionnary-container').children('select').length;
  1909. var optSubLabel = select.sublistlabel;
  1910. var option = '';
  1911. if (typeof select.childs !== 'undefined' && select.childs.length > 0) {
  1912. var newSelect = clone_input_dictionnary(input, select.id, fieldName);
  1913. if (newSelect.attr('data-disable-label') != "" && optSubLabel != "" && optSubLabel != "null" && optSubLabel != undefined) input.after('<label class="label-select">'+optSubLabel+'</label>');
  1914. newSelect.append('<option value=""> - </option>');
  1915. }
  1916. $.each(select.childs, function(index, value){
  1917. var selected = '';
  1918. if (value.hasOwnProperty("selected")) {
  1919. input.removeAttr('name');
  1920. get_selected_values(newSelect, value);
  1921. selected = "selected";
  1922. }
  1923. newSelect.attr('onchange', 'get_sub_dictionnary(this,"'+fieldName+'", '+currentDepth+');');
  1924. option += '<option value="'+value.id+'" data-parent="'+value.parent+'" data-sublabel="'+value.sublistlabel+'" '+selected+'>'+value.label+'</option>';
  1925. });
  1926. if(newSelect) newSelect.append(option);
  1927. }
  1928. // Clone du select de type "dictionnary"
  1929. function clone_input_dictionnary(input, id, name){
  1930. var newSelect = input.clone();
  1931. input.removeAttr('id').after(newSelect).after(' ');
  1932. newSelect.removeAttr("data-type").removeAttr('name').removeAttr('data-value');
  1933. // newSelect.attr('id', 'children-select-'+id).attr('name', name);
  1934. newSelect.find('option').remove();
  1935. return newSelect;
  1936. }
  1937. //Rafraîchit la table de la liste donnée
  1938. //En lien avec le composant dictionnary_table
  1939. function dictionnary_table_refresh(elem){
  1940. var id = $(elem).attr('data-dictionnary');
  1941. var table = $(elem).find('table:eq(0)');
  1942. $.action({
  1943. action: 'dictionnary_table_search',
  1944. id: id
  1945. },function(r){
  1946. table.find('tbody tr:visible').remove();
  1947. var tpl = table.find('tbody tr:hidden').get(0).outerHTML;
  1948. for(var key in r.rows){
  1949. var row = r.rows[key];
  1950. var line = $(Mustache.render(tpl,row));
  1951. line.removeClass('hidden');
  1952. table.find('tbody').append(line);
  1953. }
  1954. });
  1955. }
  1956. /** PLUGINS **/
  1957. // SEARCH
  1958. function search_plugin(callback){
  1959. $('#plugins').fill({action:'search_plugin'},function(){
  1960. if(callback!=null) callback();
  1961. });
  1962. }
  1963. // RÉCUPÉRATION PARAMÈTRE URL
  1964. var get_url_parameter = function get_url_parameter(sParam, string) {
  1965. var sPageURL = !string ? decodeURIComponent(window.location.search.substring(1)) : string;
  1966. var sURLVariables = sPageURL.split('&'),
  1967. sParameterName,
  1968. i;
  1969. for (i = 0; i < sURLVariables.length; i++) {
  1970. sParameterName = sURLVariables[i].split('=');
  1971. if (sParameterName[0] === sParam) {
  1972. return sParameterName[1] === undefined ? true : sParameterName[1];
  1973. }
  1974. }
  1975. };
  1976. // DROPZONE
  1977. function dropzone_delete_file(element){
  1978. if(!confirm('Êtes-vous sûr de vouloir supprimer cet item ?')) return;
  1979. var elem = $(element);
  1980. var line = elem.closest('li');
  1981. var container = elem.closest('div[data-type="dropzone"]');
  1982. var inputTemp = container.find('#'+container.attr('id')+'_temporary');
  1983. var values = inputTemp.val().length ? JSON.parse(inputTemp.val()) : [];
  1984. for (var i in values) {
  1985. if(values[i]['path'] == line.attr('data-path'))
  1986. values.splice(i, 1);
  1987. }
  1988. line.remove();
  1989. inputTemp.val(JSON.stringify(values));
  1990. }
  1991. /* GENERAL SETTINGS */
  1992. //Sauvegarde de la configuration générale
  1993. function general_settings_save(){
  1994. var data = $('#general-settings').getForm();
  1995. data.logo = $('#logo')[0].files[0];
  1996. data.logo_dark = $('#logo_dark')[0].files[0];
  1997. data.favicon = $('#favicon')[0].files[0];
  1998. data.password_format = $('#password-format-form').attr('data-format');
  1999. $.action(data, function(r){
  2000. $.message('success', 'Configuration enregistrée');
  2001. });
  2002. }
  2003. function general_reset_password_delay(){
  2004. if(!confirm('Êtes-vous sûr de vouloir forcer tous les utilisateurs à réinitialiser leurs mot de passe ?')) return;
  2005. $.action({
  2006. action: 'general_reset_password_delay'
  2007. }, function(r){
  2008. $.message('success', 'Validé');
  2009. });
  2010. }
  2011. function password_settings_format(){
  2012. var patterns = [];
  2013. $('#password-format-form input[data-pattern]:checked').each(function(){
  2014. patterns.push($(this).attr('data-pattern'));
  2015. });
  2016. $('#password-format-form').attr('data-format',JSON.stringify(patterns));
  2017. }
  2018. //Suppression image générale application
  2019. function general_logo_delete(element){
  2020. if(!confirm('Êtes-vous sûr de vouloir supprimer l\'image ?')) return;
  2021. var imageComposer = $(element).parent().find("input[data-type='image']");
  2022. $.action({
  2023. action: 'general_logo_delete'
  2024. }, function(r){
  2025. imageComposer.wrap('<form>').closest('form').get(0).reset();
  2026. imageComposer.unwrap();
  2027. $(element).next('img').attr('src', $(imageComposer).attr('data-default-src'));
  2028. $(element).remove();
  2029. $.message('info', 'Image supprimée');
  2030. });
  2031. }
  2032. //Suppression favicon générale application
  2033. function general_favicon_delete(element){
  2034. if(!confirm('Êtes-vous sûr de vouloir supprimer l\'image ?')) return;
  2035. var imageComposer = $(element).parent().find("input[data-type='image']");
  2036. $.action({
  2037. action: 'general_favicon_delete'
  2038. }, function(r){
  2039. imageComposer.wrap('<form>').closest('form').get(0).reset();
  2040. imageComposer.unwrap();
  2041. $(element).next('img').attr('src', $(imageComposer).attr('data-default-src'));
  2042. $(element).remove();
  2043. $.message('info', 'Favicon supprimée');
  2044. });
  2045. }
  2046. //Active/Désactive la page de maintenance du site
  2047. function toggle_maintenance(){
  2048. var checkbox = $('#maintenance');
  2049. var state = checkbox.is(':checked') ? 'd\'activer' : 'de désactiver';
  2050. if(!confirm("Vous êtes sur le point "+state+" la page de maintenance du site.\nVoulez-vous continuer ?")){
  2051. checkbox.is(':checked') ? checkbox.removeAttr('checked').prop('checked', false) : checkbox.attr('checked', true).prop('checked', true);
  2052. return;
  2053. }
  2054. general_settings_save();
  2055. }
  2056. /* FUNCTIONS */
  2057. //Récupère la plus petite valeur d'un tableau
  2058. function getMinValue(arr) {
  2059. return arr.reduce(function (p, v) {
  2060. return ( p < v ? p : v );
  2061. });
  2062. };
  2063. //Récupère la plus grande valeur d'un tableau
  2064. function getMaxValue(arr) {
  2065. return arr.reduce(function (p, v) {
  2066. return ( p > v ? p : v );
  2067. });
  2068. };
  2069. //Check si la valeur saisie dans l'input
  2070. //est une adresse e-mail bien formatée
  2071. function is_email(email) {
  2072. var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  2073. return re.test(String(email).toLowerCase());
  2074. }
  2075. //Check si la valeur saisie dans
  2076. //l'input est une url bien formatée
  2077. function is_url(str) {
  2078. regexp = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/;
  2079. if (regexp.test(str))
  2080. return true;
  2081. return false;
  2082. }
  2083. //Check format n° de téléphone
  2084. function is_phone_number(number, required, label){
  2085. if (required && number == ''){
  2086. $.message('error',label+' obligatoire');
  2087. return false;
  2088. }
  2089. if (number != ''){
  2090. number = number.replace(/\s/g, '');
  2091. number = number.replace(/\./g, '');
  2092. number = number.replace(/-/g, '');
  2093. if(is_numeric(number)) {
  2094. if(/^(?:(?:\+|00)\d{2}|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/.test(number) == false){
  2095. $.message('error',' Le format du n° de '+label.toLowerCase()+' est invalide');
  2096. return false;
  2097. }
  2098. } else {
  2099. $.message('error',' Le n° de '+label.toLowerCase()+' contient des caractères interdits');
  2100. return false;
  2101. }
  2102. }
  2103. return true;
  2104. }
  2105. //Check si la valeur de
  2106. //l'input est numérique
  2107. function is_numeric(n) {
  2108. return !isNaN(parseFloat(n)) && isFinite(n);
  2109. }
  2110. function str_price_to_decimal(str){
  2111. return str.replace(/,/g,'.').replace(/ /g,'');
  2112. }
  2113. //Comme la fonction ucfirst de PHP
  2114. function ucfirst(string) {
  2115. return string.charAt(0).toUpperCase() + string.slice(1);
  2116. }
  2117. //Permet de revenir/rester sur l'onglet courant
  2118. //au reload de page, ou au clic sur un lien particulier
  2119. if (location.hash) {
  2120. $('a[href="' + location.hash + '"]').tab('show');
  2121. }
  2122. var activeTab = localStorage.getItem('activeTab');
  2123. if (activeTab) {
  2124. $('a[href="' + activeTab + '"]').tab('show');
  2125. }
  2126. $('body').on('click', 'a[data-toggle="tab"]', function(e){
  2127. e.preventDefault();
  2128. var tab_name = this.getAttribute('href');
  2129. history.pushState ? history.pushState(null, null, tab_name) : location.hash = tab_name;
  2130. localStorage.setItem('activeTab', tab_name);
  2131. $(this).tab('show');
  2132. return false;
  2133. });
  2134. $(window).on('popstate', function () {
  2135. var anchor = location.hash || $('a[data-toggle="tab"]').first().attr('href');
  2136. $('a[data-toggle="tab"][href="' + anchor + '"]').tab('show');
  2137. });
  2138. //Check si la chaîne de caractères
  2139. //fournies est une chaîne JSON
  2140. function is_json_string(str) {
  2141. try {
  2142. JSON.parse(str);
  2143. } catch (e) {
  2144. return false;
  2145. }
  2146. return true;
  2147. }
  2148. //Sélectionne le texte de l'élément passé en paramètre
  2149. function select_text(element, event){
  2150. event.stopImmediatePropagation();
  2151. var sel, range;
  2152. var el = $(element).get(0); //get element id
  2153. if (window.getSelection && document.createRange) { //Browser compatibility
  2154. sel = window.getSelection();
  2155. if(sel.toString() == ''){ //no text selection
  2156. window.setTimeout(function(){
  2157. range = document.createRange(); //range object
  2158. range.selectNodeContents(el); //sets Range
  2159. sel.removeAllRanges(); //remove all ranges from selection
  2160. sel.addRange(range);//add Range to a Selection.
  2161. },1);
  2162. }
  2163. }else if (document.selection) { //older ie
  2164. sel = document.selection.createRange();
  2165. if(sel.text == ''){ //no text selection
  2166. range = document.body.createTextRange();//Creates TextRange object
  2167. range.moveToElementText(el);//sets Range
  2168. range.select(); //make selection.
  2169. }
  2170. }
  2171. }
  2172. //Permet de copier le contenu de l'element dans le presse-papier
  2173. function copy_to_clipboard(element) {
  2174. var element = $(element);
  2175. var temp = $('<input>');
  2176. $('body').append(temp);
  2177. temp.val(element.text()).select();
  2178. setTimeout(function(){document.execCommand('copy');},0);
  2179. temp.remove();
  2180. if(element.data('ui-tooltip')) return;
  2181. element.tooltip({
  2182. items: '*',
  2183. tooltipClass: 'quickform-tooltip',
  2184. content: 'Copié dans le presse papier',
  2185. open: {effect:'fade',duration:750},
  2186. close: {effect:'fade',duration:750}
  2187. });
  2188. element.tooltip('open');
  2189. setTimeout(function(){element.tooltip('close');element.tooltip('destroy');}, 2000);
  2190. }
  2191. //Reset des inputs d'un conteneur
  2192. function reset_inputs(container, onlyVisible, domData){
  2193. var container = container ? container : $('body');
  2194. var visibility = onlyVisible ? onlyVisible : false;
  2195. var dom = domData ? domData : false;
  2196. if(dom) container.removeAttr('data-id');
  2197. var classicInputs = visibility==true ? $('input:visible, textarea:visible', container) : $('input, textarea', container);
  2198. classicInputs.each(function(i, v){
  2199. if($(v).attr('type') == 'checkbox')
  2200. $(v).prop('checked', false);
  2201. else
  2202. $(v).val('');
  2203. });
  2204. var customInputs = visibility==true ? $('*[contenteditable="true"]:visible', container) : $('*[contenteditable="true"]', container);
  2205. customInputs.each(function(j,val){
  2206. $(val).text('');
  2207. });
  2208. }
  2209. //Check si le contenu d'un input est
  2210. //selectionné (en surbrillance avec le curseur)
  2211. function is_text_selected(input) {
  2212. if (typeof input.selectionStart == "number") {
  2213. return input.selectionStart == 0 && input.selectionEnd == input.value.length;
  2214. } else if (typeof document.selection != "undefined") {
  2215. input.focus();
  2216. return document.selection.createRange().text == input.value;
  2217. }
  2218. }
  2219. //Renvoie un tableau en ayant supprimé
  2220. //toutes les valuers dupliquées
  2221. function only_uniq_values(a) {
  2222. var seen = {};
  2223. var out = [];
  2224. var len = a.length;
  2225. var j = 0;
  2226. for(var i = 0; i < len; i++) {
  2227. var item = a[i];
  2228. if(seen[item] !== 1) {
  2229. seen[item] = 1;
  2230. out[j++] = item;
  2231. }
  2232. }
  2233. return out;
  2234. }
  2235. //Contrôle éléments saisis dans input de type number.
  2236. //Autorise ou non les décimales et les nb négatifs
  2237. function input_number_control(e, decimal, relative){
  2238. var authorizedKeys = [8,9,37,39,46,54,109,110,188,190];
  2239. switch (e.keyCode) {
  2240. case 110:
  2241. case 188:
  2242. case 190:
  2243. return decimal?decimal:false;
  2244. break;
  2245. case 109:
  2246. return relative?relative:false;
  2247. break;
  2248. case 54:
  2249. return (is_caps()) ? true : (relative?relative:false);
  2250. break;
  2251. default:
  2252. return (
  2253. ((e.keyCode >= 48 && e.keyCode <= 57) && (is_caps())) ||
  2254. (e.keyCode >= 96 && e.keyCode <= 105) ||
  2255. ((e.keyCode == 65 || e.keyCode == 86 || e.keyCode == 67) && (e.ctrlKey === true || e.metaKey === true)) ||
  2256. (authorizedKeys.indexOf(e.keyCode) !== -1)
  2257. );
  2258. break;
  2259. }
  2260. }
  2261. //Permet de checker si la
  2262. //saisie est en majuscule ou non
  2263. function is_caps(){
  2264. e = window.event;
  2265. return is_mobile ? true : (e.shiftKey !== true && e.metaKey !== true) ? (e.getModifierState("CapsLock") ? true : false) : (e.getModifierState("CapsLock") ? false : true);
  2266. }
  2267. //Check si l'objet est vide ou non
  2268. //Retourne true si vide, false sinon
  2269. function is_empty_obj(obj) {
  2270. for(var key in obj)
  2271. if(obj.hasOwnProperty(key)) return false;
  2272. return true;
  2273. }
  2274. //Permet de retourner une propriété d'un
  2275. //objet de manière totalement aléatoire
  2276. function random_property(obj) {
  2277. var keys = Object.keys(obj)
  2278. return obj[keys[ keys.length * Math.random() << 0]];
  2279. }
  2280. function get_characters_set() {
  2281. var CHARACTER_SETS = [
  2282. ["0123456789"],
  2283. ["abcdefghijklmnopqrstuvwxyz"],
  2284. ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"],
  2285. ["!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"],
  2286. ];
  2287. // Concatène les jeux de caractères
  2288. var rawCharset = "";
  2289. CHARACTER_SETS.forEach(function(entry, i) {
  2290. rawCharset += entry[0];
  2291. });
  2292. // Parse en UTF-16, supprime les doublons, convertit en tableau de string
  2293. var charset = [];
  2294. for (var i = 0; i < rawCharset.length; ++i) {
  2295. var c = rawCharset.charCodeAt(i);
  2296. if (c<0xD800 || c>=0xE000) {//Check si character UTF-16 valide
  2297. var s = rawCharset.charAt(i);
  2298. if (charset.indexOf(s) == -1) charset.push(s);
  2299. continue;
  2300. }
  2301. if (0xD800<=c && c<0xDC00 && i+1<rawCharset.length) {
  2302. var d = rawCharset.charCodeAt(i + 1);
  2303. if (0xDC00<=d && d<0xE000) {
  2304. var s = rawCharset.substring(i, i+2);
  2305. i++;
  2306. if (charset.indexOf(s) == -1) charset.push(s);
  2307. continue;
  2308. }
  2309. }
  2310. throw "Invalid UTF-16";
  2311. }
  2312. return charset;
  2313. }
  2314. //Retourne un entier aléatoire dans la plage [0, n)
  2315. //(2 méthodes utilisées)
  2316. function get_random_int(maxRange) {
  2317. //Récup d'un entier aléatoire via le module Math
  2318. //(fallback si pas de module crypto)
  2319. var x = random_int_math(maxRange);
  2320. //Récupération d'un entier aléatoire via le moule crypto
  2321. x = (x + random_int_crypto(maxRange)) % maxRange;
  2322. return x;
  2323. }
  2324. //Retourne un entier aléatoire
  2325. //Peu sécurisé (car calculable) mais
  2326. //présent sur tous les navigateurs
  2327. function random_int_math(maxRange) {
  2328. var x = Math.floor(Math.random() * maxRange);
  2329. if (x < 0 || x >= maxRange) throw "Arithmetic exception";
  2330. return x;
  2331. }
  2332. //Retourne un entier aléatoire
  2333. //Extrêmement sécurisé, mais pas
  2334. //disponible sur tous les navigateurs
  2335. //+ d'infos ici: https://developer.mozilla.org/fr/docs/Web/API/RandomSource/getRandomValues
  2336. function random_int_crypto(maxRange) {
  2337. var cryptoObject = null
  2338. if ("crypto" in window) {
  2339. //Check si le module crypto est disponible (Chrome, Firefox, Safari)
  2340. cryptoObject = crypto;
  2341. } else if ("msCrypto" in window) {
  2342. //Check si le module crypto est disponible (Edge, IE)
  2343. cryptoObject = msCrypto;
  2344. }
  2345. //Check + récupération propriétés/fonctions nécessaires du module crypto
  2346. if ("getRandomValues" in cryptoObject && "Uint32Array" in window && typeof Uint32Array == "function") {
  2347. //Génère un échantillon impartial/aléatoire
  2348. var x = new Uint32Array(1);
  2349. //Modification des élements du
  2350. //tableau par des nb aléatoires
  2351. do cryptoObject.getRandomValues(x);
  2352. while (x[0] - x[0] % maxRange > 4294967296 - maxRange);
  2353. return x[0] % maxRange;
  2354. } else {
  2355. return 0;
  2356. }
  2357. }
  2358. //Permet de générer un UUID de
  2359. //longueur souhaitée (ou 10 si pas de paramètre)
  2360. function generate_uuid(length){
  2361. var length = length !== undefined ? length : 10;
  2362. //Fallback repeat sur string
  2363. //pour les anciens navigateurs
  2364. if(!String.prototype.repeat){
  2365. String.prototype.repeat = function(length){
  2366. var length = parseInt(length);
  2367. if (length < 1) return '';
  2368. var result = '',
  2369. pattern = this.valueOf();
  2370. while (length > 1) {
  2371. if(length & 1) result += pattern;
  2372. length >>= 1, pattern += pattern;
  2373. }
  2374. return result + pattern;
  2375. };
  2376. }
  2377. var mem = "0x1"+("0".repeat(length));
  2378. return Math.floor((1 + Math.random()) * mem).toString(16).substring(1);
  2379. }
  2380. function readable_size(bytes) {
  2381. var thresh = 1000;
  2382. if(Math.abs(bytes) < thresh) {
  2383. return bytes + ' B';
  2384. }
  2385. var units = ['ko','Mo','Go','To','Po','Eo','Zo','Yo'];
  2386. var u = -1;
  2387. do {
  2388. bytes /= thresh;
  2389. ++u;
  2390. } while(Math.abs(bytes) >= thresh && u < units.length - 1);
  2391. return bytes.toFixed(1)+' '+units[u];
  2392. }
  2393. /**
  2394. * Permet e faire cligner l'onglet
  2395. * navigateur avec un message custom
  2396. * @param {string} message => le message à afficher
  2397. * @param {int} interval => l'intervalle entre 2 clignotement en ms
  2398. * @param {int} repetitions => le nombre de clignotements
  2399. */
  2400. function blink_tab(message, interval, repetitions) {
  2401. interval = interval != undefined && !isNaN(interval) ? interval : 750;
  2402. repetitions = parseInt(repetitions)*2;
  2403. if(isNaN(repetitions)) repetitions = 10;
  2404. if(repetitions%2!==0) repetitions += 1
  2405. var original = document.title,
  2406. timeoutId,
  2407. blink = function() {
  2408. document.title = document.title == message ? original : message;
  2409. if(--repetitions > 0) timeoutId = setTimeout(blink, interval);
  2410. },
  2411. clear = function() {
  2412. clearTimeout(timeoutId);
  2413. document.title = original;
  2414. window.onmousemove = null;
  2415. timeoutId = null;
  2416. };
  2417. if(!timeoutId) {
  2418. timeoutId = setTimeout(blink, interval);
  2419. window.onmousemove = clear;
  2420. }
  2421. }