/*****************************************
rock-utils.js
Contains various utility classes.
Depends on: rock.js, prototype.js
*******************************************/
 
// define namespace
rock.namespace("rock.util");

/*================================================
   Class DomUtil
 =================================================*/
rock.util.DomUtil = {};

/**
 * Get an element's inner text as string.
 */ 
rock.util.DomUtil.getInnerText = function(el) {
	if (typeof el == "string") return el;
	if (typeof el == "undefined") { return el };
	if (el.innerText) return el.innerText;	//Not needed but it is faster
	var str = "";
	
	var cs = el.childNodes;
	var l = cs.length;
	for (var i = 0; i < l; i++) {
		switch (cs[i].nodeType) {
			case 1: //ELEMENT_NODE
				str += rock.util.DomUtil.getInnerText(cs[i]);
				break;
			case 3:	//TEXT_NODE
				str += cs[i].nodeValue;
				break;
		}
	}
	return str;
}

/**
 * Get a given element's most immediate ancestor node that matchs a given tag name.
 */
rock.util.DomUtil.getParent = function(el, pTagName) {
	if (el == null) {
		return null;
	} else if (el.nodeType == 1 && 
		el.tagName.toLowerCase() == pTagName.toLowerCase()){
		return el;
	} else {
		return rock.util.DomUtil.getParent(el.parentNode, pTagName);
	}
}

/**
 * Deprecated. Use JS's builtin getElementsByTagName(tagName).
 */
rock.util.DomUtil.getChildrenByTagName = function(element, tagName){
    var result = [];
    $A(element.childNodes).each(function(node){
	if(node.tagName){
	    if(node.tagName.toLowerCase() == tagName.toLowerCase()){
	      result.push(node);		  
	    }
	}
    });

    return result;
}

/**
 * Given an element, get its children which have a CSS class name.
 */
rock.util.DomUtil.getChildrenByClassName = function(element, className){
    var result = [];
    $A(element.childNodes).each(function(node){
	if(Element.hasClassName(node, className)){
	  result.push(node);
	}
    });

    return result;
}

/**
 * Given an element, get its children which have a tag name and CSS class name.
 */
rock.util.DomUtil.getChildrenByTagAndClassName = function(element, tagName, className){
    var result = [];
    var children = element.getElementsByTagName(tagName);
    if(children){
      $A(children).each(function(node){
	if(Element.hasClassName(node, className)){
	  result.push(node);
	}
      });
    }

    return result;
}

rock.util.DomUtil.findNextSibling = function(element, tagName){
	var nextSibling = element.nextSibling;
	if(!nextSibling){
		return nextSibling;
	}
	if(nextSibling.tagName && 
		nextSibling.tagName.toLowerCase() == tagName.toLowerCase()){
		return nextSibling;
	}
	
	return rock.util.DomUtil.findNextSibling(nextSibling);
}
	
rock.util.DomUtil.removeAllChildren = function(element){
	var nodes = $A(element.childNodes);
	nodes.each(function(node){
		element.removeChild(node);
	});
}

/**
 * Given a container, removing all existing children with one child.
 */
rock.util.DomUtil.replaceChildren = function(container, child){
	this.removeAllChildren(container);
	container.appendChild(child);
}

	
/**
 * Show an element by adding CSS class invisible and removing class visible.
 */
rock.util.DomUtil.showElement = function(element){
  if(Element.hasClassName(element, 'invisible')){
    Element.removeClassName(element, 'invisible');
  }
  if(!Element.hasClassName(element, 'visible')){
    Element.addClassName(element, 'visible');
  }
}   
	
/**
 * Hide an element by removing CSS class visible and adding class invisible.
 */	
rock.util.DomUtil.hideElement = function(element){
  if(Element.hasClassName(element, 'visible')){
    Element.removeClassName(element, 'visible');
  }
  if(!Element.hasClassName(element, 'invisible')){
    Element.addClassName(element, 'invisible');
  }
} 	
	
/**
 * Insert an element into a container. If the container has children,
 * the element is inserted before the 1st child. 
 */
rock.util.DomUtil.insertElement = function(container, element){
	if(container.hasChildNodes()){
		container.insertBefore(element, container.firstChild);
	}else{
		container.appendChild(element);
	}
}


/**
 * Dynamically show a label in a container element. 
 * If not exit, the loading elememt will be created and inserted 
 * before the container's 1st child. 
 * The label element will be a div element bearing the given classname. 
 * The label element contains a nested span element that bears the given
 * message. "Show" means adding "visible" classname to the label element,
 * and removing "invisible" classname.
 */	    
rock.util.DomUtil.showLabel = function(msgHtml, container, className, appended){
    var labelElem;
    var labelElems = rock.util.DomUtil.getChildrenByTagAndClassName(container, "div", className);
    if(labelElems && labelElems.length > 0){ // the label element exists
	labelElem = labelElems[0]; // it's always the 1st one
	// update msg
	//jmaki.log("Found div with classname " + className);
	var innerElems = labelElem.getElementsByTagName("span");
	if(innerElems && innerElems.length > 0){
	    //jmaki.log("Found inner span");
	    innerElems[0].innerHTML = msgHtml;
	}
    }else{ // create the element
	labelElem = Builder.node('div', {className:className});
	var innerElem = Builder.node('span');
	innerElem.innerHTML = msgHtml; 
	labelElem.appendChild(innerElem); 
	if(appended){ // append as last child
	    //jmaki.log("rock-utils.js: appending as last child. classname: " + className);
	    container.appendChild(labelElem);
	}else{ // insert as 1st child
	    //jmaki.log("rock-utils.js: inserting as 1st child. classname: " + className);
	    rock.util.DomUtil.insertElement(container, labelElem);
	}
    }
    rock.util.DomUtil.showElement(labelElem);
}   	   

/**
 * Dynamically hide the label element within a container. "Hide" means adding "invisible" classname
 * to the element, and removing "visible" classname.
 */
rock.util.DomUtil.hideLabel = function(container, className){
    var labelElems = rock.util.DomUtil.getChildrenByTagAndClassName(container, "div", className);
    if(labelElems && labelElems.length > 0){ // the label element exists
	rock.util.DomUtil.hideElement(labelElems[0]);
    }
}   	   

/**
 * Dynamically show a spinning icon in a container element. 
 * The icon element will be the last child of the container elmeent
 * if the argument appended is true. Otherwise, it will be the 1st child.
 * It is an img element bearing the given classname.
 */	    
rock.util.DomUtil.showIcon = function(container, appended, className, iconUrl){
    var iconElem;
    var iconElems = rock.util.DomUtil.getChildrenByTagAndClassName(container, "img", className);
    if(iconElems && iconElems.length > 0){ // the loading icon exists
	iconElem = iconElems[0]; // it's always the 1st one
    }else{ // create the element
	iconElem = Builder.node('img', {src:iconUrl, className:className, border:'0'});
	if(appended){ //the icon will be the last child
	    container.appendChild(iconElem);
	}else{ // the icon will be the 1st child
	    rock.util.DomUtil.insertElement(container, iconElem);
	}
    }
    rock.util.DomUtil.showElement(iconElem);
}   	   

/**
 * Dynamically hide icon in a container.
 */
rock.util.DomUtil.hideIcon = function(container, className){
    var iconElems = rock.util.DomUtil.getChildrenByTagAndClassName(container, "img", className);
    if(iconElems && iconElems.length > 0){ // the loading icon exists
	rock.util.DomUtil.hideElement(iconElems[0]);
    }
}   	   



/**
 Alternates table rows by adding class name even or add.
 NOTE: Assume the table does not contain header row.
 */
rock.util.DomUtil.alternateTableRows = function(table) {
		// Take object table and get all it's tbodies.
		var tableBodies = rock.util.DomUtil.getChildrenByTagName(table, "TBODY");
		if(!tableBodies){
			tableBodies = table;
		}
		
		// Loop through these tbodies
		for (var i = 0; i < tableBodies.length; i++) {
			// Take the tbody, and get all it's rows
			var tableRows = rock.util.DomUtil.getChildrenByTagName(tableBodies[i], "TR");
			// Loop through these rows
			// Start at 0 because there is no header row
			for (var j = 0; j < tableRows.length; j++) {
				// Check if j is even, and apply classes for both possible results
				if ( (j % 2) == 0  ) {
					if(Element.hasClassName(tableRows[j], 'odd')){
						Element.removeClassName(tableRows[j], 'odd');
					}
					if(!Element.hasClassName(tableRows[j], 'even')){
						Element.addClassName(tableRows[j], 'even');
					}
				} else {
					if(Element.hasClassName(tableRows[j], 'even')){
						Element.removeClassName(tableRows[j], 'even');
					}
					if(!Element.hasClassName(tableRows[j], 'odd')){
						Element.addClassName(tableRows[j], 'odd');
					}
				} 
			}
		}
}

/**
 Alternates table rows by adding class name even or add.
 NOTE: Assume the 1st row is the header row.
 */
rock.util.DomUtil.alternateTableRowsWithHeader = function(table) {
		// Take object table and get all it's tbodies.
		var tableBodies = rock.util.DomUtil.getChildrenByTagName(table, "TBODY");
		if(!tableBodies){
			tableBodies = table;
		}
		
		// Loop through these tbodies
		for (var i = 0; i < tableBodies.length; i++) {
			// Take the tbody, and get all it's rows
			var tableRows = rock.util.DomUtil.getChildrenByTagName(tableBodies[i], "TR");
			// Loop through these rows
			// Start at 1 because we want to leave the heading row untouched
			for (var j = 1; j < tableRows.length; j++) {
				// Check if j is even, and apply classes for both possible results
				if ( (j % 2) == 0  ) {
					if(Element.hasClassName(tableRows[j], 'odd')){
						Element.removeClassName(tableRows[j], 'odd');
					}
					if(!Element.hasClassName(tableRows[j], 'even')){
						Element.addClassName(tableRows[j], 'even');
					}
				} else {
					if(Element.hasClassName(tableRows[j], 'even')){
						Element.removeClassName(tableRows[j], 'even');
					}
					if(!Element.hasClassName(tableRows[j], 'odd')){
						Element.addClassName(tableRows[j], 'odd');
					}
				} 
			}
		}
}

/*
Given a select element, set its selected option as the
one whose value matches a given value.
*/
rock.util.DomUtil.setSelectedOptionByValue = function(selectElement, value){	
    var options = selectElement.options;
    for(var i=0; i<options.length; i++){
     	if(options[i].value == value){
     		selectElement.selectedIndex = i;
     		//rock.debugMsg("Selected index: " + i, 0);
     		break;
     	}
    }
}

rock.util.DomUtil.createSeparator = function(separatorHtml){
	var separator = Builder.node('span');
	if(separatorHtml){
		separator.innerHTML = separatorHtml;
	}else{
		separator.innerHTML = '&nbsp;';
	}
	return separator;
}
    	
/*================================================
	Class StringUtil
 =================================================*/
rock.util.StringUtil = {};

rock.util.StringUtil.replace = function(s, t, u) {
	/*
	**  Replace a token in a string
	**    s  string to be processed
	**    t  token to be found and removed
	**    u  token to be inserted
	**  returns new String
	*/
	i = s.indexOf(t);
	r = "";
	if (i == -1) return s;
	r += s.substring(0,i) + u;
	if ( i + t.length < s.length)
	  r += this.replace(s.substring(i + t.length, s.length), t, u);
	return r;
}


/*================================================
	Class SortUtil
 =================================================*/
rock.util.SortUtil = {};

rock.util.SortUtil.sortDefault = function(value1, value2) {
	if (value1 == value2) {
		return 0;
	}
	if (value1 < value2) {
		return -1;
	}
	return 1;
}	

rock.util.SortUtil.sortIgnoreCase = function(value1, value2) {
	return rock.util.SortUtil.sortDefault(value1.toLowerCase(),
									value2.toLowerCase());
}

rock.util.SortUtil.sortNumeric = function(value1, value2) {
	var f1 = parseFloat(value1);
	if (isNaN(f1)) {
		f1 = 0;
	}
	
	var f2 = parseFloat(value2); 
	if (isNaN(f2)) {
		f2 = 0;
	}
	
	return (f1 - f2);
}

rock.util.SortUtil.sortCurrency = function(value1, value2) { 
	var n1 = value1.replace(/[^0-9.]/g,'');
	var n2 = value2.replace(/[^0-9.]/g,'');
	return rock.util.SortUtil.sortNumeric(n1, n2);
}
	
rock.util.SortUtil.sortDate = function(value1, value2) {
	// y2k notes: two digit years less than 50 are treated as 20XX, 
	// greater than 50 are treated as 19XX
	var dt1;
	var dt2;
	var yr;
	if (value1.length == 10) {
		dt1 = value1.substr(6,4) + value1.substr(3,2)+ value1.substr(0,2);
	} else {
		yr = value1.substr(6,2);
		if (parseInt(yr) < 50) { 
			yr = '20' + yr; 
		} else { 
			yr = '19' + yr; 
		}
		dt1 = yr + value1.substr(3,2)+ value1.substr(0,2);
	}
	if (value2.length == 10) {
			dt2 = value2.substr(6,4) + value2.substr(3,2) + value2.substr(0,2);
	} else {
			yr = value2.substr(6,2);
			if (parseInt(yr) < 50) { 
				yr = '20' + yr; 
			} else { 
				yr = '19' + yr; 
			}
			dt2 = yr + value2.substr(3,2) + value2.substr(0,2);
	}
	if (dt1 == dt2) {
		return 0;
	}
	if (dt1 < dt2) { 
		return -1;
	}
	return 1;
}	


/*================================================
   Class JsonUtil
 =================================================*/
rock.util.JsonUtil = {};


/**
 * Converts a JSON string to Javacript object.
 */
rock.util.JsonUtil.toObject = function(jsonStr){
    return eval('(' + jsonStr + ')');
}

/*
  * Converts a Javascript object to JSON string.
  *
  * produces a JSON string representation of a javascript object
  * usage: var jsonstring = toJSON(someobject);
  *
  * Tino Zijdel - crisp@xs4all.nl, 28/09/2006
  */
rock.util.JsonUtil.toString = function(obj){
	if (typeof obj == 'undefined'){
	    return 'undefined';
	}else if (obj === null){
	    return 'null';
	}	

	var stringescape = {
		'\b': '\\b',
		'\t': '\\t',
		'\n': '\\n',
		'\f': '\\f',
		'\r': '\\r',
		'"' : '\\"',
		'\\': '\\\\'
        }

	var json = null, i, l, v, a = [];
	switch (obj.constructor){
		case Array:
			l = obj.length;
			for (i = 0; i < l; i++){
				if ((v = rock.util.JsonUtil.toString(obj[i])) !== null)
					a.push(v);
			}
			json = '[' + a.join(',') + ']';
			break;
		case Object:
			for (i in obj)
			{
				if (obj.hasOwnProperty(i) && (v = rock.util.JsonUtil.toString(obj[i])) !== null)
					a.push(rock.util.JsonUtil.toString(String(i)) + ':' + v);
			}
			json = '{' + a.join(',') + '}';
			break;
		case String:
			json = '"' + obj.replace(/[\x00-\x1f\\"]/g, function($0)
			{
				var c;
				if ((c = stringescape[$0]))
					return c;
				c = $0.charCodeAt();
				return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
			}) + '"';
			break;
		case Number:
			json = isFinite(obj) ? String(obj) : 'null';
			break;
		case Boolean:
			json = String(obj);
			break;
	}

	return json;
}

