/**
* 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 $selected = null;
var sel_index = 0;
var has_optGroup = ($('optgroup', object).length > 0) ? true : false;
//gere les groupes d'options et prend en considération qu'il peut y avoir des options hors groupe
//(souvent le cas de la première qui sert de libellé)
if (true == has_optGroup) {
$options = $('option', object);
object.children().each(function() {
if ($(this).is('optgroup')) {
var optg_class = '';
$(this).each(function(){
tab_optg_class = Array();
if ($(this).attr('class')) {
tab_optg_class = $(this).attr('class').split(' ');
}
for (var i in tab_optg_class) {
optg_class += tab_optg_class[i] + ' ';
}
inner += '
';
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('
');
}
}
$($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 );