// 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.

// Modified by Grady and Kramer of SolutionSet
var classEventSelectors = Class.create();
classEventSelectors.prototype = {
	version: '1.0_pre_ss',
	events: [],
	eventZones: {
		DOMLoad: [],
		BINLoad: []
	},
	funcs: [],
	nodes: [],

	apply: function() {
		this._unloadEventCache();
		this._fireEventZone('BINLoad');
		this._fireEventZone('DOMLoad');
	},

	addLoadEvent: function(func) {                                      // Convenience function in global scope
		this.register({
			'window:load': func
		})
	},

	initialize: function() {                                           // Register self as the all-powerful onload handler
		// Traditional binary-inclusive full-page-load handler
		var oldOnLoad = (window.onload instanceof Function) ? window.onload : function() {};
		var newOnLoad = this._fireEventZone.bind(this, 'BINLoad');
		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._fireEventZone.bind(this, 'DOMLoad'), false);

		/* for Internet Explorer */
		/*@cc_on @*/
		/*@if (@_win32)
			document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
			var script = document.getElementById("__ie_onload");
			script.onreadystatechange = function() {
				if (this.readyState == "complete") {
					EventSelectors._fireEventZone('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)) {
					this._fireEventZone('DOMLoad'); // call the onload handler
				}
			}, 10);
		}
	},

	// onDOMLoad option (BOOL): true, rule fires on DOM load. False, rule fires on BIN load.
	register: function(rules, onDOMLoad) {                           // PUBLIC: Register functions to run against selectors
		var eventZone = 'BINLoad';
		if (onDOMLoad === true) eventZone = 'DOMLoad';
		this.eventZones[eventZone].push(rules);
	},

	_executeRules: function(rules) {
		this.timer = new Array();
		for (var key in rules) {
			var rule = {
				selector: key,
				func: rules[key]
			};
			switch (rule.selector) {                                     // Special selector tokens
				case 'window:load':
				case 'window:binLoad':
				case 'window:domLoad':
					rule.func();
				break;
				default:                                                   // Default selector operation
					var selectors = $A(rule.selector.split(','));
					for (var x=0; x<selectors.length; x++) {
						var selector = selectors[x];
						var pair = selector.split(':');
						var event = pair[1];
						var elements = $$(pair[0]);
						for (var y=0; y<elements.length; y++) {
							var element = elements[y];
							if(pair[1] == '' || pair.length == 1) {
								var funcId = this.funcs.indexOf(rule.func);
								if(funcId == -1) {
									this.funcs.push(rule.func);
									funcId = this.funcs.length-1;
								}
								if (!this.nodes[funcId])
									this.nodes[funcId] = [];
								if (this.nodes[funcId].indexOf(element) == -1) {
									this.nodes[funcId].push(element);
									rule.func(element);
								}
								continue;
							}
							if(event.toLowerCase() == 'loaded') {
								this.timer[pair[0]] = setInterval(function(element, timer, rule) {
									var node = $(element);
									if(element.tagName != 'undefined') {
										clearInterval(this.timer[timer]);
										rule.func(node);
									}
								}.bind(this, element, pair[0], rule), 15);
							} else {
								var observer = function(event) {
									var element = Event.element(event);
									if (element.nodeType == 3)                       // Safari Bug (Fixed in Webkit)
										element = element.parentNode;
									rule.func($(element), event);
								}
								this.events.push(Event.observe(element, event, observer));
							}
						}
					}
				break;
			}
		}
	},

	_fireEventZone: function(zone) {
		if (zone == 'DOMLoad') {
			if (arguments.callee.done) return;                               // quit if this function has already been called
			arguments.callee.done = true;                                    // flag this function so we don't do the same thing twice
			if (this.safariTimer) clearInterval(this.safariTimer);           // kill the timer
		}
		for (var x=0; x<this.eventZones[zone].length; x++) {
			this._executeRules(this.eventZones[zone][x]);
		}
	},

	_unloadEventCache: function() {
		if (!this.events) return;
		for (var i=0; i<this.events.length; i++) {
			Event.unObserve(this.events[i]);
			this.events[i] = null;
		}
		this.events = [];
		this._fireEventZone.done = false;									// Adam added to fix the problem that _fireEventZone only runs
																			// once. After this function has run, the _fireEventZone should
																			// run again to re-init the EventSelectors
	}
}
EventSelectors = new classEventSelectors();                          // Instantiate
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();}
//});
