/*
 * slider class
 * 
var slider = new Class({

	initialize: function(params){
		this.items = params.items;
		this.mode = params.mode || 'horizontal';
		this.modes = {horizontal:['left','width','height'], vertical:['top','height']};
		this.size = params.size || 240;
		this.box = params.box.setStyle(this.modes[this.mode][1],(this.size*this.items.length)+'px');
		this.button_event = params.button_event || 'click';
		this.handle_event = params.handle_event || 'click';
		this.onWalk = params.onWalk || null;
		this.currentIndex = null;
		this.previousIndex = null;
		this.nextIndex = null;
		this.interval = params.interval || 5000;
		this.autoPlay = params.autoPlay || false;
		this.autoHeight = params.autoHeight || false;
		this._play = null;
		this.handles = params.handles || null;
		if(this.handles){
			this.addHandleButtons(this.handles);
		}
		this.buttons = {
			previous: [],
			next: [],
			play: [],
			playback: [],
			stop: []
		};
		if(params.addButtons){
			for(var action in params.addButtons){
				this.addActionButtons(action, $type(params.addButtons[action])=='array' ? params.addButtons[action] : [params.addButtons[action]]);
			}
		}
		this.fx = new Fx.Tween(this.box,$extend((params.fxOptions||{duration:500,wait:false}),{property:this.modes[this.mode][0]}));
		// autoHeight mask
		if (this.modes[this.mode][2] == undefined) {
			this.autoHeight = false; // not capable in mode
		}
		if (this.autoHeight) {
			this.mask = this.box.getParent(); // resize object
			this.sub_boxes = this.box.getChildren(); // offsetHeight object
			this.heightFx = new Fx.Tween(this.mask, $extend((params.fxOptions||{duration:500, wait:false}), {property:this.modes[this.mode][2]}));
		}
		this.walk((params.startItem||0),true,true);
	},

	addHandleButtons: function(handles){
		for(var i=0;i<handles.length;i++){
			handles[i].addEvent(this.handle_event,this.walk.bind(this,[i,true]));
		}
	},

	addActionButtons: function(action,buttons){
		for(var i=0; i<buttons.length; i++){
			switch(action){
				case 'previous': buttons[i].addEvent(this.button_event,this.previous.bind(this,[true])); break;
				case 'next': buttons[i].addEvent(this.button_event,this.next.bind(this,[true])); break;
				case 'play': buttons[i].addEvent(this.button_event,this.play.bind(this,[this.interval,'next',false])); break;
				case 'playback': buttons[i].addEvent(this.button_event,this.play.bind(this,[this.interval,'previous',false])); break;
				case 'stop': buttons[i].addEvent(this.button_event,this.stop.bind(this)); break;
			}
			this.buttons[action].push(buttons[i]);
		}
	},

	previous: function(manual){
		this.walk((this.currentIndex>0 ? this.currentIndex-1 : this.items.length-1),manual);
	},

	next: function(manual){
		this.walk((this.currentIndex<this.items.length-1 ? this.currentIndex+1 : 0),manual);
	},

	play: function(interval,direction,wait){
		this.stop();
		if(!wait){
			this[direction](false);
		}
		this._play = this[direction].periodical(interval,this,[false]);
	},

	stop: function(){
		$clear(this._play);
	},

	walk: function(item,manual,noFx){
		if(item!=this.currentIndex){
			this.currentIndex=item;
			this.previousIndex = this.currentIndex + (this.currentIndex>0 ? -1 : this.items.length-1);
			this.nextIndex = this.currentIndex + (this.currentIndex<this.items.length-1 ? 1 : 1-this.items.length);
			if(manual){
				this.stop();
			}
			if(noFx){
				this.fx.cancel().set((this.size*-this.currentIndex)+'px');
				if (this.autoHeight) {
					this.heightFx.cancel().set((this.sub_boxes[this.currentIndex].offsetHeight)+'px');
				}
			}else{
				this.fx.start(this.size*-this.currentIndex);
				if (this.autoHeight) {
					this.heightFx.start((this.sub_boxes[this.currentIndex].offsetHeight)+'px');
				}
			}
			if(manual && this.autoPlay){
				this.play(this.interval,'next',true);
			}
			if(this.onWalk){
				this.onWalk((this.items[this.currentIndex] || null), (this.handles && this.handles[this.currentIndex] ? this.handles[this.currentIndex] : null));
			}
		}
	}
	
});
*/

/*
TableSort revisited v4.9 by frequency-decoder.com
*/

(function() {
fdTableSort = {
    regExp_Currency:        /^[Ã‚Â£$Ã¢â€šÂ¬Ã‚Â¥Ã‚Â¤]/,
    regExp_Number:          /^(\-)?[0-9]+(\.[0-9]*)?$/,
    pos:                    -1,
    uniqueHash:             1,
    thNode:                 null,
    tableId:                null,
    tableCache:             {},
    tmpCache:               {},
    sortActiveClass:        "sort-active",
    /*@cc_on
    /*@if (@_win32)
    colspan:                "colSpan",
    rowspan:                "rowSpan",
    @else @*/
    colspan:                "colspan",
    rowspan:                "rowspan",
    /*@end
    @*/
    addEvent: function(obj, type, fn, tmp) {
            tmp || (tmp = true);
            if( obj.attachEvent ) {
                    obj["e"+type+fn] = fn;
                    obj[type+fn] = function(){obj["e"+type+fn]( window.event );};
                    obj.attachEvent( "on"+type, obj[type+fn] );
            } else {
                    obj.addEventListener( type, fn, true );
            };
    },
    removeEvent: function(obj, type, fn, tmp) {
            tmp || (tmp = true);
            try {
                    if( obj.detachEvent ) {
                            obj.detachEvent( "on"+type, obj[type+fn] );
                            obj[type+fn] = null;
                    } else {
                            obj.removeEventListener( type, fn, true );
                    };
            } catch(err) {};
    },
    stopEvent: function(e) {
            e = e || window.event;

            if(e.stopPropagation) {
                    e.stopPropagation();
                    e.preventDefault();
            };
            /*@cc_on@*/
            /*@if(@_win32)
            e.cancelBubble = true;
            e.returnValue  = false;
            /*@end@*/
            return false;
    },
    parseClassName: function(head, tbl) {
            var colMatch = tbl.className.match(new RegExp(head + "((-[\\d]+([r]){0,1})+)"));
            return colMatch && colMatch.length ? colMatch[0].replace(head, "").split("-") : [];
    },
    disableSelection: function(element) {
            element.onselectstart = function() {
                    return false;
            };
            element.unselectable = "on";
            element.style.MozUserSelect = "none";
    },
    removeTableCache: function(tableId) {
            if(!(tableId in fdTableSort.tableCache)) return;

            fdTableSort.tableCache[tableId] = null;
            delete fdTableSort.tableCache[tableId];

            var tbl = document.getElementById(tableId);
            if(!tbl) return;
            var ths = tbl.getElementsByTagName("th");
            var a;
            for(var i = 0, th; th = ths[i]; i++) {
                    a = th.getElementsByTagName("a");
                    if(a.length) a[0].onkeydown = a[0].onclick = null;
                    th.onclick = th.onselectstart = th = a = null;
            };
    },
    removeTmpCache: function(tableId) {
            if(!(tableId in fdTableSort.tmpCache)) return;
            var headers = fdTableSort.tmpCache[tableId].headers;
            var a;
            for(var i = 0, row; row = headers[i]; i++) {
                    for(var j = 0, th; th = row[j]; j++) {
                            a = th.getElementsByTagName("a");
                            if(a.length) a[0].onkeydown = a[0].onclick = null;
                            th.onclick = th.onselectstart = th = a = null;
                    };
            };
            fdTableSort.tmpCache[tableId] = null;
            delete fdTableSort.tmpCache[tableId];
    },
    initEvt: function(e) {
            fdTableSort.init(false);
    },
    init: function(tableId) {
            if (!document.getElementsByTagName || !document.createElement || !document.getElementById) return;

            var tables = tableId && document.getElementById(tableId) ? [document.getElementById(tableId)] : document.getElementsByTagName("table");
            var c, ii, len, colMatch, showOnly, match, showArrow, columnNumSortObj, obj, workArr, headers, thtext, aclone, multi, colCnt, cel, allRowArr, rowArr, sortableTable, celCount, colspan, rowspan, rowLength;

            var a          = document.createElement("a");
            a.href         = "#";
            a.className    = "fdTableSortTrigger";

            var span       = document.createElement("span");

            for(var k = 0, tbl; tbl = tables[k]; k++) {

                    if(tbl.id) {
                            fdTableSort.removeTableCache(tbl.id);
                            fdTableSort.removeTmpCache(tbl.id);
                    };

                    allRowArr     = tbl.getElementsByTagName('thead').length ? tbl.getElementsByTagName('thead')[0].getElementsByTagName('tr') : tbl.getElementsByTagName('tr');
                    rowArr        = [];
                    sortableTable = false;

                    for(var i = 0, tr; tr = allRowArr[i]; i++) {
                            if(tr.getElementsByTagName('td').length || !tr.getElementsByTagName('th').length) { continue; };
                            rowArr[rowArr.length] = tr.getElementsByTagName('th');
                            for(var j = 0, th; th = rowArr[rowArr.length - 1][j]; j++) {
                                    if(th.className.search(/sortable/) != -1) { sortableTable = true; };
                            };
                    };

                    if(!sortableTable) continue;

                    if(!tbl.id) { tbl.id = "fd-table-" + fdTableSort.uniqueHash++; };

                    showArrow   = tbl.className.search("no-arrow") == -1;
                    showOnly    = tbl.className.search("sortable-onload-show") != -1;

                    columnNumSortObj = {};
                    colMatch         = fdTableSort.parseClassName(showOnly ? "sortable-onload-show" : "sortable-onload", tbl);
                    for(match = 1; match < colMatch.length; match++) {
                            columnNumSortObj[parseInt(colMatch[match], 10)] = { "reverse":colMatch[match].search("r") != -1 };
                    };

                    rowLength = rowArr[0].length;

                    for(c = 0;c < rowArr[0].length;c++){
                            if(rowArr[0][c].getAttribute(fdTableSort.colspan) && rowArr[0][c].getAttribute(fdTableSort.colspan) > 1){
                                    rowLength = rowLength + (rowArr[0][c].getAttribute(fdTableSort.colspan) - 1);
                            };
                    };

                    workArr = new Array(rowArr.length);
                    for(c = rowArr.length;c--;){ workArr[c]= new Array(rowLength); };

                    for(c = 0;c < workArr.length;c++){
                            celCount = 0;
                            for(i = 0;i < rowLength;i++){
                                    if(!workArr[c][i]){
                                            cel = rowArr[c][celCount];
                                            colspan = (cel.getAttribute(fdTableSort.colspan) > 1) ? cel.getAttribute(fdTableSort.colspan):1;
                                            rowspan = (cel.getAttribute(fdTableSort.rowspan) > 1) ? cel.getAttribute(fdTableSort.rowspan):1;
                                            for(var t = 0;((t < colspan)&&((i+t) < rowLength));t++){
                                                    for(var n = 0;((n < rowspan)&&((c+n) < workArr.length));n++) {
                                                            workArr[(c+n)][(i+t)] = cel;
                                                    };
                                            };
                                            if(++celCount == rowArr[c].length) break;
                                    };
                            };
                    };

                    for(c = 0;c < workArr.length;c++) {
                            for(i = 0;i < workArr[c].length;i++){

                                    if(workArr[c][i].className.search("fd-column-") == -1 && workArr[c][i].className.search("sortable") != -1) workArr[c][i].className = workArr[c][i].className + " fd-column-" + i;

                                    if(workArr[c][i].className.match('sortable')) {
                                            workArr[c][i].className = workArr[c][i].className.replace(/forwardSort|reverseSort/, "");

                                            if(i in columnNumSortObj) {
                                                    columnNumSortObj[i]["thNode"] = workArr[c][i];
                                                    columnNumSortObj["active"] = true;
                                            };

                                            thtext = fdTableSort.getInnerText(workArr[c][i]);

                                            for(var cn = workArr[c][i].childNodes.length; cn--;) {
                                                    // Skip image nodes and links created by the filter script.
                                                    if(workArr[c][i].childNodes[cn].nodeType == 1 && (workArr[c][i].childNodes[cn].className == "fdFilterTrigger" || /img/i.test(workArr[c][i].childNodes[cn].nodeName))) {
                                                            continue;
                                                    };
                                                    if(workArr[c][i].childNodes[cn].nodeType == 1 && /^a$/i.test(workArr[c][i].childNodes[cn].nodeName)) {
                                                            workArr[c][i].childNodes[cn].onclick = workArr[c][i].childNodes[cn].onkeydown = null;
                                                    };
                                                    workArr[c][i].removeChild(workArr[c][i].childNodes[cn]);
                                            };

                                            aclone = a.cloneNode(true);
                                            aclone.appendChild(document.createTextNode(thtext));
                                            aclone.title = "Sort on \u201c" + thtext + "\u201d";
                                            aclone.onclick = aclone.onkeydown = workArr[c][i].onclick = fdTableSort.initWrapper;
                                            workArr[c][i].appendChild(aclone);
                                            if(showArrow) workArr[c][i].appendChild(span.cloneNode(false));
                                            workArr[c][i].className = workArr[c][i].className.replace(/fd-identical|fd-not-identical/, "");
                                            fdTableSort.disableSelection(workArr[c][i]);
                                            aclone = null;
                                    };
                            };
                    };

                    fdTableSort.tmpCache[tbl.id] = {cols:rowLength, headers:workArr};

                    workArr = null;
                    multi   = 0;

                    if("active" in columnNumSortObj) {
                            fdTableSort.tableId = tbl.id;
                            fdTableSort.prepareTableData(document.getElementById(fdTableSort.tableId));

                            delete columnNumSortObj["active"];

                            for(col in columnNumSortObj) {
                                    obj = columnNumSortObj[col];
                                    if(!("thNode" in obj)) { continue; };
                                    fdTableSort.multi = true;

                                    len = obj.reverse ? 2 : 1;

                                    for(ii = 0; ii < len; ii++) {
                                            fdTableSort.thNode = obj.thNode;
                                            if(!showOnly) {
                                                    fdTableSort.initSort(false, true);
                                            } else {
                                                    fdTableSort.addThNode();
                                            };
                                    };

                                    if(showOnly) {
                                            fdTableSort.removeClass(obj.thNode, "(forwardSort|reverseSort)");
                                            fdTableSort.addClass(obj.thNode, obj.reverse ? "reverseSort" : "forwardSort");
                                            if(showArrow) {
                                                    span = fdTableSort.thNode.getElementsByTagName('span')[0];
                                                    if(span.firstChild) { span.removeChild(span.firstChild); };
                                                    span.appendChild(document.createTextNode(len == 1 ? " \u2193" : " \u2191"));
                                            };
                                    };
                            };
                            if(showOnly && (fdTableSort.tableCache[tbl.id].colStyle || fdTableSort.tableCache[tbl.id].rowStyle)) {
                                    fdTableSort.redraw(tbl.id, false);
                            };
                    } else if(tbl.className.search(/onload-zebra/) != -1) {
                            fdTableSort.tableId = tbl.id;
                            fdTableSort.prepareTableData(tbl);
                            if(fdTableSort.tableCache[tbl.id].rowStyle) { fdTableSort.redraw(tbl.id, false); };
                    };
            };

            fdTableSort.thNode = aclone = a = span = columnNumSortObj = thNode = tbl = allRowArr = rowArr = null;
    },
    initWrapper: function(e) {
            e = e || window.event;
            var kc = e.type == "keydown" ? e.keyCode != null ? e.keyCode : e.charCode : -1;
            if(fdTableSort.thNode == null && (e.type == "click" || kc == 13)) {
                    var targ = this;
                    while(targ.tagName.toLowerCase() != "th") { targ = targ.parentNode; };
                    fdTableSort.thNode = targ;
                    while(targ.tagName.toLowerCase() != "table") { targ = targ.parentNode; };
                    fdTableSort.tableId = targ.id;
                    fdTableSort.multi = e.shiftKey;
                    fdTableSort.addSortActiveClass();
                    setTimeout(fdTableSort.initSort,5,false);
                    return fdTableSort.stopEvent(e);
            };
            return kc != -1 ? true : fdTableSort.stopEvent(e);
    },
    jsWrapper: function(tableid, colNums) {
            if(!(tableid in fdTableSort.tmpCache)) { return false; };
            if(!(tableid in fdTableSort.tableCache)) { fdTableSort.prepareTableData(document.getElementById(tableid)); };
            if(!(colNums instanceof Array)) { colNums = [colNums]; };

            fdTableSort.tableId = tableid;
            var len = colNums.length, colNum;

            if(fdTableSort.tableCache[tableid].thList.length == colNums.length) {
                    var identical = true;
                    var th;
                    for(var i = 0; i < len; i++) {
                            colNum = colNums[i];
                            th = fdTableSort.tmpCache[tableid].headers[0][colNum];
                            if(th != fdTableSort.tableCache[tableid].thList[i]) {
                                    identical = false;
                                    break;
                            };
                    };
                    if(identical) {
                            fdTableSort.thNode = th;
                            fdTableSort.initSort(true);
                            return;
                    };
            };

            fdTableSort.addSortActiveClass();

            for(var i = 0; i < len; i++) {
                    fdTableSort.multi = i;
                    colNum = colNums[i];
                    fdTableSort.thNode = fdTableSort.tmpCache[tableid].headers[0][colNum];
                    fdTableSort.initSort(true);
            };
    },
    addSortActiveClass: function() {
            if(fdTableSort.thNode == null) { return; };
            fdTableSort.addClass(fdTableSort.thNode, fdTableSort.sortActiveClass);
            fdTableSort.addClass(document.getElementsByTagName('body')[0], fdTableSort.sortActiveClass);
    },
    removeSortActiveClass: function() {
            if(fdTableSort.thNode == null) return;
            fdTableSort.removeClass(fdTableSort.thNode, fdTableSort.sortActiveClass);
            fdTableSort.removeClass(document.getElementsByTagName('body')[0], fdTableSort.sortActiveClass);
    },
    doCallback: function(init) {
            if(!fdTableSort.tableId || !(fdTableSort.tableId in fdTableSort.tableCache)) { return; };
            fdTableSort.callback(fdTableSort.tableId, init ? fdTableSort.tableCache[fdTableSort.tableId].initiatedCallback : fdTableSort.tableCache[fdTableSort.tableId].completeCallback);
    },
    addClass: function(e,c) {
            if(new RegExp("(^|\\s)" + c + "(\\s|$)").test(e.className)) { return; };
            e.className += ( e.className ? " " : "" ) + c;
    },
    /*@cc_on
    /*@if (@_win32)
    removeClass: function(e,c) {
            e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
    },
    @else @*/
    removeClass: function(e,c) {
            e.className = !c ? "" : e.className.replace(new RegExp("(^|\\s)" + c + "(\\s|$)"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    },
    /*@end
    @*/
    callback: function(tblId, cb) {
            var func;
            if(cb.indexOf(".") != -1) {
                    var split = cb.split(".");
                    func = window;
                    for(var i = 0, f; f = split[i]; i++) {
                            if(f in func) {
                                    func = func[f];
                            } else {
                                    func = "";
                                    break;
                            };
                    };
            } else if(cb + tblId in window) {
                    func = window[cb + tblId];
            } else if(cb in window) {
                    func = window[cb];
            };
            if(typeof func == "function") { func(tblId, fdTableSort.tableCache[tblId].thList); };
            func = null;
    },
    prepareTableData: function(table) {
            var data = [];

            var start = table.getElementsByTagName('tbody');
            start = start.length ? start[0] : table;

            var trs = start.rows;
            var ths = table.getElementsByTagName('th');

            var numberOfRows = trs.length;
            var numberOfCols = fdTableSort.tmpCache[table.id].cols;

            var data = [];
            var identical = new Array(numberOfCols);
            var identVal  = new Array(numberOfCols);

            for(var tmp = 0; tmp < numberOfCols; tmp++) identical[tmp] = true;

            var tr, td, th, txt, tds, col, row;

            var re = new RegExp(/fd-column-([0-9]+)/);
            var rowCnt = 0;

            var sortableColumnNumbers = [];

            for(var tmp = 0, th; th = ths[tmp]; tmp++) {
                    if(th.className.search(re) == -1) continue;
                    sortableColumnNumbers[sortableColumnNumbers.length] = th;
            };

            for(row = 0; row < numberOfRows; row++) {

                    tr              = trs[row];
                    if(tr.parentNode != start || tr.getElementsByTagName("th").length || (tr.parentNode && tr.parentNode.tagName.toLowerCase().search(/thead|tfoot/) != -1)) continue;
                    data[rowCnt]    = [];
                    tds             = tr.cells;

                    for(var tmp = 0, th; th = sortableColumnNumbers[tmp]; tmp++) {
                            col = th.className.match(re)[1];

                            td  = tds[col];
                            txt = fdTableSort.getInnerText(td) + " ";
                            txt = txt.replace(/^\s+/,'').replace(/\s+$/,'');

                            if(th.className.search(/sortable-date/) != -1) {
                                    txt = fdTableSort.dateFormat(txt, th.className.search(/sortable-date-dmy/) != -1);
                            } else if(th.className.search(/sortable-numeric|sortable-currency/) != -1) {
                                    txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));
                                    if(isNaN(txt)) txt = "";
                            } else if(th.className.search(/sortable-text/) != -1) {
                                    txt = txt.toLowerCase();
                            } else if (th.className.search(/sortable-keep/) != -1) {
                                    txt = rowCnt;
                            } else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1) {
                                    if((th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData") in window) {
                                            txt = window[th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "PrepareData"](td, txt);
                                    };
                            } else if(txt != "") {
                                    fdTableSort.removeClass(th, "sortable");
                                    if(fdTableSort.dateFormat(txt) != 0) {
                                            fdTableSort.addClass(th, "sortable-date");
                                            txt = fdTableSort.dateFormat(txt);
                                    } else if(txt.search(fdTableSort.regExp_Number) != -1 || txt.search(fdTableSort.regExp_Currency) != -1) {
                                            fdTableSort.addClass(th, "sortable-numeric");
                                            txt = parseFloat(txt.replace(/[^0-9\.\-]/g,''));
                                            if(isNaN(txt)) txt = "";
                                    } else {
                                            fdTableSort.addClass(th, "sortable-text");
                                            txt = txt.toLowerCase();
                                    };
                            };

                            if(rowCnt > 0 && identical[col] && identVal[col] != txt) { identical[col] = false; };

                            identVal[col]     = txt;
                            data[rowCnt][col] = txt;
                    };
                    data[rowCnt][numberOfCols] = tr;
                    rowCnt++;
            };

            var colStyle = table.className.search(/colstyle-([\S]+)/) != -1 ? table.className.match(/colstyle-([\S]+)/)[1] : false;
            var rowStyle = table.className.search(/rowstyle-([\S]+)/) != -1 ? table.className.match(/rowstyle-([\S]+)/)[1] : false;
            var iCBack   = table.className.search(/sortinitiatedcallback-([\S-]+)/) == -1 ? "sortInitiatedCallback" : table.className.match(/sortinitiatedcallback-([\S]+)/)[1];
            var cCBack   = table.className.search(/sortcompletecallback-([\S-]+)/) == -1 ? "sortCompleteCallback" : table.className.match(/sortcompletecallback-([\S]+)/)[1];
            iCBack = iCBack.replace("-", ".");
            cCBack = cCBack.replace("-", ".");
            fdTableSort.tableCache[table.id] = { hook:start, initiatedCallback:iCBack, completeCallback:cCBack, thList:[], colOrder:{}, data:data, identical:identical, colStyle:colStyle, rowStyle:rowStyle, noArrow:table.className.search(/no-arrow/) != -1 };
            sortableColumnNumbers = data = tr = td = th = trs = identical = identVal = null;
    },
    onUnload: function() {
            for(tbl in fdTableSort.tableCache) { fdTableSort.removeTableCache(tbl); };
            for(tbl in fdTableSort.tmpCache) { fdTableSort.removeTmpCache(tbl); };
            fdTableSort.removeEvent(window, "load", fdTableSort.initEvt);
            fdTableSort.removeEvent(window, "unload", fdTableSort.onUnload);
            fdTableSort.tmpCache = fdTableSort.tableCache = null;
    },
    addThNode: function() {
            var dataObj = fdTableSort.tableCache[fdTableSort.tableId];
            var pos     = fdTableSort.thNode.className.match(/fd-column-([0-9]+)/)[1];
            var alt     = false;

            if(!fdTableSort.multi) {
                    if(dataObj.colStyle) {
                            var len = dataObj.thList.length;
                            for(var i = 0; i < len; i++) {
                                    dataObj.colOrder[dataObj.thList[i].className.match(/fd-column-([0-9]+)/)[1]] = false;
                            };
                    };
                    if(dataObj.thList.length && dataObj.thList[0] == fdTableSort.thNode) alt = true;
                    dataObj.thList = [];
            };

            var found = false;
            var l = dataObj.thList.length;

            for(var i = 0, n; n = dataObj.thList[i]; i++) {
                    if(n == fdTableSort.thNode) {
                            found = true;
                            break;
                    };
            };

            if(!found) {
                    dataObj.thList.push(fdTableSort.thNode);
                    if(dataObj.colStyle) { dataObj.colOrder[pos] = true; };
            };

            var ths = document.getElementById(fdTableSort.tableId).getElementsByTagName("th");
            for(var i = 0, th; th = ths[i]; i++) {
                    found = false;
                    for(var z = 0, n; n = dataObj.thList[z]; z++) {
                            if(n == th) {
                                    found = true;
                                    break;
                            };
                    };
                    if(!found) {
                            fdTableSort.removeClass(th, "(forwardSort|reverseSort)");
                            if(!dataObj.noArrow) {
                                    span = th.getElementsByTagName('span');
                                    if(span.length) {
                                            span = span[0];
                                            while(span.firstChild) span.removeChild(span.firstChild);
                                    };
                            };
                    };
            };

            if(dataObj.thList.length > 1) {
                    classToAdd = fdTableSort.thNode.className.search(/forwardSort/) != -1 ? "reverseSort" : "forwardSort";
                    fdTableSort.removeClass(fdTableSort.thNode, "(forwardSort|reverseSort)");
                    fdTableSort.addClass(fdTableSort.thNode, classToAdd);
                    dataObj.pos = -1
            } else if(alt) { dataObj.pos = fdTableSort.thNode };
    },
    initSort: function(noCallback, ident) {
            var thNode      = fdTableSort.thNode;
            var tableElem   = document.getElementById(fdTableSort.tableId);

            if(!(fdTableSort.tableId in fdTableSort.tableCache)) { fdTableSort.prepareTableData(document.getElementById(fdTableSort.tableId)); };

            fdTableSort.addThNode();

            if(!noCallback) { fdTableSort.doCallback(true); };

            fdTableSort.pos = thNode.className.match(/fd-column-([0-9]+)/)[1];
            var dataObj     = fdTableSort.tableCache[tableElem.id];
            var lastPos     = dataObj.pos && dataObj.pos.className ? dataObj.pos.className.match(/fd-column-([0-9]+)/)[1] : -1;
            var len1        = dataObj.data.length;
            var len2        = dataObj.data.length > 0 ? dataObj.data[0].length - 1 : 0;
            var identical   = dataObj.identical[fdTableSort.pos];
            var classToAdd  = "forwardSort";

            if(dataObj.thList.length > 1) {
                    var js  = "var sortWrapper = function(a,b) {\n";
                    var l   = dataObj.thList.length;
                    var cnt = 0;
                    var e,d,th,p,f;

                    for(var i=0; i < l; i++) {
                            th = dataObj.thList[i];
                            p  = th.className.match(/fd-column-([0-9]+)/)[1];
                            if(dataObj.identical[p]) { continue; };
                            cnt++;

                            if(th.className.match(/sortable-(numeric|currency|date|keep)/)) {
                                    f = "fdTableSort.sortNumeric";
                            } else if(th.className.match('sortable-text')) {
                                    f = "fdTableSort.sortText";
                            } else if(th.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && th.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {
                                    f = "window['" + th.className.match(/sortable-([a-zA-Z\_]+)/)[1] + "']";
                            } else  f = "fdTableSort.sortText";

                            e = "e" + i;
                            d = th.className.search('forwardSort') != -1 ? "a,b" : "b,a";
                            js += "fdTableSort.pos   = " + p + ";\n";
                            js += "var " + e + " = "+f+"(" + d +");\n";
                            js += "if(" + e + ") return " + e + ";\n";
                            js += "else { \n";
                    };

                    js += "return 0;\n";

                    for(var i=0; i < cnt; i++) {
                            js += "};\n";
                    };

                    if(cnt) js += "return 0;\n";
                    js += "};\n";

                    eval(js);
                    dataObj.data.sort(sortWrapper);
                    identical = false;
            } else if((lastPos == fdTableSort.pos && !identical) || (thNode.className.search(/sortable-keep/) != -1 && lastPos == -1)) {
                    dataObj.data.reverse();
                    classToAdd = thNode.className.search(/reverseSort/) != -1 ? "forwardSort" : "reverseSort";
                    if(thNode.className.search(/sortable-keep/) != -1 && lastPos == -1) fdTableSort.tableCache[tableElem.id].pos = thNode;
            } else {
                    fdTableSort.tableCache[tableElem.id].pos = thNode;
                    classToAdd = thNode.className.search(/forwardSort/) != -1 ? "reverseSort" : "forwardSort";
                    if(!identical) {
                            if(thNode.className.match(/sortable-(numeric|currency|date|keep)/)) {
                                    dataObj.data.sort(fdTableSort.sortNumeric);
                            } else if(thNode.className.match('sortable-text')) {
                                    dataObj.data.sort(fdTableSort.sortText);
                            } else if(thNode.className.search(/sortable-([a-zA-Z\_]+)/) != -1 && thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1] in window) {
                                    dataObj.data.sort(window[thNode.className.match(/sortable-([a-zA-Z\_]+)/)[1]]);
                            };

                            if(thNode.className.search(/(^|\s)favour-reverse($|\s)/) != -1) {
                                    classToAdd = classToAdd == "forwardSort" ? "reverseSort" : "forwardSort";
                                    dataObj.data.reverse();
                            };
                    };
            };
            if(ident) { identical = false; };
            if(dataObj.thList.length == 1) {
                    fdTableSort.removeClass(thNode, "(forwardSort|reverseSort)");
                    fdTableSort.addClass(thNode, classToAdd);
            };
            if(!dataObj.noArrow) {
                    var span = fdTableSort.thNode.getElementsByTagName('span')[0];
                    if(span.firstChild) span.removeChild(span.firstChild);
                    span.appendChild(document.createTextNode(fdTableSort.thNode.className.search(/forwardSort/) != -1 ? " \u2193" : " \u2191"));
            };
            if(!dataObj.rowStyle && !dataObj.colStyle && identical) {
                    fdTableSort.removeSortActiveClass();
                    if(!noCallback) { fdTableSort.doCallback(false); };
                    fdTableSort.thNode = null;
                    return;
            };
            if("tablePaginater" in window && "tableInfo" in tablePaginater && fdTableSort.tableId in tablePaginater.tableInfo) {
                    tablePaginater.redraw(fdTableSort.tableId, identical);
            } else {
                    fdTableSort.redraw(fdTableSort.tableId, identical);
            };
            fdTableSort.removeSortActiveClass();
            if(!noCallback) { fdTableSort.doCallback(false); };
            fdTableSort.thNode = null;
    },
    redraw: function(tableid, identical) {
            if(!tableid || !(tableid in fdTableSort.tableCache)) { return; };
            var dataObj     = fdTableSort.tableCache[tableid];
            var data        = dataObj.data;
            var len1        = data.length;
            var len2        = len1 ? data[0].length - 1 : 0;
            var hook        = dataObj.hook;
            var colStyle    = dataObj.colStyle;
            var rowStyle    = dataObj.rowStyle;
            var colOrder    = dataObj.colOrder;
            var highLight   = 0;
            var reg         = /(^|\s)invisibleRow(\s|$)/;
            var tr, tds;

            for(var i = 0; i < len1; i++) {
                    tr = data[i][len2];
                    if(colStyle) {
                            tds = tr.cells;
                            for(thPos in colOrder) {
                                    if(!colOrder[thPos]) fdTableSort.removeClass(tds[thPos], colStyle);
                                    else fdTableSort.addClass(tds[thPos], colStyle);
                            };
                    };
                    if(!identical) {
                            if(rowStyle && tr.className.search(reg) == -1) {
                                    if(highLight++ & 1) fdTableSort.addClass(tr, rowStyle);
                                    else fdTableSort.removeClass(tr, rowStyle);
                            };

                            // Netscape 8.1.2 requires the removeChild call or it freaks out, so add the line if you want to support this browser
                            // hook.removeChild(tr);
                            hook.appendChild(tr);
                    };
            };
            tr = tds = hook = null;
    },
    getInnerText: function(el) {
            if (typeof el == "string" || typeof el == "undefined") return el;
            if(el.innerText) return el.innerText;
            var txt = '', i;
            for(i = el.firstChild; i; i = i.nextSibling) {
                    if(i.nodeType == 3)            txt += i.nodeValue;
                    else if(i.nodeType == 1)       txt += fdTableSort.getInnerText(i);
            };
            return txt;
    },
    dateFormat: function(dateIn, favourDMY) {
            var dateTest = [
                    { regExp:/^(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])([- \/.])((\d\d)?\d\d)$/, d:3, m:1, y:5 },  // mdy
                    { regExp:/^(0?[1-9]|[12][0-9]|3[01])([- \/.])(0?[1-9]|1[012])([- \/.])((\d\d)?\d\d)$/, d:1, m:3, y:5 },  // dmy
                    { regExp:/^(\d\d\d\d)([- \/.])(0?[1-9]|1[012])([- \/.])(0?[1-9]|[12][0-9]|3[01])$/, d:5, m:3, y:1 }      // ymd
                    ];
            var start, cnt = 0, numFormats = dateTest.length;
            while(cnt < numFormats) {
                    start = (cnt + (favourDMY ? numFormats + 1 : numFormats)) % numFormats;
                    if(dateIn.match(dateTest[start].regExp)) {
                            res = dateIn.match(dateTest[start].regExp);
                            y = res[dateTest[start].y];
                            m = res[dateTest[start].m];
                            d = res[dateTest[start].d];
                            if(m.length == 1) m = "0" + String(m);
                            if(d.length == 1) d = "0" + String(d);
                            if(y.length != 4) y = (parseInt(y) < 50) ? "20" + String(y) : "19" + String(y);

                            return y+String(m)+d;
                    };
                    cnt++;
            };
            return 0;
    },
    sortNumeric:function(a,b) {
            var aa = a[fdTableSort.pos];
            var bb = b[fdTableSort.pos];
            if(aa == bb) return 0;
            if(aa === "" && !isNaN(bb)) return -1;
            if(bb === "" && !isNaN(aa)) return 1;
            return aa - bb;
    },
    sortText:function(a,b) {
            var aa = a[fdTableSort.pos];
            var bb = b[fdTableSort.pos];
            if(aa == bb) return 0;
            if(aa < bb)  return -1;
            return 1;
    }
};
})();
fdTableSort.addEvent(window, "load",   fdTableSort.initEvt);
fdTableSort.addEvent(window, "unload", fdTableSort.onUnload);

/*
 * expander class
 * brianj@jinies
 */
var expander = new Class({

	initialize: function (params){
		this.box = params.box;
		this.full_size = this.box.offsetHeight;
		this.size = params.size || 0;
		this.expanded_class = params.expanded_class || 'expanded';
		this.handler = params.handler || null;
		this.mask = params.mask || null;
		this.offset_size = params.offset_size || 0;
		this.offset_mask = params.offset_mask || 0;
		this.use_fade = false;
		// if height is larger than expected, set initial height, set handler		
		if (this.full_size > this.size) {
			this.box.setStyle('height', this.size+'px');
			if (this.handler){
				this.handler_size = this.handler.offsetHeight;
				this.addHandlerButton(this.handler);
			}
			this.fx = new Fx.Tween(this.box, {duration:500, wait:false, property: 'height'});		
			if (this.size<=1) {
				this.use_fade = true;
				this.box.fade('out');
			}
			if (this.mask != undefined) {
				this.maskFx = new Fx.Tween(this.mask, {duration:500, wait:false, property: 'height'});
			}
			this.expanded_state = false;
		}
		// if not then hide the handler, prepare not to do anything
		else {
			if (this.handler){
				this.handler.setStyle('display', 'none');
			}
		}
	},

	addHandlerButton: function (handle_btn) {
		handle_btn.addEvent('click', this.toggle.bind(this,[true]));
	},
	
	show: function () {
		this.toggle(this,[true]);
	},
	
	// toggle, if there is a mask then hanle it and agregate the size of the handler
	toggle: function (item) {
		// collapse
		if (this.expanded_state) {
			if (this.mask != undefined) {
				this.maskFx.start((this.size+this.handler_size+this.offset_mask)+'px');
			}
			this.fx.start(this.size+'px');
			if (this.use_fade) this.box.fade('out');
			this.handler.removeClass(this.expanded_class);
			this.expanded_state = false;
		}
		// expand
		else {
			if (this.mask != undefined) {
				//this.maskFx.start((this.full_size+this.handler_size+this.offset_mask)+'px');
				this.maskFx.start((this.full_size+this.handler_size+this.offset_mask)+'px');
			}
			this.fx.start((this.full_size+this.offset_size)+'px');
			if (this.use_fade) this.box.fade('in');
			this.handler.addClass(this.expanded_class);
			this.expanded_state = true;
		}
	}
});

function makeSessionId() {
	var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'; 
	var sid_length = 5;
	var sid = '';
	for (var i=0; i<sid_length; i++) {
		var rnum = Math.floor(Math.random() * chars.length);
		sid += chars.substring(rnum,rnum+1);
	}
	return sid;
}

function submitcheckout(treq, price, tgid, evtid) {
	var select = $(treq);
	var session = makeSessionId();
	if (select.options) {
		var count = select.options[select.selectedIndex].value;
	}
	else {
		var count = select.value;
	}
	var checkout_link = "https://tickettransaction2.com/Checkout.aspx?brokerid=3123&sitenumber=0&tgid=" + tgid + "&evtid=" + evtid + "&price=" + price + "&treq=" + count + "&SessionId=" + session;
	pageTracker._link(checkout_link);
	window.location = checkout_link;
}

function select_all_options() {
	var selected_options = $$('#options_select2 option');
	for (var i=0; i<selected_options.length; i++) {
		selected_options[i].selected = true;
	}
}

function makeRounded(css_query) {
	var all = $$(css_query);
    for(var i=0; i<all.length; i++) {
    	var b = all[i].innerHTML;
    	all[i].innerHTML = "<div class='roundc1'><div class='roundc2'><div class='roundc3'><div class='roundc4'>"+b+"</div></div></div></div>";
    }
}

function box_disappear(obj) {
	//var fx = new Fx.Tween(obj, {duration:500, wait:false, property: 'height'});
	//obj.fade('out');
	//fx.start('0px');
	var myFx = new Fx.Tween(obj);
	obj.fade('out');
	myFx.start('height', '1px').chain(function(){ obj.setStyle('display', 'none'); });
	
}
function isset(varname)  {
	if(typeof(window[varname]) != "undefined") return true;
	else return false;
}

function facebook_onlogin() {
	window.location='/user/facebook';
}


function tf_auto_select(set_byval) {
	//ticket_finder_top - ticket_sport
	$('ticket_sport').getElement('option[value="' + set_byval + '"]').selected = true;
}

var jcheck_filters = new Array();
var jcheck_subordinates = new Array();
function jjump_all_events(parent_category) {
	var new_parent = 'jnaro-narrow-' + parent_category;
	var days = $('see_all_dates').value;
	var new_url = '?events=' + parent_category + jjump_subs_url(new_parent, 'subcategory') + '&days=' + days;
	location.href = new_url;
}
function jjump_subs_url(container, var_name) {
	var new_string = '';
	var array_split = $(container).getElements('input');
	for (var i=0; i<array_split.length; i++) {
		if (array_split[i].checked) {
			new_string += '&' + var_name + '[]=' + array_split[i].value;
		}
	}
	return new_string;
}
function jcheck_register(from_object, to_local) {
	jcheck_filters[from_object] = to_local;
}
function jcheck_subordinate(subordinate_id) {
	//console.log('new subordinate: ' + subordinate_id);
	jcheck_subordinates[subordinate_id] = subordinate_id;
}
function jcontrol_table_state() {
	var table_status = 0;
	var table_rows = $('tn_table').getElements('tr');
	var find_me = table_rows.getStyle('display');
	// check if its just none exist, skips the first tr because its the table heading
	for (var x = 1; x < find_me.length; x++) {
		// they must all be none
		if (find_me[x] == "none") {
			continue;
		}
		// its got rows
		table_status = 1;
		// kill this loop
		break;
	}
	// hide / show the table and feedback
	if (table_status == 0) {
		$('tn_table').setStyle('display', 'none');
		$('tn_feedback').setStyle('display', 'inline-block');
		$('tn_feedback').set('html', '<b>Your Filter Has Yielded No Results:</b> <br />Reverse the previous action or <a href="">click to reset</a>');
	}
	if (table_status == 1) {
		$('tn_table').setStyle('display', 'table');
		$('tn_feedback').setStyle('display', 'none');
		$('tn_feedback').set('text', '');
	}
	
}
function jcheck_radio(radio_id) {
	if ($(radio_id).checked) $(radio_id).checked=false;
	else $(radio_id).checked=true;
	jcheck_change(radio_id);
}
function jcheck_change(radio_id) {
	var radio_name = $(radio_id).name;
	// set by php //$(radio_id).parentNode.parentNode.id = radio_name;
	jcheck_apply(radio_name);
}
function jcheck_disable_non_existent(radio_object) {
	var find_me = $('tn_table').getElements('tr.' + radio_object.value).getStyle('display');
	// check if its just none exist
	if (find_me.length == 0) {
		radio_object.disabled=true;
		radio_object.parentNode.addClass('box_disabled');
	}
}
function jcheck_apply_disables_on(radio_name) {
	var all_inputs = $(radio_name).getElements('input');
	for (var i=0; i<all_inputs.length; i++) {
		// reset single check block and disable items
		jcheck_disable_non_existent(all_inputs[i], true);
	}
}
function jcheck_apply(radio_name) {
	var radio_id = null;
	var radio_status = 0;
	var all_inputs = $(radio_name).getElements('input');
	var filter_reverse = ($(radio_name).hasClass('filter_reverse'))?true:false;
	var apply_reverse = false;
	var filter_seek_checked = false;
	if (filter_reverse) {
		for (var i=0; i<all_inputs.length; i++) {
			if (all_inputs[i].checked) {
				filter_seek_checked = true;
				break;
			}
		}
		if (filter_seek_checked == false) {
			apply_reverse = true;
		}
	}
	for (var x=0; x<all_inputs.length; x++) {
		radio_id = all_inputs[x].id
		radio_status = (all_inputs[x].checked)?1:0;
		if (radio_status == 1 || apply_reverse) {
			if (radio_status == 1) {
				$(radio_id).checked = true;
				$(radio_id).parentNode.addClass('box_selected');
			}
			else {
				$(radio_id).parentNode.removeClass('box_selected');
			}		
			$$('.' + all_inputs[x].value).removeClass(jcheck_filters[radio_name]);
		}
		else {
			$(radio_id).checked = false;
			$(radio_id).parentNode.removeClass('box_selected');
			$$('.' + all_inputs[x].value).addClass(jcheck_filters[radio_name]);
		}
		jcheck_disable_non_existent(all_inputs[x]);
	}
	jcontrol_table_state('check_boxes');
}
function jdate_int(val){
	return (!isNaN(val) && val.toString().length==1)?"0"+val:val;
}
function jdate_apply(field, apply_date) {
	if (field == 'date_start') {
		var current_start = apply_date;
	}
	else {
		var current_start = $('date_start').get('text');
	}
	var start_split = current_start.split("/", 3);
	var start_year = start_split[2];
	var start_month = start_split[0];
	var start_day = start_split[1];
	current_start = start_year + '-' + start_month + '-' + start_day;
	
	if (field == 'date_end') {
		var current_end = apply_date;
	}
	else {
		var current_end = $('date_end').get('text');

	}
	var end_split = current_end.split("/", 3);
	var end_year = end_split[2];
	var end_month = end_split[0];
	var end_day = end_split[1];
	current_end = end_year + '-' + end_month + '-' + end_day;

	var start = new Date();
	start.setFullYear(start_year, (parseInt(start_month) - 1), start_day);
	var end = new Date();
	end.setFullYear(end_year, (parseInt(end_month) - 1), end_day);
	//TODO: remove (start > end) and use parseInt
	// validate the request of the date range // dates crossed, reset
	if (start > end) {
		// if the start runs over the end, reset end only to last
		if (field == 'date_start') {
			$('date_start').set('text', jcheck_filters['date_start']);
		}
		else {
			$('date_end').set('text', jcheck_filters['date_end']);
		}
	}
	else {
		var date_min = jcheck_filters['date_start'].split("/", 3);
		date_min = parseInt(date_min[2] + date_min[0] + date_min[1]);
		var date_max = jcheck_filters['date_end'].split("/", 3);
		date_max = parseInt(date_max[2] + date_max[0] + date_max[1]);
		for (f = date_min; f < (date_max + 1); f++) {
			var fstr = '' + f + '';
			var this_month = fstr.substring(4,6);
			var this_day = fstr.substring(6,8);
			if (((this_day > 0) && (this_day < 32)) && ((this_month > 0) && (this_month < 13))) {
				// IF THIS DATE IS WITHIN RANGE
				if (f < parseInt(start_year+start_month+start_day)) {
					$$('.' + 'date-' + fstr.substring(0,4) + '-' + this_month + '-' + this_day).addClass('jnaro_hide');
				}
				else if (f > (parseInt(end_year+end_month+end_day))) {
					$$('.' + 'date-' + fstr.substring(0,4) + '-' + this_month + '-' + this_day).addClass('jnaro_hide');
				}
				else {
					$$('.' + 'date-' + fstr.substring(0,4) + '-' + this_month + '-' + this_day).removeClass('jnaro_hide');
				}
			}	
		}	
	}
}
var jtrow_avaliable = new Array();
function jtrow_range_register(this_int) {
	jtrow_avaliable[this_int] = parseInt(this_int);
}
function jtrow_range_filter(high, low, maximum, minimum) {
	for (var i=0; i<jtrow_avaliable.length; i++) {
		if (jtrow_avaliable[i] == undefined) {
			continue;
		}
		jtrow_avaliable[i] = parseInt(jtrow_avaliable[i]);
		if (jtrow_avaliable[i] < low) {
			$$('.' + 'filter_price_' + jtrow_avaliable[i]).addClass('jnaro_hide');
		}
		else if (jtrow_avaliable[i] > high) {
			$$('.' + 'filter_price_' + jtrow_avaliable[i]).addClass('jnaro_hide');
		}
		else {
			$$('.' + 'filter_price_' + jtrow_avaliable[i]).removeClass('jnaro_hide');
		}
	}
}
function jtrow_qty_filter(current, maximum, minimum) {
	current = parseInt(current);
	// filter rows
	for (var i=minimum; i<maximum; i++) {
		if (i < current) $$('.' + 'filter_qty_' + i).addClass('jqty_hide');
		else $$('.' + 'filter_qty_' + i).removeClass('jqty_hide');
	}
	// set select boxes
	var selectas = $$('.qty_selecta');
	for (var s=0; s < selectas.length; s++) {
		for (var opt=0; opt < selectas[s].options.length; opt++) {
			if (selectas[s].options[opt].value >= current) {
				selectas[s].options[opt].selected = true;
				break;
			}
		}
	}
}


// JavaScript Document
//
// new SortingTable( 'my_table', {
//   zebra: true,                        // Stripe the table, also on initialize
//   details: false,                     // Has details every other row
//   paginator: false,                   // Pass a paginator object
//   dont_sort_class: 'nosort',          // Class name on th's that don't sort
//   forward_sort_class: 'forward_sort', // Class applied to forward sort th's
//   reverse_sort_class: 'reverse_sort'  // Class applied to reverse sort th's
// });
//
// The above were the defaults.  The regexes in load_conversions test a cell
// begin sorted for a match, then use that conversion for all elements on that
// column.
//
// Requires mootools Class, Array, Function, Element, Element.Selectors,
// Element.Event, and you should probably get Window.DomReady if you're smart.
//

var SortingTable = new Class({

  Implements: Options,
  
  options: {
    zebra: true,
    details: false,
    paginator: false,
    dont_sort_class: 'nosort',
    forward_sort_class: 'forward_sort',
    reverse_sort_class: 'reverse_sort'
  },

  initialize: function( table, options ) {
    this.table = $(table);
    this.setOptions(options);
    
    this.tbody = this.table.getElement('tbody');
    if (this.options.zebra) {
      SortingTable.stripe_table(this.tbody.getChildren());
    }

    this.headers = this.table.getElement('thead').getElements('th');
    this.headers.each(function( header, index ) {
      if (header.hasClass( this.options.dont_sort_class )) { return }
      header.store( 'column', index )
      header.addEvent( 'mousedown', function(evt){
        this.sort_by_header( evt.target );
        if ( this.options.paginator) this.options.paginator.to_page( 1 );
      }.bind( this ) );
    }, this);

    this.load_conversions();
  },

  sort_by_header: function( header ){
    var rows = [];
    
    var before = this.tbody.getPrevious();
    this.tbody.dispose();
    
    var trs = this.tbody.getChildren();
    while ( row = trs.shift() ) {
      row = { row: row.dispose() };
      if ( this.options.details ) {
        row.detail = trs.shift().dispose();
      }
      rows.unshift( row );
    }
    
    if ( this.sort_column >= 0 &&
         this.sort_column == header.retrieve('column') ) {
      // They were pulled off in reverse
      if ( header.hasClass( this.options.reverse_sort_class ) ) {
        header.removeClass( this.options.reverse_sort_class );
        header.addClass( this.options.forward_sort_class );
      } else {
        header.removeClass( this.options.forward_sort_class );
        header.addClass( this.options.reverse_sort_class );
      }
    } else {
      this.headers.each(function(h){
        h.removeClass( this.options.forward_sort_class );
        h.removeClass( this.options.reverse_sort_class );
      }, this);
      this.sort_column = header.retrieve('column');
      if (header.retrieve('conversion_function')) {
        this.conversion_matcher = header.retrieve('conversion_matcher');
        this.conversion_function = header.retrieve('conversion_function');
      } else {
        this.conversion_function = false;
        rows.some(function(row){
          var to_match = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          if (to_match == '') return false;
          this.conversions.some(function(conversion){
            if (conversion.matcher.test( to_match )){
              this.conversion_matcher = conversion.matcher;
              this.conversion_function = conversion.conversion_function;
              return true;
            }
            return false;
          }, this);
          return !!(this.conversion_function);
        }, this);
        header.store('conversion_function', this.conversion_function );
        header.store('conversion_matcher', this.conversion_matcher );
      }
      header.addClass( this.options.forward_sort_class );
      rows.each(function(row){
        var compare_value = this.conversion_function( row );
        row.toString = function(){
          return compare_value;
        };
      }, this);
      rows.sort();
    }

    var index = 0;
    while ( row = rows.shift() ) {
      this.tbody.appendChild(row.row);
      if (row.detail) this.tbody.appendChild(row.detail);
      if ( this.options.zebra ) {
        row.row.className = row.row.className.replace( this.removeAltClassRe, '$1').clean();
        if (row.detail)
          row.detail.className = row.detail.className.replace( this.removeAltClassRe, '$1').clean();
        if (index % 2) {
          row.row.addClass( 'alt' ); 
          if (row.detail) row.detail.addClass( 'alt' );
        }
      }
      index++;
    }
   this.tbody.inject(before, 'after');
  },

  load_conversions: function() {
    this.conversions = $A([
      // 1.75 MB, 301 GB, 34 KB, 8 TB
      { matcher: /([0-9.]{1,8}).*([KMGT]{1})B/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = this.conversion_matcher.exec( cell );
          if (!cell) { return '0' }
          if (cell[2] == 'M') {
            sort_val = '1';
          } else if (cell[2] == 'G') {
            sort_val = '2';
          } else if (cell[2] == 'T') {
            sort_val = '3';
          } else {
            sort_val = '0';
          }
          var i = cell[1].indexOf('.')
          if (i == -1) {
            post = '00'
          } else {
            var dec = cell[1].split('.');
            cell[1] = dec[0];
            post = dec[1].concat('00'.substr(0,2-dec[1].length));
          }
          return sort_val.concat('00000000'.substr(0,2-cell[1].length).concat(cell[1])).concat(post);
        }
      },
      // 1 day ago, 4 days ago, 38 years ago, 1 month ago
      { matcher: /(\d{1,2}) (.{3,6}) ago/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = this.conversion_matcher.exec( cell );
          if (!cell) { return '0' }
          var sort_val;
          if (cell[2].indexOf('month') != -1) {
            sort_val = '1';
          } else if (cell[2].indexOf('year') != -1) {
            sort_val = '2';
          } else {
            sort_val = '0';
          }
          return sort_val.concat('00'.substr(0,2-cell[1].length).concat(cell[1]));
        }
      },
      // Currency
      { matcher: /((\d{1}\.\d{2}|\d{2}\.\d{2}|\d{3}\.\d{2}|\d{4}\.\d{2}|\d{5}\.\d{2}|\d{6}\.\d{2}))/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = cell.replace(/[^\d]/g, "");
          return '00000000000000000000000000000000'.substr(0,32-cell.length).concat(cell);
        }
      },
      // YYYY-MM-DD, YYYY-m-d
      { matcher: /(\d{4})-(\d{1,2})-(\d{1,2})/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          cell = this.conversion_matcher.exec( cell );
          return cell[1]+
                 '00'.substr(0,2-cell[2].length).concat(cell[2])+
                 '00'.substr(0,2-cell[3].length).concat(cell[3]);
        }
      },
      // Numbers
      { matcher: /^\d+$/,
        conversion_function: function( row ) {
          var cell = $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
          return '00000000000000000000000000000000'.substr(0,32-cell.length).concat(cell);
        }
      },
      // Fallback 
      { matcher: /.*/,
        conversion_function: function( row ) {
          return $(row.row.getElementsByTagName('td')[this.sort_column]).get('text');
        }
      }
    ]);
  }

});

SortingTable.removeAltClassRe = new RegExp('(^|\\s)alt(?:\\s|$)');
SortingTable.implement({ removeAltClassRe: SortingTable.removeAltClassRe });

SortingTable.stripe_table = function ( tr_elements  ) {
  var counter = 0;
  tr_elements.each( function( tr ) {
    if ( !tr.hasClass('collapsed') ) counter++;
    tr.className = tr.className.replace( this.removeAltClassRe, '$1').clean();
    if (counter % 2) tr.addClass( 'alt' );
  });
}

/*
Class: range_slide
        Creates a slider with two elements: a knob and a container. Returns the values.
Note:
        The Slider requires an XHTML doctype.
Arguments:
        element - the knob container
        knob - the handle
        options - see Options below
        maxknob - an optional maximum slider handle
Options:
		start - the minimum value for your slider.
		end - the maximum value for your slider.
        mode - either 'horizontal' or 'vertical'. defaults to horizontal.
        offset - relative offset for knob position. default to 0.
        knobheight - positions the max slider knob
		snap - whether the slider will slide in steps 
		numsteps - number of slide steps 
Events:
        onChange - a function to fire when the value changes.
        onComplete - a function to fire when you're done dragging.
        onTick - optionally, you can alter the onTick behavior, for example displaying an effect of the knob moving to the desired position.
                Passes as parameter the new position.
*/
var range_slide = new Class({
	options: {
		onChange: Class.empty,
		onComplete: Class.empty,
		onTick: function(pos){
			this.moveKnob.setStyle(this.p, pos);			
		},
		start: 0,
		end: 100,
		offset: 0,
		knobheight: 20,
		knobwidth: 14,
		mode: 'horizontal',
		clip_w:0, 
		clip_l:0,
		isinit:true,
		snap: false,
		range: false,
		numsteps:null
	},
    initialize: function(el, knob,bkg, options, maxknob) {
		this.setOptions(options);
		this.element = $(el);
		this.knob = $(knob);
		this.previousChange = this.previousEnd = this.step = -1;
		this.bkg = $(bkg);
		if(this.options.steps==null){
			this.options.steps = this.options.end - this.options.start;
		}
		if(maxknob!=null)
			this.maxknob = $(maxknob);
		//else
		//	this.element.addEvent('mousedown', this.clickedElement.bindWithEvent(this));
		var mod, offset;
		switch(this.options.mode){
			case 'horizontal':
				this.z = 'x';
				this.p = 'left';
				mod = {'x': 'left', 'y': false};
				offset = 'offsetWidth';
				break;
			case 'vertical':
				this.z = 'y';
				this.p = 'top';
				mod = {'x': false, 'y': 'top'};
				offset = 'offsetHeight';
		}
		this.max = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
		this.half = this.knob[offset]/2;
		this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
		this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
		this.getPos = this.element['get' + this.p.capitalize()].bind(this.element);
		this.knob.setStyle('position', 'relative').setStyle(this.p, - this.options.offset);

		this.range = this.max - this.min;
		this.steps = this.options.steps || this.full;
		this.stepSize = Math.abs(this.range) / this.steps;
		this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;
		

		if(maxknob != null) {
			this.maxPreviousChange = -1;
			this.maxPreviousEnd = -1;
			this.maxstep = this.options.end;
			this.maxknob.setStyle('position', 'relative').setStyle(this.p, + this.max - this.options.offset).setStyle('bottom', this.options.knobheight);
		}
		var lim = {};
		//status = this.z
		lim[this.z] = [- this.options.offset, this.max - this.options.offset];
		//lim[this.z] = [100, this.max - this.options.offset];

		this.drag = new Drag(this.knob, {
			limit: lim,
			modifiers: mod,
			snap: 0,
			onStart: function(){
					this.draggedKnob();
			}.bind(this),
			onDrag: function(){
					this.draggedKnob();
			}.bind(this),
			onComplete: function(){
					this.draggedKnob();
					this.end();
			}.bind(this)
		});
		if(maxknob != null) {  
			this.maxdrag = new Drag(this.maxknob, {
				limit: lim,
				modifiers: mod,
				snap: 0, 
				onStart: function(){
					this.draggedKnob(1);
				}.bind(this),
				onDrag: function(){
					this.draggedKnob(1);
				}.bind(this),
				onComplete: function(){
					this.draggedKnob(1);
					this.end();
				}.bind(this)
			});		
		}

		if (this.options.snap) {
			//this.drag.options.grid = Math.ceil(this.stepWidth);
			this.drag.options.grid = (this.full)/this.options.numsteps ;
			this.drag.options.limit[this.z][1] = this.full;
			//this.drag.options.grid = this.drag.options.grid - (this.knob[offset]/this.options.numsteps);
			status = "GRID - " + this.drag.options.grid  + "  , full = " + this.full// DEBUG

		}
		if (this.options.initialize) this.options.initialize.call(this);
    },
	setMin: function(stepMin){
		this.step = stepMin.limit(this.options.start, this.options.end);
		this.checkStep();
		this.end();
		this.moveKnob = this.knob;
		this.bkg.style.clip = "rect(0px "+  (parseInt(this.toPosition(this.step)) +3) + "px 7px 0px)";
		status =this.bkg.style.clip + "  vl= " + parseInt(this.toPosition(this.step)) ; //Debug
		this.fireEvent('onTick', this.toPosition(this.step));
		return this;
	},
	setMax: function(stepMax){
		this.maxstep = stepMax.limit(this.options.start, this.options.end);
		this.checkStep(1);
		this.end();
		this.moveKnob = this.maxknob;
		var w= Math.abs(this.toPosition(this.step)- this.toPosition(this.maxstep)) + 3 ;
		var r = parseInt(this.clip_l + w); 
		this.bkg.style.clip = "rect(0px "+  r + "px 7px "+ this.clip_l + "px)";

		this.fireEvent('onTick', this.toPosition(this.maxstep));
		// For Init Only 
		if(this.options.isinit){
			var lim = {}; var mi,mx;
			mi = - this.options.offset; 
			mx= parseInt(this.maxknob.getStyle('left')) - this.options.offset-4 ;
			lim[this.z] = [mi, mx];
			this.drag.options.limit = lim;
			this.options.isinit = false;
		}
		return this; 
	},
	clickedElement: function(event){
		var position = event.page[this.z] - this.getPos() - this.half;
		position = position.limit(-this.options.offset, this.max -this.options.offset);

		this.step = this.toStep(position);

		//this.moveKnob = this.knob;
		this.bkg.style.clip = "rect(0px "+  (parseInt(this.toPosition(this.step)) +3) + "px 7px 0px)"  
		//status =this.bkg.style.clip; //Debug
		this.checkStep();
		this.end();
		this.fireEvent('onTick', position);
	},

	draggedKnob: function(mx){
		var lim = {}; var mi,mx;
		if(mx==null) {
			this.step = this.toStep(this.drag.value.now[this.z]);	 
			this.checkStep();
		}else {
			this.maxstep = this.toStep(this.maxdrag.value.now[this.z]); 
			this.checkStep(1);
		}
	},
	checkStep: function(mx){
		var lim = {}; var mi,mx;
		var limm = {};
		if(mx==null) {if (this.previousChange != this.step){this.previousChange = this.step;}}
		else {if (this.maxPreviousChange != this.maxstep){this.maxPreviousChange = this.maxstep;}}

		if(this.maxknob!=null) {

			mi = - this.options.offset; 
			mx= parseInt(this.maxknob.getStyle('left')) - this.options.offset-4 ;
			//mx= parseInt(this.maxknob.getStyle('left')) - this.options.offset ;
			lim[this.z] = [mi, mx];
			this.drag.options.limit = lim;
		

			mi = parseInt(this.knob.getStyle('left'))-this.options.offset+22; 
			//mi = parseInt(this.knob.getStyle('left'))-this.options.offset; 
			
			mx= this.max - this.options.offset;
			limm[this.z] = [mi, mx];
			this.maxdrag.options.limit = limm; 

			if(this.step < this.maxstep){
				this.fireEvent('onChange', { minpos: this.step, maxpos: this.maxstep });
				//this.clip_l = parseInt(this.knob.getStyle('left'));
			}
			else{
				this.fireEvent('onChange', { minpos: this.maxstep, maxpos: this.step });
				//this.clip_l = (parseInt(this.maxknob.getStyle('left')) + 10) ;
			}	
			this.clip_l = parseInt(this.knob.getStyle('left')) + 10;
			//var w = Math.abs(parseInt(this.knob.getStyle('left')) - parseInt(this.maxknob.getStyle('left'))) + 3;	
			var w = Math.abs(parseInt(this.knob.getStyle('left')) - parseInt(this.maxknob.getStyle('left')));
			//if(w > 3) w = w+3;
			
			var r = parseInt(this.clip_l + w); 
			this.bkg.style.clip = "rect(0px "+  r + "px 7px "+ this.clip_l + "px)"  
			//status =this.bkg.style.clip  + " w= " + w //Debug

		}else {  
			this.fireEvent('onChange', this.step);
			this.bkg.style.clip = "rect(0px "+  (parseInt(this.drag.value.now[this.z]) +3)  + "px 7px 0px)"  
		}
	},
	end: function(){
		if (this.previousEnd !== this.step || (this.maxknob != null && this.maxPreviousEnd != this.maxstep)) {
			this.previousEnd = this.step;
			if(this.maxknob != null) {
				this.maxPreviousEnd = this.maxstep;
				if(this.step < this.maxstep)
					this.fireEvent('onComplete', { minpos: this.step + '', maxpos: this.maxstep + '' });
				else    
					this.fireEvent('onComplete', { minpos: this.maxstep + '', maxpos: this.step + '' });
			}else{  
				this.fireEvent('onComplete', this.step + '');
			}
		}
	},
	
	toStep: function(position){
		return Math.round((position + this.options.offset) / this.max * this.options.steps) + this.options.start;
	},

	toPosition: function(step){
		return (this.max * step / this.options.steps) - (this.max * this.options.start / this.options.steps) - this.options.offset;
	}

});
range_slide.implement(new Events);
range_slide.implement(new Options);

/**
 * Calendar Eightysix - MooTools datepicker and calendar class
 * @version 1.0.1
 * by dev.base86.com
 */

var CalendarEightysix = new Class({
	Implements: Options,
	
	options: {
		'slideDuration': 500,
		'fadeDuration': 200,
		'toggleDuration': 200,
		'fadeTransition': Fx.Transitions.linear,
		'slideTransition': Fx.Transitions.Quart.easeOut,
		
		'prefill': true,
		'defaultDate': null,
		'linkWithInput': true,
		
		'theme': 'default',
		'defaultView': 'month',
		'startMonday': false,
		'alwaysShow': false,
		'injectInsideTarget': false,
		'format': '%n/%d/%Y',
		'alignX': 'right',
		'alignY': 'ceiling',
		'offsetX': 0,
		'offsetY': 0,
		
		'draggable': false,
		'pickable': true,
		'toggler': null,
		'pickFunction': $empty,
		'disallowUserInput': false,
		
		'minDate': null,
		'maxDate': null,
		'excludedWeekdays': null,
		'excludedDates': null,
		
		'createHiddenInput': false,
		'hiddenInputName': 'date',
		'hiddenInputFormat': '%t'
	},
	
	initialize: function(target, options) {
		this.setOptions(options);
		
		this.target = $(target);
		this.transitioning = false;
		
		//Extend Date with unix timestamp parser
		Date.defineParser({
		    re: /^[0-9]{10}$/,
		    handler: function(bits) { return new Date.parse('Jan 01 1970').set('seconds', bits[0]);	}
		});
		
		//Selected date
		if($defined(this.options.defaultDate)) this.selectedDate = new Date().parse(this.options.defaultDate).clearTime();
		else if(this.options.linkWithInput && $chk(this.target.get('value'))) this.selectedDate = new Date().parse(this.target.get('value')).clearTime();
		if(!$defined(this.selectedDate) || !this.selectedDate.isValid()) this.selectedDate = new Date();
		this.viewDate = this.selectedDate.clone().set('date', 1).clearTime();
		
		//Base
		var innerHtml = '<div class="wrapper"><div class="header"><div class="arrow-left"></div><div class="arrow-right"></div><div class="label clickable"></div></div>'+
							'<div class="body"><div class="inner"><div class="container a"></div><div class="container b"></div></div></div><div class="footer"></div></div>';
		this.element = new Element('div', { 'class': 'calendar-eightysix', 'html': innerHtml, 'style': 'display: '+ (this.options.alwaysShow ? 'block' : 'none') }).addClass(this.options.theme);
		
		if(this.options.injectInsideTarget) this.element.injectBottom(this.target);
		else {
			this.element.injectBottom($(document.body));
			this.position();
			window.addEvent('resize', this.position.bind(this));
		}
		
		this.currentContainer = this.element.getElement('.container.a').setStyle('z-index', 999);
		this.tempContainer = this.element.getElement('.container.b').setStyle('z-index', 998);
		
		//Header
		this.header = this.element.getElement('.header');
		this.label = this.header.getElement('.label');
		this.arrowLeft = this.header.getElement('.arrow-left');
		this.arrowRight = this.header.getElement('.arrow-right');
		
		this.label.addEvent('click', this.levelUp.bind(this));
		this.arrowLeft.addEvent('click', this.slideLeft.bind(this));
		this.arrowRight.addEvent('click', this.slideRight.bind(this));
		
		//Date range
		if($defined(this.options.minDate)) {
			this.options.minDate = Date.parse(this.options.minDate).clearTime();
			if(!this.options.minDate.isValid()) this.options.minDate = null;
		}
		if($defined(this.options.maxDate)) {
			this.options.maxDate = Date.parse(this.options.maxDate).clearTime();
			if(!this.options.maxDate.isValid()) this.options.maxDate = null;
		}
		
		//Excluded dates
		if($defined(this.options.excludedDates)) {
			var excludedDates = [];
			this.options.excludedDates.each(function(date) {
				excludedDates.include(this.format(new Date().parse(date).clearTime(), '%t'));
			}.bind(this));
			this.options.excludedDates = excludedDates;
		}
		
		//Dragger
		if(this.options.draggable && !this.options.injectInsideTarget) {
			this.header.addClass('dragger');
			new Drag(this.element, { 'handle': this.header });
		}
		
		//Hidden input
		if(this.options.createHiddenInput) {
			this.hiddenInput = new Element('input', { 'type': 'hidden', 'name': this.options.hiddenInputName }).injectAfter(this.target);
		}
		
		//Prefill date
		if(this.options.prefill) this.pick();
		
		//Link with input
		if(!this.options.disallowUserInput && this.options.linkWithInput && this.target.get('tag') == 'input') {
			this.target.addEvent('keyup', function() {
				this.setDate(this.target.get('value'), false);
			}.bind(this));
		}
		
		//Disallow input
		if(this.options.disallowUserInput && this.target.get('tag') == 'input') 
			this.target.addEvents({ 'keydown': ($lambda(false)), 'contextmenu': ($lambda(false)) });
		
		//Toggler
		if($defined(this.options.toggler)) this.options.toggler = $(this.options.toggler);
		
		//Show / hide events
		($defined(this.options.toggler) ? this.options.toggler : this.target).addEvents({
			'focus': this.show.bind(this), 'click': this.show.bind(this)
		});
		
		if(!this.options.alwaysShow) document.addEvent('mousedown', this.outsideClick.bind(this));
		MooTools.lang.addEvent('langChange', function() { this.render(); this.pick(); }.bind(this));
		
		//View
		this.view = this.options.defaultView;
		this.render();
	},
	
	render: function() {
		this.currentContainer.empty();
		
		switch(this.view) {
			case 'decade': this.renderDecade(); break;
			case 'year': this.renderYear(); break;
			default: this.renderMonth();
		}
	},
	
	renderMonth: function() {
		this.view = 'month';
		this.currentContainer.empty().addClass('month');
		if(this.options.pickable) this.currentContainer.addClass('pickable');
		
		var lang = MooTools.lang.get('Date'), weekdaysCount = this.viewDate.format('%w') - (this.options.startMonday ? 1 : 0);
		if(weekdaysCount == -1) weekdaysCount = 6;
		var today = new Date();
		
		//Label
		this.label.set('html', lang.months[this.viewDate.get('month')] +' '+ this.viewDate.format('%Y'));
		
		//Day label row
		var row = new Element('div', { 'class': 'row' }).injectBottom(this.currentContainer);
		for(var i = (this.options.startMonday ? 1 : 0); i < (this.options.startMonday ? 8 : 7); i++) {
			var day = new Element('div', { 'html': lang.days[this.options.startMonday && i == 7 ? 0 : i] }).injectBottom(row);
			day.set('html', day.get('html').substr(0, 2));
		}
		
		//Add days for the beginning non-month days
		row = new Element('div', { 'class': 'row' }).injectBottom(this.currentContainer);
		y = this.viewDate.clone().decrement('month').getLastDayOfMonth();
		for(var i = 0; i < weekdaysCount; i++) {
			this.injectDay(row, this.viewDate.clone().decrement('month').set('date', y - (weekdaysCount - i) + 1), true);
		}
		
		//Add month days
		for(var i = 1; i <= this.viewDate.getLastDayOfMonth(); i++) {
			this.injectDay(row, this.viewDate.clone().set('date', i));
			if(row.getChildren().length == 7) {
				row = new Element('div', { 'class': 'row' }).injectBottom(this.currentContainer);
			}
		}
		
		//Add outside days
		var y = 8 - row.getChildren().length, startDate = this.viewDate.clone().increment('month').set('date', 1);
		for(var i = 1; i < y; i++) {
			this.injectDay(row, startDate.clone().set('date', i), true);
		}
		
		//Always have six rows
		for(var y = this.currentContainer.getElements('.row').length; y < 7; y++) {
			row = new Element('div', { 'class': 'row' }).injectBottom(this.currentContainer);
			for(var z = 0; z < 7; z++) {
				this.injectDay(row, startDate.clone().set('date', i), true);
				i++;
			}
		}
		
		this.renderAfter();
	},
	
	//Used by renderMonth
	injectDay: function(row, date, outside) {
		today = new Date();
		
		var day = new Element('div', { 'html': date.get('date') }).injectBottom(row);
		day.date = date;
		if(outside) day.addClass('outside');
		
		if(($defined(this.options.minDate) && this.format(this.options.minDate, '%t') > this.format(date, '%t')) || 
		   ($defined(this.options.maxDate) && this.format(this.options.maxDate, '%t') < this.format(date, '%t')) ||
		   ($defined(this.options.excludedWeekdays) && this.options.excludedWeekdays.contains(date.format('%w').toInt())) ||
		   ($defined(this.options.excludedDates) && this.options.excludedDates.contains(this.format(date, '%t'))))
			day.addClass('non-selectable');
		else if(this.options.pickable) day.addEvent('click', this.pick.bind(this));
		
		if(date.format('%x') == today.format('%x')) day.addClass('today');
		if(date.format('%x') == this.selectedDate.format('%x')) day.addClass('selected');
	},
	
	renderYear: function() {
		this.view = 'year';
		this.currentContainer.addClass('year-decade');
		var today = new Date(), lang = MooTools.lang.get('Date').months;
		
		//Label
		this.label.set('html', this.viewDate.format('%Y'));
		
		var row = new Element('div', { 'class': 'row' }).injectBottom(this.currentContainer);
		for(var i = 1; i < 13; i++) {
			var month = new Element('div', { 'html': lang[i - 1] }).injectBottom(row);
			month.set('html', month.get('html').substr(0, 3));  //Setting and getting the innerHTML takes care of html entity problems (e.g. [M&a]uml;r => [Mär]z)
			var iMonth = this.viewDate.clone().set('month', i - 1);
			month.date = iMonth;
			
			if(($defined(this.options.minDate) && this.format(this.options.minDate.clone().set('date', 1), '%t') > this.format(iMonth, '%t')) ||
			   ($defined(this.options.maxDate) && this.format(this.options.maxDate.clone().set('date', 1), '%t') < this.format(iMonth, '%t')))
				month.addClass('non-selectable');
			else month.addEvent('click', this.levelDown.bind(this));
			
			if(i - 1 == today.get('month') && this.viewDate.get('year') == today.get('year')) month.addClass('today');
			if(i - 1 == this.selectedDate.get('month') && this.viewDate.get('year') == this.selectedDate.get('year')) month.addClass('selected');
			if(!(i % 4) && i != 12) row = new Element('div', { 'class': 'row' }).injectBottom(this.currentContainer);
		}
		
		this.renderAfter();
	},
	
	renderDecade: function() {
		this.label.removeClass('clickable');
		this.view = 'decade';
		this.currentContainer.addClass('year-decade');
		var today = new Date();
		
		var viewYear, startYear;
		viewYear = startYear = this.viewDate.format('%Y').toInt();
		while(startYear % 12) startYear--;
		
		//Label
		this.label.set('html', startYear +' &#150; '+ (startYear + 11));
		
		var row = new Element('div', { 'class': 'row' }).injectBottom(this.currentContainer);
		for(var i = startYear; i < startYear + 12; i++) {
			var year = new Element('div', { 'html': i }).injectBottom(row);
			var iYear = this.viewDate.clone().set('year', i);
			year.date = iYear;
			
			if(($defined(this.options.minDate) && this.options.minDate.get('year') > i) ||
			   ($defined(this.options.maxDate) && this.options.maxDate.get('year') < i)) year.addClass('non-selectable');
			else year.addEvent('click', this.levelDown.bind(this));
			
			if(i == today.get('year')) year.addClass('today');
			if(i == this.selectedDate.get('year')) year.addClass('selected');
			if(!((i + 1) % 4) && i != startYear + 11) row = new Element('div', { 'class': 'row' }).injectBottom(this.currentContainer);
		}
		
		this.renderAfter();
	},
	
	renderAfter: function() {
		//Iterate rows and add classes and remove navigation if nessesary
		var rows = this.currentContainer.getElements('.row');
		for(var i = 0; i < rows.length; i++) {
			rows[i].set('class', 'row '+ ['a', 'b', 'c', 'd', 'e', 'f', 'g'][i] +' '+ (i % 2 ? 'even' : 'odd')).getFirst().addClass('first');
			rows[i].getLast().addClass('last');
			
			if(i == (this.view == 'month' ? 1 : 0) && $defined(this.options.minDate) && this.format(this.options.minDate, '%t') >= this.format(rows[i].getFirst().date, '%t')) 
				this.arrowLeft.setStyle('visibility', 'hidden');
			if(i == rows.length - 1 && $defined(this.options.maxDate)) {
				if((this.view == 'month' && this.format(this.options.maxDate, '%t') <= this.format(rows[i].getLast().date, '%t')) ||
				   (this.view == 'year' && this.format(this.options.maxDate, '%t') <= this.format(rows[i].getLast().date.clone().increment('month'), '%t')) ||
				   (this.view == 'decade' && this.format(this.options.maxDate, '%t') <= this.format(rows[i].getLast().date.clone().increment('year'), '%t')))
					this.arrowRight.setStyle('visibility', 'hidden');
			}
		};
	},
	
	slideLeft: function() {
		this.switchContainers();
		
		//Render new view
		switch(this.view) {
			case 'month':  this.viewDate.decrement('month'); break;
			case 'year':   this.viewDate.decrement('year'); break;
			case 'decade': this.viewDate.set('year', this.viewDate.get('year') - 12); break;
		}
		this.render();
		
		//Tween the new view in and old view out
		this.currentContainer.set('tween', { 'duration': this.options.slideDuration, 'transition': this.options.slideTransition }).tween('left', [-this.currentContainer.getWidth(), 0]);
		this.tempContainer.set('tween', { 'duration': this.options.slideDuration, 'transition': this.options.slideTransition }).tween('left', [0, this.tempContainer.getWidth()]);
	},
	
	slideRight: function() {
		this.switchContainers();
		
		//Render new view
		switch(this.view) {
			case 'month':  this.viewDate.increment('month'); break;
			case 'year':   this.viewDate.increment('year'); break;
			case 'decade': this.viewDate.set('year', this.viewDate.get('year') + 12); break;
		}
		this.render();
		
		//Tween the new view in and old view out
		this.currentContainer.set('tween', { 'duration': this.options.slideDuration, 'transition': this.options.slideTransition }).tween('left', [this.currentContainer.getWidth(), 0]);
		this.tempContainer.set('tween', { 'duration': this.options.slideDuration, 'transition': this.options.slideTransition }).tween('left', [0, -this.currentContainer.getWidth()]);
	},
	
	levelDown: function(e) {
		if(this.transitioning) return;
		this.switchContainers();
		this.viewDate = e.target.date;
		
		//Render new view
		switch(this.view) {
			case 'year': this.renderMonth(); break;
			case 'decade': this.renderYear(); break;
		}
		
		//Tween the new view in and old view out
		this.transitioning = true;
		this.currentContainer.set('tween', { 'duration': this.options.fadeDuration, 'transition': this.options.fadeTransition, 
											 'onComplete': function() { this.transitioning = false }.bind(this) }).setStyles({'opacity': 0, 'left': 0}).fade('in');
		this.tempContainer.set('tween', { 'duration': this.options.fadeDuration, 'transition': this.options.fadeTransition }).fade('out');
	},
	
	levelUp: function() {
		if(this.view == 'decade' || this.transitioning) return;
		this.switchContainers();
		
		//Set viewdates and render
		switch(this.view) {
			case 'month': this.renderYear(); break;
			case 'year':  this.renderDecade(); break;
		}
		
		//Tween the new view in and old view out
		this.transitioning = true;
		this.currentContainer.set('tween', { 'duration': this.options.fadeDuration, 'transition': this.options.fadeTransition, 
											 'onComplete': function() { this.transitioning = false }.bind(this) }).setStyles({'opacity': 0, 'left': 0}).fade('in');
		this.tempContainer.set('tween', { 'duration': this.options.fadeDuration, 'transition': this.options.fadeTransition }).fade('out');
	},
	
	switchContainers: function() {
		this.currentContainer = this.currentContainer.hasClass('a') ? this.element.getElement('.container.b') : this.element.getElement('.container.a');
		this.tempContainer = this.tempContainer.hasClass('a') ? this.element.getElement('.container.b') : this.element.getElement('.container.a');
		this.currentContainer.empty().removeClass('month').removeClass('year-decade').setStyles({ 'opacity': 1, 'display': 'block', 'z-index': 999 });
		this.tempContainer.setStyle('z-index', 998);
		
		this.label.addClass('clickable');
		this.arrowLeft.setStyle('visibility', 'visible');
		this.arrowRight.setStyle('visibility', 'visible');
	},
	
	pick: function(e) {
		if($defined(e)) {
			this.selectedDate = e.target.date;
			this.element.getElements('.selected').removeClass('selected');
			e.target.addClass('selected');
		}
		
		var value = this.format(this.selectedDate);
		
		if(!this.options.injectInsideTarget) {
			switch(this.target.get('tag')) {
				case 'input': this.target.set('value', value); break;
				default: this.target.set('html', value);
			}
			(this.hide.bind(this)).delay(150);
		}
		
		if($defined(this.hiddenInput)) this.hiddenInput.set('value', this.format(this.selectedDate, this.options.hiddenInputFormat));
		this.options.pickFunction(this.selectedDate);
	},
	
	position: function() {
		var top, left;
		var coordinates = this.target.getCoordinates();
		
		switch(this.options.alignX) {
			case 'left':
				left = coordinates.left;
				break;
			case 'middle':
				left = coordinates.left + (coordinates.width / 2) - (this.element.getWidth() / 2);
				break;
			case 'right': default:
				left = coordinates.left + coordinates.width;
		}
		
		switch(this.options.alignY) {
			case 'bottom':
				top = coordinates.top + coordinates.height;
				break;
			case 'top':
				top = coordinates.top - this.element.getHeight();
				break;
			case 'ceiling': default:
				top = coordinates.top;
		}
		
		left += this.options.offsetX.toInt();
		top += this.options.offsetY.toInt();
		
		this.element.setStyles({ 'top': top, 'left': left });
	},
	
	show: function() {
		if(!this.visible & !this.options.alwaysShow) {
			this.visible = true;
			if(!Browser.Engine.trident) {
				this.element.setStyles({ 'opacity': 0, 'display': 'block' });
				if(!this.options.injectInsideTarget) this.position();
				this.element.set('tween', { 'duration': this.options.toggleDuration, 'transition': this.options.fadeTransition }).fade('in');
			}  else {
				this.element.setStyles({ 'opacity': 1, 'display': 'block' });
				if(!this.options.injectInsideTarget) this.position();
			}
		}
	},
	
	hide: function() {
		if(this.visible & !this.options.alwaysShow) {
			this.visible = false;
			if(!Browser.Engine.trident) {
				this.element.set('tween', { 'duration': this.options.toggleDuration, 'transition': this.options.fadeTransition,
											'onComplete': function() { this.element.setStyle('display', 'none') }.bind(this) }).fade('out');
			} else this.element.setStyle('display', 'none');
		}
	},
	
	toggle: function() {
		if(this.visible) this.hide();
		else this.show();
	},
	
	format: function(date, format) {
		if(!$defined(format)) format = this.options.format;
		if(!$defined(date)) return;
		format = format.replace(/%([a-z%])/gi,
			function($1, $2) {
				switch($2) {
					case 'D': return date.get('date');
					case 'n': return date.get('mo') + 1;
					case 't': return (date.getTime() / 1000).toInt();
				}
				return '%'+ $2;
			}
		);
		return date.format(format);
	},
	
	outsideClick: function(e) {
		if(this.visible) {
			var elementCoords = this.element.getCoordinates();
			var targetCoords  = this.target.getCoordinates();
			if(((e.page.x < elementCoords.left || e.page.x > (elementCoords.left + elementCoords.width)) ||
			    (e.page.y < elementCoords.top  || e.page.y > (elementCoords.top + elementCoords.height))) &&
			   ((e.page.x < targetCoords.left  || e.page.x > (targetCoords.left + targetCoords.width)) ||
			    (e.page.y < targetCoords.top   || e.page.y > (targetCoords.top + targetCoords.height))) ) this.hide();
		}
	},
	
	//Version 1.0.1 addition, can easily be called from outside
	setDate: function(value, pick) {
		if(!$defined(pick)) pick = true;
		if($type(value) == 'date') {
			var date = value.clearTime();
		} else {
			var date = $chk(value) ? new Date().parse(this.target.get('value')).clearTime() : new Date().clearTime();
		}
		if(date.isValid()) {
			this.selectedDate = date.clone();
			this.viewDate = this.selectedDate.clone().set('date', 1);
			this.render();
			if(pick) this.pick();
		}
	}
});

