Show:

File: ia\InteractionGroup.js

/** 
 * An ia.Event object.
 *
 * @author J Clare
 * @class ia.InteractionEvent
 * @extends ia.Event
 * @constructor
 * @param {String} eventType The event type.
 * @param {ia.InteractionGroup} interactionGroup The InteractionGroup associated with the event.
 * @param {String[]} ids A list if ids associated with the event.
 */
ia.InteractionEvent = function(eventType, interactionGroup, ids)
{
	ia.InteractionEvent.baseConstructor.call(this, eventType, interactionGroup);

	this.ids = ids || [];
};
ia.extend(ia.Event, ia.InteractionEvent);

/**
 * Indicates that the selection has changed.
 * 
 * @static
 * @final
 * @property SELECTION_CHANGED
 * @type String
 * @default "selectionChanged"
 */
ia.InteractionEvent.SELECTION_CHANGED = "selectionChanged";

/**
 * Indicates that the highlight has changed.
 * 
 * @static
 * @final
 * @property HIGHLIGHT_CHANGED
 * @type String
 * @default "highlightChanged"
 */
ia.InteractionEvent.HIGHLIGHT_CHANGED = "highlightChanged";

/** 
 *  A list if ids associated with the event.
 * 
 * @property ids
 * @type String[]
 */
ia.InteractionEvent.prototype.ids;

/** 
 * Organizes interactions between different components.
 *
 * @author J Clare
 * @class ia.InteractionGroup
 * @constructor
 */
ia.InteractionGroup = function()
{		
	ia.InteractionGroup.baseConstructor.call(this);

	this._selection = new Array();			// Contains the selected ids.
	this._highlight = "";					// Contains the highlighted ids.
	this._components = new Array();			// Contains the none legend components.
	this._legendComponents = new Array();	// Contains the legend components.
	this.selectionMode = "multiple";
};
ia.extend(ia.EventDispatcher, ia.InteractionGroup);

/** 
 * Selection mode.
 * 
 * @property selectionMode
 * @type String
 * @default "multiple"
 */
ia.InteractionGroup.prototype.selectionMode;

/** 
 * Gets the list of components.
 * 
 * @method getComponents
 * @return {Object[]} An array of components.
 */
ia.InteractionGroup.prototype.getComponents = function(c) 
{
	return this._components.concat(this._legendComponents);
};

/** 
 * Adds a component to the group.
 * 
 * @method addComponent
 * @param {Object} component The component to add.
 */
ia.InteractionGroup.prototype.addComponent = function(c) 
{
	if (c.isLegendComponent) this._legendComponents.push(c);
	else this._components.push(c);
	
	// Add item listeners.
	c.addEventListener(ia.ItemEvent.ITEM_MOUSE_OVER, this._itemEventHandler.bind(this));
	c.addEventListener(ia.ItemEvent.ITEM_MOUSE_OUT, this._itemEventHandler.bind(this));
	c.addEventListener(ia.ItemEvent.ITEM_MOUSE_UP, this._itemEventHandler.bind(this));
	c.addEventListener(ia.ItemEvent.ITEM_CLICK, this._itemEventHandler.bind(this));
	c.addEventListener(ia.ItemEvent.NONE_ITEM_CLICK, this._itemEventHandler.bind(this));
	
	// For report object.
	c.addEventListener(ia.ItemEvent.ITEM_MOUSE_DOWN, dispatch.bind(this));
	c.addEventListener(ia.ItemEvent.ITEM_MOUSE_MOVE, dispatch.bind(this));
	function dispatch(event) {this.dispatchEvent(event)}
};

/** 
 * Removes a component from the group.
 * 
 * @method removeComponent
 * @param {Object} component The component to remove.
 */
ia.InteractionGroup.prototype.removeComponent = function(c) 
{
	var index = this._components.indexOf(c);
	if (index !== -1) this._components.splice(index, 1);

	var index = this._legendComponents.indexOf(c);
	if (index !== -1) this._legendComponents.splice(index, 1);
};

/** 
 * Returns the highlighted id.
 * 
 * @method getHighlight
 * @return {String} The id.
 */
ia.InteractionGroup.prototype.getHighlight = function()
{
	return this._highlight;
};

/** 
 * Clears the highlight.
 * 
 * @method clearHighlight
 */
ia.InteractionGroup.prototype.clearHighlight = function()
{
	this._highlight = "";

	for (var i = 0; i < this._components.length; i++) 
	{ 
		var component = this._components[i];
		component.clearHighlight();
	}
	for (var i = 0; i < this._legendComponents.length; i++) 
	{ 
		var component = this._legendComponents[i];
		component.clearHighlight();
	}
	this.dispatchEvent(new ia.InteractionEvent(ia.InteractionEvent.HIGHLIGHT_CHANGED, this, [this._highlight]));
};

/**
 * Highlight the id.
 *
 * @method setHighlight
 * @param {String} id The id.
 */
ia.InteractionGroup.prototype.setHighlight = function(id) 
{
	if (id !== "")
	{
		this._highlight = id;

		for (var i = 0; i < this._components.length; i++) 
		{ 
			this._components[i].clearHighlight();
			this._components[i].highlight(id);
		}
		this.dispatchEvent(new ia.InteractionEvent(ia.InteractionEvent.HIGHLIGHT_CHANGED, this, [this._highlight]));
	}
	else this.clearHighlight();
};

/** 
 * Returns the selected ids as an array.
 * 
 * @method getSelection
 * @return {String[]} The list of ids.
 */
ia.InteractionGroup.prototype.getSelection = function()
{
	return this._selection;
};

/** 
 * Sets the selected ids as an array.
 * 
 * @method setSelection
 * @param {String[]} s An array of ids.
 */
ia.InteractionGroup.prototype.setSelection = function(s)
{
	// Dispatch event without item because none have been clicked.
	// This is so components like map/table tool bars know that a selection has been made programmatically.
	/*var item = new Object();
	if (this._selection.length > 0) item.id = this._selection[this._selection.length - 1];
	item.state = ia.ItemLayer.SELECTED;
	this.dispatchEvent(new ia.ItemEvent(ia.ItemEvent.ITEM_CLICK, item));*/

	if (s.length > 0)
	{
		this._selection = s.concat();

		for (var i = 0; i < this._components.length; i++) 
		{ 
			for (var j = 0; j < this._selection.length; ++j) 
			{
				this._components[i].select(this._selection[j]);
			}
		}

		this.dispatchEvent(new ia.InteractionEvent(ia.InteractionEvent.SELECTION_CHANGED, this, this._selection));
	}
	else this.clearSelection();
};	

/** 
 * Clears the selection.
 * 
 * @method clearSelection
 */
ia.InteractionGroup.prototype.clearSelection = function()
{
	for (var i = 0; i < this._components.length; i++) 
	{ 
		var component = this._components[i];
		component.clearSelection();
	}
	for (var i = 0; i < this._legendComponents.length; i++) 
	{ 
		var component = this._legendComponents[i];
		component.clearSelection();
	}
	this._selection = [];

	this.dispatchEvent(new ia.Event(ia.Event.CLEAR_SELECTION, new Object())); // Keep for backwards compatibility but use InteractionEvent from now on.

	this.dispatchEvent(new ia.InteractionEvent(ia.InteractionEvent.SELECTION_CHANGED, this, this._selection));
};

/**
 * Selects the id.
 * 
 * @method clearSelection
 * @param {String} id The id.
 */
ia.InteractionGroup.prototype.select = function(id) 
{
	var index = this._selection.indexOf(id);
	if (index === -1) 
	{
		this._selection.push(id);

		/*var item = new Object();
		item.id = id;
		item.state = ia.ItemLayer.SELECTED;
		this.dispatchEvent(new ia.ItemEvent(ia.ItemEvent.ITEM_CLICK, item));*/

		for (var i = 0; i < this._components.length; i++) 
		{ 
			this._components[i].select(id);
		}

		this.dispatchEvent(new ia.InteractionEvent(ia.InteractionEvent.SELECTION_CHANGED, this, this._selection));
	}
};

/**
 * Unselects the id.
 * 
 * @method unselect
 * @param {String} id The id.
 */
ia.InteractionGroup.prototype.unselect = function(id) 
{
	var index = this._selection.indexOf(id);
	if (index !== -1) 
	{
		this._selection.splice(index, 1);
		
		/*var item = new Object();
		item.id = id;
		item.state = ia.ItemLayer.UNSELECTED;
		this.dispatchEvent(new ia.ItemEvent(ia.ItemEvent.ITEM_CLICK, item));*/

		for (var i = 0; i < this._components.length; i++) 
		{ 
			this._components[i].unselect(id);
		}
		
		this.dispatchEvent(new ia.InteractionEvent(ia.InteractionEvent.SELECTION_CHANGED, this, this._selection));
	}
};

/** 
 * Handles item events.
 * 
 * @method _itemEventHandler
 * @param {ia.ItemEvent} event An <code>ia.ItemEvent</code> dispatched by a component.
 * @private
 */	
ia.InteractionGroup.prototype._itemEventHandler = function(event) 
{
	var item = event.item;

	if (event.type === ia.ItemEvent.NONE_ITEM_CLICK)
	{
		this.clearSelection();
	}
	else if (item.parent.isLegendComponent) 
	{
		if (event.type === ia.ItemEvent.ITEM_CLICK && this.selectionMode === "single") {item.parent.unselect(item.id);}
		else this._onLegendClassEvent(event, item); 
	}
	else
	{
		this._updateComponents(event, item);
		this._updateLegendClassComponents(event, item);
	}

	this._dispatchInteractionEvent(event.type);
};

/** 
 * Dispatches an interaction event.
 * 
 * @method _dispatchInteractionEvent
 * @param {String} eventType An ia.ItemEvent type.
 * @private
 */	
ia.InteractionGroup.prototype._dispatchInteractionEvent = function(eventType) 
{
	if (eventType === ia.ItemEvent.NONE_ITEM_CLICK
		|| eventType === ia.ItemEvent.ITEM_CLICK)
	{
		//ia.log(this._selection)
		this.dispatchEvent(new ia.InteractionEvent(ia.InteractionEvent.SELECTION_CHANGED, this, this._selection));
	}
	else if (eventType === ia.ItemEvent.ITEM_MOUSE_OUT
		|| eventType === ia.ItemEvent.ITEM_MOUSE_UP 
		|| eventType === ia.ItemEvent.ITEM_MOUSE_OVER)  
	{
		//ia.log(this._highlight)
		this.dispatchEvent(new ia.InteractionEvent(ia.InteractionEvent.HIGHLIGHT_CHANGED, this, [this._highlight]));
	}
};

/** 
 * Updates the legend class components.
 * 
 * @method _onLegendClassEvent
 * @param {ia.ItemEvent} event An <code>ia.ItemEvent</code> dispatched by a component.
 * @param {Object} item An item.
 * @private
 */	
ia.InteractionGroup.prototype._onLegendClassEvent = function(event, item) 
{
	var id = item.id;
	var parent = item.parent;
	var state = item.state;

	for (var i = 0; i < this._legendComponents.length; i++) 
	{ 
		var component = this._legendComponents[i];
		if (component !== parent && parent.thematic === component.thematic)
		{
			if (event.type === ia.ItemEvent.ITEM_MOUSE_OUT
			|| event.type === ia.ItemEvent.ITEM_MOUSE_UP) 
			{
				component.clearHighlight();
			}
			else if (event.type === ia.ItemEvent.ITEM_MOUSE_OVER)
			{
				component.clearHighlight();
				component.highlight(id);
			}
			else if (event.type === ia.ItemEvent.ITEM_CLICK)
			{
				if (state === ia.ItemLayer.UNSELECTED) component.unselect(id);
				else component.select(id);
			}
		}
	}
	
	if (event.type === ia.ItemEvent.ITEM_CLICK)
	{
		// Iterate through the child items belonging to the legend class.
		var childItems = item.legendClass.items;
		var n = childItems.length;
		for (var i = 0; i < n; i++)  {this._updateComponents(event, childItems[i]);}
	}
};

/** 
 * Updates the legend class components
 * 
 * @method _updateLegendClassComponents
 * @param {ia.ItemEvent} event An <code>ia.ItemEvent</code> dispatched by a component.
 * @param {Object} item An item.
 * @private
 */	
ia.InteractionGroup.prototype._updateLegendClassComponents = function(event, item) 
{	
	for (var i = 0; i < this._legendComponents.length; i++) 
	{ 
		var component = this._legendComponents[i];
		if (event.type === ia.ItemEvent.ITEM_MOUSE_OUT 
		|| event.type === ia.ItemEvent.ITEM_MOUSE_UP) 
		{
			component.clearHighlight();
		}
		else if (event.type === ia.ItemEvent.ITEM_MOUSE_OVER)
		{
			component.clearHighlight();
			var data = component.thematic.getData();
			if (data)
			{
				var dataItem = data[item.id];
				if (dataItem && dataItem.legendClass) 
				{
					var id = dataItem.legendClass.index;
					component.highlight(id);
				}
			}
		}
	}
};

/** 
 * Updates the components
 * 
 * @method _updateComponents
 * @param {ia.ItemEvent} event An <code>ia.ItemEvent</code> dispatched by a component.
 * @param {Object} item An item.
 * @private
 */	
ia.InteractionGroup.prototype._updateComponents = function(event, item) 
{
	var id = item.id;
	var parent = item.parent;
	var state = event.item.state;

	// Clear selection after youve got the item state.
	if (event.type === ia.ItemEvent.ITEM_CLICK && this.selectionMode === "single") this.clearSelection();

	// Add or remove from selection/highlight ids lists.
	if (event.type === ia.ItemEvent.ITEM_CLICK)
	{
		if (state === ia.ItemLayer.UNSELECTED) 
		{
			var index = this._selection.indexOf(id);
			if (index !== -1) this._selection.splice(index, 1);
		}
		else this._selection.push(id);
	}
	else if (event.type === ia.ItemEvent.ITEM_MOUSE_OUT || event.type === ia.ItemEvent.ITEM_MOUSE_UP) this._highlight = "";  
	else if (event.type === ia.ItemEvent.ITEM_MOUSE_OVER) this._highlight = id;

	// Iterate through the component list
	for (var i = 0; i < this._components.length; i++) 
	{ 
		var component = this._components[i];
		if (component !== parent || this.selectionMode === "single")
		{
			if (event.type === ia.ItemEvent.ITEM_MOUSE_OUT
			|| event.type === ia.ItemEvent.ITEM_MOUSE_UP)  
			{
				component.clearHighlight();
			}
			else if (event.type === ia.ItemEvent.ITEM_MOUSE_OVER)
			{
				component.clearHighlight();
				component.highlight(id);
			}
			if (event.type === ia.ItemEvent.ITEM_CLICK)
			{
				if (state === ia.ItemLayer.UNSELECTED) component.unselect(id);
				else component.select(id);
			}
		}
	}

	this.dispatchEvent(event); // Keep for backwards compatibility but use InteractionEvent from now on.
};