main.js 93 KB

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