
/** @class skinSelect 
create a skinnable structure synchronized in both direction with the select
	
stucture injected neither in the body nor in just before the select (see options overflown) 
<div class="{classes}">
<a class="skinSelectBtn"><span>{defaultText}</span></a>
<ul>
<li><a href="#">option 1</a></li>
<li><a href="#">option 2</a></li>
<li><a href="#">option 3</a></li>
<li><a href="#">option 4</a></li>
</ul>
</div>
		
the structure pilot the <select> when selecting a link, and a change made on the select update the structure (to allow other scripts on the select)
the select is not destroy, just hide, and is still available for form submission.
	
the <div> inherit of all the className of the <select>
the first <a> 
ex:
<select class="skinSelect modSimple margin">
will generate
<div class="skinSelect modSimple margin">
<a class="btn skinSelect modSimple margin"><span>{defaultText}</span></a>
<ul class="skinSelect modSimple margin">...</ul>
</div>
	
@use
new SkinSelect(select, {
// options
})
		
@param
select: a <select> element, multiple or not, optgroups are not supported yet (soon i hope)
	
	
options:
overflow:
default "auto"
to true
		
	
	
*/



var SkinSelect = new Class({

    Implements: [Options, Events],

    options: {

        openOnTop: false, // false, ouvert en bas, true ouvert en haut, auto en haut si pas de place en bas
        limitedWidth: false, // false css, true js meme width que select, integer taille fixe
        maxHeight: false, // integer hauteur limite
        closeOnSelect: true, // integer hauteur limite

        onSelection: $empty(true),
        getDefaultLabel: function() {
            return this.select.getLabelContent();
        },
        useNullValue: $empty(true), // cette fonction est utiliser comme un filter
        onInstanciate: function() {
            this.optGroup.each(function(el, i) {
                this.ul.getElements("strong")[i].setStyle("background", el.getAttribute("color"));
                //				this.setTextColor(this.ul.getElements("strong")[i], el.getAttribute("color"));
            } .bind(this))
        }

    },

    setTextColor: function(elm, color) {
        var rgb = new Color(color);
        var medianColor = (0.3 * rgb[0]) + (0.59 * rgb[1]) + (0.11 * rgb[2]);
        elm.setStyle('color', (medianColor <= 128) ? "#fff" : "#000");
    },


    tpl: [
			'<div class="{classes}">',
				'<a href="#" class="skinSelectBtn btn"><span class="inBtn">{defaultText}</span></a>',
				'<ul class="list {classes}"></ul>',
			'</div>'
		].join(""),


    initialize: function(select, options) {
        this.setOptions(options);

        // store the instance into a class var
        SkinSelect.Instances.push(this);

        // get vars
        this.select = $(select);
        this.label = this.select.getLabel();
        this.select.store("skinSelect", this).aria("tabindex", "-1").addClass('hiddenSelect');
        this.focusIndex = -1;
        this.create();
        this.fireEvent("onInstanciate");
    },

    create: function() {

        this.opts = this.select.getElements("option").filter(this.options.useNullValue);


        this.isMultiple = this.select.multiple;
        this.defaultText = this.options.getDefaultLabel.apply(this);
        this.isOpen = false;
        this.onTop = this.select.hasClass("skinSelectTop") || this.options.openOnTop;
        // build and inject the structure
        this.struct = this.build();

        // set current value
        this.setValue();

        // prepare
        this.positionate();
        this.updateView();
        this.close();

        // events

        this.a.addEvent("click", function(e) {
            e.stop();
            this.toggle();
        } .bind(this));

        this.ul.addEvent('click', function(e) {
            e.stop();
            this.setValue(e.target);
            this.focusIndex = $(e.target).getAttribute("index")
        } .bind(this));

        this.struct.addEvent("keydown", function(e) {
            switch (e.key) {
                case "down":
                case "right":
                    e.stop();
                    if (!this.isOpen) this.open();
                    else this.focusOnNext();
                    break;
                case "up":
                case "left":
                    e.stop();
                    this.close();
                    break;
                case "enter":
                case "space":
                    e.stop();
                    this.toggle();
                    return false;
                    break;
                case "tab":
                case "esc":
                    if (this.isOpen) this.close();
                    break;
                default:
                    if (/[a-z0-9]/i.test(e.key)) {
                        this.open();
                        this.focusByKey(e.key);
                    }
                    break;

            }
        } .bind(this));

        this.ul.addEvent("keydown", function(e) {
            switch (e.key) {
                case "down":
                case "right":
                    e.stop();
                    this.focusOnNext();
                    break;
                case "up":
                case "left":
                    e.stop();
                    if ($(e.target).getParent("li").hasClass("first")) {
                        this.struct.focus();
                    }
                    else {
                        this.focusOnPrev();
                    }
                    break;
                case "space":
                case "enter":
                    e.stop();
                    this.setValue(e.target);
                    this.close();
                    break;
                case "tab":
                case "esc":
                    e.stop();
                    if (this.isOpen) this.close();
                    // this.struct.focus();
                default:
                    if (/[a-z0-9]/i.test(e.key)) this.focusByKey(e.key);
                    break;

            }
        } .bind(this));



        /*
        this.struct.addEvent("keydown", function (e){
        switch(e.key){
        case "down":
        case "right":
        e.stop();
        if(this.isOpen){
        this.focusOnNext();
        }
        else {
        this.open();
        // TODO setValue sur focus suivant, iso <select>
        }
					
					break;
        case "up":
        case "left":
        this.close();
        break;
        case "enter": 
        case "space": 
        e.stop();
        this.toggle();
        return false;
        break;
        case "tab":
        case "esc":
        if(this.isOpen) this.close();
        default:
        if(/[a-z0-9]/.test(e.key)) {
        this.focusByKey(e.key);
        }
        break;
		
			}
        }.bind(this));
		
		this.ul.addEvent("keydown", function (e){
        switch(e.key){
        case "down":
        case "right":
        e.stop();
        this.focusOnNext();
        break;
        case "up":
        case "left":
        e.stop();
        if($(e.target).getParent("li").hasClass("first")){
        this.struct.focus();
        this.focusIndex = -1;
        }
        else {
        this.focusOnPrev();
        }
        break;
        case "space": 
        case "enter": 
        e.stop();
        this.setValue(e.target);
        break;
        case "tab":
        case "esc":	
        if(this.isOpen) this.close();
        default:
        if(/[a-z0-9]/.test(e.key)) this.focusByKey(e.key);
        break;
		
			}
        }.bind(this));*/

        this.ul.addEvent("focus", function(e) {
        } .bind(this));

        this.select.addEvent("change", function(e, fromSkinSelect) {
            //console.info('add change', fromSkinSelect);
            if (!fromSkinSelect) this.setValue();
            else {
                this.close()
            }
        } .bind(this));

        this.select.addEvent("focus", function(fromSkinSelect) {
            if (!fromSkinSelect) this.open();
        } .bind(this));

        this.select.addEvent("blur", function(fromSkinSelect) {
            if (!fromSkinSelect) this.close();
        } .bind(this));

        if (this.label) {
            this.label.addEvent("click", function(e) {
                e.stop();
                this.open();
            } .bind(this));
        }

        this.bodyFn = function() {
            this.close();
        } .bind(this);

        /*
        Notifier.addEvent("onLayoutChanged", function (){
        this.positionate();
        }.bind(this));
        */

    },

    textToNode: function(txt) {
        var div = new Element('div', { "html": txt });
        return div.getFirst();
    },

    build: function() {

        var struct = this.textToNode(this.tpl.substitute({
            classes: this.select.className,
            defaultText: this.defaultText
        }));
        struct.aria("tabindex", "0");

        this.ul = struct.getElement('ul');
        this.a = struct.getElement('a');
        this.a.aria("tabindex", -1);
        this.btnText = struct.getElement('span');
        this.as = [];
        this.lis = [];
        this.optGroup = this.select.getElements("optgroup");
        $(document.body).adopt(this.ul);
        struct.inject(this.select, "after"); // after pour faciliter la gestion du focus

        if (this.optGroup.length > 0) {
            var j = 0;
            this.optGroup.each(function(group, index) {
                var li = new Element("li");
                var ul = new Element("ul", { "class": "optGroup" });
                var opts = group.getElements("option").filter(this.options.useNullValue);
                var strong = new Element("strong", { "text": group.getAttribute('label'), "class": "label" });
                li.adopt(strong);
                li.adopt(ul);
                this.ul.adopt(li);
                for (var i = 0, l = opts.length; i < l; i++) {
                    var opt = opts[i];
                    var txt = opt.innerHTML;
                    var val = opt.value || txt;
                    var li = new Element("li", { 'val': val });
                    var a = new Element("a", { "href": "#", "index": j, "html": txt });
                    this.lis.push(li);
                    this.as.push(a);
                    li.adopt(a);
                    a.aria("tabindex", -1);
                    ul.adopt(li);
                    j++;
                }

            } .bind(this))

        }
        else {
            for (var i = 0, l = this.opts.length; i < l; i++) {
                var opt = this.opts[i];
                var txt = opt.innerHTML;
                var val = opt.value || txt;
                var li = new Element("li", { 'val': val, 'class': 'listItem' });
                var a = new Element("a", { "href": "#", "index": i, "html": txt });
                this.lis.push(li);
                this.as.push(a);
                li.adopt(a);
                a.aria("tabindex", -1);
                if (i == 0) li.addClass("first");
                if (i == this.opts.length - 1) li.addClass("last");
                this.ul.adopt(li);
            }
        }

        return struct;
    },

    destroy: function() {
        this.struct.dispose();
        this.ul.dispose();
    },

    refresh: function() {
        this.destroy();
        this.create();
    },

    positionate: function() {
        var c = this.select.getCoordinates();
        //		this.struct.setStyle('position', "absolute");
        //		this.struct.setStyle('top',  this.select.offsetTop);
        //		this.struct.setStyle('left', this.select.offsetLeft );
        this.a.setStyle('width', c.width - this.a.getHStyle());
    },

    positionateList: function() {
        var cStruct = this.struct.getCoordinates();
        var cUl = this.ul.getCoordinates();
        this.onTop ? this.ul.setStyle('top', cStruct.top - cUl.height) : this.ul.setStyle('top', cStruct.top + cStruct.height);
        this.ul.hasClass("skinSelectRight") ? this.ul.setStyle('left', cStruct.left - (cUl.width - cStruct.width)) : this.ul.setStyle('left', cStruct.left);
        this.ul.hasClass("skinSelectRight") ? this.ul.setStyle('left', cStruct.left - (cUl.width - cStruct.width)) : this.ul.setStyle('left', cStruct.left);
        this.ul.setStyle('width', this.select.offsetWidth);


        if (this.options.maxHeight && this.ul.getStyle("height").toInt() > this.options.maxHeight) this.ul.setStyle("height", this.options.maxHeight).setStyle("overflow", "auto")
    },

    toggle: function() {
        !this.isOpen ? this.open() : this.close();
    },

    open: function() {
        this.closeAll();

        this.isOpen = true;
        this.focusIndex = -1;

        this.struct.addClass("skinSelectOpen");
        this.setLabel(this.defaultText);

        this.ul.removeClass('hidden').addClass("skinSelectOpen");
        this.positionateList();

        $(document.body).addEvent("click", this.bodyFn);

        this.struct.focus();
    },

    close: function(bAll) {
        this.ul.addClass('hidden');
        this.struct.removeClass("skinSelectOpen");
        this.ul.removeClass("skinSelectOpen");
        if (!bAll) {
            //this.struct.focus();
            this.setLabel();
            $(document.body).removeEvent("click", this.bodyFn);
        }
        this.isOpen = false;

    },


    // close all the open select, that means just one select could be open 
    closeAll: function() {
        SkinSelect.Instances.each(function(o) {
            o.close(true);
        });
    },


    setValue: function(elm) {
        //        console.info('setValue', elm);
        if (elm) {
            var index = $(elm).getAttribute("index");
            if (!index) return;
            this.opts[index].selected = this.opts[index].selected ? false : true;
            var e = new Event(this.select);
            this.select.fireEvent('change', [e, true]);
            this.fireEvent('onSelection', this.opts[index]);
        }
        else {
            this.setLabel(this.defaultText);
            if (this.options.closeOnSelect) this.close();
            this.fireEvent('onSelection', null);
        }
        this.updateView();
    },

    setLabel: function(str) {
        if (this.isMultiple) str = this.defaultText;
        else if (!str && this.select.selectedIndex != -1) {
            var cur = this.lis.filter(function(a, b) {
                return a.getAttribute("val") == this.select.value;
            } .bind(this));
            // ie need that way
            try { str = $(cur[0]).getElement("a").get("text"); }
            catch (e) { }
        }
        if (!str) {
            str = this.defaultText
        }

        if (!!str) this.btnText.set("text", str);
    },

    updateView: function() {
        this.opts.each(function(el, i) {
            if (el.selected) this.lis[i].addClass("selected");
            else if (this.lis[i]) this.lis[i].removeClass("selected");
        } .bind(this))
    },

    focusOnNext: function() {
        this.focusIndex++;
        if (this.focusIndex <= this.opts.length - 1) this.as[this.focusIndex].focus();
        else this.focusIndex = this.opts.length - 1;
    },

    focusOnPrev: function() {
        this.focusIndex--;
        if (this.focusIndex >= 0) this.as[this.focusIndex].focus();
        else this.focusIndex = 0;
    },

    focusByKey: function(key) {
        for (var i = 0; i < this.opts.length; i++) {
            var cur = i + this.focusIndex.toInt();
            if (cur >= this.opts.length) { cur -= this.opts.length; }
            if (i != 0 && this.opts[cur].innerHTML[0].toLowerCase() == key) {
                this.as[cur].focus();
                this.focusIndex = cur;
                break;
            }
        }
    }
});









SkinSelect.Instances = [];






$(document).addEvent('domready', function() {
    var aElem = $$('select.skinSelect');

    aElem.each(function(o) {
        new SkinSelect(o);
    });
});



/*

Module.register("SkinSelect", "select.skinSelect", {
closeOnSelect: false,
useNullValue: function (option){ 
return option.value != "";
},
onSelection: function (option){
if(!this.isMultiple && option) {
this.close();
//this.setLabel(option.innerHTML)
}
else {
		
}
}
,
onInstanciate: function (){
this.optGroup.each(function (el, i){
var ul = this.ul;
var strong = $(ul.getElements("strong")[i]);
strong.setStyle("background", el.getAttribute("color"))
this.setTextColor(this.ul.getElements("strong")[i], el.getAttribute("color"));
if(this.isMultiple){
strong.setStyle("cursor", "pointer")
strong.addEvent("click", function (e){
var selecteds = $(strong).getParent("li").getElements('li.selected').length == 0;
var alls = $(strong).getParent("li").getElements('li.selected').length == $(strong).getParent("li").getElements('li').length;
if(selecteds){
$(el).getElements('option').each(function (opt){
opt.selected = true;
});
}
else if (alls) {
$(el).getElements('option').each(function (opt){
opt.selected = false;
});
}
else {
$(el).getElements('option').each(function (opt){
opt.selected = true;
});
}
					
this.select.fireEvent("change");
					
					
}.bind(this));
}
}.bind(this))
},
	
maxHeight: 350

});

Module.register("SkinSelect", "select.skinSelect2", {
closeOnSelect: false,
useNullValue: function (option){ 
return option.value != "" && option.value != 0 ;
},
onSelection: function (option){
if(!this.isMultiple && option) {
this.close();
//this.setLabel(option.innerHTML)
}
else {
		
}
},
onInstanciate: function (){
this.optGroup.each(function (el, i){
var ul = this.ul;
var strong = $(ul.getElements("strong")[i]);
strong.setStyle("background", el.getAttribute("color"))
this.setTextColor(this.ul.getElements("strong")[i], el.getAttribute("color"));
if(this.isMultiple){
strong.addEvent("click", function (e){
$(this).getElements('option').each(function (opt){
opt.selected = !opt.selected;
});
}.bind(el));
}
}.bind(this))
},
	
maxHeight: 350
	

});		
*/

