/** * Author : Thierry http://www.electron-libre.org/freeselect.php * License : Public domain */ (function( $ ){ $.fn.freeSelect = function( options ) { if (true == $.fn.freeSelect.isMobile) { return this.each(function() { $(this).css('visibility', 'visible'); }); } if ('refreshAdd' == options) { this.fs_refreshAdd(); return null; } var defaults = { icon: '∇' }; var o = $.extend(defaults, options); return this.each(function() { object = $(this); if (object.attr('multiple') || object.attr('size')) { object.css('visibility', 'visible'); return true; } object.css({display: 'none'}); var $parent = object.parent(); var $options = $('option', object); var inner = ''; var sel = ' '; var sel_val = ''; if ($selected === null) { if ($options.length > 0) { console.log(object.attr('id')); sel = $options[0].html(); sel_val = $options[0].val(); $selected = $($options[0]); } else { sel = ''; sel_val = ''; $selected = null; } } else { sel = $selected.html(); sel_val = $selected.val(); } var style_validate = ''; var style_jselect = ''; if (object.attr('class')) { var style = object.attr('class').split(' '); for (var i in style) { if (style[i].indexOf('validate') != -1) { style_validate += style[i] + " "; } else { //alert(style[i]); style_jselect += style[i] + " "; } } } var libelle_html; if (object.attr('dir') && object.attr('dir') == 'rtl') { libelle_html = '' + o.icon + '' + sel + ''; } else { libelle_html = '' + sel + '' + o.icon + ''; } var hidden = ''; var html = '
' + libelle_html + '' + inner + hidden + '
'; var id = object.attr('id'); object.replaceWith(html); var $obj = $('#fs_' + id); //l'evenement onchange peut venir de l'attribut html "onchange" ou bien via JQuery $obj.data('onchangeHTML', object.attr('onchange')); if (object.data('events')) { jQuery.each(object.data('events'), function(i, event){ if (i == 'change') { jQuery.each(event, function(i, handler){ $obj.data('onchangeJQuery', handler['handler']); }); } }); } if (object.attr('disabled')) { $('#' + object.attr('id')).freeSelectDisable(); } if (has_optGroup) { $('li', $obj).not('.fs_optg').not('.fs_optg_iso').addClass('fs_optg_child'); //cette class ne servait qu'à marquer des li dans le but de ne pas les prendre en compte //dans l'expression ci dessus $('.fs_optg_iso').removeClass('fs_optg_iso'); } /** * Gestion des diférents évènements */ $obj.mousemove(function() { window.fsOnKeyboard = false; }); $('ul a', $obj).click(change); $('ul a', $obj).not('.fs_hover').bind('mouseover', function() { if (false == window.fsOnKeyboard) { $(this).addClass('fs_mouse_hover'); $(this).focus(); } }); $('ul a', $obj).not('.fs_hover').bind('mouseout', function() { $(this).removeClass('fs_mouse_hover'); }); //pour ie7 qui ne comprend pas :focus $('ul a', $obj).bind('focus', function() { $(this).addClass('fs_focus'); }); $('ul a', $obj).bind('blur', function() { $(this).removeClass('fs_focus'); }); //fin pour ie7 $('a:first', $obj).click(ouvre); $($obj).keydown(keyDown); $('a:first', $obj).bind('mouseover focus', function(){ $(this).addClass('fs_focus_libelle'); }); $('a:first', $obj).bind('mouseout blur', function(){ $(this).removeClass('fs_focus_libelle'); }); $('html').click(function(event){ fermeClickWindow(); }); //cas liste vide if (null == $selected) { $('#' + object.attr('id')).freeSelectDisableSubmission(); } else { $selected = $($('ul a', $obj)[sel_index]); $selected.addClass('fs_hover'); $selected.focus(); } /** * On gère maintenant les attributs restants */ if (object.attr('form')) { var tab_form_ids = object.attr('form').split(' '); for (i in tab_form_ids) { //test si cette form n'a pas deja été bindée //pour ne pas réinitialiser le tableau et perdre des valeurs if (!$('#' + tab_form_ids[i]).data('ids')) { $('#' + tab_form_ids[i]).data('ids', []); } $('#' + tab_form_ids[i]).data('ids').push(id); $('#' + tab_form_ids[i]).live('submit', function() { //comme l'évènement peut être binder plusieurs fois //inutile de repasser plusieurs fois if ($('input[name="' + $(this).data('ids')[0] + '"]', $(this)).length > 0) { return true; } for (var j in $(this).data('ids')) { $('').attr('type', 'hidden') .attr('name', $('#' + $(this).data('ids')[j]).attr('name')) .attr('value', $('#' + $(this).data('ids')[j]).val()) .appendTo($(this)); } return true; }); } } if (object.attr('tabindex')) { $obj.attr('tabindex', object.attr('tabindex')); if (object.attr('autofocus')) { $obj.focus(); } } else if (object.attr('autofocus')) { //la méthode focus() ne marche que s'il y a un tabindex $obj.attr('tabindex', 0); $obj.focus(); } if (object.attr('title')) { $obj.attr('title', object.attr('title')); } if (object.attr('dir')) { $obj.attr('dir', object.attr('dir')); } if (object.attr('lang')) { $obj.attr('lang', object.attr('lang')); } $obj.data('hasCallOnChange', false); /** * Rebind les évènements pour les nouvelles options qui ont été ajouté */ $.fn.fs_refreshAdd = function() { $('ul a.add', this).click(change); $('ul a.add', this).not('.fs_hover').bind('mouseover', function() { if (false == window.fsOnKeyboard) { $(this).addClass('fs_mouse_hover'); $(this).focus(); } }); $('ul a.add', this).not('.fs_hover').bind('mouseout', function() { $(this).removeClass('fs_mouse_hover'); }); //pour ie7 qui ne comprend pas :focus $('ul a.add', this).bind('focus', function() { $(this).addClass('fs_focus'); }); $('ul a.add', this).bind('blur', function() { $(this).removeClass('fs_focus'); }); //fin pour ie7 $('ul a.add', this).removeClass('add'); } function doOnchange(obj) { if (obj.data('onchangeHTML') !== undefined) { eval(obj.data('onchangeHTML')); } else if (obj.data('onchangeJQuery')) { obj.data('onchangeJQuery')(); } } /** * Cette fonction est appele lors d'un click sur la page ou lors de l'ouverture d'une liste * On ferme les (normalement la) listes ouvertes */ function fermeClickWindow(event) { $obj = $('.fs_is_open'); if ($obj.length > 1) { console.log('Bug: too many lists open'); } if ($obj.data('hasCallOnChange') == false) { $obj.data('hasCallOnChange', true); ferme($obj); doOnchange($obj); } else { ferme($obj); } } function ferme(obj) { if (undefined == obj) { $('.fs_is_open ul').css({display:'none'}); $('.fs_is_open').removeClass('fs_is_open'); } else { $('ul', obj).css({display:'none'}); obj.removeClass('fs_is_open'); } } /** * Le click sur le select appel par defaut la fonction ouvre * il faut ensuite determiner si l'on doit fermer ou ouvrir le select */ function ouvre(event) { event.stopPropagation(); var $obj = $(this).parents().filter('div:first'); if ($obj.hasClass('fs_disabled')) { ferme($obj); return false; } if ($('ul', $obj).css('display') == 'none') { //avant d'ouvrir on ferme tout ferme(); posList($obj); $obj.addClass('fs_is_open'); $('ul', $obj).css({display:'block'}); $('.fs_hover', $obj).focus(); } else { ferme($obj); } return false; } /** * Positionne la liste en haut du select si bas de page */ function posList(obj) { if (($('body').height() - obj.offset().top) < ($('ul', obj).height() * 1.5)) { $('ul', obj).css('bottom', $('a:first', obj).outerHeight()); } } function change(event) { var $selected = $(this); if ($selected.hasClass('fs_disabled')) { event.stopPropagation(); return; } var $obj = $(this).parents().filter('div:first'); //les valeurs peuvent être les même si l'on vient d'une navigation au clavier if ($('input', $obj).val() == $selected.attr('rel') && $obj.data('hasCallOnChange') == true) { event.stopPropagation(); ferme($obj); return false; } event.stopPropagation(); $('.fs_libelle', $obj).html($(this).html()); $('ul a', $obj).removeClass('fs_hover'); $(this).addClass('fs_hover'); $('input', $obj).val($selected.attr('rel')); //$('p', $obj).next().slideToggle(); $obj.data('hasCallOnChange', true); ferme($obj); doOnchange($obj); return false; } function keyDown(ev) { var key = ev.keyCode; var $obj = $(this); var $selected; if ($('.fs_mouse_hover', $obj).length > 0) { $selected = $('.fs_mouse_hover', $obj); } else { $selected = $('.fs_hover', $obj); } //permet d'eviter de perdre le focus lors d'une navigation au clavier //si une liste scroll et que la souris est dessus //voir les évènements mouseover et mousemove window.fsOnKeyboard = true; //touche bas if (key == 40) { //si atteint le bas de la liste if ($selected.parent('li').nextAll('li').not('.fs_optg').find('a').not('.fs_disabled').filter('a:first').length < 1) { return false; } $('a', $obj).removeClass('fs_mouse_hover').removeClass('fs_hover'); //$selected = $selected.parent('li').nextAll('li').not('.fs_optg').filter('li:first').children('a'); $selected = $selected.parent('li').nextAll('li').not('.fs_optg').find('a').not('.fs_disabled').filter('a:first'); $selected.addClass('fs_hover'); $selected.focus(); $('.fs_libelle', $obj).html($selected.html()); $('input', $obj).val($selected.attr('rel')); //si on navigue la liste fermée, donc on appel onchange //et on enleve la class focus sauf sur le selected (liste fermée => pas d'evt blur) if ($('ul', $obj).css('display') == 'none') { doOnchange($obj); $('ul a', $obj).not($selected).removeClass('fs_focus'); $obj.data('hasCallOnChange', true); } else { $obj.data('hasCallOnChange', false); } return false; } //touche haut else if (key == 38) { //si atteint le haut de la liste if ($selected.parent('li').prevAll('li').not('.fs_optg').find('a').not('.fs_disabled').filter('a:first').length < 1) { return false; } $('a', $obj).removeClass('fs_mouse_hover').removeClass('fs_hover'); //$selected = $selected.parent('li').prevAll('li').not('.fs_optg').filter('li:first').children('a'); $selected = $selected.parent('li').prevAll('li').not('.fs_optg').find('a').not('.fs_disabled').filter('a:first'); $selected.addClass('fs_hover'); $selected.focus(); $('.fs_libelle', $obj).html($selected.html()); $('input', $obj).val($selected.attr('rel')); //si on navigue la liste fermée, donc on appel onchange //et on enleve la class focus sauf sur le selected (liste fermée => pas d'evt blur) if ($('ul', $obj).css('display') == 'none') { doOnchange($obj); $('ul a', $obj).not($selected).removeClass('fs_focus'); $obj.data('hasCallOnChange', true); } else { $obj.data('hasCallOnChange', false); } return false; } //une lettre else if ((key >= 65 && key <= 90) || (key >= 97 && key <= 122)) { var character = String.fromCharCode(key).toLowerCase(); //alert(character); $('ul a', $obj).each(function(index) { if ( character == $(this).html().substr(0,1).toLowerCase()) { //seulement si n'a pas déjà le focus sinon on continue if (!$(this).hasClass('fs_hover')) { $('a', $obj).removeClass('fs_mouse_hover').removeClass('fs_hover'); $selected = $(this); //$('a', $obj).removeClass('fs_hover'); $selected.addClass('fs_hover'); $selected.focus(); $('.fs_libelle', $obj).html($selected.html()); $('input', $obj).val($selected.attr('rel')); //si on navigue la liste fermée, donc on appel onchange //et on enleve la class focus sauf sur le selected (liste fermée => pas d'evt blur) if ($('ul', $obj).css('display') == 'none') { doOnchange($obj); $('ul a', $obj).not($selected).removeClass('fs_focus'); $obj.data('hasCallOnChange', true); } else { $obj.data('hasCallOnChange', false); } return false; } } }); } } }); }; $.fn.freeSelect.isMobile = false; $.fn.freeSelectGetSelectedString = function() { if (true == $.fn.freeSelect.isMobile && !($(this).attr('multiple') || $(this).attr('size'))) { return $('option:selected', this).text(); } else { $obj = $('#fs_' + $(this).attr('id')); return $('.fs_libelle', $obj).text(); } } $.fn.freeSelectAdd = function( tab_options ) { if (undefined != tab_options && tab_options.length < 1) { return this; } return this.each(function() { var $obj; if (true == $.fn.freeSelect.isMobile && !(object.attr('multiple') || object.attr('size'))) { $obj = $(this); isMobile(); } else { $obj = $('#fs_' + $(this).attr('id')); isNotMobile($(this)); } function isMobile() { for (i in tab_options) { //si un optgroup if (undefined != tab_options[i].group_text) { if ($('optgroup[label="' + tab_options[i].group_text + '"]').length < 1) { $obj.append(''); } for (j in tab_options[i].options) { $('optgroup[label="' + tab_options[i].group_text + '"]').append(''); } } else { $obj.append(''); } } } function isNotMobile($obj_orig) { for (i in tab_options) { //si un optgroup if (undefined != tab_options[i].group_text) { //si le groupe n'existe pas on le cree if ($('li[data-text="' + tab_options[i].group_text + '"]').length < 1) { $('ul', $obj).append('
  • ' + tab_options[i].group_text + '
  • '); } //et dans tous les cas on ajoute à la suite for (j in tab_options[i].options) { $('li[data-text="' + tab_options[i].group_text + '"]').after('
  • ' + tab_options[i].options[j].text + '
  • '); } } else { $('ul', $obj).append('
  • ' + tab_options[i].text + '
  • '); } } $($obj).freeSelect('refreshAdd'); //cas où l'on aurait remplie une liste vide if ($('.fs_libelle', $obj).html() == ' ') { $('.fs_libelle', $obj).html($('ul a:first', $obj).html()); $('ul a:first', $obj).addClass('fs_hover'); $('input', $obj).val($('.fs_hover').attr('rel')); //si liste vide donc on devait avoir empêché sa soumission $obj_orig.freeSelectEnableSubmission(); } } }); }; $.fn.freeSelectRemove = function( tab_values ) { if (undefined != tab_values && tab_values.length < 1) { return this; } return this.each(function() { var $obj_js; var $obj; if (true == $.fn.freeSelect.isMobile && !($(this).attr('multiple') || $(this).attr('size'))) { $obj = $(this); isMobile(); } else { $obj_js = $('#fs_' + $(this).attr('id')); isNotMobile(); } function isMobile() { for (i in tab_values) { $('option[value="' + tab_values[i] + '"]', $obj).remove(); } //on verifie qu'il n'y a pas de groupe vide if ($('optgroup', $obj).length > 0) { $('optgroup', $obj).each(function() { if ($(this).children().length < 1) { $(this).remove(); } }); } } function isNotMobile() { for (i in tab_values) { $obj = $('a[rel="' + tab_values[i] + '"]', $obj_js); //si l'on enlève l'option qui est sélectionnée if ($obj.hasClass('fs_hover')) { $obj.parent().remove(); $('.fs_libelle', $obj_js).html($('ul a', $obj_js).not('fs_optg').filter('a:first').html()); $('ul a:first', $obj_js).addClass('fs_hover'); $('input', $obj_js).val($('.fs_hover').attr('rel')); } else { $obj.parent().remove(); } } //cas où toutes les options auraient été vidé if ($('ul a', $obj_js).length < 1) { $('.fs_libelle', $obj_js).html(' '); $('input', $obj_js).val(''); //dans le doute on enleve aussi tous les groupes éventuels $('.fs_optg', $obj_js).remove(); $('input', $obj_js).freeSelectDisableSubmission(); } //sinon on verifie qu'il n'y a pas de groupe vide else { $('.fs_optg', $obj_js).each(function() { if (false == $(this).next().hasClass('fs_optg_child')) { $(this).remove(); } }); } } }); }; $.fn.freeSelectRemoveAll = function() { return this.each(function() { var $obj; if (true == $.fn.freeSelect.isMobile && !($(this).attr('multiple') || $(this).attr('size'))) { $(this).html(''); } else { $obj = $('#fs_' + $(this).attr('id')); $('ul', $obj).html(''); $('.fs_libelle', $obj).html(' '); $('input', $obj).val(''); $(this).freeSelectDisableSubmission(); } }); } $.fn.freeSelectDisable = function(tab_values) { if (undefined != tab_values && tab_values.length < 1) { return this; } return this.each(function() { if (undefined != tab_values) { if (true == $.fn.freeSelect.isMobile && !($(this).attr('multiple') || $(this).attr('size'))) { for (i in tab_values) { $('option[value="' + tab_values[i] + '"]', this).attr('disabled', 'disabled'); } } else { for (i in tab_values) { $('a[rel="' + tab_values[i] + '"]', $('#fs_' + $(this).attr('id'))).addClass('fs_disabled'); //si on disable une option sélectionné on empêche la soumission if ($('a[rel="' + tab_values[i] + '"]', $('#fs_' + $(this).attr('id'))).hasClass('fs_hover')) { $(this).freeSelectDisableSubmission(); } } } } else { if (true == $.fn.freeSelect.isMobile && !($(this).attr('multiple') || $(this).attr('size'))) { $(this).attr('disabled', 'disabled'); } else { $('#fs_' + $(this).attr('id')).addClass('fs_disabled'); //comme la liste est disabled on empêche la soumission de sa valeur $(this).freeSelectDisableSubmission(); } } }); } $.fn.freeSelectEnable = function(tab_values) { if (undefined != tab_values && tab_values.length < 1) { return this; } return this.each(function() { if (undefined != tab_values) { if (true == $.fn.freeSelect.isMobile && !($(this).attr('multiple') || $(this).attr('size'))) { for (i in tab_values) { $('option[value="' + tab_values[i] + '"]', this).removeAttr('disabled'); } } else { for (i in tab_values) { $('a[rel="' + tab_values[i] + '"]', $('#fs_' + $(this).attr('id'))).removeClass('fs_disabled'); } //dans le doute et plutôt que de faire un if dans la boucle on autorise la soumission (voir freeSelectDisable) $(this).freeSelectEnableSubmission(); } } else { if (true == $.fn.freeSelect.isMobile && !($(this).attr('multiple') || $(this).attr('size'))) { $(this).removeAttr('disabled'); } else { $('#fs_' + $(this).attr('id')).removeClass('fs_disabled'); //on permet à nouveau la soumission de la valeur si la liste n'est pas vide if ($('.fs_libelle', $('#fs_' + $(this).attr('id'))).html() != ' ' ) { $(this).freeSelectEnableSubmission(); } } } }); } $.fn.freeSelectDisableSubmission = function() { return this.each(function() { $('#fs_' + $(this).attr('id')).attr('data-name', $(this).attr('name')); $(this).removeAttr('name', ''); //on met aussi une fake value pour ne pas être bloqué par validationEngine s' il est en place $(this).val('fake'); }); } $.fn.freeSelectEnableSubmission = function() { return this.each(function() { $(this).attr('name', $('#fs_' + $(this).attr('id')).attr('data-name')); $('#fs_' + $(this).attr('id')).removeAttr('data-name', ''); //et on remet la bonne valeur (voir freeSelectDisableSubmission) $(this).val($('.fs_hover', $('#fs_' + $(this).attr('id'))).attr('rel')); }); } })( jQuery );