Show:

File: ia\ui\DiscreteLegend.js

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

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

	this.style = {fillStyle:'#EFEFEF', strokeStyle:'#cccccc', lineWidth:'0.5', lineJoin:'miter'};
	this.isLegendComponent = true;
	this.interactive = true;
	this.layout = "vertical";
	this.thematic = new ia.Thematic();

	this._touchStart = 0;		// Timing of touch start.
};
ia.extend(ia.EventDispatcher, ia.DiscreteLegend);

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

/**
 * The style.
 * 
 * @property style
 * @type Object
 * @default {fillStyle:'#EFEFEF', strokeStyle:'#cccccc', lineWidth:'0.5', lineJoin:'miter'}
 */
ia.DiscreteLegend.prototype.style;

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

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

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

/**
 * Specifies a thematic for the legend.
 *
 * @property thematic
 * @type ia.Thematic
 */
ia.DiscreteLegend.prototype.thematic;

/**
 * Indicates this is a legend component.
 *
 * @property isLegendComponent
 * @type Boolean
 * @default true
 */
ia.DiscreteLegend.prototype.isLegendComponent;

/**
 * Indicates if theres a scrollbox attached.
 *
 * @property scrollBox
 * @type ia.ScrollBox
 * @default true
 */
ia.DiscreteLegend.prototype.scrollBox;

/**
 * Indicates if the legend is interactive.
 *
 * @property interactive
 * @type Boolean
 * @default true
 */
ia.DiscreteLegend.prototype.interactive;

/**
 * The layout of the legend.
 *
 * @property layout
 * @type String
 * @default "vertical"
 */
ia.DiscreteLegend.prototype.layout;

/**
 * Renders the legend.
 *
 * @method render
 */
ia.DiscreteLegend.prototype.render = function() 
{		
	this.renderLegend(this.thematic.getClasses()) ;
};

/**
 * Renders the numeric legend classes.
 *
 * @method renderNumeric
 * @param {ia.Legend[]} classes The legend classes.
 */
ia.DiscreteLegend.prototype.renderNumeric = function()
{
	this.renderLegend(this.thematic.numericClassifier.getClasses());
};

/**
 * Renders the categoric legend classes.
 *
 * @method renderCategoric
 * @param {ia.Legend[]} classes The legend classes.
 */
ia.DiscreteLegend.prototype.renderCategoric = function()
{
	this.renderLegend(this.thematic.categoricClassifier.getClasses());
};

/**
 * Renders the passed legend classes.
 *
 * @method renderLegend
 * @param {ia.Legend[]} classes The legend classes.
 */
ia.DiscreteLegend.prototype.renderLegend = function(classes) 
{		
	this.$table.empty();
	var tr;

	if (this.layout === "horizontal")
	{
		if (!this.container.hasClass("ia-legend-horizontal"))  
			this.container.addClass("ia-legend-horizontal");
		
		this.$table.css("height", "100%");
		this.$table.css("width","auto");
		this.container.css("width","auto");

		tr = $j("<tr>");
		this.$table.append(tr);
	}

	for (var i = 0; i < classes.length; i++) 
	{
		// A legend class.
		var legendClass = classes[i];

		// Add a row
		if (this.layout === "vertical") 
		{
			tr = $j("<tr>");
			this.$table.append(tr);
		}

		if (legendClass.size === 0) tr.css("display","none");

		// Add the symbol.
		var tdSymbol = $j("<td class='ia-legend-item'>"); 
		if (this.interactive) tdSymbol.addClass("ia-legend-item-interactive");
		tr.append(tdSymbol);

		// Take into account the border of the symbol when sizing the canvas.
		var borderWidth = this.style.lineWidth;
		var canvasWidth = legendClass.size + (borderWidth * 2);
		if (legendClass.symbol === ia.Shape.LINE) canvasWidth = 30;
		var canvasHeight = legendClass.size + (borderWidth * 2);
		
		// We need to place the canvas in a div otherwise the positioning of the canvas doesnt work.
		var divSymbol = $j("<div class='ia-legend-symbol' style='width:"+canvasWidth+";height:"+canvasHeight+";'>"); 
		tdSymbol.append(divSymbol);

		// Create a canvas to contain the symbol.
		var canvas = document.createElement('canvas');
		canvas.width = canvasWidth;
		canvas.height = canvasHeight;
		divSymbol.append($j(canvas));

		// Draw the symbol.
		var context = canvas.getContext("2d");

		for (var p in this.style) 
		{
			context[p] = this.style[p];
		}

		// We want to keep the shape corners sharp so reset to miter here in case its been changed.
			context.lineJoin = 'miter'; 
		context.fillStyle = legendClass.color;

		if (legendClass.symbol === ia.Shape.LINE)
		{
			context.strokeStyle = legendClass.color;
			context.lineWidth = legendClass.size;
			context.beginPath();
				ia.Shape.draw(legendClass.symbol, context, canvas.width/2, canvas.height/2, canvas.width);
			context.stroke();
		}
		else
		{
			// Add 0.5 to shape size to remove antialiasing
			context.beginPath();
				ia.Shape.draw(legendClass.symbol, context, canvas.width/2, canvas.height/2, legendClass.size + 0.5);
			context.fill();
			context.stroke();
		}

		// Add the label.
		var tdLabel = $j("<td class='ia-legend-item ia-legend-label'>").html(legendClass.getLabel());
		if (this.interactive) tdLabel.addClass("ia-legend-item-interactive");
		if (this.layout === "horizontal") 
		{
			tdLabel.css("white-space","nowrap");
			tdLabel.css("width","auto");
		}
		tr.append(tdLabel);

		// Add mouse events to the row.
		if (this.interactive) this._addMouseEvents(tr);
	}
};

/**
 * Adds mouse events to the passed jquery object.
 *
 * @method _addMouseEvents
 * @param {JQUERY Element} obj The jquery object.
 * @private
 */
ia.DiscreteLegend.prototype._addMouseEvents = function(obj) 
{
	var me = this;

	// Hover.
	if (!ia.IS_TOUCH_DEVICE)
	{
		obj.hover
		(
			function () 
			{
				var index = me.$table.find("tbody > tr").index($j(this));
				if ($j(this).hasClass("ia-legend-select")) 
				{
					$j(this).addClass("ia-legend-highlight-select");
					
					var c = ia.Color.adjustSV(me.selectionColor,40,100);
					me._dispatchItemEvent(index, ia.ItemEvent.ITEM_MOUSE_OVER, ia.ItemLayer.ROLLOVER_SELECTED);
				}
				else 
				{
					$j(this).addClass("ia-legend-highlight");
					
					var c = ia.Color.adjustSV(me.highlightColor,20,100);
					me._dispatchItemEvent(index, ia.ItemEvent.ITEM_MOUSE_OVER, ia.ItemLayer.ROLLOVER);
				}
				
				$j(this).css("background-color", c);
			},
			function () 
			{
				var index = me.$table.find("tbody > tr").index($j(this));
				$j(this).removeClass("ia-legend-highlight");
				if ($j(this).hasClass("ia-legend-highlight-select"))
				{
					$j(this).removeClass("ia-legend-highlight-select");
					$j(this).addClass("ia-legend-select");
					
					var c = ia.Color.adjustSV(me.selectionColor,30,100);
					me._dispatchItemEvent(index, ia.ItemEvent.ITEM_MOUSE_OUT, ia.ItemLayer.SELECTED);
				}
				else 
				{
					var c = "";
					me._dispatchItemEvent(index, ia.ItemEvent.ITEM_MOUSE_OUT, ia.ItemLayer.UNSELECTED);
				}
				 
				$j(this).css("background-color", c);
			}
		);
	}

	if (ia.IS_TOUCH_DEVICE)
	{
		obj.bind('touchstart', function(e)
		{
			me._touchStart = new Date().getTime();
		});
	}
	
	// Toggle.
	obj.bind(ia.CLICK_TYPE, function(e)
	{
		var index = me.$table.find("tbody > tr").index($j(this));

		if (ia.IS_TOUCH_DEVICE)
		{
			// Check its an actual click.
			var tEnd = new Date().getTime();
			var touchTime = tEnd - me._touchStart;
			if (touchTime < 500)
			{
				if(me.scrollBox && !me.scrollBox.isScrolling)
				{
					// Unselect.
					if ($j(this).hasClass("ia-legend-select"))
					{
						$j(this).removeClass("ia-legend-select");
						me._dispatchItemEvent(index, ia.ItemEvent.ITEM_CLICK, ia.ItemLayer.UNSELECTED);
					
						var c = "";
					}
					// Select.
					else
					{
						$j(this).addClass("ia-legend-select");
						me._dispatchItemEvent(index, ia.ItemEvent.ITEM_CLICK, ia.ItemLayer.SELECTED);
						
						var c = ia.Color.adjustSV(me.selectionColor,40,100);
					}
					$j(this).css("background-color", c);
				}
			}
		}
		else
		{
			// Unselect.
			if ($j(this).hasClass("ia-legend-highlight-select"))
			{
				$j(this).removeClass("ia-legend-highlight-select").removeClass("ia-legend-select").addClass("ia-legend-highlight"); 
				me._dispatchItemEvent(index, ia.ItemEvent.ITEM_CLICK, ia.ItemLayer.UNSELECTED);
				
				var c = ia.Color.adjustSV(me.highlightColor,20,100);
			}
			// Select.
			else
			{
				$j(this).removeClass("ia-legend-highlight").addClass("ia-legend-highlight-select");
				me._dispatchItemEvent(index, ia.ItemEvent.ITEM_CLICK, ia.ItemLayer.SELECTED);
					
				var c = ia.Color.adjustSV(me.selectionColor,40,100);
			}
			
			$j(this).css("background-color", c);
		}
	});
};

/**
 * Dispatches item events.
 *
 * @method ._dispatchItemEvent
 * @private
 */
ia.DiscreteLegend.prototype._dispatchItemEvent = function(classIndex, eventType, state)
{
	var item = new Object();
	item.id = classIndex;
	item.parent = this;
	item.state = state;
	item.legendClass = this.thematic.getClasses()[classIndex];
	this.dispatchEvent(new ia.ItemEvent(eventType, item));
};

/**
 * Selects.
 *
 * @method select
 * @param {String} id The id of the item.
 */
ia.DiscreteLegend.prototype.select = function(id) 
{
	var row = this.$table.find('tr:eq('+id+')');
	if (row !== undefined)
	{
		row.removeClass("ia-legend-highlight").addClass("ia-legend-select");
		row.css("background-color", ia.Color.adjustSV(this.selectionColor,40,100));
	}
};

/**
 * Unselects.
 *
 * @method unselect
 * @param {String} id The id of the item.
 */
ia.DiscreteLegend.prototype.unselect = function(id) 
{
	var row = this.$table.find('tr:eq('+id+')');
	if (row !== undefined)
	{
		row.removeClass("ia-legend-highlight-select").removeClass("ia-legend-select").addClass("ia-legend-highlight"); 
		row.css("background-color", "");
	}
};

/**
 * Clears all selections.
 *
 * @method clearSelection
 */
ia.DiscreteLegend.prototype.clearSelection = function() 
{	
	this.$table.find("tbody > tr").removeClass("ia-legend-select").css("background-color", "");
};

/**
 * Highlights the legend class that contains the given id.
 *
 * @method highlight
 * @param {String} id The id of the item.
 */
ia.DiscreteLegend.prototype.highlight = function(id) 
{	
	var row = this.$table.find('tr:eq('+id+')');
	if (row.hasClass("ia-legend-select")) 
	{
		row.addClass("ia-legend-highlight-select");
		var c = ia.Color.adjustSV(this.selectionColor,40,100);
	}
	else 
	{
		row.addClass("ia-legend-highlight");
		var c = ia.Color.adjustSV(this.highlightColor,20,100);
	}
	row.css("background-color", c);
};

/**
 * Clears all highlights.
 *
 * @method clearHighlight
 */
ia.DiscreteLegend.prototype.clearHighlight = function() 
{
	var rows = this.$table.find("tbody > tr");
	rows.removeClass("ia-legend-highlight").removeClass("ia-legend-highlight-select");
	var me = this;
	rows.each(function(index) 
	{
		if ($j(this).hasClass("ia-legend-select"))
		{
			var c = ia.Color.adjustSV(me.selectionColor,30,100);
		}
		else
		{
			var c = "";
		}
		$j(this).css("background-color", c);
	});		
};

/**
 * Hides the legend.
 *
 * @method hide
 */
ia.DiscreteLegend.prototype.hide = function()
{
	this.container.css("display", "none");
};

/**
 * Shows the legend.
 *
 * @method show
 */
ia.DiscreteLegend.prototype.show = function()
{
	this.container.css("display", "inline");
};