/* form styled */
var _$ = jQuery;

_JF = {
    addSelect  : function(form, options) {
        _$('select.jforms', form).each(function(i, el) { new _JF.select(el, options); });
        _$('html').bind('click', function() {
            _$('div.jfselect').each(function() { _$(this).removeClass('opened'); });
        });
    },

    init        : function(form, options) {
        this.addSelect(form, options);
    }
};


        /* Select extension plugin */

        _JF.select = function(el, options) {

            this.nodes = {
                'element'       : undefined, /* select */
                'fragment'      : undefined, /* replacing fragment */
                'caption'       : undefined, /* element with actual value */
                'value'         : undefined,
                'list'          : undefined,
                'links'         : undefined,
                'listitems'     : undefined,
                'scroller'	    : undefined,
		        'scrollerup' 	: undefined,
		        'scrollerunder' : undefined
            };


            this.currentListValue   = null;
            this.stepsArray         = [ ];
            this.visibleOptions     = 7;
	        this.liHeight           = 0;
	        this.scrollerHeight     = 0;
	        this.scrollerMaxHeight  = 0;
            this.visibleOptionWindow = {
                'first'	    : 0,
                'last'	    : 0
            };

            var _createDOMFragment = function(_s) {

                /* element replacing _s.nodes.fragment */
                _s.nodes.fragment = _$('<div><a class="selectvalue"><span></span></a><div class="selectlist"><ul></ul></div></div>');
                _s.nodes.fragment
                    .attr({
                        'id'    : [(_s.nodes.element.id || _s.nodes.element.name), 'jforms'].join('_'),
                        'class' : 'jforms jfselect'
                    })
                    .bind('click', function() {
                            (_s.nodes.value).get(0).focus();
                            return (!!_$(this).hasClass('disabled'))? false : _toggleSelectVisibility(this, _$(this).hasClass('opened'), _s);
                    })
                    .find('a').attr('href', '#').end()
                    .insertAfter(_s.nodes.element);

                _s.nodes.caption    = (_s.nodes.fragment).find('span');
                _s.nodes.value      = (_s.nodes.fragment).find('a');
                _s.nodes.list       = (_s.nodes.fragment).find('ul');

                /* creates values list */
                _$('option', _s.nodes.element).each(function(i, option) {

                    var option = _$(option);
                    _$('<li><a></a></li>').find('a').attr({'rel' : i, 'href' : '#'})

                    /* Add caption */
                        .html(option.html())
                    /* Add click and mouseenter event */
                        .bind('click',      function(e) {
                            var previndex = _s.nodes.element.selectedIndex;

                            /* we trigger a 'changed' event if the value has changed from the previous one */
                            var triggerfunction = !(previndex === parseInt(_$(this).attr('rel'), 10));

                            _addCurrentListValue(this, _s);
                            if (triggerfunction) { _$(_s.nodes.element).trigger('changed'); };

                            _s.nodes.fragment.removeClass('opened');
                            return false;
                        })


                        .bind('mouseenter', function() { return _remCurrentListValue(this, _s); })

                    /* li element */
                    .parent().attr({
                        'id'    : option.attr('id'),
                        'class' : option.attr('class')
                    })
                    .toggleClass('currentlistvalue', i == _s.nodes.element.selectedIndex)
                    .appendTo(_s.nodes.list);
                    option.removeAttr('id');
                });

                /* collect list-items and links */
                    _s.nodes.links      = (_s.nodes.list).find('li a');
                    _s.nodes.listitems  = (_s.nodes.list).find('li');

                /* set current value */
                    _s.currentListValue  = _$('.currentlistvalue', _s.nodes.list);
                    _s.nodes.caption.html((_s.currentListValue).find('a').html());
                    if (_s.currentListValue.attr('id')) {
                        _s.nodes.caption.parent().attr('id', "rel_" + _s.currentListValue.attr('id'));
                    };
                /* hide the real element */
                    _$(_s.nodes.element).hide();


                /* Key navigation (blur and focus) */
                    (_s.nodes.value).bind("blur", function() {
                        //(_s.nodes.caption).removeClass('noellipsis');
                        (_s.nodes.fragment).unbind("keypress");
                        (_s.nodes.fragment).unbind("keydown");
                        /*console.log('select blur');*/
                    });

                    (_s.nodes.value).bind("focus", function() {
                        //(_s.nodes.caption).addClass('noellipsis');
                        if (_$.browser.mozilla)
                            (_s.nodes.fragment).keypress(function(e) { _setArrowsSelection(e.keyCode, _s); return false; })
                        else
                            (_s.nodes.fragment).keydown(function(e) { _setArrowsSelection(e.keyCode, _s);  return false; });
                    });

		    /*! Mousewheel bind */
		    _$(_s.nodes.fragment).bind('mousewheel', function(event, delta) {
			var si = _s.nodes.element.selectedIndex;
			if (delta > 0) { /* up */
			        if (si == 0 || !_s.nodes.fragment.hasClass('opened')) return;
			        _addCurrentListValue(_s.nodes.links[si - 1], _s, 'up');
			}
			else {
			        if (si == (_s.nodes.listitems.length - 1) || !_s.nodes.fragment.hasClass('opened')) return;
				_addCurrentListValue(_s.nodes.links[si + 1], _s, 'down');
			};
			return false;
		    });
            };
            /* end of _createDOMFragment() method */



	/* utils */
	var _listPosition = function(_s, index) {
		_$(_s.nodes.list).css({ 'top': -index * _s.liHeight });
	};

	var _scrollPosition = function(_s, index) {
		_$('.selectscroller span', _s.nodes.fragment).css({
			'top'	: _s.stepsArray[index]['offset']
		});
		//console.log('_scrollPosition : ' + index);
	};

	var _setOptionsWindow = function(_s, i) {
        _s.visibleOptionWindow['first'] = i;
        _s.visibleOptionWindow['last']  = i - 1 + _s.visibleOptions;
		//console.log('_setOptionsWindow : ' + _s.visibleOptionWindow['first'] + ':'+ _s.visibleOptionWindow['last']);
	};

	var _getOptionsWindow = function(_s) {
		return _s.visibleOptionWindow['first'];
	};

	var _addOptionsWindow = function(_s, inc) {

		if (_s.visibleOptionWindow['first'] + inc < 0) return;
		if (_s.visibleOptionWindow['last'] + inc > _s.nodes.listitems.length) return;
        _s.visibleOptionWindow['first'] += inc;
        _s.visibleOptionWindow['last']  += inc;
		//console.log('_addOptionsWindow : ' + _s.visibleOptionWindow['first']);
		return _s.visibleOptionWindow['first'];
	};

	var _isInsideWindow = function(_s, i) {
		return (i >= _s.visibleOptionWindow['first'] && i <= _s.visibleOptionWindow['last']);
	}

	var _doScroll = function(_s, i) {
		var fitem = _addOptionsWindow(_s, i);
		_scrollPosition(_s, fitem);
		_listPosition(_s, fitem);
		_setScrollerLinks(_s);
		_s.nodes.value.focus();
	};

	var _setScrollerLinks = function(_s) {
		var ot = (_s.nodes.scroller).get(0).offsetTop;
		_$(_s.nodes.scrollerup).css({ 'height'	: ot });
		_$(_s.nodes.scrollerunder).css({ 'height'  : _s.scrollerMaxHeight - _s.scrollerHeight - ot });
	};
	/* utils */


	var _createScrollers = function(fragment, _s) {

                var listcnt = _s.nodes.list.parent();
                listcnt.addClass('scrollable');
                _$('<div class="jfscrollers"></div>').insertBefore(_s.nodes.list)
                    .append('<a class="up" href="#">Up</a>')
                    .append('<div class="selectscroller"><a class="scrollup" href="#">over</a><span>Scroller</span><a class="scrollunder" href="#">under</a></div>')
                    .append('<a class="down" href="#">Down</a>');

                /* force li width */
                var ulWidth = _$(_s.nodes.list).width();
                _$('li', _s.nodes.list).css({ 'width'     : ulWidth });

                _s.liHeight = _$(_s.nodes.list).find('li').height();
                var maxHeight = _s.liHeight * _s.visibleOptions;


                /* we set the height of .selectlist element due to a IE6 bug */
                var listheight = (_$(_s.nodes.list).height()  >  maxHeight)?  maxHeight: _$(_s.nodes.list).height();
                _$('.selectlist', fragment).css({
                    'height'    : listheight + 'px' ,
                    'zoom'      : 1
                });

                if (!!_$.browser.opera)   _$('.selectlist', fragment).css({ 'position' : 'relative' }); /* Opera hack */

                /* calculating the initial offset top of the list */

                var scrollableOptions = _s.nodes.listitems.length - _s.visibleOptions;
                var firstItemVisible = (_s.nodes.element.selectedIndex > (scrollableOptions))
                    ? (scrollableOptions) : _s.nodes.element.selectedIndex;

		        _setOptionsWindow(_s, firstItemVisible);

                var initialTop = -(firstItemVisible * _s.liHeight);
                _$(_s.nodes.list).css({ 'top' : initialTop + 'px' });

		        //console.log('w: (createscrollers) '+ _s.visibleOptionWindow['first'] + ':' + _s.visibleOptionWindow['last']);

                _s.scrollerMaxHeight = listheight - (_$('.jfscrollers a', fragment).height() * 2);
                _$('.selectscroller', fragment).css({ 'height': _s.scrollerMaxHeight + 'px' });
                var scrollerHeight = _s.visibleOptions * _s.scrollerMaxHeight / _s.nodes.listitems.length;
		        _s.scrollerHeight = Math.round(scrollerHeight);

                /* Pixel amount of scroller step */
                var availY = _s.scrollerMaxHeight - _s.scrollerHeight;
                var stepsY = availY / (scrollableOptions);
                    stepsY = ((availY - (Math.ceil(stepsY) * (scrollableOptions-1))) >= 1)? Math.ceil(stepsY) : Math.floor(stepsY);


                /* Creating the scroller Array: stepAmount contains the grid available for scroller position */
                var sopt = 0;
                var laststep = availY - (stepsY * (scrollableOptions-1));

                while (sopt <= scrollableOptions) {
                    _s.stepsArray[sopt] = { };
                    _s.stepsArray[sopt]['step'] = sopt;
                    _s.stepsArray[sopt]['offset'] = (sopt === 0)? 0 : _s.stepsArray[sopt - 1]['offset'] +
                                  ((sopt === scrollableOptions)? laststep : stepsY);
                    sopt++;
                };


                var scrollerTopPos = (_s.nodes.element.selectedIndex >= (scrollableOptions))
                    ? _s.stepsArray[scrollableOptions]['offset'] : _s.stepsArray[_s.nodes.element.selectedIndex]['offset'];

                _s.nodes.scroller = _$('.selectscroller span', fragment);
                _s.nodes.scrollerup = _$('.selectscroller a.scrollup', fragment);
                _s.nodes.scrollerunder = _$('.selectscroller a.scrollunder', fragment);
                _s.nodes.scroller.css({
                    'height' : _s.scrollerHeight + 'px' ,
                    'top'    : scrollerTopPos + 'px'
                }).bind('click', function() { return false; });


                /* draggable scrollbar*/
                _$('.selectscroller span', _s.nodes.fragment).draggable({
                    axis                : 'y',
                    containment         : 'parent',
                    drag                : function(event, ui) {

                        (function(s, array) {
                            var _alen = array.length, a = 0, b = 0;
                            if (_alen === 1) {
                                _listPosition(_s, array[0]['step']);
                                _setOptionsWindow(_s, array[0]['step']);
                                _setScrollerLinks(_s);
                                return;
                            };

                            var j = Math.ceil(_alen / 2);
                            if (array[j]['offset'] > s)  { a = 0; b = j; }
                            if (array[j]['offset'] < s)  { a = j; b = _alen; }
                            if (array[j]['offset'] == s) {
                                _listPosition(_s, array[j]['step']);
                                _setOptionsWindow(_s, array[j]['step']);
                                _setScrollerLinks(_s);
                                return;
                            };

                            arguments.callee(s, array.slice(a, b));
                        })(ui.position.top, _s.stepsArray);
                    }
                });


                /* up and down links */

                _$('.up', fragment).bind('click', function() {
                     if (_getOptionsWindow(_s) > 0) _doScroll(_s, -1);
                     return false;
                });

                _$('.scrollup', fragment).bind('click', function() {
                     if ((gow = _getOptionsWindow(_s)) > 0) {
                        var amount = (gow < _s.visibleOptions)? gow : _s.visibleOptions;
                        _doScroll(_s, -amount);
                     };
                     return false;
                });

                _$('.down', fragment).bind('click', function() {
                     if (_getOptionsWindow(_s) < (_s.nodes.listitems.length - _s.visibleOptions)) _doScroll(_s, 1);
                     return false;
                });

                _$('.scrollunder', fragment).bind('click', function() {
                     if ((gow = _getOptionsWindow(_s)) < (_s.nodes.listitems.length - _s.visibleOptions)) {
                        var amount = ((gow + _s.visibleOptions) <= _s.stepsArray.length)? (_s.visibleOptions) : (_s.stepsArray.length - gow - 1);
                        _doScroll(_s, amount);
                     };
                     return false;
                });

		 _setScrollerLinks(_s);

            };



            /* other private methods */
            var _toggleSelectVisibility = function(fragment, opened, _s) {

                _$('html').trigger('click');
                _$(fragment).toggleClass('opened', !opened);

                if (_$(fragment).hasClass('opened')) {
                    if (_s.nodes.listitems.length  >  _s.visibleOptions) {
                        if (_$('.jfscrollers', fragment).length === 0) {
                            _createScrollers(fragment, _s);
                        }
                        else {
                             var scrollableOptions = _s.nodes.listitems.length - _s.visibleOptions;
                             var firstItemVisible = (_s.nodes.element.selectedIndex > (scrollableOptions))
                            ? (scrollableOptions) : _s.nodes.element.selectedIndex;

                            _setOptionsWindow(_s, firstItemVisible);
                            _listPosition(_s, firstItemVisible);
                            _scrollPosition(_s, firstItemVisible);
                            _setScrollerLinks(_s);
                        }
                    }
                    else {
                        _s.nodes.list.css({ 'width' : _$('.selectlist', fragment).width() });
                    };

		};
                return false;
            };






            var _remCurrentListValue = function(selectedItem, _s) {
                _$('li', _s.nodes.list).each(function(i, li) { _$(li).removeClass('currentlistvalue'); });
                _$(selectedItem).parent().addClass('currentlistvalue');
            };




            var _addCurrentListValue = function(selectedItem, _s, direction) {

                var d = direction || '';
                jQuery.each(jQuery.browser, function(i, val) {
                      if (i=="mozilla" && jQuery.browser.version.substr(0,3)=="1.9") {
                         /* for FF2-3.0.x . Due to the xml binding for ellipsis truncation */
                         _s.nodes.caption.remove();
                         _s.nodes.caption = _$('<span></span>').html(_$(selectedItem).html()).appendTo(_s.nodes.value);
                      }
                      else {
                        _s.nodes.caption.html(_$(selectedItem).html());
                      }
                });


                if (_$(selectedItem).parent().attr('id')) {
                    _s.nodes.caption.parent().attr('id', "rel_" + _$(selectedItem).parent().attr('id'));
                };

		        var prevsi = _s.nodes.element.selectedIndex;
                _s.nodes.element.selectedIndex = parseInt(_$(selectedItem).attr('rel'), 10);
		        _$('li.currentlistvalue', _s.nodes.fragment).removeClass();
                _s.currentListValue  = _$(selectedItem).parent();
		        _s.currentListValue.addClass('currentlistvalue');

                var si = _s.nodes.element.selectedIndex;

                /* update list and scroller position */
                if (_$('.jfscrollers', _s.nodes.fragment).length > 0) {
                    if (!_isInsideWindow(_s, si)) {
			            if (_isInsideWindow(_s, prevsi)) {
                            var inc = (d === 'up')? -1 : 1;
                            _addOptionsWindow(_s, inc);
                            _listPosition(_s, _getOptionsWindow(_s));
                        }
                    };
                    _scrollPosition(_s, _getOptionsWindow(_s));
                    _setScrollerLinks(_s);
                };
            };




            /* add keyboards navigation capability */
            var _setArrowsSelection = function(code, _s) {

                var selectedIndex = parseInt(_s.nodes.element.selectedIndex, 10);

                switch (code.toString()) {

                    case "38":
                        if (selectedIndex == 0 || !_s.nodes.fragment.hasClass('opened')) return;
                        _addCurrentListValue(_s.nodes.links[selectedIndex - 1], _s, 'up');
                        break;

                    case "40":
                        if (selectedIndex == (_s.nodes.listitems.length - 1) || !_s.nodes.fragment.hasClass('opened')) return;
                        _addCurrentListValue(_s.nodes.links[selectedIndex + 1], _s, 'down');
                        break;

                    case "10":
                    case "13":
                        if (_s.nodes.fragment.hasClass('opened')) {
                            _$(_s.nodes.links[selectedIndex]).trigger({ type: 'click', triggered : false });
                        }
                        else {
                            _toggleSelectVisibility(_s.nodes.fragment, _s.nodes.fragment.hasClass('opened'), _s);
                        };
                        break;
                };
                return false;
            };




            /* public constructor */

            return {
                init : (function(_s) {
                    _s.nodes.element = el;
                    _createDOMFragment(_s);
                })(this)
            }
        };
        /* end select */

/* end styled form */



/*! Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
 *
 * Version: 3.0.2
 *
 * Requires: 1.2.2+
 */

(function(_$) {

var types = ['DOMMouseScroll', 'mousewheel'];

_$.event.special.mousewheel = {
	setup: function() {
		if ( this.addEventListener )
			for ( var i=types.length; i; )
				this.addEventListener( types[--i], handler, false );
		else
			this.onmousewheel = handler;
	},

	teardown: function() {
		if ( this.removeEventListener )
			for ( var i=types.length; i; )
				this.removeEventListener( types[--i], handler, false );
		else
			this.onmousewheel = null;
	}
};

_$.fn.extend({
	mousewheel: function(fn) {
		return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
	},

	unmousewheel: function(fn) {
		return this.unbind("mousewheel", fn);
	}
});


function handler(event) {
	var args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true;

	event = _$.event.fix(event || window.event);
	event.type = "mousewheel";

	if ( event.wheelDelta ) delta = event.wheelDelta/120;
	if ( event.detail     ) delta = -event.detail/3;

	// Add events and delta to the front of the arguments
	args.unshift(event, delta);

	return _$.event.handle.apply(this, args);
}

})(jQuery);