/**
 * EventSelectors
 * Copyright (c) 2005-2006 Justin Palmer (http://encytemedia.com)
 * Examples and documentation (http://encytemedia.com/event-selectors)
 *
 * EventSelectors allow you access to Javascript events using a CSS style syntax.
 * It goes one step beyond Javascript events to also give you :loaded, which allows
 * you to wait until an item is loaded in the document before you begin to interact
 * with it.
 *
 * Inspired by the work of Ben Nolan's Behaviour (http://bennolan.com/behaviour)
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Heavily modified by Grady and Kramer of SolutionSet to the extent
 * that it is virtually unrecognizable in comparison with its original
 * form.
 *
 * @see Prototype.js
 * @requires prototype 1.6
 **/
var EventSelectorsClass = Class.create();
EventSelectorsClass.prototype = {
	version: '1.1_pre_ss',
	rules: [],
	timers: [],
	// Safety flag. Prevents BINLoad events from getting executed (due to this.apply()) before the window has truly loaded.
	windowLoaded: false,

	initialize: function() {
		Event.observe(window, 'load', function() {this.windowLoaded = true; this._fireEventPhase('BINLoad', true);}.bind(this), false);
		Event.observe(document, 'dom:loaded', this._fireEventPhase.bind(this, 'DOMLoad', true), false);
	},

	register: function(rules, phase) {                           // PUBLIC: Register functions to run against selectors
		// "phase" option used to be bool, this is for backwards-compatibility.
		if (phase === true)
			phase = 'BINLoad';
		else if (typeof(phase) == 'undefined' || phase === false || phase != 'BINLoad')
			phase = 'DOMLoad';
		for (var selector in rules) {
			switch (selector.toLowerCase()) {
				case 'window:binload':
					phase = 'BINLoad';
				break;
				case 'window:domload':
					phase = 'DOMLoad';
				break;
			}
			
			this.rules.push({
				selector: selector,
				func: rules[selector],
				phase: phase,
				applications: []
			});
		}
	},

	apply: function() {
		this._fireEventPhase('DOMLoad', true);
		this._fireEventPhase('BINLoad', true);
	},

	_executeRule: function(rule, unredundantly) {
		var selectors = $A(rule.selector.split(','));
		for (var x=0; x<selectors.length; x++) {
			var selector = selectors[x];
			var pair = selector.split(':');
			var eventName = (pair.length > 1) ? pair[1] : '';
			var elements = (pair[0] == 'window') ? [window] : $$(pair[0]);
			for (var y=0; y<elements.length; y++) {
				var element = elements[y];
				var event = false;

				var isRedundant = false;
				if (unredundantly === true) {                                                                         // Check to see if this will be a redundant application
					for (var z=0; z<rule.applications.length; z++) {
						if (rule.applications[z].node.parentNode == null && rule.applications[z].node != window) {        // node has gone stale, application is useless...
							rule.applications.splice(z,1);                                                                  // ...so remove it while we're here.
						} else if (rule.applications[z].node == element) {                                                // Node has already seen application of this function...
							isRedundant = true;                                                                             // Flag it
						}
					}
				}

				if ((unredundantly === true && isRedundant == false) || unredundantly !== true) {
					switch (eventName.toLowerCase()) {
						case '':
						case 'load':
						case 'binload':
						case 'domload':
						case 'loaded':
							if (pair[0] == 'window' || eventName == '') {
								rule.func(element);
							} else {
								this.timers[pair[0]] = setInterval(function(element, timer, rule) {
									var node = $(element);
									if(element.tagName != 'undefined') {
										clearInterval(this.timers[timer]);
										rule.func(node);
									}
								}.bind(this, element, pair[0], rule), 15);
							}
						break;
						default:
							var event = Event.observe(element, eventName, rule.func.bind(this, element));
						break;
					}
					rule.applications.push({
						node: element,
						event: event
					});
				}
			}
		}
	},

	_fireEventPhase: function(phase, unredundantly) {
		if ((phase == 'BINLoad' && this.windowLoaded) || phase != 'BINLoad') {
			for (var x=0; x<this.rules.length; x++) {
				if (this.rules[x].phase == phase) this._executeRule(this.rules[x], unredundantly);
			}
		}
	},

	_unloadEventCache: function() {
		for (var i=0; i<this.rules.length; i++) {
			for (var x=0; x<this.rules[i].applications.length; x++) {
				Event.unObserve(this.rules[i].applications[x].event);
			}
			this.rules[i].applications = [];
		}
	}
}
EventSelectors = new EventSelectorsClass();                          // Instantiate

// Convenience/Legacy aliases (Don't use any of these in new code)
EventSelectors.onDOMLoad = function(func) {
	EventSelectors.register({
		'window:domload': func
	});
};
EventSelectors.onBINLoad = function(func) {
	EventSelectors.register({
		'window:binload': func
	});
};
EventSelectors.addLoadEvent = EventSelectors.onBINLoad;
Behaviour = EventSelectors;                                          // For old code that expects Behaviour

// Remove/Comment this if you do not wish to reapply Rules automatically
// on Ajax request.
Ajax.Responders.register({
	onComplete: function() { EventSelectors.apply();}
});

