SortableManager = function () {
    this.thead = null;
    this.tbody = null;
    this.columns = [];
    this.rows = [];
    this.sortState = {};
    this.sortkey = 0;
};

mouseOverFunc = function () {
    addElementClass(this, "over");
};

mouseOutFunc = function () {
    removeElementClass(this, "over");
};

ignoreEvent = function (ev) {
    if (ev && ev.preventDefault) {
        ev.preventDefault();
        ev.stopPropagation();
    } else if (typeof(event) != 'undefined') {
        event.cancelBubble = false;
        event.returnValue = false;
    }
};

function zeroPad(num,count)
{
	var numZeropad = num + '';
	while(numZeropad.length < count) {
		numZeropad = "0" + numZeropad;
	}
	return numZeropad;
}

update(SortableManager.prototype, {

    "initWithTable": function (table) {
        /***

            Initialize the SortableManager with a table object
        
        ***/
        // Ensure that it's a DOM element
        var initsortorder = false;
        table = getElement(table);
        if(table != null) {    
	        // Find the thead
	        this.thead = table.getElementsByTagName('thead')[0];
	        // get the mochi:format key and contents for each column header
	        var cols = this.thead.getElementsByTagName('th');
	        for (var i = 0; i < cols.length; i++) {
	            var node = cols[i];
	            var attr = null;
	            try {
	                attr = node.getAttribute("mochi:format");
	            } catch (err) {
	                // pass
	            }
	            var o = node.childNodes;
	            this.columns.push({
	                "format": attr,
	                "element": node,
	                "proto": node.cloneNode(true)
	            });
	        }
	        // scrape the tbody for data
	        this.tbody = table.getElementsByTagName('tbody')[0];
	        // every row
	        var rows = this.tbody.getElementsByTagName('tr');
	        for (var i = 0; i < rows.length; i++) {
	            // every cell
	            var row = rows[i];
	            var cols = row.getElementsByTagName('td');
	            var rowData = [];
	            for (var j = 0; j < cols.length; j++) {
	                // scrape the text and build the appropriate object out of it
	                var cell = cols[j];
	                var obj = scrapeText(cell);
	                switch (this.columns[j].format) {
	                    case 'isodate':
	                        obj = isoDate(obj);
	                        break;
	                    case 'date':
	                        obj = obj;
	                        break;                        
	                    case 'str':
	                        break;
	                    case 'astr':
	                    	initsortorder = true;
	                        break;	                        
	                    case 'istr':
	                        obj = obj.toLowerCase();
	                        break;
	                    case 'az':
	                    	//fucking IE6: gibt kein \n im Zelleninhalt zurück
	                    	if(obj.indexOf("\n") >= 0) {  
		                    	var el = obj.split("\n");
		                    	el = el[1].split("/");
		                    	if(el.length == 2){
		                    		obj = el[1]+zeroPad(el[0],7);
		                    	} else if(el.length == 3){
		                    		obj = el[2]+zeroPad(el[0],7);
		                    	}
	                		} else {
		                    	var el = obj.split("/");
		                    	if(el.length == 2){
		                    		obj = el[1].substring(0,4)+zeroPad(el[0],7);
		                    	} else if(el.length == 3){
		                    		obj = el[2].substring(0,4)+zeroPad(el[0],7);
		                    	}
	                		}
	                    	break;
	                    // cases for numbers, etc. could be here
	                    default:
	                        break;
	                }
	                rowData.push(obj);
	            }
	            // stow away a reference to the TR and save it
	            rowData.row = row.cloneNode(true);
	            this.rows.push(rowData);
	
	        }
	
	        // do initial sort on first column
	        this.drawSortedRows(this.sortkey, initsortorder, false);
        }

    },

    "onSortClick": function (name) {
        /***

            Return a sort function for click events

        ***/
        return method(this, function () {
            log('onSortClick', name);
            var order = this.sortState[name];
            if (order == null) {
                order = true;
            } else if (name == this.sortkey) {
                order = !order;
            }
            this.drawSortedRows(name, order, true);
        });
    },

    "drawSortedRows": function (key, forward, clicked) {
        /***

            Draw the new sorted table body, and modify the column headers
            if appropriate

        ***/
        log('drawSortedRows', key, forward);
        this.sortkey = key;
        // sort based on the state given (forward or reverse)
        var cmp = (forward ? keyComparator : reverseKeyComparator);
        this.rows.sort(cmp(key));
        // save it so we can flip next time
        this.sortState[key] = forward;
        // get every "row" element from this.rows and make a new tbody
        var newBody = TBODY(null, map(itemgetter("row"), this.rows));
        // swap in the new tbody
        this.tbody = swapDOM(this.tbody, newBody);
        for (var i = 0; i < this.columns.length; i++) {
            var col = this.columns[i];
            var node = col.proto.cloneNode(true);
            // remove the existing events to minimize IE leaks
            col.element.onclick = null;
            col.element.onmousedown = null;
            col.element.onmouseover = null;
            col.element.onmouseout = null;
            // set new events for the new node
            node.onclick = this.onSortClick(i);
            node.onmousedown = ignoreEvent;
            node.onmouseover = mouseOverFunc;
            node.onmouseout = mouseOutFunc;
            // if this is the sorted column
            if (key == i) {
                // \u2193 is down arrow, \u2191 is up arrow
                // forward sorts mean the rows get bigger going down
                var arrow = (forward ? "\u2193" : "\u2191");
                // add the character to the column header
                node.appendChild(SPAN(null, arrow));
                if (clicked) {
                    node.onmouseover();
                }
            }
 
            // swap in the new th
            col.element = swapDOM(col.element, node);
        }
    }
});

sortableManager = new SortableManager();

addLoadEvent(function () {
    sortableManager.initWithTable('sortable_table');
});

// rewrite the view-source links
addLoadEvent(function () {
    var elems = getElementsByTagAndClassName("A", "view-source");
    var page = "sortable_tables/";
    for (var i = 0; i < elems.length; i++) {
        var elem = elems[i];
        var href = elem.href.split(/\//).pop();
        elem.target = "_blank";
        elem.href = "../view-source/view-source.html#" + page + href;
    }
});

