Show:

File: ia\ui\Table.js

/** 
 * A class for rendering a data table.
 *
 * @author J Clare
 * @class ia.Table
 * @extends ia.EventDispatcher
 * @constructor
 * @param {String} id The id of the table.
 */
ia.Table = function(id)
{		
	ia.Table.baseConstructor.call(this);

	this.id = id;
	this._data = undefined;
	this._cId = undefined;
	this._sDirection = undefined;
	this._selectedRows = new Object();
	this._scrollTimeout = undefined;	
	this._highlightRows = new Object();
	this._stickyIds = new Array();

	this.showLegendColor = true;
	this.allowUserSorting = true;

	// A div to contain the table and allow correct scrolling.
	this.container = $j("<div id='"+id+"' class='ia-table'>"); 

	// Use this for identifying unique table rows.
	this._containerId = id + "-";
	
	// Calculated border-radius for elements inside panels.
	this._borderRadius = parseInt($j(".ia-panel").css("border-top-left-radius")) - 1;
	if (this._borderRadius < 0) this._borderRadius = 0;

	this.$tableCorner = $j("<span class='ia-table-header ia-table-header-corner'>");
	this.$tableCorner.css("border-top-right-radius", this._borderRadius+"px");
	this.container.append(this.$tableCorner);
			
	// The table used to render the headers.
	this.$tableHeaders = $j("<table>");
	this.container.append(this.$tableHeaders);
	
	// The table used to render the data.
	this.$tableContainer = $j("<div id='"+id+"-container' class='ia-table-scrollbox'>"); 
	this._scrollBox = new ia.ScrollBox(this.$tableContainer);
	this.container.append(this.$tableContainer);
	
	this.$table = $j("<table>");
	this.$tableContainer.append(this.$table);
	this._addMouseEvents();
	
	var resizeTimeout;
	var me = this;
	this.container.resize(function(e) 
	{		
		clearTimeout(resizeTimeout);
		resizeTimeout = setTimeout(function()
		{
			clearTimeout(resizeTimeout);
			me._size();

		}, 500);
	});
};
ia.extend(ia.EventDispatcher, ia.Table);

/** 
 * The id.
 * 
 * @property id
 * @type String
 */
ia.Table.prototype.id;

/** 
 * The item selection color.
 * 
 * @property selectionColor
 * @type String
 */
ia.Table.prototype.selectionColor;

/** 
 * The item highlight color.
 * 
 * @property highlightColor
 * @type String
 */
ia.Table.prototype.highlightColor;

/** 
 * Should the legend color be displayed.
 * 
 * @property showLegendColor
 * @type Boolean
 * @default true
 */
ia.Table.prototype.showLegendColor;

/** 
 * Should user sorting be allowed.
 * 
 * @property allowUserSorting
 * @type Boolean
 * @default true
 */
ia.Table.prototype.allowUserSorting;

/**
 * The container that holds the object.
 * 
 * @property container
 * @type JQUERY Element
 */
ia.Table.prototype.container;

/**
 * The columns to be rendered. 
 *
 * The columns are an array with the following structure:
 * They dictate which columns should be used from the data.
 * The id points to the id in the data.
 *
 * [{id:"name", label:"Features", type:"categoric'"},
 * {id:"value", label:"Indicator", type:"numeric'"},
 * {id:"associate1", label:"Associate 1", type:"numeric'"},
 * {id:"associate2", label:"Associate 2", type:"categoric'"}]
 * 
 * @property columns
 * @type JSON
 */
ia.Table.prototype.columns;

/** 
 * Sizes all the element to make the scrolling work.
 *
 * @method _size
 * @private
 */
ia.Table.prototype._size = function()
{
	var containerWidth = this.container.width()
	var scrollBarWidth = this.$tableContainer.get(0).offsetWidth - this.$tableContainer.get(0).clientWidth;
	
	var tableWidth = containerWidth - scrollBarWidth
	this.$table.width(tableWidth);
	this.$tableHeaders.width(tableWidth);
	
	var containerHeight = this.container.height();
	var headerHeight = this.$tableHeaders.outerHeight();
	var contentHeight = containerHeight - headerHeight;
	this.$tableContainer.height(contentHeight);
	
	//var bgcolor = $j(".ia-table-header").css("background-color");
	//this.$tableCorner.css("background-color",bgcolor)
	this.$tableCorner.width(scrollBarWidth);
	this.$tableCorner.height(headerHeight);
};

/**
 * Gets a data object for the table.
 *
 * @method getData
 * @return ["eh11"]{id:"eh11", name:"polwarth", value:2345, associate1:25}
 * <br>["eh12"]{id:"eh12", name:"morningside", value:4347, associate1:45}
 * <br>["eh13"]{id:"eh13", name:"merchiston", value:2496, associate1:25}
 */
ia.Table.prototype.getData = function()
{
	return this._data;
};

/**
 * Sets a data object for the table.
 *
 * @method setData
 * @param value ["eh11"]{id:"eh11", name:"polwarth", value:2345, associate1:25}
 * <br>["eh12"]{id:"eh12", name:"morningside", value:4347, associate1:45}
 * <br>["eh13"]{id:"eh13", name:"merchiston", value:2496, associate1:25}
 */
ia.Table.prototype.setData = function(value)
{
	this._data = value;
};

/**
 * Renders the table.
 *
 * @method render
 */
ia.Table.prototype.render = function() 
{		
	var me = this;

	// Empty the previous table.
	this.$table.empty();
	this.$tableHeaders.empty();

	// Add the col section.
	var thead = $j("<thead>");
	var tr = $j("<tr>");
	thead.append(tr)
	this.$tableHeaders.append(thead);

	// Iterate through each col.
	var displayHeaders = false;
	var col;
	var nCol = this.columns.length;
	for (var i = 0; i < nCol; i++) 
	{
		col = this.columns[i];
		var colWidth = col.width*110;

		// The col label.
		var thLabel = $j("<th id='"+col.id+"' class='ia-table-header' style='width:"+colWidth+"%'>").html(col.label);
		thLabel.data('type', col.type);

		if (col.label !== "") displayHeaders = true;

		if (i === 0) thLabel.css("border-top-left-radius", this._borderRadius+"px");

		tr.append(thLabel);

		if (this.allowUserSorting)
		{
			// Add Mouse Events - Click sort col.
			(function() // Execute immediately
			{ 
				var colId = col.id;

				thLabel.click
				(
					function () 
					{
						// Get the sort direction.
						var sortDirection = "ascending";
						if ($j(this).is(".sort-asc")) sortDirection = "descending";

						// Call the sort function.
						me.sort(colId, sortDirection);
					}
				);
			})();
		}
	}

	// Hide the headers if no labels have been defined for them.
	if (displayHeaders === false) thead.css("display", "none");
	
	// Dont use jquery for table rows as its too slow for large tables.
	var r = new Array(), i = -1;
	var rowData;
	var rowIndex = 0;
	var cellData;
	var formattedData;
	var colWidth;
	
	r[++i] = '<tbody>';
	
	// Iterate through each row.
	for(id in this._data)
	{
		rowData = this._data[id];
		var rowId = String(id).replace(/'/g, "#quote#").replace(/"/g, "#double-quote#"); // Fix for quotations breaking ids.
		
		// Style even and odd rows differently.
		if (rowIndex%2 === 0) 	
			r[++i] = "<tr id='"+this._containerId+rowId+"' class='ia-table-row ia-table-row-even'>";
		else  			
			r[++i] = "<tr id='"+this._containerId+rowId+"' class='ia-table-row ia-table-row-odd'>";
		rowIndex++;					
				
		var itemColor = undefined;
		var itemSize = 0;
		if (rowData.color && this.showLegendColor) 
		{
			itemColor = ia.Color.toRGBA(rowData.color, 1);
			itemSize = rowData.symbolSize;
		}
			
		// Iterate through each table cell in the row - match to columns.
		for (var j = 0; j < nCol; j++) 
		{
			col = this.columns[j];
			
			if (col.id.indexOf("~") !== -1)
			{
				var colId = col.id.split("~")[0];
				var suffix = col.id.split("~")[1];
				cellData = this["data"+suffix][id][colId];
				formattedData = this["data"+suffix][id][colId+"_formatted"];
			}
			else		
			{
				cellData = rowData[col.id];
				formattedData = rowData[col.formattedId];
			}
			colWidth = col.width*110;
			
			if (col.type  === "categoric") 
				r[++i] = "<td class='ia-table-cell ia-table-cell-categoric' data-value='"+formattedData+"'  style='width:"+colWidth+"%'>";
			else  
				r[++i] = "<td class='ia-table-cell ia-table-cell-numeric' data-value='"+cellData+"'  style='width:"+colWidth+"%'>";
		
			if (itemColor && col.id === "name" && itemSize > 0)
			{
				r[++i] = "<span class='ia-table-legend-swatch' style='background-color:"+itemColor+";'></span>";
			}
			if (rowData.href && col.id === "name")
			{
				r[++i] = "<span id='"+rowData.href+"' class='ia-table-notes-icon'></span>";
			}
		
			r[++i] = formattedData;
			r[++i] = "</td>";
		}
		
		r[++i] = "</tr>";
	}
	
	r[++i] = '</tbody>';
	this.$table.append(r.join(""));
	
	this._scrollBox.refresh();
	
	this._size();
	
	// If sort has been set.
	if (this._cId !== undefined) this.sort(this._cId, this._sDirection);
	
	this.renderSelection();
};

/** 
 * Exports as an image in a new page.
 * 
 * @method exportData
 * @param {String} txt Any text to go with image.
 */
ia.Table.prototype.exportData = function(txt)
{
	// Dont use jquery for table rows as its too slow for large tables.
	var nCol = this.columns.length;
	var r = new Array(), i = -1;
	var rowData;
	var cellData;
	var formattedData;
	
	// Iterate through each header.
	for (var j = 0; j < nCol; j++) 
	{
		var col = this.columns[j];
		if (j > 0) r[++i] = ','
		r[++i] = '"'+col.label+'"';
	}
	r[++i] = '\n';
	
	// If rows are selected only return selected rows.
	// Otherwise return all rows.
	var selLength = Object.keys(this._selectedRows).length;
	if (selLength > 0)
	{	
		for (id in this._selectedRows)
		{
			var rowId = String(id).replace(/#quote#/g, "'").replace(/#double-quote#/g, '"'); // Fix for quotations breaking ids.
			rowData = this._data[rowId];

			for (var j = 0; j < nCol; j++) 
			{
				col = this.columns[j];
				
				if (col.id.indexOf("~") !== -1)
				{
					var colId = col.id.split("~")[0]
					var suffix = col.id.split("~")[1]
					cellData = this["data"+suffix][rowId][colId];
					formattedData = this["data"+suffix][rowId][colId+"_formatted"];
				}
				else		
				{
					cellData = rowData[col.id];
					formattedData = rowData[col.formattedId];
				}

				if (j > 0) r[++i] = ','
				if (ia.isNumber(cellData)) r[++i] = formattedData;  
				else r[++i] = '"'+formattedData+'"';
			}

			r[++i] = '\n';
		};
	}
	else
	{
		for (rowId in this._data)
		{
			rowData = this._data[rowId];

			for (var j = 0; j < nCol; j++) 
			{
				col = this.columns[j];

				if (col.id.indexOf("~") !== -1)
				{
					var colId = col.id.split("~")[0]
					var suffix = col.id.split("~")[1]
					cellData = this["data"+suffix][rowId][colId];
					formattedData = this["data"+suffix][rowId][colId+"_formatted"];
				}
				else		
				{
					cellData = rowData[col.id];
					formattedData = rowData[col.formattedId];
				}

				if (j > 0) r[++i] = ','
				if (ia.isNumber(cellData)) r[++i] = formattedData;  
				else r[++i] = '"'+formattedData+'"';
			}

			r[++i] = '\n';
		};
	}
	var tableString = r.join("");

	var htmlString;
	if (txt !== undefined)
	{
		htmlString = '<div style="font-family:Verdana;font-size:12px;color:#888888;">'+txt+'</div>'
		htmlString += '<p><textarea rows="25" cols="60">'+tableString+'</textarea></p>';
	}
	else htmlString = '<textarea rows="25" cols="60">'+tableString+'</textarea>';
	window.open().document.write(htmlString);
};

/** 
 * Renders the selection.
 *
 * @method renderSelection
 */
ia.Table.prototype.renderSelection = function()
{
	for (var rowId in this._selectedRows)
	{	
		//var row = $j("#"+this._containerId+rowId);
		var row = $j("[id='"+this._containerId+rowId+"']");
		row.addClass("ia-table-row-select");
		var c = ia.Color.adjustSV(this.selectionColor,30,100);
		row.css("background-color", c);
	}
};

/**
 * Adds mouse events to the passed jquery object.
 * Uses delegation to reduce number of events added to rows and rendering time!
 *
 * @method _addMouseEvents
 * @param {JQUERY Element} obj The jquery object.
 * @private
 */
ia.Table.prototype._addMouseEvents = function() 
{	
	var me = this;
	if (ia.IS_TOUCH_DEVICE)
	{
		// Toggle.
		me.$table.delegate('tr', 'touchend', function(e)
		{
			if (!me._scrollBox.isScrolling)
			{
				var id = $j(this).attr("id").substring(me._containerId.length);

				// Unselect.
				if ($j(this).hasClass("ia-table-row-select"))
				{
					$j(this).removeClass("ia-table-row-select"); 

					delete me._selectedRows[id];
					me._dispatchItemEvent(id, ia.ItemEvent.ITEM_CLICK, ia.ItemLayer.UNSELECTED);
				
					var c = "";
				}
				// Select.
				else
				{
					$j(this).addClass("ia-table-row-select");

					me._selectedRows[id] = id;
					me._dispatchItemEvent(id, ia.ItemEvent.ITEM_CLICK, ia.ItemLayer.SELECTED);
					
					var c = ia.Color.adjustSV(me.selectionColor,30,100);
				}
			
				$j(this).css("background-color", c);
			}
		});
	}
	else
	{
		// Mouseover.
		this.$table.delegate('tr', 'mouseover', function(e)
		{
			var id = $j(this).attr("id").substring(me._containerId.length);
			me._highlightRows[id] = $j(this);

			if ($j(this).hasClass("ia-table-row-select")) 
			{
				$j(this).addClass("ia-table-row-highlight-select");
					
				var c = ia.Color.adjustSV(me.selectionColor,40,100);
			}
			else 
			{
				$j(this).addClass("ia-table-row-highlight");
				var c = ia.Color.adjustSV(me.highlightColor,20,100);
			}

			$j(this).css("background-color", c);

			me._dispatchItemEvent(id, ia.ItemEvent.ITEM_MOUSE_OVER, ia.ItemLayer.ROLLOVER);
		});
		// Mouseout
		this.$table.delegate('tr', 'mouseout', function(e)
		{
			var id = $j(this).attr("id").substring(me._containerId.length);
			delete me._highlightRows[id];

			$j(this).removeClass("ia-table-row-highlight");
			if ($j(this).hasClass("ia-table-row-highlight-select"))
			{
				$j(this).removeClass("ia-table-row-highlight-select").addClass("ia-table-row-select");
				me._dispatchItemEvent(id, ia.ItemEvent.ITEM_MOUSE_OUT, ia.ItemLayer.SELECTED);
					
				var c = ia.Color.adjustSV(me.selectionColor,30,100);
			}
			else
			{
				me._dispatchItemEvent(id, ia.ItemEvent.ITEM_MOUSE_OUT, ia.ItemLayer.UNSELECTED);
			
				var c = "";
			}

			$j(this).css("background-color", c);
		});
		
		// Toggle.
		this.$table.delegate('tr', ia.CLICK_TYPE, function(e)
		{
			var id = $j(this).attr("id").substring(me._containerId.length);
			// Unselect.
			if ($j(this).hasClass("ia-table-row-highlight-select"))
			{
				$j(this).removeClass("ia-table-row-highlight-select").removeClass("ia-table-row-select").addClass("ia-table-row-highlight"); 

				delete me._selectedRows[id];
				me._dispatchItemEvent(id, ia.ItemEvent.ITEM_CLICK, ia.ItemLayer.UNSELECTED);
				
				var c = ia.Color.adjustSV(me.highlightColor,20,100);
			}
			// Select.
			else
			{
				$j(this).removeClass("ia-table-row-highlight").addClass("ia-table-row-highlight-select");

				me._selectedRows[id] = id;
				me._dispatchItemEvent(id, ia.ItemEvent.ITEM_CLICK, ia.ItemLayer.SELECTED);
					
				var c = ia.Color.adjustSV(me.selectionColor,40,100);
			}

			$j(this).css("background-color", c);
		});
	}
	// Notes.
	this.$table.delegate('span.ia-table-notes-icon', ia.CLICK_TYPE, function(e)
	{
		e.stopPropagation();
		var link = $j(this).attr("id")
		window.open(link, "_blank");
	});
	this.$table.delegate('span.ia-table-notes-icon', 'mouseover touchstart', function(e)
	{
		e.stopPropagation();
	});
};

/**
 * Sorts the table. The table must be rendered before it can be sorted.
 *
 * @method sort
 * @param {Number} colIndex The id of the column to sort by.
 * @param {String} sortDirection The sort direction "ascending" or "descending". Default is "ascending".
 */
ia.Table.prototype.sort = function(colId, sortDirection)
{	
	var colIndex = this._getColumnIndex(colId);
	if (colIndex !== -1)
	{
		this._cId = colId;
		this._sDirection = sortDirection;

		if (sortDirection !== undefined) sortDirection = sortDirection.toLowerCase()

		var dir = 1;
		if (sortDirection === "descending") dir = -1;

		// Get the sort col element from the given column index.
		var th = this.$tableHeaders.find("th:eq("+colIndex+")");

		// Remove any previous sort desc/asc styles.
		this.$tableHeaders.find("th").removeClass("sort-asc").removeClass("sort-desc");

		// Style col according to the sort direction.
		var sortTextValue;
		if (dir === 1) 
		{
			th.addClass("sort-asc");
			sortTextValue = Infinity;
		}
		else 
		{
			th.addClass("sort-desc");
			sortTextValue = -Infinity;
		}

		// Set the sort function needed depending on the data type.
		var sortFunction;

		// Alphabetical sort.
		if (th.data("type") === "categoric")
		{
			sortFunction = function(text)
			{
				return text.toUpperCase();
			}
		}
		// Numerical sort is default.
		else 
		{
			sortFunction = function(text)
			{
				var key = parseFloat(text);
				return isNaN(key) ? sortTextValue : key;
			}
		}

		// Get the table rows as an array of DOM nodes.
		var sortedRows = new Array();
		var rows = this.$table.find("tbody > tr").get();

		// Do expensive sort work and store sort function in a new 'sortKey' property.
		var cellData
		var n = rows.length;
		for (var i = 0; i < n; i++) 
		{
			cellData = $j(rows[i].childNodes[colIndex]).attr("data-value");
			rows[i].sortKey = sortFunction(cellData);
		}

		// Speeds up the sort.
		var save = Object.prototype.toString;
		Object.prototype.toString = function () {return this.key;};

		// Sort the rows.
		rows.sort(function(a, b)
		{
			if (a.sortKey < b.sortKey) return -dir;
			if (a.sortKey > b.sortKey) return dir;
			return 0;
		});

		// Speeds up the sort. Reset.
		Object.prototype.toString = save;

		// Append the rows in new position (append moves rather than clones).
		for (var i = 0; i < n; i++) 
		{
			sortedRows.push(rows[i]);
			rows[i].sortKey = null;
		}
		this.$table.children('tbody').append(sortedRows);

		// Need to re-apply alternate row colors after sort.
		this.$table.find("tbody > tr.ia-table-row:odd").removeClass("ia-table-row-even").addClass("ia-table-row-odd");
		this.$table.find("tbody > tr.ia-table-row:even").removeClass("ia-table-row-odd").addClass("ia-table-row-even");

		// Bring any sticky rows to the top.
		this.promoteToTop(this._stickyIds);
	}
};

/**
 * Sticks a list of row ids to the the top of the table.
 *
 * @method stickToTop
 * @param {String[]} ids The list of ids.
 */
ia.Table.prototype.stickToTop = function(ids)
{	
	this._stickyIds = ids.concat();
	this.promoteToTop(this._stickyIds);
};

/**
 * Promotes a list of row ids to the the top of the table.
 *
 * @method promoteToTop
 * @param {String[]} ids The list of ids.
 */
ia.Table.prototype.promoteToTop = function(ids)
{	
	var n = ids.length;
	for (var i = n-1; i >= 0; i--) 
	{
		var row = $j("tr[id='"+this._containerId+ids[i]+"']");
		if (row) this.$table.prepend(row);
	}
};

/**
 * Selects all the text in the table.
 *
 * @method selectText
 */
ia.Table.prototype.selectText = function() 
{
	var t = this.container.get(0);
	var body = document.body;
	
	if (body.createTextRange) 
	{
		range = body.createTextRange();
		range.moveToElementText(t);
		range.select();
	} 
	else if (document.createRange && window.getSelection) 
	{
		sel = window.getSelection();
		sel.removeAllRanges();
		var range = document.createRange();
		range.selectNodeContents(t);
		sel.addRange(range); 
	}
};

/**
 * Dispatches item events.
 *
 * @method _dispatchItemEvent
 * @param {String} id The id.
 * @param {String} eventType The event type.
 * @param {String} state The state.
 * @private
 */
ia.Table.prototype._dispatchItemEvent = function(id, eventType, state)
{
	var rowId = String(id).replace(/#quote#/g, "'").replace(/#double-quote#/g, '"'); // Fix for quotations breaking ids.
	var item = new Object();
	item.id = rowId;
	item.state = state;
	item.parent = this;
	this.dispatchEvent(new ia.ItemEvent(eventType, item));
};

/**
 * Returns the column index for the given column id.
 *
 * @method _getColumnIndex
 * @param {String} colId The column id.
 * @return {Number} The index of the column (first column is 0).
 * @private
 */
ia.Table.prototype._getColumnIndex = function(colId)
{
	// Iterate through each col.
	var colIndex = -1;
	for (var i = 0; i < this.columns.length; i++) 
	{
		if (this.columns[i].id === colId)
		{
			colIndex = i;
			break;
		}
	}
	return colIndex;
};

/**
 * Selects a row.
 *
 * @method select
 * @param {String} rowId The id of the row to select.
 */	
ia.Table.prototype.select = function(rowId)
{	
	var rowId = String(rowId).replace(/'/g, "#quote#").replace(/"/g, "#double-quote#"); // Fix for quotations breaking ids.

	var row = $j("tr[id='"+this._containerId+rowId+"']");
	row.addClass("ia-table-row-select");
	this._selectedRows[rowId] = rowId;
	
	var c = ia.Color.adjustSV(this.selectionColor,30,100);
	row.css("background-color", c);

	// Use this for when the legend or pie chart are
	// clicked which results in a massive amount of scrolling
	// and slows down the selection.
	clearTimeout(this._scrollTimeout);
	var me = this;
	this._scrollTimeout = setTimeout(function()
	{
		clearTimeout(me._scrollTimeout);
		me._scrollIntoView(row);
	}, 500);
};
	
/**
 * Scrolls a row into view.
 *
 * @method _scrollIntoView
 * @param {JQUERY Element} element The row as a jquery object.
 * @private
 */	
ia.Table.prototype._scrollIntoView = function(element) 
{
	var ele = element.get(0);
	if (ele !== undefined)
	{
		if (ia.IS_TOUCH_DEVICE) 
		{
			this._scrollBox.scrollToElement(ele);
		}
		else
		{
			var containerTop = this.$tableContainer.scrollTop(); 
			var containerBottom = containerTop + this.$tableContainer.height(); 
			var elemTop = ele.offsetTop;
			var elemBottom = elemTop + element.height(); 
			if ((elemBottom > containerBottom) || (elemTop < containerTop))  this.$tableContainer.scrollTop(elemTop);
		}
	}
};

/**
 * Unselects a row.
 *
 * @method unselect
 * @param {String} rowId The id of the row to unselect.
 */
ia.Table.prototype.unselect = function(rowId)
{	
	var rowId = String(rowId).replace(/'/g, "#quote#").replace(/"/g, "#double-quote#"); // Fix for quotations breaking ids.
	var row = $j("tr[id='"+this._containerId+rowId+"']");
	row.removeClass("ia-table-row-select");
	delete this._selectedRows[rowId];

	var c = "";
	row.css("background-color", c);
};
	
/**
 * Clears all selections.
 *
 * @method clearSelection
 */
ia.Table.prototype.clearSelection = function()
{	
	for (var rowId in this._selectedRows) 
	{
		this.unselect(rowId);
	}
};
	
/**
 * Hightlights a row.
 *
 * @method highlight
 * @param {String} rowId The id of the row to select.
 */
ia.Table.prototype.highlight = function(rowId)
{		
	var rowId = String(rowId).replace(/'/g, "#quote#").replace(/"/g, "#double-quote#"); // Fix for quotations breaking ids.
	var row = $j("tr[id='"+this._containerId+rowId+"']");

	if (row.hasClass("ia-table-row-select")) 
	{
		row.addClass("ia-table-row-highlight-select");
		
		var c = ia.Color.adjustSV(this.selectionColor,40,100);
	}
	else
	{
		row.addClass("ia-table-row-highlight");
							
		var c = ia.Color.adjustSV(this.highlightColor,20,100);
	}
	
	row.css("background-color", c);
	
	this._highlightRows[rowId] = row;
};

/**
 * Clears all highlights.
 *
 * @method clearHighlight
 */
ia.Table.prototype.clearHighlight = function()
{	
	for (var id in this._highlightRows)
	{
		var row = this._highlightRows[id];
		row.removeClass("ia-table-row-highlight").removeClass("ia-table-row-highlight-select");
		delete this._highlightRows[id];
		
		if (row.hasClass("ia-table-row-select")) 
		{
			var c = ia.Color.adjustSV(this.selectionColor,30,100);
		}
		else
		{
			var c = "";
		}
		row.css("background-color", c);
	}
};