/**
 * 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
 **/
var EventSelectorsClass = Class.create();
EventSelectorsClass.prototype = {
	version: '1.1_pre_ss',
	rules: [],
	timers: [],
	safariTimer: null,
	windowLoaded: false,                                                                                        // Safety flag. Prevents BINLoad events from getting executed (due to this.apply()) before the window has truly loaded.

	initialize:         function() {                                                                            // Register self as the all-powerful traditional binary-inclusive full-page-load handler
		var oldOnLoad = (window.onload instanceof Function) ? window.onload : function() {};
		var newOnLoad = function() {
			this.windowLoaded = true;                                                                               // Make a note that window.load() has run, thus we're now free to run BINLoad events.
			this._fireEventPhase('BINLoad', true);                                                                  // Run BINLoad events.
		}.bind(this);
		window.onload = function() {
			oldOnLoad();
			newOnLoad();
		};

		/**
		 * Special (expedient) binary-exclusive DOM-load handler
		 *
		 * Fires an event after the DOM has loaded, but before binary
		 * assets have necessarily loaded.
		 *
		 * Original code by:
		 *   Dean Edwards/Matthias Miller/John Resig (http://dean.edwards.name/weblog/2006/06/again/)
		 *
		 * Modified to work with EventSelectors by:
		 *   Kramer (060915)
		 **/

		/* for Mozilla/Opera9 */
		if (document.addEventListener) document.addEventListener("DOMContentLoaded", this._fireEventPhase.bind(this, 'DOMLoad'), false);

		/* for Internet Explorer */
		/*@cc_on @*/
		/*@if (@_win32)
			document.write("<script id=__ie_onload defer src=''><\/script>");
			var script = document.getElementById("__ie_onload");
			script.onreadystatechange = function() {
				if (this.readyState == "complete") {
					EventSelectors._fireEventPhase('DOMLoad'); // call the onload handler
				}
			};
		/*@end @*/

		/* for Safari */
		if (/WebKit/i.test(navigator.userAgent)) { // sniff
			this.safariTimer = setInterval(function() {
				if (/loaded|complete/.test(document.readyState)) {
					clearInterval(this.safariTimer);
					EventSelectors._fireEventPhase('DOMLoad'); // call the onload handler
				}
			}.bind(this), 10);
		}
	},

	register: function(rules, phase) {                           // PUBLIC: Register functions to run against selectors
		if (phase === true) phase = 'BINLoad';                     // "phase" option used to be bool, this is for backwards-compatibility.
		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: (typeof(phase) == 'undefined') ? 'DOMLoad' : 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;
addLoadEvent = EventSelectors.addLoadEvent;
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();}
});