/* COMPOSANT */ function init_components(selector){ //Suppression apr defaut des légendes pour les graphiques if(window.Chart) Chart.defaults.global.legend.display = false; var selected = selector ? $('[data-type]',selector) : '[data-type]'; $(selected).each(function(i,input){ var input = $(input); switch($(input).attr('data-type')){ /** * data-format : Supporte les formats dd/mm/yyyy ou yyyy/mm/dd avec séparateur "/" ou "-" * 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) * 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) * data-workdays : si attribut est présent, seuls les jours ouvrés sont sélectionnables */ case 'date': //Les séparateurs doivent être similaires entre les éléments de la chaîne var dateFormatRegex = /^(dd|yy)([\/|-])(mm)\2(dd|yy)$/; var dateFormat = input.attr('data-format') != undefined && input.attr('data-format').match(dateFormatRegex) ? input.attr('data-format').match(dateFormatRegex)[0] : "dd/mm/yy"; var begin = input.attr('data-begin'); begin = (begin != undefined && (begin.match(/-?\d+/) || begin.match(/^(\d{2}(?:\d{2})?)\/(\d{2})\/(\d{2}(?:\d{2})?)$/))) ? begin : null ; var end = input.attr('data-end'); end = (end != undefined && (end.match(/-?\d+/) || end.match(/^(\d{2}(?:\d{2})?)[-|\/](\d{2})[-|\/](\d{2}(?:\d{2})?)$/))) ? end : null ; input.removeClass('hasDatepicker'); input.date({ placeholder: input.attr('placeholder') != undefined ? input.attr('placeholder') : "jj/mm/aaaa", dateFormat: dateFormat, beginDate: begin, endDate: end, workdays: input.attr('data-workdays') != null }).click(function(event) { $(this).select(); }); break; /** * data-format : Supporte les formats dd/mm/yyyy ou yyyy/mm/dd avec séparateur "/" ou "-" * data-step : L'intervalle entre 2 valeurs en minutes */ case 'hour': var timeFormatRegex = /^(H):(i)$/; var timeFormat = input.attr('data-format') != undefined && input.attr('data-format').match(timeFormatRegex) ? input.attr('data-format').match(timeFormatRegex)[0] : "H:i"; var step = input.attr('data-step'); input.hour({ placeholder: input.attr('placeholder') != undefined ? input.attr('placeholder') : "hh:mm", timeFormat: timeFormat, step: is_numeric(step) && step>0 ? step : 1 }).click(function(event){ $(this).select(); }); break; case 'decimal': input.off('keydown').on('keydown',function(e){ switch(e.key){ case ',': //on interdit la saisie si la chaine contient déja un . ou une , if(input.val().match(/[\.,]/i)) return false; //remplace les , par les . pour chiffre valide en db input.val(input.val()+'.'); return false; break; case '.': //on interdit la saisie si la chaine contient déja un . ou une , if(input.val().match(/[\.,]/i)) return false; break; case 'Backspace': case 'ArrowRight': case 'ArrowLeft': case 'Delete': case 'Insert': case '-': return true break; default: //autorise le coller ctrl+v if(e.key == 'v' && e.ctrlKey ) return true; //dégage les caractères différents de .,0123456789 if(!e.key.match(/[\-0-9,\.]/i)) return false; break; } return true; }); //supprime les caracteres pourris sur un coller input.off('keyup blur').on('keyup blur',function(e){ var value = input.val().replace(/[^0-9\-,.]/ig,''); //on remplace les "," par les "." var value = value.replace(/,/ig,'.'); //on s'assure qu'il n'existe pas plusieurs "." var splits = value.split('.'); if(splits.length > 1){ var newValue = ''; for(var i in splits) newValue+= (i!=splits.length-1 ? '' : '.')+splits[i]; value = newValue; } //On s'assure que le - est en début ou n'existe pas if(value.indexOf('-') !== -1 && value.indexOf('-')!==0 ) value = value.replace('-',''); input.val(value); }); break; case 'color': input.colorInput(); break; case 'history': var data = input.data(); if(isNaN(input.attr('data-uid'))) break; input.off(); if(data.showImportant){ if(!window.componentQueue['history']) window.componentQueue['history'] = {timeout : null,scopes :{} }; clearTimeout(window.componentQueue['history'].timeout); if(!window.componentQueue['history'].scopes[data.scope]) window.componentQueue['history'].scopes[data.scope] = []; window.componentQueue['history'].scopes[data.scope].push(data.uid); window.componentQueue['history'].timeout = setTimeout(function(){ $.action({ action : 'core_history_search', scopes : window.componentQueue['history'].scopes, showImportant : true },function(r){ var tpl = $('#history-notification-tpl').html(); for(var k in r.rows){ var row = r.rows[k]; var html = Mustache.render(tpl, {number: row.number}); inputs = $('[data-scope="'+row.scope+'"][data-uid="'+row.uid+'"][data-type="history"][data-show-important="true"]'); inputs.addClass('position-relative').append(html); if(row.number == 0) inputs.find('.history-notification').addClass('hidden'); init_components(inputs); } }); window.componentQueue['history'] = {timeout : null,scopes :{} }; },50); } input.click(function(event){ var data = input.data(); var panel = $('.history-panel'); var uid; if(panel.hasClass('hidden')) panel.removeClass('hidden'); panel.toggleClass('fold').one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) { var currentPanel = $(this); if(!currentPanel.hasClass('fold')) return; currentPanel.addClass('hidden'); }); if(panel.hasClass('fold')) return; if(input.attr('data-uid')) panel.attr('data-uid',input.attr('data-uid')); if(data.scope) panel.attr('data-scope',data.scope); core_history_search(function(){ //Redimensionnement du panel historique setTimeout(function(){ uid = $('.history-panel').panelResize({direction : 'left'}); }, 100); }); event.stopPropagation(); //Fermeture du panel au click en dehors du composant $(window).click(function(e) { var target = $(e.target); var panel = target.closest('.history-panel'); var handler = target.closest('.panel-resize-handler'); var targetInPanel = panel.length || handler.length; //Si un commentaire était en cours d'édition, on le sauvegarde si on a cliqué en dehors du wysiwyg et uniquement s'il n'est pas vide if(target.hasClass('history-panel') || target.hasClass('comments') || !targetInPanel){ $('li.comment:not(.hidden)').each(function(){ if($(this).find('.history-content .trumbowyg-editor-visible').length && $(this).find('.history-content .trumbowyg-editor').text().length) core_history_save(this); }); } if(targetInPanel) return; $('.history-panel').addClass('fold'); $('#'+uid).remove(); }); }); break; /** * data-regex : regex de vérification (defaut : ^[0-9]{10}$, les espaces sont trimés pour vérification) * data-spacing : espace automatiquement les chiffre si collés (défaut true) * data-empty-field : vide le champs si invalide (défaut false) * data-invalid-class : définit la classe a ajouter si invalide (défaut .invalid-value) * data-blur : si défini, trigger le blur indiqué après validation du champ (le onblur classique trigger avant) * data-type-only : empeche l'utilisateur de taper autre caractere que définis dans cette expression (defaut : [0-9\+\s]) */ case 'phone': input.phone(input.data()); break; /** * data-delete : définit la fonction JS qui sera appelée au clic sur la croix pour supprimer l'image * data-save : définit la fonction JS qui sera appelée après le changement de l'image * data-default-src : chemin vers l'image par défaut à utiliser */ case 'image': console.warn('[DEPRECATED] : Image doit être remplacé par le composant file'); if(input.hasClass('hidden')) return; if(input.closest('.type-image-bloc').length!=0) break; input.attr('type', 'file'); input.wrap( "
"); var save = input.attr('data-save'); var src = ($(input).attr('value')!='') ? $(input).attr('value') : 'img/default-image-muted.png'; src += src.indexOf('?')!=-1 ? '&' : '?'; src += 't='+(Math.random()*1000); var thumbnail = $(''); var deleteBtn = !input.attr('data-delete') ? '' : '
'; input.before(thumbnail); if(thumbnail.attr('src').indexOf('default-') === -1) thumbnail.before(deleteBtn); input.addClass('noPrint'); input.change(function(){ var reader = new FileReader(); reader.onload = function (e) { thumbnail.attr('src', e.target.result); thumbnail.before(deleteBtn); } reader.readAsDataURL(input.get(0).files[0]); if(save) window[save](input); }); break; //Sélection de valeurs multiples case 'checkbox-list': var data = input.data(); if(!data.depth) data.depth = 1; var liClasses = ''; var isDropDown = data.display == 'dropdown'; //Si défini à false => bloque le choix multi niveau var multiLevelSelect = data.multiLevelSelect; var initValues = function(){ ul.find('li input').prop("checked",false); ul.find('li[data-folded="0"]').attr('data-folded','1'); if(input.val() ){ var checkeds = input.val().split(',') for(var i in checkeds){ var checkbox = ul.find('li[data-value="'+checkeds[i]+'"] input').eq(0); checkbox.prop("checked",true); checkbox.closest('ul').parents().attr('data-folded','0'); } refreshLabel(); } } var refreshLabel = function(){ if(!isDropDown) return; var checkedLabels = []; ul.find('>li input:checked').each(function(){ checkedLabels.push($(this).closest('li').find('label').eq(0).text()); }); var button = container.find('.dropdown-toggle'); button.html(checkedLabels.length>0 ? checkedLabels.join(',') : data.button); $('.checkbox-list-counter',container).text(checkedLabels.length); } var printChilds = function(childs,level){ if(!level) level = 1; html = ''; if(level==1){ html += '
  • 0 Sélection(s) '; html += '
  • '; } for(var k in childs){ var child = childs[k]; var hasChilds = child.childs && child.childs.length!=0; if(!data.multiLevelSelect && level == 1 && hasChilds && (child.childs.length < data.depth)) continue; html += '
  • '; if(level == data.depth){ html += ''; }else{ if(hasChilds || multiLevelSelect){ html += '' html += hasChilds ? ' '; } } if(hasChilds) html += ''; html += '
  • '; } return html; } if(!input.data("data-component")){ container = $('
    '); if(isDropDown){ data.button = !data.button ? 'Choix': data.button; container.append(''); liClasses += ' dropdown-item '; } input.before(container); input.data("data-component", container); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant if(input.attr("required")) container.attr("required", ""); if(input.attr("disabled")) container.attr("disabled", ""); if(input.attr("readonly")) container.attr("readonly", ""); var ul = container.find('>ul'); ul.on('click','>li',function(e){ e.stopPropagation(); }).on('click','>li input',function(e){ e.stopPropagation(); var checked = []; ul.find('>li input:checked').each(function(){ checked.push($(this).closest('li').attr('data-value')); }); input.val(checked.join(',')); refreshLabel(); input.trigger('change'); }); }else{ container = input.data("data-component"); var ul = container.find('>ul'); } var html = ''; data.values = input.attr('data-values'); if(data.values){ data.values = JSON.parse(input.attr('data-values')); for(var k in data.values) if(typeof(data.values[k]) === "string") data.values[k] = {id: k, label: data.values[k]}; html += printChilds(data.values); } ul.html(html); ul.find('[data-type="checkbox"]').removeClass('component-raw-value'); var initCheckbox = function(ul, callback){ init_components(ul); ul.find('[data-type="checkbox"]').removeClass('component-raw-value'); ul.find('.checkbox-toggle-fold').click(function(){ var li = $(this).closest('li'); li.attr('data-folded',li.attr('data-folded')=='1'?0:1); }); $('.dropdown-element').mouseover(function(){ $(this).find('.checkbox-list-check-all').eq(0).removeClass('hide'); }); $('.dropdown-element').mouseout(function(){ $(this).find('.checkbox-list-check-all').eq(0).addClass('hide'); }); container.find('.checkbox-list-check-all').click(function(event){ event.stopPropagation(); var element = $(this); var parentContainer = element.parent().parent(); //cas du checkbox-all racine if(parentContainer.hasClass('checkbox-list-header')) parentContainer = parentContainer.parent(); parentContainer.find('input[type="checkbox"]').click(); var li = element.closest('li'); li.attr('data-folded',0); if(multiLevelSelect) li.find('li').attr('data-folded',0); }); if(callback!=null) callback(); } if(data.slug){ if(!data.depth) data.depth = 1; $.action({ action: 'core_dictionary_component_load', slug: data.slug, depth: data.depth, multiLevelSelect: data.multiLevelSelect, },function(response){ html = ''; if(response.content.childs && response.content.childs.length!=0){ html += printChilds(response.content.childs); }else{ html += '
  • Liste vide ou introuvable
  • '; } ul.append(html); initCheckbox(ul, initValues); }); }else{ initCheckbox(ul, initValues); } break; //Selection de tag case 'tag': var container; var picker; var pickerLi; if(!input.data("data-component")){ var placeholder = input.attr("placeholder") != undefined ? input.attr("placeholder") : ""; container = $('
    '); input.before(container); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant input.data("data-component", container); if(input.attr("required")) container.attr("required",""); if(input.attr("disabled")) container.attr("disabled",""); if(input.attr("readonly")) container.attr("readonly",""); } else { container = input.data("data-component"); } picker = container.find('input:eq(0)'); pickerLi = container.find('ul li.tag-picker-li'); if(container.attr('disabled') || container.attr('readonly')) pickerLi.find('input').attr('readonly',''); container.find('.tag-picker-tag').remove(); pickerLi.removeClass('hidden'); var methods = { //Récuperation des valeurs sélectionnées (objet et id) en fonction des tags visuels présents values : function(container){ var tags = container.find('ul .tag-picker-tag'); var values = {object:[],id:[]}; tags.each(function(i,element){ if($(element).attr('data-value') == '') return; var data = $(element).data(); values['object'].push(data); values['id'].push(data.value); }); return values; }, remove : function(tag){ tag.remove(); var values = methods.values(container); var plainValue = []; for(var k in values.object){ plainValue.push(values.object[k].value); } input.val(plainValue.length!=0? plainValue.join(',') :''); input.data('values',values['object']); if((values['id'].length-1) ==1 && input.attr('data-multiple') == null){ pickerLi.addClass('hidden'); }else{ pickerLi.removeClass('hidden'); } //on trigger le change input.trigger('change'); }, //Ajout d'un tag visuel et mise à jour de l'input brut en fonction de l'objet fournis add : function(container,tag){ pickerLi = container.find('ul li.tag-picker-li'); if( container.find('li[data-id="'+tag.id+'"]').length>0) return; var closeBtn = input.is('[readonly]') || input.is('[disabled]') ? '' : ''; var tag = $('
  • '+tag.label+closeBtn+'
  • '); pickerLi.before(tag); var values = methods.values(container); input.val(values['id'].join(',')); input.data('values',values['object']); if((values['id'].length) == 1 && input.attr('data-multiple') == null){ pickerLi.addClass('hidden'); }else{ pickerLi.removeClass('hidden'); } tag.find('.btn-remove').click(function(){ if(input.is('[readonly]') || input.is('[disabled]')) return; methods.remove($(this).closest('.tag-picker-tag')); }); } } //Gestion des champs déja remplis au chargement de la page if(input.val() !=''){ var id = input.val(); //picker.addClass('hidden'); var tags = id.split(','); if(input.data('dictionarySlug') && input.data("data-autocomplete-value") != 'label'){ $.action({ action : 'core_tag_list_by_id', id : id },function(response){ for(var key in response.tags){ if(response.tags[key] == '') continue; methods.add( container,{label:response.tags[key].label,value:response.tags[key].id}); } }); }else{ for(var key in tags){ if(tags[key] == '') continue; methods.add( container,{label:tags[key],value:tags[key]}); } } } picker.off('blur').blur(function(e){ if(window.tagBlur) clearTimeout(window.tagBlur); window.tagBlur = setTimeout(function(){ var value = picker.val(); if(value.trim() == '') return; methods.add(container,{label:value,value:value}); picker.val(''); $('.typeahead.dropdown-menu').hide(); },500); }); if(!input.data('autocomplete') && input.data('dictionarySlug')) input.data('autocomplete','core_tag_list_autocomplete'); if(input.data('autocomplete')){ var dictionarySlug = input.data('dictionarySlug'); $(picker).autocomplete({ action: input.data('autocomplete'), selectEqual : false, //evite le select auto du tag test quand on veux taper test2 suggest : true, dynamicData : function(){ return dictionarySlug ? { parent : dictionarySlug} : {} }, onClick : function(selected,element){ if(window.tagBlur) clearTimeout(window.tagBlur); methods.add(container,{label:selected.name,value: (input.data("data-autocomplete-value") == 'label' ? selected.name : selected.id) }); //on vide l'input de saisie picker.val(''); //on trigger le change input.trigger('change'); }, }); } picker.off('keydown').keydown(function(e){ var validationChars = ['Enter',' ',',','Tab']; if(e.key == 'Backspace' && picker.val() == ''){ methods.remove($('.tag-picker-tag:last',container)); return; } //si appui sur Entrée, virgule ou espace ou tab on valide le tag if( validationChars.indexOf(e.key) === -1 || input.is('[readonly]') || input.is('[disabled]') ) return; var value = picker.val(); if(value.trim() == '') return; //on cache l'automcomplete si present $('.typeahead.dropdown-menu').hide(); //on ajoute le tag visuel methods.add(container,{label:value,value:value}); //on vide l'input de saisie picker.val(''); //on trigger le change input.trigger('change'); return false; }); break; //composant permettant les listes sous //form de dropdown avec icones et couleurs case 'dropdown-select': var container; if(!input.data("data-component")){ container = $('
    \ \ \
    '); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant input.before(container); input.data("data-component", container); if(input.attr("required")) container.attr("required",""); var button = container.find('.dropdown-toggle-button'); } else { container = input.data("data-component"); var button = container.find('.dropdown-toggle-button'); } container.find('.dropdown-menu').html(''); var html = ''; $.each(input.get(0).options, function(i,element){ var option = $(element); var classes = option.attr('class') ? option.attr('class') : ''; var icon = option.attr("data-icon") ? ' ': ''; var title = option.attr("data-title") ? 'title="'+option.attr("data-title")+'"': ''; var backColor = option.get(0).style.getPropertyValue('background-color') ? option.get(0).style.getPropertyValue('background-color') : '#ffffff'; var fontColor = color_light(backColor) < 50 ? '#ffffff' : '#333333' ; html += ''+icon+option.html()+''; }); container.find('.dropdown-menu').append(html); var changeMenu = function(container,menu){ $('.dropdown-menu a',container).removeClass('active'); var item = $('.dropdown-menu a[data-value="'+menu+'"]', container); var backColor = item.css('background-color') ? item.css('background-color') : '#ffffff' ; var fontColor = color_light(backColor) < 50 ? '#ffffff' : '#333333' ; container.find('.dropdown-toggle-button').html(item.html()).css({ backgroundColor:backColor, color:fontColor }); item.addClass('active'); }; $('.dropdown-menu a',container).click(function(){ if(input.attr("readonly") == 'readonly') return; var item = $(this); changeMenu(container,$(this).attr('data-value')); input.val(item.attr('data-value')).trigger('change'); }); if(input[0].hasAttribute('data-no-toggle')) button.removeClass('dropdown-toggle'); var state = input.attr('data-value') != null ? input.attr('data-value') : input.val(); if(state == '') return; var item = $('.dropdown-menu a[data-value="'+state+'"]', container); //Utilisation background-color pour fonctionner sous FF var backColor = item.get(0) && item.get(0).style.getPropertyValue('background-color') ? item.get(0).style.getPropertyValue('background-color') : '#ffffff' ; var fontColor = color_light(backColor) < 50 ? '#ffffff' : '#333333' ; button.html(item.html()).css({ backgroundColor:backColor, color:fontColor }); item.addClass('active'); input.val(item.attr('data-value')); input.change(function(){ changeMenu(container,input.val()); }); break; case 'firm': //autocomplete classique input.component_autocomplete('firm',{ edit : 'core_firm_by_uid', autocomplete : 'core_firm_autocomplete' }); break; case 'map': //on recréé le container a chaque fois car leaflet //ne sais pas refresh sans tout recharger (a verifier) var container = $('
    '); container.attr('class',input.attr('class')); input.data('container',container); input.before(container); if(container.height() <1){ container.css('height',(input.height() <1 ? '300px' : input.height()+'px')); container.css('width',(input.width() <1 ? '300px' : input.width()+'px')); } var data = input.data(); var json = input.get(0).innerHtml; if(!json) json = '[]'; var points = JSON.parse(json); var existingMap = L.DomUtil.get(container.get(0)); if(existingMap != null) existingMap._leaflet_id = null; var map = L.map(container.get(0)); map.invalidateSize(); //si une latitude/longitude pour la vue global est définie on l'utilise if(data.latitude && data.longitude){ if(!data.zoom) data.zoom = 13; map.setView([data.longitude, data.latitude], data.zoom); //Sinon on se base sur le groupe de marqueurs proposés pour définie une vue englobante }else{ var pointArray = []; if(points.length!=0){ for(var k in points){ var point = points[k]; if(point.longitude && point.latitude)pointArray.push([point.latitude,point.longitude]); } var markerGroup = new L.LatLngBounds(pointArray); map.fitBounds(markerGroup); } } if(data.zoom) map.setZoom(data.zoom); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '' }).addTo(map); /* var defaultIcon = L.icon({ iconUrl: 'img/leaflet/marker-icon.png', shadowUrl: 'img/leaflet/marker-shadow.png', iconSize: [25, 41], // size of the icon shadowSize: [41, 41], // size of the shadow iconAnchor: [0, 0], // point of the icon which will correspond to marker's location shadowAnchor: [0, 0], // the same for the shadow popupAnchor: [10, 0] // point from which the popup should open relative to the iconAnchor });*/ map.on('click', function(args){ if(data.click && window[data.click]) window[data.click]({ longitude : args.latlng.lng, latitude : args.latlng.lat, layerX : args.layerPoint.x, layerY : args.layerPoint.y, x : args.containerPoint.x, y : args.containerPoint.y, event : args.originalEvent, target : args.target, sourceTarget : args.sourceTarget }); }); var mapPoints = {}; var markers = L.markerClusterGroup(); for(var k in points){ var point = points[k]; if(!point.type) point.type = 'marker'; switch(point.type){ case 'marker': var currentPoint = L.marker([point.latitude,point.longitude]/*, {icon: defaultIcon}*/); break; case 'circle': if(!point.borderColor) point.borderColor = '#38f'; if(!point.backgroundColor) point.backgroundColor = '#38f'; if(!point.opacity) point.opacity = 0.5; if(!point.radius) point.radius = 500; var currentPoint = L.circle([point.latitude,point.longitude], { color: point.borderColor, fillColor: point.backgroundColor, fillOpacity: point.opacity, radius: point.radius }); break; case 'polygon': if(!point.borderColor) point.borderColor = '#38f'; if(!point.backgroundColor) point.backgroundColor = '#38f'; if(!point.opacity) point.opacity = 0.5; if(!point.radius) point.radius = 500; var currentPoint = L.polygon(point.points, { color: point.borderColor, fillColor: point.backgroundColor, fillOpacity: point.opacity, radius: point.radius }); break; } markers.addLayer(currentPoint); if(point.label) currentPoint.bindPopup(point.label); currentPoint.id = k; if(point.id) currentPoint.id = point.id ; mapPoints[currentPoint.id] = currentPoint; //currentPoint.addTo(map); } map.addLayer(markers); input.data('points',mapPoints); break; case 'contact': //autocomplete classique input.component_autocomplete('contact',{ edit : 'core_contact_by_uid', autocomplete : 'core_contact_autocomplete', skin : function(item){ var html = ''; var re = new RegExp(input.val(),"gi"); name = item.label.replace(re, function (x) { return ''+x+''; }); html += '
    '+name+''; if(item.job) html += '
    '+item.job+''; html += '
    '; return html; }, data : function(){ return {scope : input.attr('data-scope'),uid : input.attr('data-uid')? input.attr('data-uid').split(','):[] }; } }); break; case 'user': var userContainer; var userPicker; var pickerLi; if(!input.data("data-component")){ userContainer = $('
    '); input.before(userContainer); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant input.data("data-component", userContainer); if(input.attr("required")) userContainer.attr("required",""); if(input.attr("disabled")) userContainer.attr("disabled",""); if(input.attr("readonly")) userContainer.attr("readonly",""); } else { userContainer = input.data("data-component"); } userPicker = userContainer.find('input:eq(0)'); pickerLi = userContainer.find('ul li.user-picker-li'); if(userContainer.attr('disabled') || userContainer.attr('readonly')) pickerLi.find('input').attr('readonly',''); userContainer.find('.user-picker-tag').remove(); pickerLi.removeClass('hidden'); var pickerFunctions = { //Récuperation des valeurs sélectionnées (objet et uid) en fonction des tags visuels présents getValues : function(userContainer){ var tags = userContainer.find('ul .user-picker-tag'); var values = {object:[],uid:[]}; tags.each(function(i,element){ if($(element).attr('data-uid') == '') return; var object = $(element).data(); values['object'].push(object); values['uid'].push(object.uid); }); return values; }, checkComponentOverflow : function(container){ var overflow = container.get(0).scrollHeight > container.outerHeight(); overflow ? container.addClass('overflowing') : container.removeClass('overflowing'); }, //Ajout d'un tag visuel et mise à jour de l'input brut en fonction de l'objet user fournis addTag : function(userContainer,user,input){ pickerLi = userContainer.find('ul li.user-picker-li'); if(userContainer.find('li[data-uid="'+user.uid+'"]').length>0) return; var closeBtn = userContainer.attr('disabled') || userContainer.attr('readonly') ? '' : ''; var tag = $('
  • '+user.name+closeBtn+'
  • '); pickerLi.before(tag); var values = pickerFunctions.getValues(userContainer); input.val(values['uid'].join(',')); input.data('values',values['object']); if((values['uid'].length) == 1 && input.attr('data-multiple') == null){ pickerLi.addClass('hidden'); }else{ pickerLi.removeClass('hidden'); } pickerFunctions.checkComponentOverflow(userContainer); tag.find('i').click(function(){ if(userContainer.attr('disabled') || userContainer.attr('readonly')) return; $(this).closest('.user-picker-tag').remove(); var values = pickerFunctions.getValues(userContainer); input.val(values['uid'].join(',')); input.data('values',values['object']); pickerLi = userContainer.find('ul li.user-picker-li'); if((values['uid'].length-1) ==1 && input.attr('data-multiple') == null){ pickerLi.addClass('hidden'); }else{ pickerLi.removeClass('hidden'); } input.trigger('change'); pickerFunctions.checkComponentOverflow(userContainer); }); if(userContainer.find('ul li.user-picker-tag')) userContainer.find('ul li.user-picker-li input').removeAttr('placeholder'); } } //Gestion des champs déja remplis au chargement de la page if(input.val() !=''){ var uid = input.val(); if(!window.componentQueue.user) window.componentQueue.user = {timeout : null,components : [],uids :{} }; clearTimeout(window.componentQueue.user.timeout); var uids = uid.split(','); for(var k in uids) window.componentQueue.user.uids[uids[k]] = 1; window.componentQueue.user.components.push({ input : input, picker : userPicker, container : userContainer, values : uids }); userContainer.find('ul').append('
  • Chargement
  • '); userPicker.addClass('hidden'); window.componentQueue.user.timeout = setTimeout(function(){ $.action({ action : 'core_user_by_uid', uids : Object.keys(window.componentQueue.user.uids) },function(r){ for(var key in window.componentQueue.user.components){ var component = window.componentQueue.user.components[key]; component.container.find('.user-picker-loader').remove(); component.picker.removeClass('hidden'); input = window.componentQueue.user.components[key].input; for(var i in component.values){ var value = component.values[i]; if(!r.users[value]) continue; pickerFunctions.addTag(component.container,r.users[value],input); } delete window.componentQueue.user.components[key]; } }); },50); } userContainer.find('.unfold').click(function(e){ e.stopPropagation(); var position = userContainer.position(); var width = userContainer.outerWidth(); userContainer .addClass('unfolded') .css({ top: position.top+'px', width: width+'px', left: position.left+'px' }); $(document).mousedown(function(e){ if($(e.target).closest('div.data-type-user').length || !userContainer.hasClass('unfolded')) return; window.userPickerTimeout = userContainer.removeClass('unfolded') .css({ top: 'inherit', width: 'inherit', left: 'inherit' }); pickerFunctions.checkComponentOverflow(userContainer); }); }); //Sélectionne l'input d'auto-completion ou que l'on clique dans le composant userContainer.find('ul').click(function(e){ if(input.attr("readonly") == "readonly") return; userPicker.focus(); e.stopPropagation(); }); //Selectionne l'item dropdown actif lors de l'appui sur entrée userPicker.keyup(function(e){ if(e.keyCode!=13 || input.is('[readonly]') || input.is('[disabled]')) return; if(input.attr("readonly") == "readonly") return; var active = $('.user-picker-li .dropdown-menu .active'); if(active.length==0) return; active.trigger('click').trigger('change'); }); var types = ['user']; if(input.data('types') && input.data('types')!='') types = input.data('types').split(','); //aucompletion sur le nom des users / rangs userPicker.autocomplete({ action : 'core_user_autocomplete', data : { types : types, scope : input.attr('data-scope') }, skin : function(item){ var html = ''; var re = new RegExp(escape_regex_chars(userPicker.val()),"gi"); name = item.name.replace(re, function (x) { return ''+x+''; }); if(item.type=='user'){ if(item.avatar) html += ''; html += '
    '+name+' - Utilisateur (@'+item.id+')'; html += item.function ? '
    '+item.function+'
    ' : '
    '; html += '
    '; }else{ html += ''; html += '
    '+name+' - Rang'; html += item.description ? '
    '+item.description+'
    ' : '
    '; html += '
    '; } return html; }, highlight : function(item){ return item; }, onClick : function(selected,element){ clearTimeout(window.userPickerTimeout); userPicker.val(''); pickerFunctions.addTag(userContainer,selected,input); input.trigger('click').trigger('change'); }, onBlur : function(selected){ if(input.attr('data-force')!='false' && input.val() == '') userPicker.val(''); } }); break; /** * data-labels : tableau des libellés entre double quotes eg : ["Libellé 1","Libellé 2"] * data-values : taleau des valeurs eg : [12,13] * data-colors : taleau des couleurs entre double quotes eg : ["#cecece","#222222"] */ case 'doughnut': var data = input.data(); if(data.height) $(input).attr('height',data.height); var myChart = new Chart(input.get(0).getContext('2d'), { type: 'doughnut', data: { labels: data.labels, datasets: [{ data: data.values, backgroundColor: data.colors }] }, options: { cutoutPercentage:80, legend: { display: (data.legend && data.legend===true) } } }); break; /** * data-labels : tableau des libellés entre double quotes eg : ["Libellé1","Libellé 2"] * data-values : taleau des valeurs eg : [12,13] * data-colors : taleau des couleurs entre double quotes eg : ["#cecece","#222222"] */ case 'bar': var data = input.data(); var myChart = new Chart(input.get(0).getContext('2d'), { type: 'bar', data: { labels: data.labels, datasets: [{ label : input.html(), data: data.values, backgroundColor: data.colors }] }, options: { scales: { xAxes: [{ ticks: { beginAtZero:true, autoSkip:false, maxRotation:90, minRotation:80 } }], yAxes: [{ ticks: { beginAtZero:true, autoSkip:false } }] }, legend: { display: (data.legend && data.legend==true) } } }); break; /** * data-labels : tableau des 2 libellés courant et max entre double quotes eg : ["Atteint","A Atteindre"] * data-values : taleau des valeurs courant et max : [50,100] (donne une jauge allant de 0 à 100 remplie a 50%) * data-colors : taleau des 2 couleurs "remplis" et "non remplis" entre double quotes eg : ["#cecece","#222222"] (optionnel) * data-unity : unité à affher a coté des libellé min, max et courant */ case 'gauge': var data = input.data(); Chart.pluginService.register({ beforeDraw: function(chart) { // Get ctx from string var ctx = chart.chart.ctx; if(chart.config.options.elements.center){ // Get options from the center object in options var centerConfig = chart.config.options.elements.center; var sidePaddingCalculated = (centerConfig.sidePadding / 100) * (chart.innerRadius * 2); // Get the width of the string and also the width of the element minus 10 to give it 5px side padding var stringWidth = ctx.measureText(centerConfig.text).width; var elementWidth = (chart.innerRadius * 2) - sidePaddingCalculated; var centerWidth = stringWidth; // Find out how much the font can grow in width. var widthRatio = elementWidth / stringWidth; var newFontSize = Math.floor(30 * widthRatio); var elementHeight = (chart.innerRadius * 2); // Pick a new font size so it will not be larger than the height of label. var fontSizeToUse = Math.min(newFontSize, elementHeight, centerConfig.maxFontSize); // Set font settings to draw it correctly. ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; var centerX = ((chart.chartArea.left + chart.chartArea.right) / 2); var centerY = ((chart.chartArea.top + chart.chartArea.bottom) / 2) + 20; ctx.font = "1.7em Arial"; ctx.fillStyle = centerConfig.color; ctx.fillText(centerConfig.text, centerX, centerY); } if(chart.config.options.elements.minimum){ // Get options from the center object in options var minimumConfig = chart.config.options.elements.minimum; var sidePaddingCalculated = (minimumConfig.sidePadding / 100) * (chart.innerRadius * 2); // Get the width of the string and also the width of the element minus 10 to give it 5px side padding var stringWidth = ctx.measureText(minimumConfig.text).width; var elementWidth = (chart.innerRadius * 2) - sidePaddingCalculated; // Find out how much the font can grow in width. var widthRatio = elementWidth / stringWidth; var newFontSize = Math.floor(30 * widthRatio); var elementHeight = (chart.innerRadius * 2); // Pick a new font size so it will not be larger than the height of label. var fontSizeToUse = Math.min(newFontSize, elementHeight, minimumConfig.maxFontSize); // Set font settings to draw it correctly. ctx.textAlign = 'center'; ctx.textBaseline = 'center'; var minimumX = chart.radiusLength +20; var minimumY = chart.height - chart.offsetY - 8; ctx.font = "1em Arial"; ctx.fillStyle = minimumConfig.color; ctx.fillText(minimumConfig.text, minimumX, minimumY); } if(chart.config.options.elements.maximum){ // Get options from the center object in options var maximumConfig = chart.config.options.elements.maximum; var sidePaddingCalculated = (maximumConfig.sidePadding / 100) * (chart.innerRadius * 2); // Get the width of the string and also the width of the element minus 10 to give it 5px side padding var stringWidth = ctx.measureText(maximumConfig.text).width; var elementWidth = (chart.innerRadius * 2) - sidePaddingCalculated; // Find out how much the font can grow in width. var widthRatio = elementWidth / stringWidth; var newFontSize = Math.floor(30 * widthRatio); var elementHeight = (chart.innerRadius * 2); // Pick a new font size so it will not be larger than the height of label. var fontSizeToUse = Math.min(newFontSize, elementHeight, maximumConfig.maxFontSize); // Set font settings to draw it correctly. ctx.textAlign = 'left'; ctx.textBaseline = 'center'; var maximumX = chart.width - chart.radiusLength - 15 - stringWidth; var maximumY = chart.height - chart.offsetY - 8; ctx.font = "1em Arial"; ctx.fillStyle = maximumConfig.color; ctx.fillText(maximumConfig.text, maximumX, maximumY); } } }); if(data.height) $(input).attr('height',data.height); if (!data.colors){ var doneColor = '#e74c3c'; var percentage = data.values[0] * 100 / data.values[1]; percentage = percentage > 100 ? 100 : percentage; //Calcul de la couleur en fonction du pourcentage de progression if(percentage<=20) doneColor = '#e74c3c'; if(percentage>20 && percentage<=40) doneColor = '#e67e22'; if(percentage>40 && percentage<=60) doneColor = '#2980b9'; if(percentage>60 && percentage<=80) doneColor = '#16a085'; if(percentage>80) doneColor = '#27ae60'; //[couleur du fait,couleur du restant data.colors = [doneColor,'#bdc3c7']; } var myChart = new Chart(input.get(0).getContext('2d'), { type: 'doughnut', data: { labels: [data.labels[0],data.labels[1]], datasets: [{ data: [percentage,100-percentage], backgroundColor: data.colors }], realDatasets : [data.values[0],data.values[1]] }, options: { tooltips : { callbacks : { title: function(tooltipItem, data) { return data['labels'][tooltipItem[0]['index']]; }, label: function(tooltipItem, data) { return data['realDatasets'][tooltipItem['index']]; }, afterLabel: function(tooltipItem, data) { return '('+data['datasets'][0]['data'][tooltipItem['index']]+'%)'; } } }, elements: { minimum: { text: '0'+data.unity, color: '#333333', // Default is #000000 sidePadding: 20, // Default is 20 (as a percentage) minFontSize: 12, // Default is 20 (in px), set to false and text will not wrap. lineHeight: 12 // Default is 25 (in px), used for when text wraps }, center: { text: data.values[0]+data.unity, color: '#333333', // Default is #000000 sidePadding: 20, // Default is 20 (as a percentage) minFontSize: 32, // Default is 20 (in px), set to false and text will not wrap. lineHeight: 12 // Default is 25 (in px), used for when text wraps }, maximum: { text: data.values[1]+data.unity, color: '#333333', // Default is #000000 sidePadding: 20, // Default is 20 (as a percentage) minFontSize: 12, // Default is 20 (in px), set to false and text will not wrap. lineHeight: 12 // Default is 25 (in px), used for when text wraps } }, circumference: Math.PI , rotation: -Math.PI , cutoutPercentage:75, legend: { display: (data.legend && data.legend===true) } } }); break; /** * data-labels : tableau des libellés entre double quotes eg : ["Libellé1","Libellé 2"] * data-values : taleau des valeurs eg : [12,13] * data-color : couleurs eg : #cecece */ case 'line': var data = input.data(); var myChart = new Chart(input.get(0).getContext('2d'), { type: 'line', data: { labels: data.labels, datasets: [{ label : input.html(), data: data.values, borderColor: [data.color] }] }, options: { scales: { xAxes: [{ ticks: { beginAtZero:true, autoSkip:false, maxRotation:90, minRotation:80 } }], yAxes: [{ ticks: { beginAtZero:true, autoSkip:false } }] } } }); break; /** * data-table : tableau dynamique js tri, recherche, pagination... * data-pageLength : nombre de résultats à afficher */ case 'data-table': var data = input.data(); var options = { pageLength: input.attr('data-pageLength') != null ? input.attr('data-pageLength') : 20, destroy: true, pagingType: "simple_numbers", fixedHeader: true, lengthMenu: [ [10, 20, 40, -1], [10, 20, 40, "Tous"] ], language: { url: "plugin/statistic/js/dataTables.fr.translation.json" }, order: [], // Disable on load sort ordering: 'true' // Manage orderable options }; options = $.extend(options,data); input.DataTable(options); break; /** * data-filter-country : filtre par pays à rechercher séparés par virgule (format ISO 3 numériques, ex : FRA) * data-filter-items : nb de résultats à retourner * data-filter-language : langue dans laquelle retourner les résultats * * data-select-callback : fonction de callback appelée lors de la sélection d'un élément * * data-filter-geocode : retourne les infos complémentaires de l'élément sélectionné (latitude, longitude,etc...) * data-geocode-callback : fonction de callback appelée pour traiter les infos retournées via geocode */ case 'location': input.location({ items: input.attr('data-filter-items'), language: input.attr('data-filter-language'), country: input.attr('data-filter-country'), geocode: input.attr('data-filter-geocode') != null ? true : false, select: function(location){ if(input.attr('data-select-callback')!=null && input.attr('data-select-callback').length) { var select = input.attr('data-select-callback').replace(/[^a-z0-9]/i,'_'); if(window[select] !=null) window[select](location); } }, geocode: function(location){ $.action({ action: 'core_location_detail_search', locationId: location.locationId }, function(r){ if(input.attr('data-geocode-callback')!=null && input.attr('data-geocode-callback').length) { var geocode = input.attr('data-geocode-callback').replace(/[^a-z0-9]/i,'_'); if(window[geocode] !=null) window[geocode](r); } }); } }); var data = input.data(); input.removeData(); input.data({ type : data.type, country : data.country, zip : data.zip, city : data.city, complement : data.complement, street : data.street }); break; /** * data-source : tableau de clés / valeurs au format json base 64 encodé * data-value : la valeur de l'entité à récup en base (clé de l'item) */ /*case 'list': var source = input.attr('data-source'); if(source != '' && source != null && btoa(atob(source)) == source){ input.html(''); input.append(''); var items = JSON.parse(atob(source)); //boucle sur les entrées possibles de sources de données for(const [key, source] of Object.entries(items)) //boucle sur les couples clés valeurs de la source de données for(const [index, value] of Object.entries(source)) input.append(''); } break;*/ /** * data-depth : nb de profondeur de liste (ex: 2, affichera 2 select au maximum), 1 par défaut * data-slug : le slug de la liste mère à afficher, listes primaires par défaut * data-value : la valeur de l'entité à récup en base * data-disable-label : cache le label de sous-liste si mentionné * data-hierarchy : si mentionné à false, ne récupère pas * data-parent-id : l'id de la liste parente associée * data-output : id/slug définit la valueur de sortie de l'input (slug de la liste ou id de la liste (defaut)) */ case 'dictionary': var data = input.data(); var slug = input.attr('data-slug') ? input.attr('data-slug') : ""; var parentId = input.attr('data-parent-id') && input.attr('data-parent-id').length ? input.attr('data-parent-id') : ""; //if (!slug.match(/^[a-z\d\-_]+$/i) && parentId == '') return; data.output = data.output ? data.output : 'id'; $.action({ action : 'core_dictionary_component_load', slug: slug, parentId : parentId, depth : data.depth, hierarchy : input.attr('data-hierarchy') == 'false' ? 0 : 1, value: input.attr('data-value') },function(r){ var children = r.content && r.content.childs ? r.content.childs : r.content; input.attr('onchange', 'get_sub_dictionary(this, "'+input.attr('name')+'",'+1+');'); input.html(''); input.append(''); $.each(children, function (index, value){ if (value.selected) { input.append(''); get_selected_values(input, value); }else{ input.append(''); } }); }); break; /** * data-label : le label affiché dans la zone * data-delete : méthode de suppression de doc de l'entité * data-save : méthode de sauvegarde de doc de l'entité (si mentionné, save automatique) * data-readonly: Empeche l'ajout/suppression de documents * data-allowed : les extensions de fichier acceptées */ case 'dropzone': console.warn('[DEPRECATED] : Dropzone doit être remplacé par le composant file'); if(input.find('form').length != 0 || input.find('ul>li').length) break; if(!input.attr('data-action')) input.attr('data-action','action.php?action=core_temporary_file_upload'); var readonly = input.attr('data-readonly') == "true" ? true : false; if(!input.get(0).hasAttribute('id')) input.attr('id',generate_uuid(10)); var customTpl = input.find('> *:not(:visible)'); var customActions = ''; if(customTpl && customTpl.length){ $.each(customTpl, function(i, action){ if(i>2) return; var valCalc = readonly ? i*28+3 : i*28+25; customActions += $(action).removeClass('hidden').css('right', valCalc).get(0).outerHTML; }); } var preview = '
  • '+ ''; preview += input.get(0).hasAttribute('data-preview') ? '' : ''; preview += ' {{name}}{{lastModification}}'+customActions+'
  • '; var valueFiles = input.html()!='' && is_json_string(input.text()) ? JSON.parse(input.text()) : []; input.html(''); var allowed = input.attr('data-allowed'); if(allowed) allowed = allowed.split(','); var save = input.attr('data-save'); var size = input.attr('data-max-size'); var maxFile = input.attr('data-max-files'); maxFile = 100; //maxFile=='' || ! maxFile ? 0 : maxFile; input.upload({ allowed : allowed, size : size == '' ? 0 : size, readonly: readonly, addData: function(){ return {index: input.attr('id')}; }, start: function(){ isProcessing = true; if(maxFile!=0 && $('li:visible',files).length >=maxFile ) return -1; preload.removeClass('hidden'); }, success: function(response){ isProcessing = false; if(response.error){ preload.addClass('hidden'); $.message('error', response.error, 0); return; } if(response.previews.length && response.previews[0].name) { var inputTemp = $('#'+input.attr('id')+'_temporary'); var currVal = inputTemp.val().length ? JSON.parse(inputTemp.val()) : []; for(var i in response.previews){ files.append(Mustache.render(preview,response.previews[i])); currVal.push(response.previews[i]); } inputTemp.val(JSON.stringify(currVal)); if(save) window[save](response.previews); } preload.addClass('hidden'); }, complete: function(){ isProcessing = false; preload.addClass('hidden'); }, error: function(){ isProcessing = false; preload.addClass('hidden'); }, }); var preload = $(''); input.append(preload); var files = $(''); input.append(files); var filesValues = $(''); input.append(filesValues); for(var i in valueFiles) { files.append(Mustache.render(preview,valueFiles[i])); if(readonly) files.find('li > i.fa-times').remove(); } if(!valueFiles.length && readonly) input.append('
    Aucun document
    '); break; case 'file': if(!input.get(0).hasAttribute('data-id')) input.attr('data-id',generate_uuid(10)); var droppedFiles = false; //Récuperation des data attributs de l'input var data = input.data(); //extra données (eg.id de l'entité liée) if(input.attr('data-data')){ try{ data.data = input.attr('data-data') ? JSON.parse(input.attr('data-data')) : {}; }catch(e){ data.data = {}; } } var options = $.extend({ allStart : function(files){return true}, allComplete : function(){}, fileStart : function(file){return true}, fileComplete : function(file){}, fileError : function(file){}, fileProgress : function(file){}, over : 'file-hover', label : 'Faites glisser vos fichiers ici', data : {}, limit : 0, readonly : false, onVisible : true },data); if(input.hasAttr('readonly')) options.readonly = true; var component = data.component; //initialisation du composant et liaison à l'input si pas déja fait if(!component){ var component = $($('#component-file-template').get(0).outerHTML); component.attr('class',input.attr('class')).removeClass('hidden').removeAttr('id'); input.after(component).data('component',component); input.attr('data-multiple-values','true'); } if(options.onVisible === true && !component.is(':visible')) break; var lineTpl = $('#component-file-template .upload-list').html(); var removeLine = function(element){ form.find('input[type="file"]').val(''); if(element.attr('data-path')==''){ element.remove(); component.attr('data-file-number',uploadList.find('>li').length); return; } $.action({ action: data.action, type: 'delete', path: atob(element.attr('data-path')), data: data.data },function(r){ updateValue(input,{path:element.attr('data-path')},true); element.remove(); component.attr('data-file-number',uploadList.find('>li').length); }); } //Ajout d'une ligne fichier var addLine = function(tpl,data){ var component = input.data('component'); if(data.path) data.path = btoa(data.path); var line = $(Mustache.render(tpl,data)); if(options.buttons) line.find('.file-button').html($(options.buttons).html()); component.find('.upload-list').append(line); //affichage du preview avec pré-chargement pour eviter le blink if(data.preview){ $('').attr('src', data.preview).on('load', function() { $(this).remove(); line.find('.file-preview').attr('style','background-image:url('+data.preview+')'); line.addClass('preview-loaded'); }); }else{ line.addClass('no-preview'); } line.find('.btn-delete').click(function(){ if(!line.hasClass('.upload-error')){ if(!confirm('Êtes-vous sûr de vouloir supprimer ce fichier ?')) return; } removeLine(line); input.trigger('change'); }); return line; } //Ajout d'un ensemble de fichiers var refreshFiles = function(tpl,input,data){ var component = input.data('component'); component.find('.upload-list li').remove(); for (var k in data.files) { addLine(tpl,data.files[k]); } } var updateValue = function(input,data,toDelete){ var jsonValue = JSON.parse(input.val()?input.val():'[]'); if(toDelete){ //delete for(var k in jsonValue) if(btoa(jsonValue[k].path) == data.path) jsonValue.splice(k,1); }else{ //add data.path = atob(data.path); jsonValue.push(data); } input.val(JSON.stringify(jsonValue)); } if(input.hasAttr('required')) component.attr('required',''); if(!options.data.id && $.urlParam('id')) options.data.id = $.urlParam('id'); //Récuperation des extensions, de la taille, des fichiers existants $.action({ action : options.action, type : 'search', data : options.data },function(r){ if(r.options) options = $.extend(options,r.options); refreshFiles(lineTpl,input,{files : r.files}); component.attr('data-file-number',r.files.length); }); //formulaire d'envois var form = component.find('form'); //input d'upload caché var fileInput = form.find('input[type="file"]'); //drop & click zone var uploadZone = component.find('.upload-zone'); //liste des fichiers var uploadList = component.find('.upload-list'); form.attr('action','action.php?action='+options.action+'&type=upload'); uploadZone.find('.file-upload-label').html(options.label); component.attr('data-file-number',0); if(options.readonly !== false) component.addClass('readonly'); //test if d&d is enabled n browser var div = document.createElement('div'); var dragAndDropEnabled = (('draggable' in div) || ('ondragstart' in div && 'ondrop' in div)) && 'FormData' in window && 'FileReader' in window; //set events if(options.readonly === false){ uploadZone .off('click drag dragstart dragend dragenter dragleave drop dragover').on('click', function (e) { fileInput.trigger('click'); e.preventDefault(); e.stopPropagation(); }) .on('drag dragstart dragend dragover dragenter dragleave drop', function (e) { e.preventDefault(); e.stopPropagation(); }) .on('dragover dragenter', function () { form.addClass(options.hover); }) .on('dragleave dragend drop', function () { form.removeClass(options.hover); }) .on('drop', function (e) { droppedFiles = e.originalEvent.dataTransfer.files; form.trigger('submit'); }); } //si l'input file change (on a choisis un fichier) on lance la soumission du formulaire fileInput.off('change').on('change', function (e) { form.trigger('submit'); }); //sur soumission du formulaire form.off('submit').on('submit', function (e) { e.preventDefault(); //fonctionnement browser modernes if (!dragAndDropEnabled) throw 'Drag & drop non fonctionnel, veuillez mettre à jour votre navigateur'; var fileProcessed = []; //si pas de drop mode, on récuepre le mode click if(!droppedFiles) droppedFiles = $('input',form).get(0).files; var fileQueue = []; var allowedExtensions = options.extension ? options.extension.split(',') : []; $.each(droppedFiles, function (i, file) { //line data var lineData = { extension : file.name.split('.').pop(), label : file.name, sort : i }; if(options.limit!=0 && i > options.limit) return; //dom element var fileLine = addLine(lineTpl,lineData); //ajout a la file d'upload fileQueue.push({ file : file, data : lineData, element : fileLine }); }); droppedFiles = null; if(!options.allStart(fileQueue)) return; for(var k in fileQueue) { var file = fileQueue[k].file; fileQueue[k].element.attr('data-sort',k); try{ if(allowedExtensions.length !=0 && allowedExtensions.indexOf(fileQueue[k].data.extension.toLowerCase()) === -1 ) throw "Extension non permise, autorisé : "+allowedExtensions.join(', '); if(options.size && file.size > options.size) throw "Taille fichier "+file.size+" octets trop grande (max autorisé:"+options.size+" octets"; var ajaxData = new FormData(); ajaxData.append(input.attr('data-id'), file); ajaxData.append('index', input.attr('data-id')); ajaxData.append('sort', k); if(!options.fileStart(fileQueue[k])) continue; $.ajax({ url: form.attr('action'), type: form.attr('method'), data: ajaxData, dataType: 'json', cache: false, contentType: false, processData: false, complete: function (data) { var response = data.responseJSON ? data.responseJSON : {error :data.responseText,sort:-1 }; if(response.sort!=-1){ var currentFile = fileQueue[response.sort]; if(response.error) currentFile.element.addClass('upload-error').find('.error-message').html(response.error).attr('title',response.error); currentFile.element.addClass('upload-complete'); }else{ if(response.error) $.message('error',response.error); } fileQueue[response.sort] = true; var completed = 0; for(var u in fileQueue){ if(fileQueue[u]===true) completed++; } if(fileQueue.length == completed){ if(window[options.allComplete]) window[options.allComplete](response); input.trigger('change'); } }, success: function (data) { var currentFile = fileQueue[data.sort]; options.fileComplete(currentFile); var newElement = addLine(lineTpl,data); currentFile.element.replaceWith(newElement); updateValue(input,data); var lines = uploadList.find('>li'); if(options.limit != 0 && lines.length > options.limit){ uploadList.find('>li').each(function(i,li){ if(i < lines.length - options.limit ) $(li).remove(); }); } component.attr('data-file-number',lines.length); newElement.addClass('upload-success'); }, error: function (data) { var response = data.responseJSON ? data.responseJSON : {error :data.responseText,sort:-1 }; if(response.sort!=-1){ var currentFile = fileQueue[data.sort]; options.fileError(currentFile); currentFile.element.addClass('upload-error'); fileQueue[response.sort] = true; }else{ $.message('error',response.error); } }, xhr: function() { var xhr = new window.XMLHttpRequest(); xhr.sort = k; xhr.upload.addEventListener("progress", function(evt){ if (evt.lengthComputable) { var percentComplete = (evt.loaded / evt.total) * 100; percentComplete = Math.round(percentComplete * 100) / 100; currentFile = fileQueue[xhr.sort]; options.fileProgress(fileQueue[xhr.sort],percentComplete); var progressBar = $('.progress-bar',currentFile.element) .css('width',percentComplete+'%'); if(percentComplete >= 100) currentFile.element.addClass('upload-process'); } }, false); return xhr; } }); }catch(e){ options.fileError(fileQueue[k],e); fileQueue[k].element.addClass('upload-error').find('.error-message').html(e).attr('title',e); fileQueue[k] = true; continue; } }; }); break; /* Composant table regroupant a terme toutes les options de "table avancée" : * les colonnes dynamiques * les multi select checkbox * les colonnes sortables * futures features tableau (ex: filtre sur les colonnes façon excel) ce composant est voué a être un composant core un fois rodé */ case 'search-table': var SearchTable = function(table,options){ var table = $(table); table.data('searchTable',this); this.table = table; this.options = options; this.slug = options.slug; this.actionButton = $(this.options.checkboxAction); var object = this; //Gestion des checkbox var globalCheckBox = object.table.find('.table_checkbox_global'); if(globalCheckBox.length==0 && object.options.checkboxAction){ var globalCheckBox = object.table.find('thead > tr:eq(0)').prepend(''); globalCheckBox = object.table.find('.table_checkbox_global'); globalCheckBox.click(function(){ object.table.find('tbody td:visible input[type="checkbox"]').click(); }); object.table.find('tbody > tr:eq(0)').prepend(''); object.table.on('click','tr:visible input.table_checkbox_line',function(){ var element = $(this); var checked = element.prop('checked'); var tr = element.closest('tr'); var id = tr.attr('data-id'); if(!id) return; var ids = object.checkboxes(); if(checked) { ids.push(id); }else{ var i = ids.indexOf(id); if(i > -1) ids.splice(i, 1); } object.checkboxes(ids) object.refreshActionButton(); }); } if(object.table.attr('data-loaded')!=1){ var search = window[options.entitySearch]; //Activation du tri colonne si au moins une en-tete contient le data-sortable if(object.table.find('[data-sortable]').length>0){ object.table.sortable_table({ onSort : function(){search({keepChecked:true})} }); } //lancement de la recherche automatique if(search) search(); object.table.attr('data-loaded',1); } } SearchTable.prototype.refreshActionButton = function(){ var ids = this.checkboxes(); this.actionButton.attr('data-count',ids.length).find('span.count').text(ids.length); }; SearchTable.prototype.resetCheckbox = function(){ this.checkboxes([]); this.refreshActionButton(); }; SearchTable.prototype.fillCheckbox = function(){ var selected = this.checkboxes(); for(var k in selected) $('tr:visible[data-id="'+selected[k]+'"] input.table_checkbox_line',this.table).prop('checked',true); }; SearchTable.prototype.resetGlobalCheckbox = function(){ this.table.find('.table_checkbox_global').prop('checked',false); }; SearchTable.prototype.checkboxes = function(data){ var tablesearch = localStorage.getItem('component.tablesearch.checked'); tablesearch = !tablesearch ? {} : JSON.parse(tablesearch); if(!data) return tablesearch[this.slug] ? tablesearch[this.slug] : []; tablesearch[this.slug] = data; localStorage.setItem('component.tablesearch.checked', JSON.stringify(tablesearch)); }; var searchTable = new SearchTable(input,input.data()); break; /** * data-simple : Si "true" alors interface avec moins de boutons * data-minimal: Si présent alors affichage simple en mode input inline */ case 'wysiwyg': var buttons = [ ['strong','em','underline','del'], ['foreColor', 'backColor'], ['fontfamily'], ['fontsize'], ['undo', 'redo'], // Only supported in Blink browsers ['formatting'], ['superscript', 'subscript'], ['link'], ['insertImage'], ['table'], ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'], ['unorderedList', 'orderedList'], ['horizontalRule'], ['removeformat'], ['fullscreen'], ['viewHTML'] ] if(input.attr('data-simple')!=null || input.attr('data-simple') == 'true'){ buttons = [ ['strong','em','underline','del'], ['foreColor', 'backColor'], ['undo', 'redo'], // Only supported in Blink browsers ['formatting'], ['link'], ['unorderedList', 'orderedList'], ['insertImage'] ] } if(input.attr('data-buttons')!=null && input.attr('data-buttons')!='') { buttons = is_json_string(input.attr('data-buttons')) ? JSON.parse(input.attr('data-buttons')) : [ input.attr('data-buttons').split(',') ]; } var defaultOptions = { btns: buttons, lang: 'fr', autogrow: input.attr('data-minimal') == null, semantic: false, plugins : {} }; var data = input.data(); defaultOptions.tagsToRemove= ['script', 'link']; var scriptAllow = input.get(0).hasAttribute('data-script-allow'); if(scriptAllow) defaultOptions.tagsToRemove= []; // gestion des mentions defaultOptions.plugins.mention = {keys : {}}; if(input.attr('data-mention-user') && input.attr('data-mention-user') != ''){ //mention @ defaultOptions.plugins.mention.keys['@'] = { load : function(data){ data.event.preventDefault(); document.execCommand('insertHTML', false, '@'); var inputContainer = $('.mention-user-picker-container'); init_components(inputContainer); var picker = $('.mention-user-picker-container input'); picker.focus(); var timeout = null; picker.click(function(){ clearTimeout(timeout); var value = $(this).val(); inputContainer.remove(); data.editor.focus(); data.textarea.trumbowyg('restoreRange'); document.execCommand('insertHTML', false, '@'+value+' '); data.editor.trigger('keyup'); }).blur(function(){ //on supprime le composant si blur sans valeur (attente de 200 ms car le blur est trigger avant le click) timeout = setTimeout( function(){ inputContainer.remove(); data.editor.trigger('keyup'); },200); }); picker.keydown(function(e){ var code = e.keyCode || e.which; //Si appuis sur espace ça casse l'autocompletion if(code == 32){ e.preventDefault(); var value = picker.val(); $('.mention-user-picker-container').remove(); data.editor.focus(); data.textarea.trumbowyg('restoreRange'); document.execCommand('insertHTML', false, '@'+value+' '); data.editor.trigger('keyup'); } }); } }; } if(input.attr('data-mention-object') && input.attr('data-mention-object') != ''){ //mention # defaultOptions.plugins.mention.keys['#'] = { load : function(data){ data.event.preventDefault(); document.execCommand('insertHTML', false, '#'); var picker = $('.mention-object-picker'); picker.focus(); picker.keydown(function(e){ var code = e.keyCode || e.which; //Si appuis sur espace ça casse l'autocompletion if(code == 32){ e.preventDefault(); var value = picker.val(); $('.mention-object-picker-container').remove(); data.editor.focus(); data.textarea.trumbowyg('restoreRange'); document.execCommand('insertHTML', false, '#'+value+' '); data.editor.trigger('keyup'); } }); var timeout = null; picker.autocomplete({ action : input.attr('data-mention-object'), skin : function(item){ var html = ''; var re = new RegExp(picker.val(),"gi"); name = item.name.replace(re, function (x) { return ''+x+''; }); html += '
    '+name+''; html += '
    '; return html; }, highlight : function(item){ return item; }, onClick : function(selected,element){ clearTimeout(timeout); picker.val(''); $('.mention-object-picker-container').remove(); data.textarea.trumbowyg('restoreRange'); slug = selected.slug ? selected.slug : selected.name; document.execCommand('insertHTML', false, '#'+slug+' '); data.editor.trigger('keyup'); }, onBlur : function(selected){ if(input.val() == '') picker.val(''); timeout = setTimeout(function(){ $('.mention-object-picker-container').remove(); data.editor.trigger('keyup'); },200); } }); } }; } var options = $.extend(defaultOptions,data); options = $.extend(options, input.data()); var obj = input.trumbowyg(options); //fix pour accepter les tags non sémantiques dans la gestion du highlight de bouttons trumb = input.data('trumbowyg'); trumb.tagToButton['b'] = 'strong'; trumb.tagToButton['i'] = 'em'; trumb.tagToButton['u'] = 'underline'; input.data('trumbowyg',trumb); obj.data('trumbowyg',trumb); obj.on('tbwblur', function(){ input.trigger('blur'); }); obj.on('tbwchange', function(){ input.trigger('keypress'); }); var trumbowygBox = $(input).closest('div.trumbowyg-box'); trumbowygBox.addClass(input.attr('class')).removeClass('trumbowyg-textarea'); if(input.attr('required')) trumbowygBox.attr('required', true); if(input.attr('readonly')) obj.trumbowyg('disable'); if(input.val()!='') input.trumbowyg('html', input.val()); if(input.attr('data-minimal') != null){ trumbowygBox.addClass('trumbowyg-minimal'); var btnPane = trumbowygBox.find('.trumbowyg-button-pane'); var editor = trumbowygBox.find('.trumbowyg-editor'); btnPane.addClass('hidden'); editor.removeAttr('style').keydown(function(e) { $(this).find('*').removeProp('font-size'); switch (e.keyCode) { case 13: return false; break; default: return true; break; } }).on('paste',function(e){ e.preventDefault(); e.stopImmediatePropagation(); var text = (e.originalEvent || e).clipboardData.getData('text/plain'); document.execCommand("insertHTML", false, text); }); } break; /** * Permet la recherche combinatoire multi critères. * Voir filter.component.js pour plus d'infos. */ case 'filter': var box = new FilterBox(input,input.data()); break; /** * Ouvre une popup de definition des droits ciblée sur une entité, une section, un id data-scope : section / partie de code ou de module concerné par le droit (defaut : core) data-uid : Id spécifique d'entité concerné par le droit (defaut : $.urlParam('id')) data-edit : Afficher la case edition (defaut : true) data-read : Afficher la case lecture (defaut : true) data-recursive : Afficher la case récursif (defaut : false) data-configure : Afficher la case configuration (defaut : false) data-firm : Scoper sur un établissement, cache la selection d'établissement si définit (defaut : null) data-saveAction : Définir une action de save custom du droit (defaut : core_right_save) data-deleteAction : Définir une action de delete custom du droit (defaut : core_right_delete) */ case 'right': input.off('click').click(function(){ var data = $.extend({ scope : 'core', uid : $.urlParam('id'), edit : true, read : true, recursive : false, configure : false, firm : null, saveAction : 'core_right_save', deleteAction : 'core_right_delete' },input.data()); core_right_edit(data); }); break; /** * data-table (ex : #contacts) : id de la table de résultat liée * data-value (ex: 20) : valeur initiale d'item par page si rien dans le local storage * data-step (ex: 5) : valeur du pas entre chaque proposition du nb d'item par page * data-items (ex: 10,25,50,150) : liste des items proposés pour la préférence de pagination séparés par virgule * data-max-item (ex: 50) : valeur maximum possible d'item par pages */ case 'pagination-preference': var pagination = localStorage.getItem('pagination') ? JSON.parse(localStorage.getItem('pagination')) : {}; var table = $(input.attr('data-table')); var container = input.data('container'); var items = input.data('items'); var uid = ($.urlParam('module')?$.urlParam('module'):'main')+'-'+($.urlParam('page')?$.urlParam('page') :'main')+'-'+table.attr('id'); if(!container){ var finalItems = []; if(!items) { var step = parseInt(input.attr('data-step')); for(var i=parseInt(input.attr('data-max-item')), j=1; i>=j; i-=(step?step:10)) finalItems.push({number:i}); } else { var items = items.split(','); for(var i=0; i
    ';}); input.data('data-component',input.parent()); var container = input.parent('.password-field'); container.append(''); container.find('i.password-toggler').attr((input.attr('data-toggle-event') == 'hover')?{ 'onmouseover': 'toggle_password(this);', 'onmouseleave': 'toggle_password(this);' }:{'onclick': 'toggle_password(this);'}); if(input.attr('data-generator') !== undefined) { container.append('') container.find('i.password-generator').on('click', function(){ 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; //Longueur attendue (12 par défaut) var length = input.attr('data-length') !== undefined && input.attr('data-length').length ? parseInt(input.attr('data-length')) : 12; //Regex norme ANSSI var strong = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[=/\()%ยง!@#$%^&*])(?=.{'+length+',})'); var forbidden = input.attr('data-forbidden') !== undefined && input.attr('data-forbidden').length ? input.attr('data-forbidden') : ''; do { password = generate_password(length, forbidden); } while(!strong.test(password)) input.val(password).trigger('change'); if(input.attr('data-show-strength') !== undefined) check_password_strength(input); }); function generate_password(length,forbidden){ var charset = get_characters_set(); var length = length ? length : 12+get_random_int(6); var forbidden = forbidden!=null ? forbidden : ''; var password = ""; for(var i=0; i -1) charset.splice(index, 1); } for (var i=0; i
    '); // Strength validation on keyup-event input.on("keyup mouseup", function(e) { check_password_strength($(this)); }); //Check password strength function check_password_strength(input) { var value = $(input).val(); var container = $(input).closest('.password-field'); $(".line", container).removeClass("strength-low strength-medium strength-hard").addClass("strength-transparent"); if(!value.length) return; var strongRegex = new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[=/\()%ยง!@#$%^&*])(?=.{12,})'), mediumRegex = new RegExp('^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})'); if(strongRegex.test(value)) { $(".line", container).removeClass("strength-transparent").addClass("strength-hard"); } else if (mediumRegex.test(value)) { $(".line:not(:last-of-type)", container).removeClass("strength-transparent").addClass("strength-medium"); } else { $(".line:nth-child(1)", container).removeClass("strength-transparent").addClass("strength-low"); } } } break; case 'url': if(input.closest('.url-field').length) return; var container = $('
    '+(!input[0].hasAttribute("data-no-lock")?' ':'')+ (!input[0].hasAttribute("data-no-link")?'':'')+'
    '); input.after(container); container.prepend(input.detach()); input.data('data-component',container); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant var link = $('.url-link',container); var icon = $('.url-icon',container); var keyActions = function(){ var value = input.val(); if(!value){ link.addClass('hidden'); }else{ link.removeClass('hidden'); } if(value.match('https://')){ icon.removeClass('text-muted').addClass('text-success').attr('title','Site sécurisé'); }else{ icon.removeClass('text-success').addClass('text-muted').attr('title','Site non sécurisé'); } } keyActions(); input.keyup(keyActions); var scheme = input.attr('data-default-scheme'); if(scheme){ input.blur(function(){ if(input.val() && !input.val().match('https?://')){ input.val(scheme+'://'+input.val()); keyActions(); } }); } container.find('i.url-link').click(function(){ window.open(input.val()); }); break; case 'icon': input.addClass('hidden'); input.next('.component-icon').remove(); var data = { value : input.val(), choices : [] }; var icons = $.fontAwesome(); var line = []; for(var key in icons){ if(line.length==15){ data.choices.push(line); line = []; } line.push(icons[key].icon); } var selector = input.data('data-component'); if(!selector){ var selector = $(Mustache.render($('.component-icon.hidden').get(0).outerHTML,data)); selector.addClass(input.attr('class')); selector.removeClass('hidden form-control'); input.data('data-component',selector); input.after(selector); } selector.on('show.bs.dropdown', function () { if(input.is('[readonly]') || input.is('[disabled]')){ selector.find('.dropdown-menu').addClass('hidden'); }else{ selector.find('.dropdown-menu').removeClass('hidden'); } setTimeout(function(){selector.find('input.form-control').focus();},0); }) selector.find('.dropdown-icon-item').click(function(){ selector.find('.dropdown-icon-item').removeClass('hidden'); selector.find('input.form-control').val(''); var icon = $(this).attr('data-icon'); input.val(icon); selector.find('.dropdown-toggle i').attr('class',icon); input.trigger('change'); }); selector.find('input.form-control').keyup(function(){ var value = $(this).val(); $('.dropdown-icon-item i',selector).each(function(i,iconElement){ iconElement = $(iconElement); parent = iconElement.parent(); iconElement.attr('class').indexOf(value)!=-1 ? parent.removeClass('hidden') : parent.addClass('hidden'); }); }); if(input.hasAttr('required')) selector.attr('required',''); if(input.hasAttr('readonly')) selector.attr('readonly',''); break; /** * data-title (*) : Le titre du modal * 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) * data-params : Les paramètres utilisés pour l'appel du callback * data-action (*) : Appel de la modal par action et non par ajax * data-precall : fonction appelée avant le chargement du modal (méthode de check sur l'UI par exemple) * Pour customiser l'icône du quickform, il suffit de placer le contenu que l'on veut dans la div de data-type="quickform" */ case 'quickform': if(input.find('span').length) return; if(input.attr('disabled')) return; var cbLoaded = input.attr('data-loaded'); // var params = input.attr('data-params') ? input.attr('data-params').split(',') : []; var params = input.attr('data-params') ? JSON.parse(input.attr('data-params')) : []; var preCb = input.attr('data-precall') ? input.attr('data-precall') : ''; if(input.find('> *').length){ input.find('> *').wrap(''); } else { input.append(' div:first-of-type', modal).nextAll().remove(); modal.clear(); $('form', modal).attr('data-id',''); if(cbLoaded) window[cbLoaded](params); modal.modal('show'); init_components('#quickform-modal'); }); }); }); break; case 'checkbox': //3 cas possibles : // - input sans rien, data-type="checkbox" --> on fait tout // - input avec coquille sans data-uuid, --> on génère simplement un data-uuid et maj valeur // - input avec coquille et data-uuid --> on maj simplement sa valeur if(input.get(0).hasAttribute('data-uuid')) return; if(input.attr('type') != 'checkbox') input.attr('type', 'checkbox'); if(!input.closest('label.check-component').length) { var labelBox = $(''); var checkbox = $('
    '); var id = input.attr('id'); var title = input.attr('title'); var customClass = input.attr('data-class'); if(id) labelBox.attr('for', id); if(title) labelBox.attr('title', title); if(input.attr('class')) labelBox.addClass(input.attr('class')); if(customClass) labelBox.addClass(customClass); if(input.prop('disabled') == true) labelBox.attr('disabled', true); input.removeAttr('title data-class').wrap(labelBox); input.after(checkbox); } var uuid = generate_uuid(15); input.attr('data-uuid', uuid); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant if(input.hasAttr('required')) input.closest('label.check-component').attr('required',''); if(input.hasAttr('readonly')) input.closest('label.check-component').attr('readonly',''); if(input.hasAttr('readonly')) input.closest('label.check-component').click(function(e){ e.preventDefault()}); if(input.get(0).hasAttribute('id')) return; input.closest('label.check-component').off('click','div.box').on('click','div.box',function(e){ if(input.hasAttr('readonly')) return e.preventDefault(); var checkboxInput = $('input[data-uuid="'+uuid+'"]'); checkboxInput.prop('checked', checkboxInput.prop('checked')); }); break; /** * data-label : Libellé affiché à côté de l'input radio * name (*) : Le nom du groupe dont fait partie l'input radio */ case 'radio': if(input.get(0).hasAttribute('data-uuid')) return; if(input.attr('type') != 'radio') input.attr('type', 'radio'); var uuid = generate_uuid(15); var id = input.get(0).hasAttribute('id')? input.attr('id') : ''; var title = input.get(0).hasAttribute('title') ? input.attr('title') : ''; var label = input.attr('data-label'); var labelBox = $(''); labelBox.attr({ 'for': id, 'title': title }); if(input.prop('disabled') == true) labelBox.attr('disabled', true); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant input.addClass('radio-component').removeAttr('title data-label').attr('data-uuid', uuid); input.after(labelBox); if(label) labelBox.after(''); if(input.attr('id') && input.attr('id').length) return; labelBox.click(function(e){ var radioInput = $('input[data-uuid="'+uuid+'"]'); var name = radioInput.attr('name'); $('input[name="'+name+'"]').prop('checked', false).removeAttr('checked'); if(radioInput.prop('disabled') != true) radioInput.prop('checked', true); $(input).change(); }); break; /** * data-action : l'action php pour récupérer l'UI de la card * data-parameters : paramètres à passer avec l'action json encodés */ case 'card': if(!input.is(':visible')) return; var action = input.attr('data-action'); if(!action) { console.warn('CARD COMPONENT: Need "data-action" to get card content'); return; } var showDelay = input.attr('data-show-delay') ? input.attr('data-show-delay') : 250; var hideDelay = input.attr('data-hide-delay') ? input.attr('data-hide-delay') : 600; input.addClass('card-component-container').attr('data-uuid', generate_uuid()); var timeout; input.off('mouseenter').mouseenter(function(event){ var e = event.target || event.relatedTarget; if (e.parentNode != this && e != this) return; event.stopImmediatePropagation(); event.stopPropagation(); $('*[data-type="card"]').find('.card-component') .addClass('card-component-hide') .removeClass('card-component-hover') .one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) { var card = $(this); if(card.closest('.card-component-container').attr('data-uuid') == input.attr('data-uuid')) return; card.closest('.wrapper').remove(); }); var parameters = input.attr('data-parameters'); var data = parameters!=null && parameters.length ? JSON.parse(parameters) : {}; data.action = action; timeout = setTimeout(function(){ var currInput = input; if(!currInput.find('.card-component').length) { $.action(data, function(r){ if(!r.content) return; //On utilise un wrapper pour gérer les overflows "out of the box" var card = $(r.content); var topOffset = currInput.offset().top; currInput.append(card); var position = {}; var cardWidth = card.outerWidth(); card.wrap($('
    ')); card.addClass('card-component card-component-hover'); if($('body').width() < (currInput.offset().left + cardWidth)) position['right'] = cardWidth; else position['left'] = 0; //Gestion des overflows pour positionnement de la card var cardHeight = card.outerHeight(); var htmlHeight = $('html').height(); if(htmlHeight-100 < (topOffset + cardHeight)){ var spaceBottom = htmlHeight-topOffset; var height = 0; if(spaceBottom > topOffset){ if(cardHeight > spaceBottom) height = (spaceBottom-100); } else { position['top'] = -cardHeight; if(cardHeight > topOffset){ height = (topOffset-100); position['top'] = -(topOffset-100); } } if(height!=0){ card.css({ height: height+'px', 'overflow-y': 'auto', 'overflow-x': 'hidden' }); } } currInput.find('.wrapper').css(position); init_components(currInput.find('.wrapper')); }); } else { var card = $(currInput.find('.card-component')); card.addClass('card-component-hover').removeClass('card-component-hide'); } clearTimeout(currInput.data('tOutbox')); }, showDelay); }); input.add(input.find('.card-component')).off('mouseleave').mouseleave(function(e){ // e.stopImmediatePropagation(); // e.stopPropagation(); var input = $(this), card = input.find('.card-component'), tOutbox = setTimeout(function(){ if(!$('.card-component:hover').length) { card.addClass('card-component-hide').removeClass('card-component-hover'); card.one('webkitAnimationEnd oanimationend msAnimationEnd animationend', function(e) { $(this).closest('.wrapper').remove(); }); } }, hideDelay); //Clear du timeout d'apparition clearTimeout(timeout); //Set du l'id de timeout, permet de clear ce trigger si la souris revient sur l'input input.data('tOutbox', tOutbox); }); break; case 'choice': if(input.get(0).tagName !='INPUT') return console.warn('Choice autorisé sur input uniquement'); var data = input.data(); var container; if(!input.data("data-component")){ container = $('
    '); input.before(container); input.data("data-component", container); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant } container = input.data("data-component"); //récuperation du name servant d'id de groupe pour les radio var name = input.attr('name'); if(!name) name = data.name; if(!name) name = input.attr('id'); if(!name) name = Math.floor(Math.random() * 100); //rafraichissement des options possibles if(!data.values) return; html = ''; for(var k in data.values){ html+=' '; } container.html(html); $('input',container).change(function(){ input.val($(this).val()); input.change(); }); init_components(container); //supprime les component-raw-value généré par l'utilisation des data-type = radio //pour les filtres on ne veut que l'input du choice container.find('.component-raw-value').removeClass('component-raw-value'); //set de la valeur si définie dans l'input if(input.val()){ $('input',container).prop('checked',false); $('input[value="'+input.val()+'"]',container).prop('checked',true); } if(input.hasAttr("required")) container.attr('required',''); break; case 'jsontable': if(input.get(0).tagName !='INPUT') return; var data = input.data(); if(data.format!='key-value' && data.format!='multiple-values') return console.warn('seul les formats key-value et multiple-values sont implémentés sur le composant jsontable'); var container; if(!input.data("data-component")){ container = $('\ \
    '); input.before(container); input.data("data-component", container); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant } container = input.data("data-component"); var head = $('thead tr',container); var body = $('tbody',container); var refreshTable = function(){ head.find('th:not(.option-head)').remove(); body.find('tr').remove(); var html = ''; if(data.format == 'key-value') data.columns = {"key":"Clé","value":"Valeur"}; for(var k in data.columns){ html+= ''+data.columns[k]+''; } html+= ''; head.prepend(html); var values = {}; try{ values = input.val(); values = JSON.parse(input.val()); }catch(e){ values = {}; } var columnLength = Object.keys(data.columns).length+1; body.append(' AJOUTER'); for(var k in values){ line = values[k]; if(data.format == 'key-value') line = {key:k,value:values[k]}; addLine(line,data.columns); } $('.btn-add-line',container).click(function(){ addLine(null,data.columns); }); if(input.hasAttr('required')) container.attr('required',''); } var addLine = function(data,header){ if(!data){ for(var k in header)header[k] = ''; data = header; } var html = ''; for(var key in data) html += ''; html +=''; html += ''; var line = $(html); body.find('tr:last-child').before(line); $('.btn-remove',line).click(function(){ removeLine($(this).closest('tr')); }); $('input',line).change(function(){ refreshInput(); input.change(); }); refreshInput(); input.change(); } var removeLine = function(element){ $(element).remove(); refreshInput(); input.change(); } var refreshInput = function(){ var json = data.format=='multiple-values' ? [] : {}; body.find('tr.line').each(function(){ var tr = $(this); if(data.format=='key-value'){ json[tr.find('td:eq(0) input').val()] = tr.find('td:eq(1) input').val(); }else if(data.format=='multiple-values'){ var line = {}; tr.find('td').each(function(){ var td = $(this); var inputValue = td.find('input'); if(inputValue.length==0) return; line[inputValue.attr('data-key')] = inputValue.val(); }); json.push(line); } }); input.val(JSON.stringify(json)); } refreshTable(); if(input.hasAttr('readonly')){ container.attr('readonly',''); container.find('tbody input').attr('readonly',''); } break; case 'entitypicker': var component = input.data('data-component'); var modal = $('#entitypicker-modal'); if(!component){ var component = $('
    '); input.data('data-component',component); input.after(component); if(modal.length!=0) modal.remove(); modal = $(''); $('body').append(modal); } modal = $('#entitypicker-modal'); var searchEntities = function(){ var plugin = $('.plugin.active',modal).attr('data-path'); if(!plugin) return; $('.entity-list').fill({ action:'core_entity_search', plugin : plugin },function(){ $('.entity-list .entity').click(function(){ input.val($(this).attr('data-path')); refreshValue(); modal.modal('hide'); input.change(); }); }); }; var refreshValue = function(){ var value = input.val(); if(!value) return $('>span',component).html(''); $.action({ action : 'core_entity_edit', path : value },function(response){ $('>span',component).html(''+response.plugin.name+' '+response.label); }); } $(component).off('click').click(function(){ modal.modal('show'); $('.plugin-list',modal).fill({action:'core_plugin_search',includeCore : true},function(){ $('.plugin',modal).click(function(){ $('.plugin',modal).removeClass('active'); $(this).addClass('active'); searchEntities(); }); }); }); $('.entitypicker-clear',component).click(function(event){ event.stopPropagation(); input.val(''); refreshValue(); }); refreshValue(); break; case 'filepicker': if(input.get(0).tagName !='INPUT') return; var data = input.data(); var container; var refreshComponent = function (){ var folders = input.val().split('/'); var html = ''; for (var i in folders) { var folder = folders[i]; if(!folder) continue; html += '
  • '+folder+'
  • '; } container.find('>ul').html(html); $('.pathlabel',container).click(function(event){ event.stopPropagation(); if(!input.hasAttr('readonly')) $('.filepicker-browse',container).click(); }).keyup(function(){ if(input.hasAttr('readonly') || !data.editable) return; var folders = input.val().split('/'); folders[$(this).closest('li').index()] = $(this).text(); input.val(folders.join('/')); }); } if(!input.data("data-component")){ container = $('
      '); input.before(container); input.data("data-component", container); input.addClass('component-raw-value'); // utilisé par les filtres / etc.. pour dissocier la valeur brute du composant $('.filepicker-clear',container).click(function(event){ event.stopPropagation(); input.val(''); refreshComponent(); input.change(); }); container.click(function(){ if(input.hasAttr('readonly')) return; $('#filepicker-modal').remove(); var modal = $(''); $('body').append(modal); $('.tree-folders',modal).on('click','li',function(event){ var li = $(this); if(li.hasClass('folder-focused')){ var parent = li.parent().parent(); treeSearch(parent.attr('data-path') == null ? '.' : parent.attr('data-path')); }else{ treeSearch(li.attr('data-path')); } if(event) event.stopPropagation(); }); $('.btn-select',modal).click(function(){ var path = $('.folder-focused',modal).attr('data-path'); if(!path) return $.message('error','Aucun chemin n\'est sélectionné'); input.val(path); refreshComponent(); modal.modal('hide'); input.change(); }); var treeSearch = function(folderPath){ $.action({ action: 'core_file_search', keyword : '', //sort : sort, root :data.root, folder : folderPath ? folderPath : '' },function(r){ var tree = $('ul.tree-folders',modal); $('li:not(:eq(0))',tree).remove(); var tpl = $('li:not(:visible)',tree).get(0).outerHTML; tree.find('li:visible').remove(); var selected = folderPath ? folderPath : '' ; if(selected.length>=2 && selected.substring(0,2) == './') selected = selected.substring(2); for(var k in r.tree){ var path = r.tree[k]; var parts = path.split('/'); var recipient = tree.parent(); for(var i in parts){ var part = parts[i]; var path = parts.slice(0,parseInt(i)+1).join('/'); var element = $('> .folders-container > li[data-path="'+path+'"]',recipient); if(element.length ==0){ var classes = ''; if(selected==path) classes+= ' folder-focused '; if(selected && (selected.indexOf(path+'/')!==-1 || selected==path) ) classes+= ' folder-open '; var row = { path : path, label : part, className : classes }; var element = $(Mustache.render(tpl,row)); element.removeClass('hidden'); recipient.find('> .folders-container').append(element); } recipient = element; } } }); } treeSearch(input.val()); modal.modal('show'); }); }else{ container = input.data("data-component"); } if(input.hasAttr('readonly')){ container.attr('readonly',''); }else{ container.removeAttr('readonly'); } if(input.hasAttr('required')) container.attr('required',''); if(input.val()) refreshComponent(); break; /** * data-dictionary : Le slug du dictionary à utiliser */ case 'dictionary-table': if(input.hasClass('component-dictionary-table')) return; if(!input.attr('data-dictionary') || !input.attr('data-dictionary').length) return; var tpl = $('.component-dictionary-table').get(0).outerHTML; var inputData = input.data(); $.action({ action: 'core_dictionary_component_load', slug : input.attr('data-dictionary') },function(r){ if(!r.content) { input.append('
      Liste spécifiée inexistante
      '); return; } var list = $(Mustache.render(tpl,{label:'{{label}}', slug:'{{slug}}', id:'{{id}}',parent:r.content})); input.append(list); list.removeClass('hidden'); dictionary_table_refresh(list); //Save input.on('click','thead .btn-success',function(){ var line = $(this).closest('tr'); var data = { action: inputData.saveAction ? inputData.saveAction : 'core_dictionary_table_save', label: line.find('input.list-label').val(), id: list.attr('data-id'), list: list.attr('data-dictionary') }; if(list.find('input.list-slug')) data.slug = $('input.list-slug', list).val(); $.action(data, function(r){ list.attr('data-id', ''); line.find('input').val(''); $.message('success','Enregistré'); dictionary_table_refresh(list); }); }); //Suppression input.on('click','tbody tr .btn-danger',function(){ if(!confirm('Êtes-vous sûr de vouloir supprimer cet élément de liste ?')) return; var line = $(this).closest('tr'); $.action({ action: inputData.deleteAction ? inputData.deleteAction : 'core_dictionary_delete', id: line.attr('data-id') },function(r){ line.remove(); line.closest('.edit-field').val(''); $.message('info', 'Élément de liste supprimé'); }); }); //Édition input.on('click','tbody tr .btn-edit',function(){ var line = $(this).closest('tr'); $.action({ action: inputData.editAction ? inputData.editAction : 'core_dictionary_edit', id: line.attr('data-id') },function(r){ list.find('input.list-label').val(r.label); if(list.find('input.list-slug')) $('input.list-slug', list).val(r.slug); list.attr('data-id',r.id); }); }); }); break; //Permet aux plugins d'ajouter leurs composants //via la fonction init_components_nomcomposant(input); default: var type = input.attr('data-type').replace(/[^a-z0-9]/i,'_'); if(window['init_components_'+type] !=null) window['init_components_'+type](input); break; } }); init_tooltips(); } /** TOOLTIPS **/ function init_tooltips(selector){ //Clean des tooltip qui restent en suspend sans hover de la souris $('div.tooltip[role="tooltip"]').tooltip('dispose'); var selected = selector ? $('[data-tooltip]',selector) : '[data-tooltip]'; $(selected).each(function(){ var element = $(this); var options = {html:true}; if(element.attr('data-placement')) options.placement = element.attr('data-placement'); element.tooltip('dispose'); element.tooltip(options); }); }