/**
* 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);
this._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;
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();}
});