/*

Behaviour.2.js

The goal was to provide an event argument input syntax that leverages the Event library's ability
to pass arguments to individual event instances. The object passed as an event description for
a given 'parseSelectorPath / eventType mirrors the structure of the extended Event parameter
syntax i.e.:

	Events.add({
		label: "handly label for debugging",
		bLogEvent: false,
		trace: false,
		element: el,
		type: eventType, 
		handler: eventDesc,
		data: new Object(),
		context: window
	});


Nomenclature:
	An "address" is a parseSelector path.
	An "eventType" is an event handler name with no "on" in front as per the Events library.
	A "rule" is either a function reference or an object containing
		a function reference and other Events.add() input object parameters
	
	
Sample Event Map:

	var eventMap = {
		'#researchDisplay tbody tr a': {
			'click': loadPDF,
			'mouseover': {handler: showHilite, args: {}}
		}
	}
	
	
Sample syntax, shows simple (function only), and extended version 

	var [map] = {
		'[address]': {
			'[eventType]': fn,
			'[eventType]': {
				handler: fn, 
				data: {}, 
				context: context
			}
		}
	}


// This version is also backwards compatible with Behaviour.js if you want to simply pass
// the element to a fuction without restricting it to Event commands

	var eventMap = {
		'input' : function(el, num) {
			Element.addClass(el,"disabled");
		}
	}

*/


Behaviour_class = function() { 
	this.timingEnabled = false;
	this._stackEvents = false;
}

// "public"

Behaviour_class.prototype.apply = function(map,parentEl,timingEnabled) {
	if (timingEnabled !== undefined) { this.timingEnabled = !!timingEnabled; }
	this.walkMap(map, parentEl||null);
}

Behaviour_class.prototype.applyRules = Behaviour_class.prototype.apply;

// "private"

Behaviour_class.prototype.walkMap = function(map, parentEl) {
	var totalTime = 0;
	var startTime, endTime, selectorTime;
	var rElements, rule, i, el;
	for(var address in map) {
		startTime = new Date();
		rElements = Element.parseSelector(address,parentEl||null);
		for(i=0; el = rElements[i]; i++) {
			if (typeof map[address] == "function") {
				map[address](el,i)
			}
			else {
				this.applyEventRule.apply(this, [ map[address], el ]);
			}
		}
		endTime = new Date();
		selectorTime = endTime-startTime;
		// console
		this.outputTiming(selectorTime,"Behaviour timing for " + address + " ("+rElements.length+")");

		totalTime += selectorTime;
	}
	this.outputTiming(totalTime,"Total Behaviour timing");
}


// Applies a single rule/ruleset to a single element
Behaviour_class.prototype.applyEventRule = function(rule, el) {

	var evtArgs, prop;
	for(var eventType in rule) {

		eventDesc = rule[eventType];

		// assume single function provided in inputs
		var evtArgs = {
			label: eventType + ": " + rule,
			element: el,
			type: eventType,
			handler: eventDesc,
			data: new Object(),
			context: window,
			trace: false
		}

		// check to see if it's actually a full events argument
		// Note: eventDesc must now contain a property called
		// "handler" as per the Event library .add() syntax
		if (typeof(eventDesc) == "object") {
			// dbg("type", typeof(eventDesc));
			evtArgs.handler = null;
			if (this.validEventParameter(eventDesc)) {
				for(prop in eventDesc) {
					evtArgs[prop] = eventDesc[prop];
				}
			} else {
				var elementDbg = el.tagName + ( (el.id) ? "#" + el.id : "" ) + ( (el.className) ? "." + el.className : "" );
				dbg("Invalid event parameter for eventType: " + eventType, elementDbg);
			}
		}

		// wipe the old event
		if (Events.removeEvent && !this._stackEvents) {
			Events.removeEvent(el, eventType);
		}

		Events.add(evtArgs);
	}

}

Behaviour_class.prototype.validEventParameter = function(o) {
	return (o.handler !== undefined);
}

Behaviour_class.prototype.outputTiming = function(timing,desc) {
	if (this.timingEnabled) {
		try { console.info((desc?desc+": ":"")+timing);  } catch(e) {}
	}
}

//Determines whether event handlers should be stacked for a given element (bStackEvents = true)
//or whether they should be overwritten (bStackEvents = false)
Behaviour_class.prototype.setEventStacking = function(bStackEvents) {
	this._stackEvents = bStackEvents;
}

var Behaviour = new Behaviour_class();


