1 /*!
  2 	Copyright 2010 British Broadcasting Corporation
  3 
  4 	Licensed under the Apache License, Version 2.0 (the "License");
  5 	you may not use this file except in compliance with the License.
  6 	You may obtain a copy of the License at
  7 
  8 	   http://www.apache.org/licenses/LICENSE-2.0
  9 
 10 	Unless required by applicable law or agreed to in writing, software
 11 	distributed under the License is distributed on an "AS IS" BASIS,
 12 	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 	See the License for the specific language governing permissions and
 14 	limitations under the License.
 15 */
 16 /**
 17 	@name glow
 18 	@namespace
 19 	@version @VERSION@
 20 	@description The glow library namespace
 21 		The library can also be used as a function, which is a shortcut to
 22 		{@link glow.NodeList}.
 23 		
 24 	@example
 25 		var links = glow('a');
 26 		// is the same as
 27 		var links = new glow.NodeList('a');
 28 */
 29 if (!window.Glow) { // loading packages via user SCRIPT tags?
 30 	window.Glow = {
 31 		provide: function(f) {
 32 			f(glow);
 33 		},
 34 		complete: function(n, version) {
 35 			glow.version = version;
 36 		}
 37 	};
 38 	
 39 	window.glow = function(nodeListContents) {
 40 		return new glow.NodeList(nodeListContents);
 41 	};
 42 	
 43 	glow.load = function() {
 44 		throw new Error('Method load() is not available without glow.js');
 45 	}
 46 }
 47 
 48 
 49 Glow.provide(function(glow) {
 50 	/*!debug*/
 51 	var glowbug = {
 52 	errors: []
 53 	,
 54 	log: function(message, fileName, lineNumber) {
 55 		this._add('Log', message, fileName, lineNumber);
 56 	}
 57 	,
 58 	warn: function(message, fileName, lineNumber) {
 59 		this._add('Warn', message, fileName, lineNumber);
 60 	}
 61 	,
 62 	error: function(message, fileName, lineNumber) {
 63 		this._add('Error', message, fileName, lineNumber);
 64 	}
 65 	,
 66 	_add: function(level, message, fileName, lineNumber) {
 67 		var e = new Error(message, fileName, lineNumber);
 68 		
 69 		e.message = message;
 70 		e.name = 'Glow'+level;
 71 		e.level = level.toLowerCase();
 72 		
 73 		var match = /\[([^\]]+)\]/.exec(message);
 74 		if (match) e.type = match[1]; // may be undefined
 75 		else e.type = 'message';
 76 		
 77 		this.errors.push(e);
 78 		
 79 		this.out(e);
 80 	}
 81 	,
 82 	out: function(e) {
 83 		var message = '['+e.level+'] '+e.message;
 84 		
 85 		if (window.console) {
 86 			if (e.level === 'warn' && window.console.warn) {
 87 				console.warn(message);
 88 			}
 89 			else if (e.level === 'error' && window.console.error) {
 90 				console.error(message);
 91 			}
 92 			else if (window.console.log) {
 93 				console.log(message);
 94 			}
 95 		}
 96 		else if (window.opera && opera.postError) {
 97 			opera.postError(message);
 98 		}
 99 		else { // use our own console
100 			glowbug.console.log(e.level, message);
101 		}
102 	}
103 };
104 
105 glowbug.console = {
106 	messages: [],
107 	log: function(level, message) {
108 		if (!this._w) {
109 			try {
110 				this._w = window.open('', 'report', 'width=350,height=250,menubar=0,toolbar=0,location=no,status=0,scrollbars=1,resizable=1');
111 				this._w.document.write(
112 					'<html><head><title>Console<\/title><style>body{background-color: #ddd;} .message{background-color:#FFF;padding:4px;margin:0px;border-bottom:1px solid #ccc;} .warn {background-color: #E5E6B6;} .error{background-color: #D39C9E;}<\/style><\/head>'
113 					+ '<body style="font: 11px monaco"><code id="messages"><\/code><\/body><\/html>'
114 				)
115 				this._w.document.close();
116 			}
117 			catch(ignored) {
118 				this._w = null;
119 			}
120 		}
121 		
122 		if (this._w) {
123 			var p = this._w.document.createElement('P');
124 			p.className = 'message ' + level;
125 			p.innerHTML = message;
126 			this._w.document.getElementById('messages').appendChild(p);
127 			
128 			var dh = this._w.document.body.scrollHeight
129 			var ch = this._w.document.body.clientHeight
130 			if (dh > ch) { this._w.scrollTo(0, dh-ch); }
131 		}
132 	}
133 }
134 	if (typeof glowbug != 'undefined') { glow.debug = glowbug; }
135 	/*gubed!*/
136 });
137 Glow.provide(function(glow) {
138 	/**
139 		@name glow.env
140 		@namespace
141 		@description Information about the browser and characteristics
142 	*/
143 	
144 	// parse the useragent string, setting NaN if match isn't found
145 	var ua = navigator.userAgent.toLowerCase(),
146 		nanArray = [0, NaN],
147 		opera  = (/opera[\s\/]([\w\.]+)/.exec(ua) || nanArray)[1],
148 		ie     = opera ? NaN : (/msie ([\w\.]+)/.exec(ua) || nanArray)[1],
149 		gecko  = (/rv:([\w\.]+).*gecko\//.exec(ua) || nanArray)[1],
150 		webkit = (/applewebkit\/([\w\.]+)/.exec(ua) || nanArray)[1],
151 		khtml  = (/khtml\/([\w\.]+)/.exec(ua) || nanArray)[1],
152 		toNumber = parseFloat,
153 		env = {};
154 	
155 	/**
156 		@name glow.env.gecko
157 		@type number
158 		@description Gecko version number to one decimal place (eg 1.9) or NaN on non-gecko browsers.
159 			The most popular browser using the Gecko engine is Firefox.
160 		
161 		@see <a href="http://en.wikipedia.org/wiki/Gecko_(layout_engine)#Usage">Versions of Gecko used by browsers</a>
162 		
163 		@example
164 			if (glow.env.gecko < 1.9) {
165 				// runs in Firefox 2 and other browsers that use Gecko earlier than 1.9
166 			}
167 	*/
168 	env.gecko = toNumber(gecko);
169 	
170 	/**
171 		@name glow.env.ie
172 		@type number
173 		
174 		@description IE version number to one decimal place (eg 6.0) or NaN on non-IE browsers.
175 			This number will also be populated for browser based on IE's trident engine
176 			
177 		@example
178 			if (glow.env.ie < 9) {
179 				// runs in IE pre-9.0
180 				glow('#content').css('background', 'deadmoomin.png');
181 			}
182 	*/
183 	env.ie = toNumber(ie);
184 	
185 	/**
186 		@name glow.env.opera
187 		@type number
188 		
189 		@description Opera version number to one decimal place (eg 10.0) or NaN on non-Opera browsers.
190 		
191 		@example
192 			if (glow.env.opera < 10) {
193 				// runs in Opera pre-10.0
194 			}
195 	*/
196 	env.opera = toNumber(opera);
197 	
198 	/**
199 		@name glow.env.webkit
200 		@type number
201 		
202 		@description Webkit version number to one decimal place (eg 531.9) or NaN on non-Webkit browsers.
203 			Safari and Google Chrome are the most popular browsers using Webkit.
204 			
205 		@see <a href="http://en.wikipedia.org/wiki/Safari_version_history#Release_history">Versions of Webkit used by Safari</a>
206 		@see <a href="http://en.wikipedia.org/wiki/Google_Chrome#Release_history">Versions of Webkit used by Google Chrome</a>
207 			
208 		@example
209 			if (glow.env.webkit < 526) {
210 				// runs in Safari pre-4.0, and Chrome pre-1.0
211 			}
212 	*/
213 	env.webkit = toNumber(webkit);
214 
215 	/**
216 		@name glow.env.khtml
217 		@type number
218 		
219 		@description KHTML version number to one decimal place or NaN on non-KHTML browsers.
220 			Konqueror is the most popular browsers using KHTML.
221 	*/
222 	env.khtml = toNumber(khtml);
223 	
224 	/**
225 		@name glow.env.standardsMode
226 		@type boolean
227 		@description True if the browser reports itself to be in 'standards mode'
228 			Otherwise, the browser is in 'quirks mode'
229 			
230 		@see <a href="http://en.wikipedia.org/wiki/Quirks_mode">Quirks Mode vs Standards Mode</a>
231 	*/
232 	env.standardsMode = document.compatMode != "BackCompat" && (!env.ie || env.ie >= 6);
233 	
234 	/**
235 		@name glow.env.version
236 		@type string
237 		@description Version number of the browser in use as a string.
238 			This caters for version numbers that aren't 'real' numbers, like "7b" or "1.9.1"
239 	*/
240 	env.version = ie || gecko || webkit || opera || khtml || '';
241 	
242 	// export
243 	glow.env = env;
244 });
245 // start-source: core/ready.js
246 /*debug*///log.info('executing core/ready.js');
247 Glow.provide(
248 	function(glow) {
249 		var readyQueue = [],
250 			domReadyQueue = [],
251 			blockersActive = 0,
252 			processingReadyQueue = false;
253 			
254 		glow._readyBlockers = {};
255 		
256  		/*debug*///log.info('overwriting Glow ready with glow.ready');	
257 		glow.ready = function (f) { /*debug*///log.info('glow.ready()');
258 			if (this.isReady) {
259 				f();
260 			}
261 			else {
262 				readyQueue.push(f);
263 			}
264 			return glow;
265 		};
266 		
267 		glow.onDomReady = function(f) {
268 			//just run function if already ready
269 			if (glow.isDomReady) {
270 				f();
271 			}
272 			else {
273 				domReadyQueue.push(f);
274 			}
275 		};
276 		
277 		glow._addReadyBlock = function(name) { /*debug*///log.info('_addReadyBlock('+name+')');
278 			if (typeof glow._readyBlockers[name] === 'undefined') { glow._readyBlockers[name] = 0; }
279 			glow._readyBlockers[name]++;
280 			glow.isReady = false;
281 			blockersActive++; /*debug*///log.info('  » blockersActive '+blockersActive+'.');
282 			return glow;
283 		}
284 			
285 		glow._removeReadyBlock = function(name) { /*debug*///log.info('_removeReadyBlock('+name+')');
286 			if (glow._readyBlockers[name]) {
287 				glow._readyBlockers[name]--;
288 				blockersActive--;  /*debug*///log.info('  » blockersActive '+blockersActive+'.');
289 				// if we're out of blockers
290 				if (!blockersActive) {
291 					// call our queue
292 					glow.isReady = true;
293 					runReadyQueue();
294 				}
295 			}
296 			return glow;
297 		}
298 		
299 		// add blockers for any packages that started loading before core (this package) was built
300 		if (glow._build) { // only defined when using big Glow
301 			for (var i = 0, len = glow._build.loading.length; i < len; i++) {
302 				glow._addReadyBlock('glow_loading_'+glow._build.loading[i]);
303 			}
304 			
305 			for (var j = 0, lenj = glow._build.callbacks.length; j < lenj; j++) {
306 				if (glow._addReadyBlock) { glow._addReadyBlock('glow_loading_loadedcallback'); }
307 			}
308 		}
309 		
310 		function runDomReadyQueue() { /*debug*///log.info('runDomReadyQueue()');
311 			glow.isDomReady = true;
312 			// run all functions in the array
313 			for (var i = 0, len = domReadyQueue.length; i < len; i++) {
314 				domReadyQueue[i]();
315 			}
316 		}
317 	
318 		function runReadyQueue() { /*debug*///log.info('runReadyQueue()');
319 			// if we're already processing the queue, just exit, the other instance will take care of it
320 			if (processingReadyQueue) { return; }
321 			
322 			/*debug*///log.info('readyQueue: '+readyQueue.length);
323 			processingReadyQueue = true;
324 			while (readyQueue.length) {
325 				var callback = readyQueue.shift();
326 				/*debug*///log.info('callback: '+callback);
327 				callback(glow);
328 				
329 				// check if the previous function has created a blocker
330 				if (blockersActive) { break; }
331 			}
332 			processingReadyQueue = false;
333 		}
334 		
335 		/**
336 			@private
337 			@function
338 			@name bindReady
339 			@description Add listener to document to detect when page is ready.
340 		 */
341 		function bindReady() { /*debug*///log.info('bindReady()');
342 			//don't do this stuff if the dom is already ready
343 			if (glow.isDomReady) { return; }
344 			glow._addReadyBlock('glow_domReady'); // wait for dom to be ready
345 			
346 			function onReady() { /*debug*///log.info('onReady()');
347 				runReadyQueue();
348 				glow._removeReadyBlock('glow_domReady');
349 			}
350 					
351 			if (document.readyState == 'complete') { // already here!
352 				 /*debug*///log.info('already complete');
353 				onReady();
354 			}
355 			else if (glow.env.ie && document.attachEvent) { /*debug*///log.info('bindready() - document.attachEvent');
356 				// like IE
357 				
358 				// not an iframe...
359 				if (document.documentElement.doScroll && window == top) {
360 					(function() {  /*debug*///log.info('doScroll');
361 						try {
362 							document.documentElement.doScroll('left');
363 						}
364 						catch(error) {
365 							setTimeout(arguments.callee, 0);
366 							return;
367 						}
368 				
369 						// and execute any waiting functions
370 						onReady();
371 					})();
372 				}
373 				else {
374 					// an iframe...
375 					document.attachEvent(
376 						'onreadystatechange',
377 						function() { /*debug*///log.info('onreadystatechange');
378 							if (document.readyState == 'complete') {
379 								document.detachEvent('onreadystatechange', arguments.callee);
380 								onReady();
381 							}
382 						}
383 					);
384 				}
385 			}
386 			else if (document.readyState) { /*debug*///log.info('bindready() - document.readyState');
387 				// like pre Safari
388 				(function() { /*debug*///log.info('loaded|complete');
389 					if ( /loaded|complete/.test(document.readyState) ) {
390 						onReady();
391 					}
392 					else {
393 						setTimeout(arguments.callee, 0);
394 					}
395 				})();
396 			}
397 			else if (document.addEventListener) {/*debug*///log.info('bindready() - document.addEventListener');
398 				// like Mozilla, Opera and recent webkit
399 				document.addEventListener( 
400 					'DOMContentLoaded',
401 					function(){ /*debug*///log.info('glow DOMContentLoaded');
402 						document.removeEventListener('DOMContentLoaded', arguments.callee, false);
403 						onReady();
404 					},
405 					false
406 				);
407 			}
408 			else {
409 				throw new Error('Unable to bind glow ready listener to document.');
410 			}
411 		}
412 	
413 		glow.notSupported = ( // here are the browsers we don't support
414 			glow.env.ie < 6 ||
415 			(glow.env.gecko < 1.9 && !/^1\.8\.1/.test(env.version)) ||
416 			glow.env.opera < 9 ||
417 			glow.env.webkit < 412
418 		);
419 		// deprecated
420 		glow.isSupported = !glow.notSupported;
421 		
422 		// block 'ready' if browser isn't supported
423 		if (glow.notSupported) {
424 			glow._addReadyBlock('glow_browserSupport');
425 		}
426 		
427 		bindReady();
428 	}
429 );
430 // end-source: core/ready.js
431 /**
432 	@name glow.util
433 	@namespace
434 	@description Core JavaScript helpers
435 */
436 Glow.provide(function(glow) {
437 	var util = {},
438 		undefined;
439 	
440 	/**
441 		@name glow.util.apply
442 		@function
443 		@description Copies properties from one object to another
444 			All properties from 'source' will be copied onto
445 			'destination', potentially overwriting existing properties
446 			on 'destination'.
447 			
448 			Properties from 'source's prototype chain will not be copied.
449 		
450 		@param {Object} destination Destination object
451 		
452 		@param {Object} source Properties of this object will be copied onto the destination
453 		
454 		@returns {Object} The destination object.
455 		
456 		@example
457 			var obj = glow.util.apply({foo: "hello", bar: "world"}, {bar: "everyone"});
458 			//results in {foo: "hello", bar: "everyone"}
459 	*/
460 	util.apply = function(destination, source) {
461 		/*!debug*/
462 			if (arguments.length != 2) {
463 				glow.debug.warn('[wrong count] glow.util.apply expects 2 arguments, not '+arguments.length+'.');
464 			}
465 			if (typeof destination != 'object') {
466 				glow.debug.warn('[wrong type] glow.util.apply expects argument "destination" to be of type object, not ' + typeof destination + '.');
467 			}
468 			if (typeof source != 'object') {
469 				glow.debug.warn('[wrong type] glow.util.apply expects argument "source" to be of type object, not ' + typeof source + '.');
470 			}
471 		/*gubed!*/
472 		for (var i in source) {
473 			if ( source.hasOwnProperty(i) ) {
474 				destination[i] = source[i];
475 			}
476 		}
477 		return destination;
478 	};
479 	
480 	/**
481 		@name glow.util.extend
482 		@function
483 		@description Copies the prototype of one object to another
484 			The 'subclass' can also access the 'base class' via subclass.base
485 
486 		@param {Function} sub Class which inherits properties.
487 		@param {Function} base Class to inherit from.
488 		@param {Object} additionalProperties An object of properties and methods to add to the subclass.
489 
490 		@example
491 			function MyClass(arg) {
492 				this.prop = arg;
493 			}
494 			MyClass.prototype.showProp = function() {
495 				alert(this.prop);
496 			};
497 			function MyOtherClass(arg) {
498 				//call the base class's constructor
499 				arguments.callee.base.apply(this, arguments);
500 			}
501 			glow.util.extend(MyOtherClass, MyClass, {
502 				setProp: function(newProp) {
503 					this.prop = newProp;
504 				}
505 			});
506 
507 			var test = new MyOtherClass("hello");
508 			test.showProp(); // alerts "hello"
509 			test.setProp("world");
510 			test.showProp(); // alerts "world"
511 	*/
512 	util.extend = function(sub, base, additionalProperties) {
513 		/*!debug*/
514 			if (arguments.length < 2) {
515 				glow.debug.warn('[wrong count] glow.util.extend expects at least 2 arguments, not '+arguments.length+'.');
516 			}
517 			if (typeof sub != 'function') {
518 				glow.debug.error('[wrong type] glow.util.extend expects argument "sub" to be of type function, not ' + typeof sub + '.');
519 			}
520 			if (typeof base != 'function') {
521 				glow.debug.error('[wrong type] glow.util.extend expects argument "base" to be of type function, not ' + typeof base + '.');
522 			}
523 		/*gubed!*/
524 		var f = function () {}, p;
525 		f.prototype = base.prototype;
526 		p = new f();
527 		sub.prototype = p;
528 		p.constructor = sub;
529 		sub.base = base;
530 		if (additionalProperties) {
531 			util.apply(sub.prototype, additionalProperties);
532 		}
533 	};
534 	
535 	// export
536 	glow.util = util;
537 });
538 Glow.provide(function(glow) {
539 	/**
540 	@name glow.events
541 	@namespace
542 	@description Handling custom events
543 	*/
544 	var events = {};
545 		
546 	/* storage variables */
547 	
548 	var eventListeners = {}, 
549 		eventId = 1, /* TODO: camelCase */
550 		objIdCounter = 1, 
551 		eventKey = '__eventId' + glow.UID; 
552 		
553 	
554 	/**
555 	@name glow.events.addListeners
556 	@function
557 	@param {Object[]} attachTo Array of objects to add listeners to.
558 	@param {string} name Name of the event to listen for.
559 		Event names are case sensitive.
560 	@param {function} callback Function to call when the event is fired.
561 		The callback will be passed a single event object. The type of this
562 		object depends on the event (see documentation for the event
563 		you're listening to).
564 	@param {Object} [thisVal] Value of 'this' within the callback.
565 		By default, this is the object being listened to.
566 	@see glow.events.Target#fire
567 	@description Convenience method to add listeners to many objects at once.
568 		If you want to add a listener to a single object, use its
569 		'on' method.
570 	*/
571 	events.addListeners = function (attachTo, name, callback, thisVal) {
572 		var listenerIds = [],
573 			objIdent,
574 			listener,
575 			eventsOnObject,
576 			currentListeners;
577 	
578 		//attach the event for each element, return an array of listener ids
579 		var i = attachTo.length;
580 		while (i--) {
581 			objIdent = attachTo[i][eventKey];
582 			if (!objIdent){
583 				objIdent = attachTo[i][eventKey] = objIdCounter++;
584 			}
585 					
586 			listener = [ callback, thisVal ];
587 			eventsOnObject = eventListeners[objIdent];
588 			if(!eventsOnObject){
589 				eventsOnObject = eventListeners[objIdent] = {};
590 			}
591 					
592 			currentListeners = eventsOnObject[name];
593 			if(!currentListeners){
594 				eventsOnObject[name] = [listener];
595 			}
596 			else{
597 				currentListeners[currentListeners.length] = listener;
598 			}							
599 		}
600 		return events;
601 	};
602 	
603 	events._getPrivateEventKey = function(node) {
604 		if (!node[eventKey]) {
605 			node[eventKey] = objid++;
606 		}
607 		
608 		return node[eventKey];
609 	}
610 	
611 	/**
612 	@name glow.events.fire
613 	@function
614 	@param {Object[]} items      Array of objects to add listeners to
615 	@param {string}   eventName  Name of the event to fire
616 	@param {glow.events.Event|Object} [event] Event object to pass into listeners.
617        You can provide a simple object of key-value pairs which will
618        be added as properties on the glow.events.Event instance.
619 		
620 	@description Convenience method to fire events on multiple items at once.
621 		If you want to fire events on a single object, use its
622 		'fire' method.
623 	*/
624 		
625 	events.fire = function (items, eventName, event) {
626 		if (! event) {
627 			event = new events.Event();
628 		}
629 		else if ( event.constructor === Object ) {
630 			event = new events.Event( event )
631 		}
632 		
633 		// for loop, because order matters!
634 		for(var i = 0, len = items.length; i < len; i++) { 
635 			callListeners(items[i], eventName, event);
636 		}
637 			
638 		return event;
639 	};
640 
641 	
642 	/**
643 	 @name glow.events-callListeners
644 	 @private
645 	*/
646 	function callListeners(item, eventName, event, thisVal) {
647 		var objIdent = item[eventKey],
648 			listenersForEvent,
649 			returnedVal;			
650 
651 		if (!objIdent || !eventListeners[objIdent]) {
652 			return event;
653 		}
654 				
655 		listenersForEvent = eventListeners[objIdent][eventName];
656 			
657 		if (!listenersForEvent) {
658 			return event;
659 		}
660 		// Slice to make sure we get a unique copy.
661 		listenersForEvent = listenersForEvent.slice(0);
662 		for (var i = 0, len = listenersForEvent.length; i < len; i++){
663 			returnVal = listenersForEvent[i][0].call((listenersForEvent[i][1] || thisVal || item), event);
664 			if (returnVal === false){
665 				event.preventDefault();
666 			}
667 		}
668 			
669 		return event;
670 	}
671 	events._callListeners = callListeners;
672 		
673 		
674 	/**
675 	@name glow.events.removeAllListeners
676 	@function
677 	@param {Object[]} items Items to remove events from		    
678 	@description Removes all listeners attached to a given object.
679 		This removes not only listeners you added, but listeners others
680 		added too. For this reason it should only be used as part of a cleanup
681 		operation on objects that are about to be destroyed.
682 	*/
683 	
684 	events.removeAllListeners = function (items) {
685 		var objIdent,
686 		i = items.length;		
687 		
688 		while(i--){
689 			
690 			objIdent = items[i][eventKey];
691 			
692 			if (!objIdent) {
693 				return false;
694 			}
695 			else {
696 				delete eventListeners[objIdent];
697 			}
698 		}
699 
700 		return true;
701 	};
702 
703 
704 	/**
705 	@name glow.events.removeListeners
706 	@function
707 	@param {Object[]} items Items to remove events from.
708 	@param {string} eventName Name of the event to remove.
709 	@param {function} callback A reference to the original callback used when the listener was added.
710 	@decription Removes listeners for an event.
711 	*/
712 	events.removeListeners = function (item, eventName, callback) { /* TODO: items! */
713 		var objIdent,
714 			listenersForEvent,
715 			i = item.length;
716 		
717 	
718 		while(i--){
719 			
720 			objIdent = item[i][eventKey];
721 				
722 			if(!objIdent || !eventListeners[objIdent]){
723 				return events;
724 			}
725 			
726 		
727 			listenersForEvent = eventListeners[objIdent][eventName];
728 			if(!listenersForEvent){
729 				return events;
730 			}
731 			
732 			// for loop, because order matters
733 			for(var j = 0, lenj = listenersForEvent.length; j < lenj; j++){						
734 				if (listenersForEvent[j][0] === callback){
735 					listenersForEvent.splice(j, 1);
736 					break;
737 				}
738 		
739 			}
740 		}
741 		
742 		return events;			
743 	};
744 	
745 	/**
746 		Copies the events from one nodelist to another
747 		@private
748 		@name glow.events._copyEvent
749 		@see glow.NodeList#clone
750 		@function
751 	*/
752 	events._copyEvent = function(from, to){
753 		var listenersToCopy,
754 		i = [from].length,
755 		listenersForEvent,
756 		name,
757 		callback,
758 		thisVal;
759 		
760 		while(i--){
761 			
762 			var objIdent = [from][i][eventKey];
763 			
764 			listenersForEvent = eventListeners[objIdent];
765 			
766 				
767 			if(!objIdent){
768 					
769 				return false;
770 			}
771 			else{
772 				for ( var eventName in eventListeners[objIdent] ) {
773 					name = eventName;
774 					callback = eventListeners[objIdent][eventName][0][0];
775 					thisVal = eventListeners[objIdent][eventName][0][1];
776 				}				
777 				events._addDomEventListener([to], name, callback, thisVal);
778 		}
779 	
780 		return;
781 		}
782 		
783 	}
784 	///**
785 	//@name glow.events.getListeners
786 	//@function
787 	//@param {Object[]} item Item to find events for
788 	//@decription Returns a list of listeners attached for the given item.
789 	//
790 	//*/	
791 	//glow.events.getListeners = function(item){
792 	//	var objIdent; 
793 	//	for (var i = 0, len = item.length; i < len; i++) {
794 	//		
795 	//		objIdent = item[i][eventKey];
796 	//		
797 	//		if (!objIdent) {
798 	//			return false;
799 	//		}
800 	//		else {
801 	//			// todo: need to return listeners in a sensible format
802 	//			return eventListeners[objIdent];
803 	//		}
804 	//	}
805 	//
806 	//
807 	//	return false;
808 	//};
809 	//
810 	///**
811 	//@name glow.events.hasListener
812 	//@function
813 	//@param {Object[]} item  Item to find events for
814 	//@param {String}   eventName  Name of the event to match
815 	//@decription Returns true if an event is found for the item supplied
816 	//
817 	//*/
818 	//
819 	//glow.events.hasListener = function (item, eventName) {
820 	//	var objIdent,
821 	//		listenersForEvent;
822 	//		
823 	//	for (var i = 0, len = item.length; i < len; i++) {	
824 	//		objIdent = item[i][eventKey];
825 	//			
826 	//		if (!objIdent || !eventListeners[objIdent]) {
827 	//			return false;
828 	//		}
829 	//				
830 	//		listenersForEvent = eventListeners[objIdent][eventName];
831 	//		if (!listenersForEvent) {
832 	//			return false;
833 	//		}
834 	//		else {
835 	//			return true;							
836 	//		}					
837 	//	}
838 	//	
839 	//	return false;			
840 	//};
841 	
842 	/**
843 	@name glow.events.Target
844 	@class
845 	@description An object that can have event listeners and fire events.
846 		Extend this class to make your own objects have 'on' and 'fire'
847 		methods.
848 		
849 	@example
850 		// Ball is our constructor
851 		function Ball() {
852 			// ...
853 		}
854 		       
855 		// make Ball inherit from Target
856 		glow.util.extend(Ball, glow.events.Target, {
857 			// additional methods for Ball here, eg:
858 			bowl: function() {
859 				// ...
860 			}
861 		});
862 		       
863 		// now instances of Ball can receive event listeners
864 		var myBall = new Ball();
865 		myBall.on('bounce', function() {
866 			alert('BOING!');
867 		});
868 		       
869 		// and events can be fired from Ball instances
870 		myBall.fire('bounce');
871 	*/
872 	
873 	events.Target = function () {
874 			
875 	};
876 	var targetProto = events.Target.prototype;
877 		
878 	/**
879 	@name glow.events.Target.extend
880 	@function
881 	@param {Object} obj Object to add Target instance methods to.
882 		
883 	@description Convenience method to add Target instance methods onto an object.
884 		If you want to add Target methods to a class, extend glow.events.Target instead.
885 		       
886 	@example
887 		var myApplication = {};
888 		       
889 		glow.events.Target.extend(myApplication);
890 		       
891 		// now myApplication can fire events...
892 		myApplication.fire('load');
893 		       
894 		// and other objects can listen for those events
895 		myApplication.on('load', function(e) {
896 			alert('App loaded');
897 		});
898 	*/
899 	
900 	events.Target.extend = function (obj) {
901 		glow.util.apply( obj, glow.events.Target.prototype );
902 	};
903 		
904 	/**
905 	@name glow.events.Target#on
906 	@function
907 	@param {string} eventName Name of the event to listen for.
908 	@param {function} callback Function to call when the event fires.
909 		The callback is passed a single event object. The type of this
910 		object depends on the event (see documentation for the event
911 		you're listening to).
912 	@param {Object} [thisVal] Value of 'this' within the callback.
913 		By default, this is the object being listened to.
914 		
915 	@description Listen for an event
916 		
917 	@returns this
918 		
919 	@example
920 		myObj.on('show', function() {
921 		    // do stuff
922 		});
923 	*/
924 	
925 	targetProto.on = function(eventName, callback, thisVal) {
926 		glow.events.addListeners([this], eventName, callback, thisVal);
927 		return this;
928 	}
929 		
930 	/**
931 	@name glow.events.Target#detach
932 	@function
933 	@param {string} eventName Name of the event to remove.
934 	@param {function} callback Callback to detach.
935 	@param {Object} [thisVal] Value of 'this' within the callback.
936 		By default, this is the object being listened to.
937 	@description Remove an event listener.
938 		
939 	@returns this Target object
940 		
941 	@example
942 		function showListener() {
943 		    // ...
944 		}
945 		       
946 		// add listener
947 		myObj.on('show', showListener);
948 		       
949 		// remove listener
950 		myObj.detach('show', showListener);
951 		       
952 	@example
953 		// note the following WILL NOT WORK
954 		       
955 		// add listener
956 		myObj.on('show', function() {
957 		    alert('hi');
958 		});
959 		       
960 		// remove listener
961 		myObj.detach('show', function() {
962 			alert('hi');
963 		});
964 		       
965 		// this is because both callbacks are different function instances
966 	
967 	*/
968 		
969 	targetProto.detach = function(eventName, callback) {
970 		glow.events.removeListeners(this, eventName, callback);
971 		return this;
972 	}
973 		
974 	/**
975 	@name glow.events.Target#fire
976 	@function
977 	@param {string} eventName Name of the event to fire.
978 	@param {glow.events.Event|Object} [event] Event object to pass into listeners.
979 		    You can provide a simple object of key-value pairs which will
980 		    be added as properties of a glow.events.Event instance.
981 		
982 	@description Fire an event.
983 		
984 	@returns glow.events.Event
985 		
986 	@example
987 		myObj.fire('show');
988 		       
989 	@example
990 		// adding properties to the event object
991 		myBall.fire('bounce', {
992 		    velocity: 30
993 		});
994 	       
995 	@example
996 		// BallBounceEvent extends glow.events.Event but has extra methods
997 		myBall.fire( 'bounce', new BallBounceEvent(myBall) );
998 	*/
999 	
1000 	targetProto.fire = function(eventName, event) {			
1001 		return callListeners(this, eventName, event);
1002 	}
1003 		
1004 	/**
1005 	@name glow.events.Event
1006 	@class
1007 	@param {Object} [properties] Properties to add to the Event instance.
1008 		Each key-value pair in the object will be added to the Event as
1009 		properties.
1010 	       
1011 	@description Describes an event that occurred.
1012 		You don't need to create instances of this class if you're simply
1013 		listening to events. One will be provided as the first argument
1014 		in your callback.
1015 	       
1016 	@example
1017 		// creating a simple event object
1018 		var event = new glow.events.Event({
1019 			velocity: 50,
1020 			direction: 180
1021 		});
1022 		       
1023 		// 'velocity' and 'direction' are simple made-up properties
1024 		// you may want to add to your event object
1025 		       
1026 	@example
1027 		// inheriting from glow.events.Event to make a more
1028 		// specialised event object
1029 		       
1030 		function RocketEvent() {
1031 			// ...
1032 		}
1033 		       
1034 		// inherit from glow.events.Event
1035 		glow.util.extend(RocketEvent, glow.events.Event, {
1036 			getVector: function() {
1037 				return // ...
1038 			}
1039 		});
1040 		       
1041 		// firing the event
1042 		rocketInstance.fire( 'landingGearDown', new RocketEvent() );
1043 		       
1044 		// how a user would listen to the event
1045 		rocketInstance.on('landingGearDown', function(rocketEvent) {
1046 			var vector = rocketEvent.getVector();
1047 		});
1048 	*/
1049 		
1050 	events.Event = function(obj) {			
1051 		if (obj) {
1052 			glow.util.apply(this, obj);
1053 		}
1054 	};
1055 	var eventProto = events.Event.prototype;
1056 	/**
1057 	@name glow.events.Event#attachedTo
1058 	@type {Object}
1059 	@description The object the listener was attached to.
1060 		If null, this value will be populated by {@link glow.events.Target#fire}
1061 	*/
1062 		
1063 	/**
1064 	@name glow.events.Event#source
1065 	@type Element
1066 	@description The actual object/element that the event originated from.
1067 			
1068 		For example, you could attach a listener to an 'ol' element to 
1069 		listen for clicks. If the user clicked on an 'li' the source property 
1070 		would be the 'li' element, and 'attachedTo' would be the 'ol'.
1071 	*/
1072 		
1073 
1074 		
1075 	/**
1076 	@name glow.events.Event#preventDefault
1077 	@function
1078 	@description Prevent the default action of the event.
1079 		Eg, if the click event on a link is cancelled, the link
1080 		is not followed.
1081 		       
1082 		Returning false from an event listener has the same effect
1083 		as calling this function.
1084 		       
1085 		For custom events, it's down to whatever fired the event
1086 		to decide what to do in this case. See {@link glow.events.Event#defaultPrevented defaultPrevented}
1087 		       
1088 	@example
1089 		myLinks.on('click', function(event) {
1090 			event.preventDefault();
1091 		});
1092 		       
1093 		// same as...
1094 		       
1095 		myLinks.on('click', function(event) {
1096 			return false;
1097 		});
1098 	*/
1099 	
1100 	eventProto.preventDefault = function () {	
1101 		this._defaultPrevented = true;		
1102 	};
1103 
1104 		
1105 	/**
1106 	@name glow.events.Event#defaultPrevented
1107 	@function
1108 	@description Has the default been prevented for this event?
1109 		This should be used by whatever fires the event to determine if it should
1110 		carry out of the default action.
1111 		
1112 	@returns {Boolean} Returns true if {@link glow.events.Event#preventDefault preventDefault} has been called for this event.
1113 		
1114 	@example
1115 		// fire the 'show' event
1116 		// read if the default action has been prevented
1117 		if ( overlayInstance.fire('show').defaultPrevented() == false ) {
1118 		    // go ahead and show
1119 		}
1120 	*/
1121 	
1122 	eventProto.defaultPrevented = function () {
1123 		return this._defaultPrevented;
1124 	};
1125 
1126 	
1127 	/* Export */
1128 	glow.events = events;
1129 });
1130 Glow.provide(function(glow) {
1131 	var document = window.document,
1132 		undef = undefined,
1133 		domEventHandlers = []; // like: domEventHandlers[uniqueId][eventName].count, domEventHandlers[uniqueId][eventName].callback
1134 	
1135 	/** 
1136 		@name glow.events.DomEvent
1137 		@constructor
1138 		@extends glow.events.Event
1139 		
1140 		@param {Event|string} nativeEvent A native browser event read properties from, or the name of a native event.
1141 		
1142 		@param {Object} [properties] Properties to add to the Event instance.
1143 		   Each key-value pair in the object will be added to the Event as
1144 		   properties
1145 		
1146 		@description Describes a DOM event that occurred
1147 		   You don't need to create instances of this class if you're simply
1148 		   listening to events. One will be provided as the first argument
1149 		   in your callback.
1150 	*/
1151 	function DomEvent(e, properties) {
1152 		/** 
1153 			@name glow.events.DomEvent#nativeEvent
1154 			@type {Event | MouseEvent | UIEvent}
1155 			@description The native event object provided by the browser.
1156 		 */
1157 		 this.nativeEvent = e;
1158 		
1159 		/** 
1160 			@name glow.events.DomEvent#type
1161 			@type {string}
1162 			@description The native type of the event, like 'click' or 'keydown'.
1163 		 */
1164 		this.type = e.type;
1165 		
1166 		/** 
1167 			@name glow.events.DomEvent#source
1168 			@type {HTMLElement}
1169 			@description The element that the event originated from.
1170 				For example, you could attach a listener to an <ol> element to listen for
1171 				clicks. If the user clicked on an <li> the source property would be the
1172 				<li> element, and {@link glow.DomEvent#attachedTo attachedTo} would be
1173 				the <ol>.
1174 		*/
1175 		if (e.target) { this.source = e.target; } // like FF
1176 		else if (e.srcElement) { this.source = e.srcElement; } // like IE
1177 		if (this.source && this.source.nodeType !== 1) { // like a textNode
1178 			this.source = this.source.parentNode;
1179 		}
1180 		
1181 		/** 
1182 			@name glow.events.DomEvent#related
1183 			@type {HTMLElement}
1184 			@description A related HTMLElement
1185 				For mouseover / mouseenter events, this will refer to the previous element
1186 				the mouse was over.
1187 				
1188 				For mouseout / mouseleave events, this will refer to the element the mouse
1189 				is now over.
1190 		*/
1191 		this.related = e.relatedTarget || (this.type == 'mouseover' ? e.fromElement : e.toElement);
1192 		
1193 		/** 
1194 			@name glow.events.DomEvent#shiftKey
1195 			@type {boolean | undefined}
1196 			@description Was the shift key pressed during the event?
1197 		*/
1198 		this.shiftKey = (e.shiftKey === undef)? undef : !!e.shiftKey;
1199 		
1200 		/** 
1201 			@name glow.events.DomEvent#altKey
1202 			@type {boolean | undefined}
1203 			@description Was the alt key pressed during the event?
1204 		*/
1205 		this.altKey = (e.altKey === undef)? undef : !!e.altKey;
1206 		
1207 		/** 
1208 			@name glow.events.DomEvent#ctrlKey
1209 			@type {boolean | undefined}
1210 			@description Was the ctrl key pressed during the event?
1211 		*/
1212 		this.ctrlKey = (e.ctrlKey === undef)? undef : !!e.ctrlKey;
1213 		
1214 		/**
1215 			@name glow.events.DomEvent#button
1216 			@type {number | undefined}
1217 			@description A number representing which button was pressed.
1218 				0 for the left button, 1 for the middle button or 2 for the right button.
1219 		*/
1220 		this.button = glow.env.ie ? (e.button & 1 ? 0 : e.button & 2 ? 2 : 1) : e.button;
1221 		
1222 		/** 
1223 			@name glow.events.DomEvent#mouseTop
1224 			@type {number}
1225 			@description The vertical position of the mouse pointer in the page in pixels.
1226 		*/
1227 		/** 
1228 			@name glow.events.DomEvent#mouseLeft
1229 			@type {number}
1230 			@description The horizontal position of the mouse pointer in the page in pixels.
1231 		*/
1232 		if (e.pageX !== undef || e.pageY !== undef) {
1233 			this.mouseTop = e.pageY;
1234 			this.mouseLeft = e.pageX;
1235 		}
1236 		else if (e.clientX !== undef || e.clientY !== undef) {
1237 			this.mouseTop = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
1238 			this.mouseLeft = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
1239 		}
1240 		
1241 		/** 
1242 			@name glow.events.DomEvent#wheelData
1243 			@type {number}
1244 			@description The number of clicks the mouse wheel moved.
1245 				Up values are positive, down values are negative.
1246 		*/
1247 		if (this.type == 'mousewheel') {
1248 			// this works in latest opera, but have read that it needs to be switched in direction
1249 			// if there was an opera bug, I can't find which version it was fixed in
1250 			this.wheelDelta =
1251 				e.wheelDelta ? e.wheelDelta / 120 :
1252 				e.detail ? - e.detail / 3 :
1253 				0;
1254 		}
1255 		
1256 		for (var key in properties) {
1257 			this[key] = properties[key];
1258 		}
1259 	}
1260 	
1261 	glow.util.extend(DomEvent, glow.events.Event); // DomEvent extends Event
1262 
1263 	
1264 	/**
1265 		Add listener for an event fired by the browser.
1266 		@private
1267 		@name glow.events._addDomEventListener
1268 		@see glow.NodeList#on
1269 		@function
1270 	*/
1271 	glow.events._addDomEventListener = function(nodeList, eventName, callback, thisVal, selector) {
1272 		var i = nodeList.length, // TODO: should we check that this nodeList is deduped?
1273 			attachTo,
1274 			id,
1275 			eId = eventName + (selector? '/'+selector : '');
1276 	
1277 		while (i-- && nodeList[i]) {
1278 			attachTo = nodeList[i];
1279 
1280 			// will add a unique id to this node, if there is not one already
1281 			glow.events.addListeners([attachTo], eventName, callback, thisVal);
1282 			id = glow.events._getPrivateEventKey(attachTo);
1283 
1284 			// check if there is already a handler for this kind of event attached
1285 			// to this node (which will run all associated callbacks in Glow)
1286 			if (!domEventHandlers[id]) { domEventHandlers[id] = {}; }
1287 
1288 			if (domEventHandlers[id][eId] && domEventHandlers[id][eId].count > 0) { // already have handler in place
1289 				domEventHandlers[id][eId].count++;
1290 				continue;
1291 			}
1292 
1293 			// no bridge in place yet
1294 			domEventHandlers[id][eId] = { callback: null, count:1 };
1295 			
1296 			// attach a handler to tell Glow to run all the associated callbacks
1297 			(function(attachTo) {
1298 				var handler = domHandle(attachTo, eventName, selector);
1299 				
1300 				if (attachTo.addEventListener) { // like DOM2 browsers	
1301 					attachTo.addEventListener(handler.domName, handler, (eventName === 'focus' || eventName === 'blur')); // run in bubbling phase except for focus and blur, see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
1302 				}
1303 				else if (attachTo.attachEvent) { // like IE
1304 					if (eventName === 'focus')  attachTo.attachEvent('onfocusin', handler); // see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
1305 					else if (eventName === 'blur') attachTo.attachEvent('onfocusout', handler); // cause that's how IE rolls...
1306 					attachTo.attachEvent('on' + handler.domName, handler);
1307 				}
1308 				// older browsers?
1309 				
1310 				domEventHandlers[id][eId].callback = handler;
1311 			})(attachTo);
1312 		}
1313 	}
1314 	
1315 	function domHandle(attachTo, eventName, selector) {
1316 		var handler;
1317 		
1318 		if (eventName === 'mouseenter') {
1319 			handler = function(nativeEvent, node) {
1320 				var e = new glow.events.DomEvent(nativeEvent),
1321 					container = node || attachTo;
1322 				
1323 				if (!new glow.NodeList(container).contains(e.related)) {
1324 					var result = glow.events._callListeners(attachTo, eventName, e, node); // fire() returns result of callback
1325 					
1326 					if (typeof result === 'boolean') { return result; }
1327 					else { return !e.defaultPrevented(); }
1328 				}
1329 			};
1330 			
1331 			if (selector) {
1332 				handler = delegate(attachTo, eventName, selector, handler);
1333 			}
1334 			
1335 			handler.domName = 'mouseover';
1336 		}
1337 		else if (eventName === 'mouseleave') {
1338 			handler = function(nativeEvent, node) {
1339 				var e = new glow.events.DomEvent(nativeEvent),
1340 					container = node || attachTo;
1341 					
1342 				if (!new glow.NodeList(container).contains(e.related)) {
1343 					var result = glow.events._callListeners(attachTo, eventName, e, node); // fire() returns result of callback
1344 					
1345 					if (typeof result === 'boolean') { return result; }
1346 					else { return !e.defaultPrevented(); }
1347 				}
1348 			};
1349 			
1350 			if (selector) {
1351 				handler = delegate(attachTo, eventName, selector, handler);
1352 			}
1353 			
1354 			handler.domName = 'mouseout';
1355 		}
1356 		else {
1357 			handler = function(nativeEvent, node) {
1358 				var domEvent = new glow.events.DomEvent(nativeEvent);
1359 				var result = glow.events._callListeners(attachTo, eventName, domEvent, node); // fire() returns result of callback
1360 				
1361 				if (typeof result === 'boolean') { return result; }
1362 				else { return !domEvent.defaultPrevented(); }
1363 			};
1364 			
1365 			if (selector) {
1366 				handler = delegate(attachTo, eventName, selector, handler);
1367 			}
1368 			
1369 			handler.domName = eventName;
1370 		}
1371 		
1372 		return handler;
1373 	}
1374 	
1375 	// wraps a handler in code to detect delegation
1376 	function delegate(attachTo, eventName, selector, handler) {
1377 	
1378 		return function(nativeEvent) { //console.log('dispatched, selector is: '+selector);
1379 			var e = new glow.events.DomEvent(nativeEvent);
1380 				node = e.source;
1381 			
1382 			// if the source matches the selector
1383 			while (node) {
1384 				if (!!glow._sizzle.matches(selector, [node]).length) {
1385 					// the wrapped handler is called here, pass in the node that matched so it can be used as `this`
1386 					return handler(nativeEvent, node);
1387 					//
1388 				}
1389 				
1390 				if (node === attachTo) { break; } // don't check parents above the attachTo
1391 				
1392 				node = node.parentNode;
1393 			}
1394 		};
1395 	}
1396 	
1397 	/**
1398 		Remove listener for an event fired by the browser.
1399 		@private
1400 		@name glow.events._removeDomEventListener
1401 		@see glow.NodeList#detach
1402 		@function
1403 	*/
1404 	glow.events._removeDomEventListener = function(nodeList, eventName, callback, selector) {
1405 		var i = nodeList.length, // TODO: should we check that this nodeList is deduped?
1406 			attachTo,
1407 			id,
1408 			eId = eventName + (selector? '/'+selector : ''),
1409 			bridge,
1410 			handler;
1411 			
1412 		while (i-- && nodeList[i]) {
1413 			attachTo = nodeList[i];
1414 			
1415 			// skip if there is no bridge for this kind of event attached
1416 			id = glow.events._getPrivateEventKey(attachTo);
1417 			if (!domEventHandlers[id] && !domEventHandlers[id][eId]) { continue; }
1418 			
1419 			glow.events.removeListeners([attachTo], eventName, callback);
1420 
1421 			bridge = domEventHandlers[id][eId]
1422 			
1423 			if (bridge.count > 0) {
1424 				bridge.count--; // one less listener associated with this event
1425 		
1426 				if (bridge.count === 0) {  // no more listeners associated with this event
1427 					// detach bridge handler to tell Glow to run all the associated callbacks
1428 					
1429 					handler = bridge.callback;
1430 														
1431 					if (attachTo.removeEventListener) { // like DOM2 browsers	
1432 						attachTo.removeEventListener(handler.domName, handler, (eventName === 'focus' || eventName === 'blur')); // run in bubbling phase except for focus and blur, see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
1433 					}
1434 					else if (attachTo.detachEvent) { // like IE
1435 						if (eventName === 'focus')  attachTo.detachEvent('onfocusin', handler); // see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
1436 						else if (eventName === 'blur') attachTo.detachEvent('onfocusout', handler); // cause that's how IE rolls...
1437 						attachTo.detachEvent('on' + handler.domName, handler);
1438 					}
1439 				}
1440 			}
1441 		}
1442 	}
1443 
1444 // see: http://developer.yahoo.com/yui/3/event/#eventsimulation
1445 // see: http://developer.yahoo.com/yui/docs/YAHOO.util.UserAction.html
1446 // 	function simulateDomEvent(nodeList, domEvent) {
1447 // 		var i = nodeList.length,
1448 // 			eventName = domEvent.type,
1449 // 			nativeEvent,
1450 // 			node,
1451 // 			fire;
1452 // 		
1453 // 		if (document.createEvent) {
1454 // 			var nativeEvent = document.createEvent('MouseEvent'); // see: 
1455 // 			nativeEvent.initEvent(eventName, true, true);
1456 // 			
1457 // 			fire = function(el) {
1458 // 				return !el.dispatchEvent(nativeEvent);
1459 // 			}
1460 // 		}
1461 // 		else {
1462 // 			fire = function(el) {
1463 // 				var nativeEvent = document.createEventObject(); 
1464 // 				return el.fireEvent('on'+eventName, nativeEvent);
1465 // 			}
1466 // 		}
1467 // 		
1468 // 		while (i--) {
1469 // 			node = nodeList[i];
1470 // 			if (node.nodeType !== 1) { continue; }
1471 // 			fire(node);
1472 // 		}
1473 // 	}
1474 	
1475 	// export
1476 	glow.events.DomEvent = DomEvent;
1477 });
1478 Glow.provide(function(glow) {
1479 	var document = window.document,
1480 		undefined,
1481         keyboardEventProto,
1482 		$env = glow.env,
1483 		// the keyCode for the last keydown (returned to undefined on keyup)
1484 		activeKey,
1485 		// the charCode for the last keypress (returned to undefined on keyup & keydown)
1486 		activeChar,
1487 		DomEvent = glow.events.DomEvent,
1488 		_callListeners = glow.events._callListeners,
1489 		_getPrivateEventKey = glow.events._getPrivateEventKey,
1490 		// object of event names & listeners, eg:
1491 		// {
1492 		//    eventId: [
1493 		//        2, // the number of glow listeners added for this node
1494 		//        keydownListener,
1495 		//        keypressListener,
1496 		//        keyupListener
1497 		//    ]
1498 		// }
1499 		// This lets us remove these DOM listeners from the node when the glow listeners reaches zero
1500 		eventKeysRegistered = {}; 
1501 	
1502 	/** 
1503 		@name glow.events.KeyboardEvent
1504 		@constructor
1505 		@extends glow.events.DomEvent
1506 		
1507 		@param {Event} nativeEvent A native browser event read properties from
1508 		
1509 		@param {Object} [properties] Properties to add to the Event instance.
1510 		   Each key-value pair in the object will be added to the Event as
1511 		   properties
1512 		
1513 		@description Describes a keyboard event that occurred
1514 		   You don't need to create instances of this class if you're simply
1515 		   listening to events. One will be provided as the first argument
1516 		   in your callback.
1517 	*/
1518 	function KeyboardEvent(nativeEvent) {
1519 		if (activeKey) {
1520 			this.key = keyCodeToId(activeKey);
1521 		}
1522 		if (activeChar) {
1523 			this.keyChar = String.fromCharCode(activeChar);
1524 		}
1525 		DomEvent.call(this, nativeEvent);
1526 	}
1527     
1528     glow.util.extend(KeyboardEvent, DomEvent, {
1529         /** 
1530             @name glow.events.KeyboardEvent#key
1531             @type {string}
1532             @description The key pressed
1533 				This is a string representing the key pressed.
1534 				
1535 				Alphanumeric keys are represented by 0-9 and A-Z uppercase. Other safe cross-browser values are:
1536 				
1537 				<dl>
1538 					<li>backspace</li>
1539 					<li>tab</li>
1540 					<li>return</li>
1541 					<li>shift</li>
1542 					<li>alt</li>
1543 					<li>escape</li>
1544 					<li>space</li>
1545 					<li>pageup</li>
1546 					<li>pagedown</li>
1547 					<li>end</li>
1548 					<li>home</li>
1549 					<li>left</li>
1550 					<li>up</li>
1551 					<li>right</li>
1552 					<li>down</li>
1553 					<li>insert</li>
1554 					<li>delete</li>
1555 					<li>;</li>
1556 					<li>=</li>
1557 					<li>-</li>
1558 					<li>f1</li>
1559 					<li>f2</li>
1560 					<li>f3</li>
1561 					<li>f4</li>
1562 					<li>f5</li>
1563 					<li>f6</li>
1564 					<li>f7</li>
1565 					<li>f8</li>
1566 					<li>f9</li>
1567 					<li>f10</li>
1568 					<li>f11</li>
1569 					<li>f12</li>
1570 					<li>numlock</li>
1571 					<li>scrolllock</li>
1572 					<li>pause</li>
1573 					<li>,</li>
1574 					<li>.</li>
1575 					<li>/</li>
1576 					<li>[</li>
1577 					<li>\</li>
1578 					<li>]</li>
1579 				</dl>
1580 				
1581 				Some keys may trigger actions in your browser and operating system, some
1582 				are not cancelable.
1583                 
1584             @example
1585 				glow(document).on('keypress', function(event) {
1586 					switch (event.key) {
1587 						case 'up':
1588 							// do stuff
1589 							break;
1590 						case 'down':
1591 							// do stuff
1592 							break;
1593 					}
1594 				});
1595         */
1596         key: '',
1597         /** 
1598             @name glow.events.KeyboardEvent#keyChar
1599             @type {string}
1600             @description The character entered.
1601                 This is only available during 'keypress' events.
1602                 
1603                 If the user presses shift and 1, event.key will be "1", but event.keyChar
1604                 will be "!".
1605                 
1606             @example
1607                 // only allow numbers to be entered into the ageInput field
1608 				glow('#ageInput').on('keypress', function(event) {
1609 					return !isNaN( Number(event.keyChar) );
1610 				});
1611         */
1612         keyChar: ''
1613     });
1614 	
1615 	// add a dom listener
1616 	function addListener(elm, name, callback) {
1617 		if (elm.addEventListener) { // like DOM2 browsers	
1618 			elm.addEventListener(name, callback, false);
1619 		}
1620 		else if (elm.attachEvent) { // like IE
1621 			elm.attachEvent('on' + name, callback);
1622 		}
1623 	}
1624 	
1625 	// remove a dom listener
1626 	function removeListener(elm, name, callback) {
1627 		if (elm.removeEventListener) { // like DOM2 browsers	
1628 			elm.removeEventListener(name, callback, false);
1629 		}
1630 		else if (elm.detachEvent) { // like IE
1631 			elm.detachEvent('on' + name, callback);
1632 		}
1633 	}
1634 	
1635 	// takes a keyCode from a keydown listener and returns true if the browser will also fire a keypress
1636 	function expectKeypress(keyCode, defaultPrevented) {
1637 		var keyName;
1638 		
1639 		// for browsers that fire keypress for the majority of keys
1640 		if ($env.gecko || $env.opera) {
1641 			return !noKeyPress[keyCode];
1642 		}
1643 		
1644 		// for browsers that only fire keypress for printable chars
1645 		keyName = keyCodeToId(keyCode);
1646 		
1647 		// is this a printable char?
1648 		if (keyName.length === 1 && !noKeyPress[keyCode]) {
1649 			// webkit doesn't fire keypress if the keydown has been prevented
1650 			return !($env.webkit && defaultPrevented);
1651 		}
1652 		return false;
1653 	}
1654 	
1655 	// Add the key listeners for firing glow's normalised key events.
1656 	// returns an entry for eventKeysRegistered
1657 	function addDomKeyListeners(attachTo) {
1658 		var keydownHandler, keypressHandler, keyupHandler,
1659 			// Even though the user may only be interested in one key event, we need all 3 listeners to normalise any of them
1660 			// hash of which keys are down, keyed by keyCode
1661 			keysDown = {};
1662 		
1663 		keydownHandler = function(nativeEvent) {
1664 			var keyCode = nativeEvent.keyCode,
1665 				preventDefault,
1666 				preventDefaultKeyPress;
1667 			
1668 			// some browsers repeat this event while a key is held down, we don't want to do that
1669 			if ( !keysDown[keyCode] ) {
1670 				activeKey = keyCode;
1671 				activeChar = undefined;
1672 				preventDefault = _callListeners( attachTo, 'keydown', new KeyboardEvent(nativeEvent) ).defaultPrevented();
1673 				keysDown[keyCode] = true;
1674 			}
1675 			// we want to fire a keyPress event here if the browser isn't going to fire one itself
1676 			if ( !expectKeypress(keyCode, preventDefault) ) {
1677 				preventDefaultKeyPress = _callListeners( attachTo, 'keypress', new KeyboardEvent(nativeEvent) ).defaultPrevented();
1678 			}
1679 			// return false if either the keydown or fake keypress event was cancelled
1680 			return !(preventDefault || preventDefaultKeyPress);
1681 		};
1682 		
1683 		keypressHandler = function(nativeEvent) {
1684 			// some browsers store the charCode in .charCode, some in .keyCode
1685 			activeChar = nativeEvent.charCode || nativeEvent.keyCode;
1686 			// some browsers fire this event for non-printable chars, look at the previous keydown and see if we're expecting a printable char
1687 			if ( keyCodeToId(activeKey).length > 1 ) {
1688 				// non-printable chars have an ID length greater than 1
1689 				activeChar = undefined;
1690 			}
1691 			var preventDefault = _callListeners( attachTo, 'keypress', new KeyboardEvent(nativeEvent) ).defaultPrevented();
1692 			return !preventDefault;
1693 		};
1694 		
1695 		keyupHandler = function(nativeEvent) {
1696 			var keyCode = nativeEvent.keyCode,
1697 				preventDefault;
1698 				
1699 			activeKey = keyCode;
1700 			activeChar = undefined;
1701 			preventDefault = _callListeners( attachTo, 'keyup', new KeyboardEvent(nativeEvent) ).defaultPrevented();
1702 			keysDown[keyCode] = false;
1703 			activeKey = undefined;
1704 			return !preventDefault;
1705 		};
1706 		
1707 		// add listeners to the dom
1708 		addListener(attachTo, 'keydown',  keydownHandler);
1709 		addListener(attachTo, 'keypress', keypressHandler);
1710 		addListener(attachTo, 'keyup',    keyupHandler);
1711 		
1712 		return [1, keydownHandler, keypressHandler, keyupHandler];
1713 	}
1714 	
1715 	/**
1716 		@name glow.events._addKeyListener
1717 		@private
1718 		@function
1719 		@description Add listener for a key event fired by the browser.
1720 		@see glow.NodeList#on
1721 	*/
1722 	glow.events._addKeyListener = function(nodeList, name, callback, thisVal) {
1723 		var i = nodeList.length,
1724 			attachTo,
1725 			eventKey;
1726 		
1727 		// will add a unique id to this node, if there is not one already
1728 		glow.events.addListeners(nodeList, name, callback, thisVal);
1729 	
1730 		while (i--) {
1731 			attachTo = nodeList[i];
1732 
1733 			// get the ID for this event
1734 			eventKey = _getPrivateEventKey(attachTo);
1735 			
1736 			// if we've already attached DOM listeners for this, don't add them again
1737 			if ( eventKeysRegistered[eventKey] ) {
1738 				eventKeysRegistered[eventKey][0]++;
1739 				continue;
1740 			}
1741 			else {
1742 				eventKeysRegistered[eventKey] = addDomKeyListeners(attachTo);
1743 			}
1744 		}
1745 	}
1746 	
1747 	/**
1748 		Remove listener for an event fired by the browser.
1749 		@private
1750 		@name glow.events._removeKeyListener
1751 		@see glow.NodeList#detach
1752 		@function
1753 	*/
1754 	glow.events._removeKeyListener = function(nodeList, name, callback) {
1755 		var i = nodeList.length,
1756 			attachTo,
1757 			eventKey,
1758 			eventRegistry;
1759 		
1760 		// remove the glow events
1761 		glow.events.removeListeners(nodeList, name, callback);
1762 		
1763 		while (i--) {
1764 			attachTo = nodeList[i];
1765 			
1766 			// get the ID for this event
1767 			eventKey = _getPrivateEventKey(attachTo);
1768 			eventRegistry = eventKeysRegistered[eventKey];
1769 			// exist if there are no key events registered for this node
1770 			if ( !eventRegistry ) {
1771 				return;
1772 			}
1773 			if ( --eventRegistry[0] === 0 ) {
1774 				// our glow listener count is zero, we have no need for the dom listeners anymore
1775 				removeListener( attachTo, 'keydown',   eventRegistry[1] );
1776 				removeListener( attachTo, 'keypress',  eventRegistry[2] );
1777 				removeListener( attachTo, 'keyup',     eventRegistry[3] );
1778 				eventKeysRegistered[eventKey] = undefined;
1779 			}
1780 		}
1781 	}
1782 	
1783 	// convert a keyCode to a string name for that key
1784 	function keyCodeToId(keyCode) {
1785 		// key codes for 0-9 A-Z are the same as their char codes
1786 		if ( (keyCode >= keyCodeA && keyCode <= keyCodeZ) || (keyCode >= keyCode0 && keyCode <= keyCode9) ) {
1787 			return String.fromCharCode(keyCode).toLowerCase();
1788 		}
1789 		return keyIds[keyCode] || 'unknown' + keyCode;
1790 	}
1791 	
1792 	// keyCode to key name translation
1793 	var keyCodeA = 'A'.charCodeAt(0),
1794 		keyCodeZ = 'Z'.charCodeAt(0),
1795 		keyCode0 = '0'.charCodeAt(0),
1796 		keyCode9 = '9'.charCodeAt(0),
1797 		// key codes for non-alphanumeric keys
1798 		keyIds = {
1799 			8: 'backspace',
1800 			9: 'tab',
1801 			13: 'return',
1802 			16: 'shift',
1803 			17: 'control',
1804 			18: 'alt',
1805 			19: 'pause',
1806 			27: 'escape',
1807 			32: 'space',
1808 			33: 'pageup',
1809 			34: 'pagedown',
1810 			35: 'end',
1811 			36: 'home',
1812 			37: 'left',
1813 			38: 'up',
1814 			39: 'right',
1815 			40: 'down',
1816 			44: 'printscreen', // Only fires keyup in firefox, IE. Doesn't fire in webkit, opera.
1817 			45: 'insert',
1818 			46: 'delete',
1819 			59: ';',
1820 			61: '=',
1821 			91: 'meta',
1822 			93: 'menu', // no keycode in opera, doesn't fire in Chrome
1823 			
1824 			// these are number pad numbers, but Opera doesn't distinguish them from normal number keys so we normalise on that
1825 				96: '0', 
1826 				97: '1',
1827 				98: '2',
1828 				99: '3',
1829 				100: '4',
1830 				101: '5',
1831 				102: '6',
1832 				103: '7',
1833 				104: '8',
1834 				105: '9',
1835 				106: '*', // opera fires 2 keypress events
1836 				107: '+', // opera fires 2 keypress events
1837 				109: '-', // opera sees - as insert
1838 				110: '.', // opera sees this as n
1839 				111: '/',
1840 			// end of numpad
1841 			
1842 			112: 'f1',
1843 			113: 'f2',
1844 			114: 'f3',
1845 			115: 'f4',
1846 			116: 'f5',
1847 			117: 'f6',
1848 			118: 'f7',
1849 			119: 'f8',
1850 			120: 'f9',
1851 			121: 'f10',
1852 			122: 'f11',
1853 			123: 'f12',
1854 			144: 'numlock',
1855 			145: 'scrolllock',
1856 			188: ',',
1857 			189: '-',
1858 			190: '.',
1859 			191: '/',
1860 			192: "'",
1861 			219: '[',
1862 			220: '\\',
1863 			221: ']',
1864 			222: '#', // opera sees # key as 3. Pah.
1865 			223: '`',
1866 			224: 'meta', // same as [ in opera
1867 			226: '\\' // this key appears on a US layout in webkit windows
1868 		},
1869 		noKeyPress = {};
1870 	
1871 	// corrections for particular browsers :(
1872 	if ($env.gecko) {
1873 		keyIds[107] = '=';
1874 		
1875 		noKeyPress = {
1876 			16: 1,  // shift
1877 			17: 1,  // control
1878 			18: 1,  // alt
1879 			144: 1, // numlock
1880 			145: 1  // scrolllock
1881 		};
1882 	}
1883 	else if ($env.opera) {
1884 		keyIds[42] = '*';
1885 		keyIds[43] = '+';
1886 		keyIds[47] = '/';
1887 		keyIds[222] = "'";
1888 		keyIds[192] = '`';
1889 		
1890 		noKeyPress = {
1891 			16: 1,  // shift
1892 			17: 1,  // control
1893 			18: 1   // alt
1894 		};
1895 	}
1896 	else if ($env.webkit || $env.ie) {
1897 		keyIds[186] = ';';
1898 		keyIds[187] = '=';
1899 	}
1900 	
1901 	// export
1902 	glow.events.KeyboardEvent = KeyboardEvent;
1903 });
1904 Glow.provide(function(glow) {
1905 	var NodeListProto, undefined,
1906 		// shortcuts to aid compression
1907 		document = window.document,
1908 		arraySlice = Array.prototype.slice,
1909 		arrayPush = Array.prototype.push;
1910 	
1911 	/**
1912 		@name glow.NodeList
1913 		@constructor
1914 		@description An array-like collection of DOM Nodes
1915 			It is recommended to create a NodeList using the shortcut function {@link glow}.
1916 			
1917 		@param {string | glow.NodeList | Node | Node[] | Window} contents Items to populate the NodeList with.
1918 			This parameter will be passed to {@link glow.NodeList#push}.
1919 			
1920 			Strings will be treated as CSS selectors unless they start with '<', in which
1921 			case they'll be treated as an HTML string.
1922 			
1923 		@example
1924 			// empty NodeList
1925 			var myNodeList = glow();
1926 
1927 		@example
1928 			// using glow to return a NodeList then chaining methods
1929 			glow('p').addClass('eg').append('<div>Hello!</div>');
1930 			
1931 		@example
1932 			// creating an element from a string
1933 			glow('<div>Hello!</div>').appendTo('body');
1934 		
1935 		@see <a href="http://wiki.github.com/jeresig/sizzle/">Supported CSS selectors</a>
1936 	*/
1937 	function NodeList(contents) {
1938 		// call push if we've been given stuff to add
1939 		contents && this.push(contents);
1940 	}
1941 	NodeListProto = NodeList.prototype;
1942 	
1943 	/**
1944 		@name glow.NodeList#length
1945 		@type Number
1946 		@description Number of nodes in the NodeList
1947 		@example
1948 			// get the number of paragraphs on the page
1949 			glow('p').length;
1950 	*/
1951 	NodeListProto.length = 0;
1952 	
1953 	/**
1954 		@name glow.NodeList._strToNodes
1955 		@private
1956 		@function
1957 		@description Converts a string to an array of nodes
1958 		
1959 		@param {string} str HTML string
1960 		
1961 		@returns {Node[]} Array of nodes (including text / comment nodes)
1962 	*/
1963 	NodeList._strToNodes = (function() {
1964 		var	tmpDiv = document.createElement('div'),
1965 			// these wraps are in the format [depth to children, opening html, closing html]
1966 			tableWrap = [1, '<table>', '</table>'],
1967 			emptyWrap = [0, '', ''],
1968 			// webkit won't accept <link> elms to be the only child of an element,
1969 			// it steals them and hides them in the head for some reason. Using
1970 			// broken html fixes it for some reason
1971 			paddingWrap = [1, 'b<div>', '</div>'],
1972 			trWrap = [3, '<table><tbody><tr>', '</tr></tbody></table>'],
1973 			wraps = {
1974 				caption: tableWrap,
1975 				thead: tableWrap,
1976 				th: trWrap,
1977 				colgroup: tableWrap,
1978 				tbody: tableWrap,
1979 				tr: [2, '<table><tbody>', '</tbody></table>'],
1980 				td: trWrap,
1981 				tfoot: tableWrap,
1982 				option: [1, '<select multiple="multiple">', '</select>'],
1983 				legend: [1, '<fieldset>', '</fieldset>'],
1984 				link: paddingWrap,
1985 				script: paddingWrap,
1986 				style: paddingWrap,
1987 				'!': paddingWrap
1988 			};
1989 		
1990 		function strToNodes(str) {
1991 			var r = [],
1992 				tagName = ( /^<([\w!]+)/.exec(str) || [] )[1],
1993 				// This matches str content with potential elements that cannot
1994 				// be a child of <div>.  elmFilter declared at top of page.
1995 				wrap = wraps[tagName] || emptyWrap, 
1996 				nodeDepth = wrap[0],
1997 				childElm = tmpDiv,
1998 				exceptTbody,
1999 				rLen = 0,
2000 				firstChild;
2001 			
2002 			// Create the new element using the node tree contents available in filteredElm.
2003 			childElm.innerHTML = (wrap[1] + str + wrap[2]);
2004 			
2005 			// Strip newElement down to just the required elements' parent
2006 			while(nodeDepth--) {
2007 				childElm = childElm.lastChild;
2008 			}
2009 			
2010 			// pull nodes out of child
2011 			if (wrap === tableWrap && str.indexOf('<tbody') === -1) {
2012 				// IE7 (and earlier) sometimes gives us a <tbody> even though we didn't ask for one
2013 				while (firstChild = childElm.firstChild) {
2014 					if (firstChild.nodeName != 'TBODY') {
2015 						r[rLen++] = firstChild;
2016 					}
2017 					childElm.removeChild(firstChild);
2018 				}
2019 			}
2020 			else {
2021 				while (firstChild = childElm.firstChild) {
2022 					r[rLen++] = childElm.removeChild(firstChild);
2023 				}
2024 			}
2025 			
2026 			return r;
2027 		}
2028 		
2029 		return strToNodes;
2030 	})();
2031 	
2032 	// takes a collection and returns an array
2033 	var collectionToArray = function(collection) {
2034 		return arraySlice.call(collection, 0);
2035 	};
2036 	
2037 	try {
2038 		// look out for an IE bug
2039 		arraySlice.call( document.documentElement.childNodes, 0 );
2040 	}
2041 	catch(e) {
2042 		collectionToArray = function(collection) {
2043 			// We can't use this trick on IE collections that are com-based, like HTMLCollections
2044 			// Thankfully they don't have a constructor, so that's how we detect those
2045 			if (collection instanceof Object) {
2046 				return arraySlice.call(collection, 0);
2047 			}
2048 			var i   = collection.length,
2049 				arr = [];
2050 				
2051 			while (i--) {
2052 				arr[i] = collection[i];
2053 			}
2054 			return arr;
2055 		}
2056 	}
2057 	
2058 	/**
2059 		@name glow.NodeList#push
2060 		@function
2061 		@description Adds nodes to the NodeList
2062 		
2063 		@param {string | Node | Node[] | glow.NodeList} nodes Node(s) to add to the NodeList
2064 			Strings will be treated as CSS selectors or HTML strings.
2065 		
2066 		@returns {glow.NodeList}
2067 		
2068 		@example
2069 			myNodeList.push('<div>Foo</div>').push('h1');
2070 	*/
2071 	NodeListProto.push = function(nodes) {
2072 		/*!debug*/
2073 			if (arguments.length !== 1) {
2074 				glow.debug.warn('[wrong count] glow.NodeList#push expects 1 argument, not '+arguments.length+'.');
2075 			}
2076 		/*gubed!*/
2077 		
2078 		if (nodes) {
2079 			if (typeof nodes === 'string') {
2080 				// if the string begins <, treat it as html, otherwise it's a selector
2081 				if (nodes.charAt(0) === '<') {
2082 					nodes = NodeList._strToNodes(nodes);
2083 				}
2084 				else {
2085 					nodes = glow._sizzle(nodes)
2086 				}
2087 				arrayPush.apply(this, nodes);
2088 			}
2089 			
2090 			else if ( nodes.nodeType || nodes.window == nodes ) {
2091 				if (this.length) {
2092 					arrayPush.call(this, nodes);
2093 				}
2094 				else {
2095 					this[0] = nodes;
2096 					this.length = 1;
2097 				}
2098 			}
2099 			else if (nodes.length !== undefined) {
2100 				if (nodes.constructor != Array) {
2101 					// convert array-like objects into an array
2102 					nodes = collectionToArray(nodes);
2103 				}
2104 				arrayPush.apply(this, nodes);
2105 			}
2106 			/*!debug*/
2107 			else {
2108 				glow.debug.warn('[wrong type] glow.NodeList#push: Ignoring unexpected argument type, failing silently');
2109 			}
2110 			/*gubed!*/
2111 		}
2112 		/*!debug*/
2113 		else {
2114 			glow.debug.warn('[wrong type] glow.NodeList#push: Ignoring false argument type, failing silently');
2115 		}
2116 		/*gubed!*/
2117 		return this;
2118 	};
2119 	
2120 	/**
2121 		@name glow.NodeList#eq
2122 		@function
2123 		@description Compares this NodeList to another
2124 			Returns true if both NodeLists contain the same items in the same order
2125 		
2126 		@param {Node | Node[] | glow.NodeList} nodeList The NodeList to compare to.
2127 		
2128 		@returns {boolean}
2129 		
2130 		@see {@link glow.NodeList#is} for testing if a NodeList item matches a selector
2131 		
2132 		@example
2133 			// the following returns true
2134 			glow('#blah').eq( document.getElementById('blah') );
2135 	*/
2136 	NodeListProto.eq = function(nodeList) {
2137 		/*!debug*/
2138 			if (arguments.length !== 1) {
2139 				glow.debug.warn('[wrong count] glow.NodeList#eq expects 1 argument, not ' + arguments.length + '.');
2140 			}
2141 			if (typeof nodeList !== 'object') {
2142 				glow.debug.warn('[wrong type] glow.NodeList#eq expects object argument, not ' + typeof nodeList + '.');
2143 			}
2144 		/*gubed!*/
2145 		
2146 		var len = this.length,
2147 			i = len;
2148 		
2149 		// normalise param to NodeList
2150 		if ( !(nodeList instanceof NodeList) ) {
2151 			nodeList = new NodeList(nodeList);
2152 		}
2153 		
2154 		// quickly return false if lengths are different
2155 		if (len != nodeList.length) {
2156 			return false;
2157 		}
2158 		
2159 		// loop through and return false on inequality
2160 		while (i--) {
2161 			if (this[i] !== nodeList[i]) {
2162 				return false;
2163 			}
2164 		}
2165 		
2166 		return true;
2167 	};
2168 	
2169 	/**
2170 		@name glow.NodeList#slice
2171 		@function
2172 		@description Get a section of an NodeList
2173 			Operates in the same way as an Array's slice method
2174 		
2175 		@param {number} start Start index
2176 			If negative, it specifies a position measured from the end of the list
2177 		
2178 		@param {number} [end] End index
2179 			By default, this is the end of the list. A negative end specifies
2180 			a position measured from the end of the list.
2181 		
2182 		@returns {glow.NodeList} A new sliced NodeList
2183 		
2184 		@example
2185 		var myNodeList = glow("<div></div><p></p>");
2186 		myNodeList.slice(1, 2); // selects the paragraph
2187 		myNodeList.slice(-1); // same thing, selects the paragraph
2188 	*/
2189 	NodeListProto.slice = function(/*start, end*/) {
2190 		return new NodeList( arraySlice.apply(this, arguments) );
2191 	};
2192 	
2193 	/**
2194 		@name glow.NodeList#sort
2195 		@function
2196 		@description Sort the elements in the list.
2197 			Items will already be in document order if a CSS selector
2198 			was used to fetch them.
2199 		
2200 		@param {Function} [func] Function to determine sort order
2201 			This function will be passed 2 elements (elementA, elementB). The function
2202 			should return a number less than 0 to sort elementA lower than elementB
2203 			and greater than 0 to sort elementA higher than elementB.
2204 			
2205 			If no function is provided, elements will be sorted in document order.
2206 		
2207 		@returns {glow.NodeList} A new sorted NodeList
2208 		
2209 		@example
2210 			//get links in alphabetical (well, lexicographical) order
2211 			var links = glow("a").sort(function(elementA, elementB) {
2212 				return glow(elementA).text() < glow(elementB).text() ? -1 : 1;
2213 			})
2214 	*/
2215 	NodeListProto.sort = function(func) {
2216 		var items = collectionToArray(this),
2217 			sortedElms = func ? items.sort(func) : glow._sizzle.uniqueSort(items);
2218 		
2219 		return new NodeList(sortedElms);
2220 	};
2221 	
2222 	/**
2223 		@name glow.NodeList#item
2224 		@function
2225 		@description Get a single item from the list as an NodeList
2226 			Negative numbers can be used to get items from the end of the
2227 			list.
2228 		
2229 		@param {number} index The numeric index of the node to return.
2230 		
2231 		@returns {glow.NodeList} A new NodeList containing a single item
2232 		
2233 		@example
2234 			// get the html from the fourth element
2235 			myNodeList.item(3).html();
2236 			
2237 		@example
2238 			// add a class name to the last item
2239 			myNodeList.item(-1).addClass('last');
2240 	*/
2241 	NodeListProto.item = function(index) {
2242 		/*!debug*/
2243 			if ( arguments.length !== 1 ) {
2244 				glow.debug.warn('[wrong count] glow.NodeList#item expects 1 argument, got ' + arguments.length);
2245 			}
2246 		/*gubed!*/
2247 		// TODO: test which of these methods is faster (use the current one unless significantly slower)
2248 		return this.slice(index, (index + 1) || this.length);
2249 		// return new NodeList( index < 0 ? this[this.length + index] : this[index] );
2250 	};
2251 	
2252 	/**
2253 		@name glow.NodeList#each
2254 		@function
2255 		@description Calls a function for each node in the list.
2256 		
2257 		@param {Function} callback The function to call for each node.
2258 			The function will be passed 2 arguments, the index of the current item,
2259 			and the NodeList being iterated over.
2260 			
2261 			Inside the function 'this' refers to the Node.
2262 			
2263 			Returning false from this function stops further iterations
2264 		
2265 		@returns {glow.NodeList}
2266 		
2267 		@example
2268 			// add "link number: x" to each link, where x is the index of the link
2269 			glow("a").each(function(i, nodeList) {
2270 				glow(this).append(' link number: ' + i);
2271 			});
2272 		@example
2273 			// breaking out of an each loop
2274 			glow("a").each(function(i, nodeList) {
2275 				// do stuff
2276 				if ( glow(this).hasClass('whatever') ) {
2277 					// we don't want to process any more links
2278 					return false;
2279 				}
2280 			});
2281 	*/
2282 	NodeListProto.each = function(callback) {
2283 		/*!debug*/
2284 			if ( arguments.length !== 1 ) {
2285 				glow.debug.warn('[wrong count] glow.NodeList#each expects 1 argument, got ' + arguments.length);
2286 			}
2287 			if (typeof callback != 'function') {
2288 				glow.debug.warn('[wrong type] glow.NodeList#each expects "function", got ' + typeof callback);
2289 			}
2290 		/*gubed!*/
2291 		for (var i = 0, len = this.length; i<len; i++) {
2292 			if ( callback.call(this[i], i, this) === false ) {
2293 				break;
2294 			}
2295 		}
2296 		return this;
2297 	};
2298 	
2299 	/**
2300 		@name glow.NodeList#filter
2301 		@function
2302 		@description Filter the NodeList
2303 		 
2304 		@param {Function|string} test Filter test
2305 			If a string is provided it's treated as a CSS selector. Elements
2306 			which match the CSS selector are added to the new NodeList.
2307 			
2308 			If 'test' is a function, it will be called per node in the NodeList.
2309 			
2310 			The function is passed 2 arguments, the index of the current item,
2311 			and the ElementList being itterated over.
2312 			
2313 			Inside the function 'this' refers to the node.
2314 			Return true to add the element to the new NodeList.
2315 		 
2316 		@returns {glow.NodeList} A new NodeList containing the filtered nodes
2317 		 
2318 		@example
2319 			// return images with a width greater than 320
2320 			glow("img").filter(function () {
2321 				return glow(this).width() > 320;
2322 			});
2323 		
2324 		@example
2325 			// Get items that don't have an alt attribute
2326 			myElementList.filter(':not([alt])');
2327 	*/
2328 	NodeListProto.filter = function(test) {
2329 		/*!debug*/
2330 			if ( arguments.length !== 1 ) {
2331 				glow.debug.warn('[wrong count] glow.NodeList#filter expects 1 argument, got ' + arguments.length);
2332 			}
2333 			if ( !/^(function|string)$/.test(typeof test) ) {
2334 				glow.debug.warn('[wrong type] glow.NodeList#each expects function/string, got ' + typeof test);
2335 			}
2336 		/*gubed!*/
2337 		var r = [],
2338 			ri = 0;
2339 		
2340 		if (typeof test === 'string') {
2341 			r = glow._sizzle.matches(test, this);
2342 		}
2343 		else {	
2344 			for (var i = 0, len = this.length; i<len; i++) {
2345 				if ( test.call(this[i], i, this) ) {
2346 					r[ri++] = this[i];
2347 				}
2348 			}
2349 		}
2350 		
2351 		return new NodeList(r);
2352 	};
2353 
2354 	
2355 	/**
2356 		@name glow.NodeList#is
2357 		@function
2358 		@description Tests if the first element matches a CSS selector
2359 
2360 		@param {string} selector CSS selector
2361 		
2362 		@returns {boolean}
2363 		
2364 		@example
2365 			if ( myNodeList.is(':visible') ) {
2366 				// ...
2367 			}
2368 	*/
2369 	NodeListProto.is = function(selector) {
2370 		/*!debug*/
2371 			if ( arguments.length !== 1 ) {
2372 				glow.debug.warn('[wrong count] glow.NodeList#is expects 1 argument, got ' + arguments.length);
2373 			}
2374 			if ( typeof selector !== 'string' ) {
2375 				glow.debug.warn('[wrong type] glow.NodeList#is expects string, got ' + typeof selector);
2376 			}
2377 		/*gubed!*/
2378 		if ( !this[0] ) {
2379 			return false;
2380 		}
2381 		return !!glow._sizzle.matches( selector, [ this[0] ] ).length;
2382 	};
2383 	
2384 	// export
2385 	glow.NodeList = NodeList;
2386 });
2387 Glow.provide(function(glow) {
2388 	var undef
2389 		, NodeListProto = glow.NodeList.prototype
2390 	
2391 		/**
2392 			@private
2393 			@name glow.NodeList-dom0PropertyMapping
2394 			@description Mapping of HTML attribute names to DOM0 property names.
2395 		*/
2396 		, dom0PropertyMapping = { // keys must be lowercase
2397 			'class'     : 'className',
2398 			'for'       : 'htmlFor',
2399 			'maxlength' : 'maxLength'
2400 		}
2401 		
2402 		/**
2403 			@private
2404 			@name glow.NodeList-dataPropName
2405 			@type String
2406 			@description The property name added to the DomElement by the NodeList#data method.
2407 		*/
2408 		, dataPropName = '_uniqueData' + glow.UID
2409 		
2410 		/**
2411 			@private
2412 			@name glow.NodeList-dataIndex
2413 			@type String
2414 			@description The value of the dataPropName added by the NodeList#data method.
2415 		*/
2416 		, dataIndex = 1 // must be a truthy value
2417 			
2418 		/**
2419 			@private
2420 			@name glow.NodeList-dataCache
2421 			@type Object
2422 			@description Holds the data used by the NodeList#data method.
2423 			
2424 			The structure is like:
2425 			[
2426 				{
2427 					myKey: "my data"
2428 				}
2429 			]
2430 		*/
2431 		, dataCache = [];
2432 			
2433 	/**
2434 	@name glow.NodeList#addClass
2435 	@function
2436 	@description Adds a class to each node.
2437 
2438 	@param {string} name The name of the class to add.
2439 
2440 	@returns {glow.NodeList}
2441 
2442 	@example
2443 		glow("#login a").addClass("highlight");
2444 	*/
2445 	NodeListProto.addClass = function(name) {
2446 		var i = this.length;
2447 		
2448 		/*!debug*/
2449 			if (arguments.length !== 1) {
2450 				glow.debug.warn('[wrong count] glow.NodeList#addClass expects 1 argument, not '+arguments.length+'.');
2451 			}
2452 			else if (typeof arguments[0] !== 'string') {
2453 				glow.debug.warn('[wrong type] glow.NodeList#addClass expects argument 1 to be of type string, not '+typeof arguments[0]+'.');
2454 			}
2455 		/*gubed!*/
2456 		
2457 		while (i--) {
2458 			if (this[i].nodeType === 1) {
2459 				_addClass(this[i], name);
2460 			}
2461 		}
2462 		
2463 		return this;
2464 	};
2465 	
2466 	function _addClass(node, name) { // TODO: handle classnames separated by non-space characters?
2467 		if ( (' ' + node.className + ' ').indexOf(' ' + name + ' ') === -1 ) {
2468 			node.className += (node.className? ' ' : '') + name;
2469 		}
2470 	}
2471 	
2472 	/**
2473 	@name glow.NodeList#attr
2474 	@function
2475 	@description Gets or sets attributes.
2476 
2477 		When getting an attribute, it is retrieved from the first
2478 		node in this NodeList. Setting attributes applies the change
2479 		to each element in this NodeList.
2480 
2481 		To set an attribute, pass in the name as the first
2482 		parameter and the value as a second parameter.
2483 
2484 		To set multiple attributes in one call, pass in an object of
2485 		name/value pairs as a single parameter.
2486 
2487 		For browsers that don't support manipulating attributes
2488 		using the DOM, this method will try to do the right thing
2489 		(i.e. don't expect the semantics of this method to be
2490 		consistent across browsers as this is not possible with
2491 		currently supported browsers).
2492 
2493 	@param {string | Object} name The name of the attribute, or an object of name/value pairs
2494 	@param {string} [value] The value to set the attribute to.
2495 
2496 	@returns {string | undefined | glow.NodeList}
2497 
2498 		When setting attributes this method returns its own NodeList, otherwise
2499 		returns the attribute value. The attribute name is always treated as
2500 		case-insensitive. When getting, the returned value will be of type string unless
2501 		that particular attribute was never set and there is no default value, in which
2502 		case the returned value will be an empty string.
2503 
2504 	@example
2505 		var myNodeList = glow(".myImgClass");
2506 
2507 		// get an attribute
2508 		myNodeList.attr("class");
2509 
2510 		// set an attribute
2511 		myNodeList.attr("class", "anotherImgClass");
2512 
2513 		// set multiple attributes
2514 		myNodeList.attr({
2515 		  src: "a.png",
2516 		  alt: "Cat jumping through a field"
2517 		});
2518 	 */
2519 	 // see: http://tobielangel.com/2007/1/11/attribute-nightmare-in-ie/
2520 	NodeListProto.attr = function(/*arguments*/) {
2521 		var args = arguments,
2522 			argsLen = args.length,
2523 			thisLen = this.length,
2524 			name = keyvals = args[0], // using this API: attr(name) or attr({key: val}) ?
2525 			dom0Property = '',
2526 			node,
2527 			attrNode;
2528 		
2529 		/*!debug*/
2530 			if (arguments.length === 2 && typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#attr expects name to be of type string, not '+typeof arguments[0]+'.'); }
2531 			else if (arguments.length === 1 && (typeof arguments[0] !== 'string' && arguments[0].constructor !== Object)) {glow.debug.warn('[wrong type] glow.NodeList#attr expects argument 1 to be of type string or an instance of Object.'); }
2532 			else if (arguments.length === 0 ||  arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#attr expects 1 or 2 arguments, not '+arguments.length+'.'); }
2533 		/*gubed!*/
2534 		
2535 		if (this.length === 0) { // is this an empty nodelist?
2536 			return (argsLen > 1)? this : undef;
2537 		}
2538 		
2539 		if (typeof keyvals === 'object') { // SETting value from {name: value} object
2540 			for (name in keyvals) {
2541 				if (!keyvals.hasOwnProperty(name)) { continue; }
2542 				
2543 				// in IE6 and IE7 the attribute name needs to be translated into dom property name
2544 				if (glow.env.ie < 8) {
2545 					dom0Property = dom0PropertyMapping[name.toLowerCase()];
2546 				}
2547 				
2548 				var i = thisLen;
2549 				while (i--) {
2550 					node = this[i];
2551 					
2552 					if (node.nodeType !== 1) { continue; }
2553 					
2554 					if (dom0Property) {
2555 						node[dom0Property] = keyvals[name];
2556 					}
2557 					else {
2558 						node.setAttribute(name, keyvals[name], 0); // IE flags, 0: case-insensitive
2559 					}
2560 				}
2561 			}
2562 			
2563 			return this;
2564 		}
2565 		else {
2566 			node = this[0];
2567 				
2568 			if (node.nodeType !== 1) {
2569 				return (argsLen > 1)? this : undef;
2570 			}
2571 
2572 			if (argsLen === 1) { // GETting value from name
2573 				if (node.attributes[name]) { // in IE node.getAttributeNode sometimes returns unspecified default values so we look for specified attributes if we can
2574 					return (!node.attributes[name].specified)? '' : node.attributes[name].value;
2575 				}
2576 				else if (node.getAttributeNode) { // in IE getAttribute() does not always work so we use getAttributeNode if we can
2577 					attrNode = node.getAttributeNode(name, 0);
2578 					return (attrNode === null)? '' : attrNode.value;
2579 				}
2580 				else {
2581 					value = node.getAttribute(name, 0, 2); // IE flags, 0: case-insensitive, 2: as string
2582 					return (value === null)? '' : value;
2583 				}	
2584 			}
2585 			else { // SETting a single value like attr(name, value), normalize to an keyval object
2586 				if (glow.env.ie < 8) {
2587 					dom0Property = dom0PropertyMapping[name.toLowerCase()];
2588 				}
2589 				
2590 				if (dom0Property) {
2591 					node[dom0Property] = args[1];
2592 				}
2593 				else {
2594 					node.setAttribute(name, args[1], 0); // IE flags, 0: case-insensitive
2595 				}
2596 				return this;
2597 			}
2598 		}
2599 	};
2600 	/**
2601 		Copies the data from one nodelist to another
2602 		@private
2603 		@name glow.NodeList._copyData
2604 		@see glow.NodeList#clone
2605 		@function
2606 	*/
2607 	glow.NodeList._copyData = function(from, to){
2608 		if ( !from[dataPropName] ){
2609 			return;
2610 		}
2611 		else{			
2612 			to = new glow.NodeList(to);
2613 			to.data( dataCache[from[dataPropName]] );			
2614 			return;
2615 		}
2616 		
2617 	}
2618 	/**
2619 		Used to remove the data when a node is destroyed
2620 		@private
2621 		@name glow.NodeList._copyData
2622 		@see glow.NodeList#destroy
2623 		@function
2624 	*/
2625 	glow.NodeList._destroyData = function(removeFrom){
2626 		if ( !removeFrom && !removeFrom[0][dataPropName] ){
2627 			return;
2628 		}
2629 		else{
2630 			removeFromNode = new glow.NodeList(removeFrom);
2631 			removeFromNode.removeData();			
2632 			return;
2633 		}
2634 		
2635 	}
2636 	/**
2637 	@name glow.NodeList#data
2638 	@function
2639 	@description Use this to safely attach arbitrary data to any DOM Element.
2640 	
2641 	This method is useful when you wish to avoid memory leaks that are possible when adding your own data directly to DOM Elements.
2642 	
2643 	When called with no arguments, will return glow's entire data store for the first node in this NodeList.
2644 	
2645 	Otherwise, when given a name, will return the associated value from the first node in this NodeList.
2646 	
2647 	When given both a name and a value, will store that data on every node in this NodeList.
2648 	
2649 	Optionally you can pass in a single object composed of multiple name, value pairs.
2650 	
2651 	@param {string|Object} [key] The name of the value in glow's data store.
2652 	@param {Object} [val] The value you wish to associate with the given name.
2653 	@see glow.NodeList#removeData
2654 	@example
2655 	
2656 	glow("p").data("tea", "milky");
2657 	var colour = glow("p").data("tea"); // milky
2658 	@returns {Object} When setting a value this method can be chained, as in that case it will return itself.
2659 	@see glow.NodeList#removeData
2660 	*/
2661 	NodeListProto.data = function (key, val) { /*debug*///console.log("data("+key+", "+val+")");
2662 		var args = arguments,
2663 			argsLen = args.length,
2664 			keyvals = key, // like: data({key: val}) or data(key, val)
2665 			index,
2666 			node;
2667 		
2668 		/*!debug*/
2669 			if (arguments.length === 2 && typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#data expects name argument to be of type string.'); }
2670 			else if (arguments.length === 1 && (typeof arguments[0] !== 'string' && arguments[0].constructor !== Object)) {glow.debug.warn('[wrong type] glow.NodeList#data expects argument 1 to be of type string or an instance of Object.'); }
2671 			else if (arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#data expects 0, 1 or 2 arguments.'); }
2672 		/*gubed!*/
2673 		
2674 		if (argsLen > 1) { // SET key, val on every node
2675 			var i = this.length;
2676 			while (i--) {
2677 				node = this[i];
2678 				if (node.nodeType !== 1) { continue; }
2679 				
2680 				index = node[dataPropName];
2681 				if (!index) { // assumes index is always > 0
2682 					index = dataIndex++;
2683 					
2684 					node[dataPropName] = index;
2685 					dataCache[index] = {};
2686 				}
2687 				dataCache[index][key] = val;
2688 			}
2689 			
2690 			return this; // chainable with (key, val) signature
2691 		}
2692 		else if (typeof keyvals === 'object') { // SET keyvals on every node
2693 			var i = this.length;
2694 			while (i--) {
2695 				node = this[i];
2696 				if (node.nodeType !== 1) { continue; }
2697 				
2698 				index = node[dataPropName];
2699 				if (!index) { // assumes index is always > 0
2700 					index = dataIndex++;
2701 					
2702 					node[dataPropName] = index;
2703 					dataCache[index] = {};
2704 				}
2705 				for (key in keyvals) {
2706 					dataCache[index][key] = keyvals[key];
2707 				}
2708 			}
2709 			
2710 			return this; // chainable with ({key, val}) signature
2711 		}
2712 		else { // GET from first node
2713 			node = this[0];
2714 			if (node === undef || node.nodeType !== 1) { return undef; }
2715 				
2716 			if ( !(index = node[dataPropName]) ) {
2717 				return undef;
2718 			}
2719 			
2720 			if (key) {
2721 				return dataCache[index][key];
2722 			}
2723 			
2724 			// get the entire data cache object for this node
2725 			return dataCache[index];
2726 		}
2727 	};
2728 	
2729 	/**
2730 	@name glow.NodeList#hasAttr
2731 	@function
2732 	@description Does the node have a particular attribute?
2733 		
2734 		The first node in this NodeList is tested.
2735 		
2736 	@param {string} name The name of the attribute to test for.
2737 
2738 	@returns {boolean|undefined} Returns undefined if the first node is not an element,
2739 	or if the NodeList is empty, otherwise returns true/false to indicate if that attribute exists
2740 	on the first element.
2741 
2742 	@example
2743 		if ( glow("#myImg").hasAttr("alt") ){
2744 			// ...
2745 		}
2746 	*/
2747 	NodeListProto.hasAttr = function(name) {
2748 		var node;
2749 		
2750 		/*!debug*/
2751 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#hasAttr expects 1 argument.'); }
2752 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#hasAttr expects argument 1 to be of type string.'); }
2753 		/*gubed!*/
2754 		
2755 		node = this[0];
2756 		
2757 		if (this.length && node.nodeType === 1) {
2758 			if (node.attributes[name]) { // is an object in  IE, or else: undefined in IE < 8, null in IE 8
2759 				return !!node.attributes[name].specified;
2760 			}
2761 			
2762 			if (node.hasAttribute) { return node.hasAttribute(name); } // like FF, Safari, etc
2763 			else { return node.attributes[name] !== undef; } // like IE7
2764 		}
2765 	};
2766 	
2767 	/**
2768 	@name glow.NodeList#hasClass
2769 	@function
2770 	@description Does the node have a particular class?
2771 
2772 		The first node in this NodeList is tested.
2773 
2774 	@param {string} name The name of the class to test for.
2775 
2776 	@returns {boolean}
2777 
2778 	@example
2779 		if ( glow("#myInput").hasClass("errored") ){
2780 			// ...
2781 		}
2782 	*/
2783 	NodeListProto.hasClass = function (name) {
2784 		/*!debug*/
2785 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#hasClass expects 1 argument.'); }
2786 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#hasClass expects argument 1 to be of type string.'); }
2787 		/*gubed!*/
2788 		
2789 		if (this.length && this[0].nodeType === 1) {
2790 			return ( (' ' + this[0].className + ' ').indexOf(' ' + name + ' ') > -1 );
2791 		}
2792 	};
2793 	
2794 	/**
2795 	@name glow.NodeList#prop
2796 	@function
2797 	@description Gets or sets node properties.
2798 	
2799 		This function gets / sets node properties, to get attributes,
2800 		see {@link glow.NodeList#attr NodeList#attr}.
2801 		
2802 		When getting a property, it is retrieved from the first
2803 		node in this NodeList. Setting properties to each element in
2804 		this NodeList.
2805 		
2806 		To set multiple properties in one call, pass in an object of
2807 		name/value pairs.
2808 		
2809 	@param {string | Object} name The name of the property, or an object of name/value pairs
2810 	@param {string} [value] The value to set the property to.
2811 
2812 	@returns {string | glow.NodeList}
2813 
2814 		When setting properties it returns the NodeList, otherwise
2815 		returns the property value.
2816 
2817 	@example
2818 		var myNodeList = glow("#formElement");
2819 
2820 		// get the node name
2821 		myNodeList.prop("nodeName");
2822 
2823 		// set a property
2824 		myNodeList.prop("_secretValue", 10);
2825 
2826 		// set multiple properties
2827 		myNodeList.prop({
2828 			checked: true,
2829 			_secretValue: 10
2830 		});
2831 	*/
2832 	NodeListProto.prop = function(name, val) {
2833 		var hash = name,
2834 			argsLen = arguments.length;
2835 		
2836 		/*!debug*/
2837 			if (arguments.length === 1 && (typeof name !== 'string' && name.constructor !== Object)) {glow.debug.warn('[wrong type] glow.NodeList#prop expects argument 1 to be of type string or Object.'); }
2838 			else if (arguments.length === 2 && typeof name !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#prop expects name to be of type string.'); }
2839 			else if (arguments.length === 0 || arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#prop expects 1 or 2 arguments.'); }
2840 		/*gubed!*/
2841 		
2842 		if (this.length === 0) return;
2843 		
2844 		if (argsLen === 2 && typeof name === 'string') {
2845 			for (var i = 0, ilen = this.length; i < ilen; i++) {
2846 				if (this[i].nodeType === 1) { this[i][name] = val; }
2847 			}
2848 			return this;
2849 		}
2850 		else if (argsLen === 1 && hash.constructor === Object) {
2851 			for (var key in hash) {
2852 				for (var i = 0, ilen = this.length; i < ilen; i++) {
2853 					if (this[i].nodeType === 1) { this[i][key] = hash[key]; }
2854 				}
2855 			}
2856 			return this;
2857 		}
2858 		else if (argsLen === 1 && typeof name === 'string') {
2859 			if (this[0].nodeType === 1) { return this[0][name]; }
2860 		}
2861 		else {
2862 			throw new Error('Invalid parameters.');
2863 		}
2864 	};
2865 	
2866 	/**
2867 	@name glow.NodeList#removeAttr
2868 	@function
2869 	@description Removes an attribute from each node.
2870 
2871 	@param {string} name The name of the attribute to remove.
2872 
2873 	@returns {glow.NodeList}
2874 
2875 	@example
2876 		glow("a").removeAttr("target");
2877 	*/
2878 	NodeListProto.removeAttr = function (name) {
2879 		var dom0Property;
2880 		
2881 		/*!debug*/
2882 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#removeAttr expects 1 argument.'); }
2883 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeAttr expects argument 1 to be of type string.'); }
2884 		/*gubed!*/
2885 	
2886 		for (var i = 0, leni = this.length; i < leni; i++) {
2887 			if (this[i].nodeType === 1) {
2888 				if (glow.env.ie < 8) {
2889 					if ( (dom0Property = dom0PropertyMapping[name.toLowerCase()]) ) {
2890 						this[i][dom0Property] = '';
2891 					}
2892 				}
2893 				
2894 				if (this[i].removeAttribute) this[i].removeAttribute(name);
2895 			}
2896 		}
2897 		return this;
2898 	};
2899 	
2900 	/**
2901 	@name glow.NodeList#removeClass
2902 	@function
2903 	@description Removes a class from each node.
2904 
2905 	@param {string} name The name of the class to remove.
2906 
2907 	@returns {glow.NodeList}
2908 
2909 	@example
2910 		glow("#footer #login a").removeClass("highlight");
2911 	*/
2912 	NodeListProto.removeClass = function(name) {
2913 		var node;
2914 					
2915 		/*!debug*/
2916 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#removeClass() expects 1 argument.'); }
2917 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeClass() expects argument 1 to be of type string.'); }
2918 		/*gubed!*/
2919 		
2920 		var i = this.length;
2921 		while (i--) {
2922 			node = this[i];
2923 			if (node.className) {
2924 				_removeClass(node, name);
2925 			}
2926 		}
2927 		return this;
2928 	};
2929 	
2930 	function _removeClass(node, name) {
2931 		var oldClasses = node.className.split(' '),
2932 			newClasses = [];
2933 			
2934 		oldClasses = node.className.split(' ');
2935 		newClasses = [];
2936 		
2937 		var i = oldClasses.length;
2938 		while (i--) {
2939 			if (oldClasses[i] !== name) {
2940 				oldClasses[i] && newClasses.unshift(oldClasses[i]); // unshift to maintain original order
2941 			}
2942 		}
2943 		node.className = (newClasses.length)? newClasses.join(' ') : '';
2944 	}
2945 	
2946 	/**
2947 	@name glow.NodeList#removeData
2948 	@function
2949 	@description Removes data previously added by {@link glow.NodeList#data} from each node in this NodeList.
2950 	
2951 	When called with no arguments, will delete glow's entire data store for each node in this NodeList.
2952 	
2953 	Otherwise, when given a name, will delete the associated value from each node in this NodeList.
2954 	
2955 	@param {string} [key] The name of the value in glow's data store.
2956 	@see glow.NodeList#data
2957 	*/
2958 	NodeListProto.removeData = function(key) {
2959 		var elm,
2960 			i = this.length,
2961 			index;
2962 			// uses private scoped variables: dataCache, dataPropName
2963 		
2964 		/*!debug*/
2965 			if (arguments.length > 1) { glow.debug.warn('[wrong count] glow.NodeList#removeData expects 0 or 1 arguments.'); }
2966 			else if (arguments.length === 1 && typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeData expects argument 1 to be of type string.'); }
2967 		/*gubed!*/
2968 		
2969 		while (i--) {
2970 			elm = this[i];
2971 			index = elm[dataPropName];
2972 			
2973 			if (index !== undef) {
2974 				switch (arguments.length) {
2975 					case 0:
2976 						dataCache[index] = undef;
2977 						elm[dataPropName] = undef;
2978 						try {
2979 							delete elm[dataPropName]; // IE 6 goes wobbly here
2980 						}
2981 						catch(e) { // remove expando from IE 6
2982 							elm.removeAttribute && elm.removeAttribute(dataPropName);
2983 						}
2984 						break;
2985 					case 1:
2986 						dataCache[index][key] = undef;
2987 						delete dataCache[index][key];
2988 						break;
2989 				}
2990 			}
2991 		}
2992 		
2993 		return this; // chainable
2994 	};
2995 	
2996 	/**
2997 	@name glow.NodeList#toggleClass
2998 	@function
2999 	@description Toggles a class on each node.
3000 
3001 	@param {string} name The name of the class to toggle.
3002 
3003 	@returns {glow.NodeList}
3004 
3005 	@example
3006 		glow(".onOffSwitch").toggleClass("on");
3007 	 */
3008 	NodeListProto.toggleClass = function(name) {
3009 		var node;
3010 		
3011 		/*!debug*/
3012 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#toggleClass() expects 1 argument.'); }
3013 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#toggleClass() expects argument 1 to be of type string.'); }
3014 		/*gubed!*/
3015 		
3016 		for (var i = 0, leni = this.length; i < leni; i++) {
3017 			node = this[i];
3018 			if (node.className) {
3019 				if ( (' ' + node.className + ' ').indexOf(' ' + name + ' ') > -1 ) {
3020 					_removeClass(node, name);
3021 				}
3022 				else {
3023 					_addClass(node, name);
3024 				}
3025 			}
3026 		}
3027 		
3028 		return this;
3029 	};
3030 	
3031 	/**
3032 	@name glow.NodeList#val
3033 	@function
3034 	@description Gets or sets form values for the first node.
3035 
3036 		<p><em>This method is not applicable to XML NodeLists.</em></p>
3037 
3038 		<p><em>Getting values from form elements</em></p>
3039 
3040 		The returned value depends on the type of element, see below:
3041 
3042 		<dl>
3043 		<dt>Radio button or checkbox</dt>
3044 		<dd>If checked, then the contents of the value attribute, otherwise an empty string.</dd>
3045 		<dt>Select</dt>
3046 		<dd>The contents of value attribute of the selected option</dd>
3047 		<dt>Select (multiple)</dt>
3048 		<dd>An array of selected option values.</dd>
3049 		<dt>Other form element</dt>
3050 		<dd>The value of the input.</dd>
3051 		</dl>
3052 
3053 		<p><em>Getting values from a form</em></p>
3054 
3055 		If the first element in the NodeList is a form, then an
3056 		object is returned containing the form data. Each item
3057 		property of the object is a value as above, apart from when
3058 		multiple elements of the same name exist, in which case the
3059 		it will contain an array of values.
3060 
3061 		<p><em>Setting values for form elements</em></p>
3062 
3063 		If a value is passed and the first element of the NodeList
3064 		is a form element, then the form element is given that value.
3065 		For select elements, this means that the first option that
3066 		matches the value will be selected. For selects that allow
3067 		multiple selection, the options which have a value that
3068 		exists in the array of values/match the value will be
3069 		selected and others will be deselected.
3070 
3071 		Currently checkboxes and radio buttons are not checked or
3072 		unchecked, just their value is changed. This does mean that
3073 		this does not do exactly the reverse of getting the value
3074 		from the element (see above) and as such may be subject to
3075 		change
3076 
3077 		<p><em>Setting values for forms</em></p>
3078 
3079 		If the first element in the NodeList is a form and the
3080 		value is an object, then each element of the form has its
3081 		value set to the corresponding property of the object, using
3082 		the method described above.
3083 
3084 	@param {String | Object} [value] The value to set the form element/elements to.
3085 
3086 	@returns {glow.dom.NodeList | String | Object}
3087 
3088 		When used to set a value it returns the NodeList, otherwise
3089 		returns the value as described above.
3090 
3091 	@example
3092 		// get a value
3093 		var username = glow.dom.get("input#username").val();
3094 
3095 	@example			
3096 		/ get values from a form
3097 		var userDetails = glow.dom.get("form").val();
3098 
3099 	@example
3100 		// set a value
3101 		glow.dom.get("input#username").val("example username");
3102 
3103 	@example
3104 		// set values in a form
3105 		glow.dom.get("form").val({
3106 			username : "another",
3107 			name     : "A N Other"
3108 		});
3109 	*/
3110 	NodeListProto.val = function(){
3111 		/*
3112 			PrivateFunction: elementValue
3113 			Get a value for a form element.
3114 		*/
3115 
3116 		function elementValue (el) {
3117 			var elType = el.type,
3118 				elChecked = el.checked,
3119 				elValue = el.value,
3120 				vals = [],
3121 				i = 0;
3122 
3123 			if (elType == "radio") {
3124 				return elChecked ? elValue : "";
3125 			} else if (elType == "checkbox") {
3126 				return elChecked ? elValue : "";
3127 			} else if (elType == "select-one") {
3128 				return el.selectedIndex > -1 ?
3129 					el.options[el.selectedIndex].value : "";
3130 				} else if (elType == "select-multiple") {
3131 				for (var length = el.options.length; i < length; i++) {
3132 					if (el.options[i].selected) {
3133 						vals[vals.length] = el.options[i].value;
3134 					}
3135 				}
3136 				return vals;
3137 			} else {
3138 				return elValue;
3139 			}
3140 		}
3141 
3142 		/*
3143 		PrivateMethod: formValues
3144 			Get an object containing form data.
3145 		*/
3146 		function formValues (form) {
3147 			var vals = {},
3148 				radios = {},
3149 				formElements = form.elements,
3150 				i = 0,
3151 				length = formElements.length,
3152 				name,
3153 				formElement,
3154 				j,
3155 				radio,
3156 				nodeName;
3157 
3158 			for (; i < length; i++) {
3159 				formElement = formElements[i];
3160 				nodeName = formElement.nodeName.toLowerCase();
3161 				name = formElement.name;
3162 				
3163 				// fieldsets & objects come back as form elements, but we don't care about these
3164 				// we don't bother with fields that don't have a name
3165 				// switch to whitelist?
3166 				if (
3167 					nodeName == "fieldset" ||
3168 					nodeName == "object" ||
3169 					!name
3170 				) { continue; }
3171 				if (formElement.type == "checkbox" && ! formElement.checked) {
3172 					if (! name in vals) {
3173 						vals[name] = undefined;
3174 					}
3175 				} else if (formElement.type == "radio") {
3176 					if (radios[name]) {
3177 						radios[name][radios[name].length] = formElement;
3178 					} else {
3179 						radios[name] = [formElement];
3180 					}
3181 				} else {
3182 					var value = elementValue(formElement);
3183 					if (name in vals) {
3184 						if (vals[name].push) {
3185 							vals[name][vals[name].length] = value;
3186 						} else {
3187 							vals[name] = [vals[name], value];
3188 						}
3189 					} else {
3190 						vals[name] = value;
3191 					}
3192 				}
3193 			}
3194 			for (i in radios) {
3195 				j = 0;
3196 				for (length = radios[i].length; j < length; j++) {
3197 					radio = radios[i][j];
3198 					name = radio.name;
3199 					if (radio.checked) {
3200 						vals[radio.name] = radio.value;
3201 						break;
3202 					}
3203 				}
3204 				if (! name in vals) { vals[name] = undefined; }
3205 			}
3206 			return vals;
3207 		}
3208 
3209 		/*
3210 		PrivateFunction: setFormValues
3211 			Set values of a form to those in passed in object.
3212 		*/
3213 		function setFormValues (form, vals) {
3214 			var prop, currentField,
3215 				fields = {},
3216 				storeType, i = 0, n, len, foundOne, currentFieldType;
3217 
3218 			for (prop in vals) {
3219 				currentField = form[prop];
3220 				if (currentField && currentField[0] && !currentField.options) { // is array of fields
3221 					//normalise values to array of vals
3222 					vals[prop] = vals[prop] && vals[prop].push ? vals[prop] : [vals[prop]];
3223 					//order the fields by types that matter
3224 					fields.radios = [];
3225 					fields.checkboxesSelects = [];
3226 					fields.multiSelects = [];
3227 					fields.other = [];
3228 
3229 					for (i = 0; currentField[i]; i++) {
3230 						currentFieldType = currentField[i].type;
3231 						if (currentFieldType == "radio") {
3232 							storeType = "radios";
3233 					} else if (currentFieldType == "select-one" || currentFieldType == "checkbox") {
3234 							storeType = "checkboxesSelects";
3235 						} else if (currentFieldType == "select-multiple") {
3236 							storeType = "multiSelects";
3237 						} else {
3238 							storeType = "other";
3239 						}
3240 						//add it to the correct array
3241 						fields[storeType][fields[storeType].length] = currentField[i];
3242 					}
3243 
3244 					for (i = 0; fields.multiSelects[i]; i++) {
3245 						vals[prop] = setValue(fields.multiSelects[i], vals[prop]);
3246 					}
3247 					for (i = 0; fields.checkboxesSelects[i]; i++) {
3248 						setValue(fields.checkboxesSelects[i], "");
3249 						for (n = 0, len = vals[prop].length; n < len; n++) {
3250 							if (setValue(fields.checkboxesSelects[i], vals[prop][n])) {
3251 								vals[prop].slice(n, 1);
3252 								break;
3253 							}
3254 						}
3255 					}
3256 					for (i = 0; fields.radios[i]; i++) {
3257 						fields.radios[i].checked = false;
3258 						foundOne = false;
3259 						for (n = 0, len = vals[prop].length; n < len; n++) {
3260 							if (setValue(fields.radios[i], vals[prop][n])) {
3261 								vals[prop].slice(n, 1);
3262 								foundOne = true;
3263 								break;
3264 							}
3265 							if (foundOne) { break; }
3266 						}
3267 					}
3268 					for (i = 0; fields.other[i] && vals[prop][i] !== undefined; i++) {
3269 						setValue(fields.other[i], vals[prop][i]);
3270 					}
3271 				} else if (currentField && currentField.nodeName) { // is single field, easy
3272 					setValue(currentField, vals[prop]);
3273 				}
3274 			}
3275 		}
3276 
3277 		/*
3278 		PrivateFunction: setValue
3279 			Set the value of a form element.
3280 			Returns:
3281 			values that weren't able to set if array of vals passed (for multi select). Otherwise true if val set, false if not
3282 		*/
3283 		function setValue (el, val) {
3284 			var i = 0,
3285 				length,
3286 				n = 0,
3287 				nlen,
3288 				elOption,
3289 				optionVal;
3290 
3291 				if (el.type == "select-one") {
3292 				for (length = el.options.length; i < length; i++) {
3293 					if (el.options[i].value == val) {
3294 						el.selectedIndex = i;
3295 						return true;
3296 					}
3297 				}
3298 				return false;
3299 			} else if (el.type == "select-multiple") {
3300 				var isArray = !!val.push;
3301 				for (i = 0, length = el.options.length; i < length; i++) {
3302 					elOption = el.options[i];
3303 					optionVal = elOption.value;
3304 					if (isArray) {
3305 						elOption.selected = false;
3306 						for (nlen = val.length; n < nlen; n++) {
3307 							if (optionVal == val[n]) {
3308 								elOption.selected = true;
3309 								val.splice(n, 1);
3310 								break;
3311 							}
3312 						}
3313 					} else {
3314 						return elOption.selected = val == optionVal;
3315 					}
3316 				}
3317 				return false;
3318 			} else if (el.type == "radio" || el.type == "checkbox") {
3319 				el.checked = val == el.value;
3320 				return val == el.value;
3321 			} else {
3322 				el.value = val;
3323 				return true;
3324 			}
3325 		}
3326 
3327 		// toplevel implementation
3328 	
3329 		var args = arguments,
3330 			val = args[0],
3331 			that = this,
3332 			i = 0,
3333 			length = that.length;
3334 
3335 		if (args.length === 0) {
3336 			return that[0].nodeName == 'FORM' ?
3337 				formValues(that[0]) :
3338 				elementValue(that[0]);
3339 		}
3340 		if (that[0].nodeName == 'FORM') {
3341 			if (! typeof val == 'object') {
3342 				throw 'value for FORM must be object';
3343 			}
3344 			setFormValues(that[0], val);
3345 		} else {
3346 			for (; i < length; i++) {
3347 				setValue(that[i], val);
3348 			}
3349 		}
3350 		return that;		
3351 	};
3352 });
3353 /*!
3354  * Sizzle CSS Selector Engine - v1.0
3355  *  Copyright 2009, The Dojo Foundation
3356  *  Released under the MIT, BSD, and GPL Licenses.
3357  *  More information: http://sizzlejs.com/
3358  */
3359 (function(){
3360 
3361 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
3362 	done = 0,
3363 	toString = Object.prototype.toString,
3364 	hasDuplicate = false,
3365 	baseHasDuplicate = true;
3366 
3367 // Here we check if the JavaScript engine is using some sort of
3368 // optimization where it does not always call our comparision
3369 // function. If that is the case, discard the hasDuplicate value.
3370 //   Thus far that includes Google Chrome.
3371 [0, 0].sort(function(){
3372 	baseHasDuplicate = false;
3373 	return 0;
3374 });
3375 
3376 var Sizzle = function(selector, context, results, seed) {
3377 	results = results || [];
3378 	var origContext = context = context || document;
3379 
3380 	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
3381 		return [];
3382 	}
3383 	
3384 	if ( !selector || typeof selector !== "string" ) {
3385 		return results;
3386 	}
3387 
3388 	var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
3389 		soFar = selector;
3390 	
3391 	// Reset the position of the chunker regexp (start from head)
3392 	while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
3393 		soFar = m[3];
3394 		
3395 		parts.push( m[1] );
3396 		
3397 		if ( m[2] ) {
3398 			extra = m[3];
3399 			break;
3400 		}
3401 	}
3402 
3403 	if ( parts.length > 1 && origPOS.exec( selector ) ) {
3404 		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
3405 			set = posProcess( parts[0] + parts[1], context );
3406 		} else {
3407 			set = Expr.relative[ parts[0] ] ?
3408 				[ context ] :
3409 				Sizzle( parts.shift(), context );
3410 
3411 			while ( parts.length ) {
3412 				selector = parts.shift();
3413 
3414 				if ( Expr.relative[ selector ] ) {
3415 					selector += parts.shift();
3416 				}
3417 				
3418 				set = posProcess( selector, set );
3419 			}
3420 		}
3421 	} else {
3422 		// Take a shortcut and set the context if the root selector is an ID
3423 		// (but not if it'll be faster if the inner selector is an ID)
3424 		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
3425 				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
3426 			var ret = Sizzle.find( parts.shift(), context, contextXML );
3427 			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
3428 		}
3429 
3430 		if ( context ) {
3431 			var ret = seed ?
3432 				{ expr: parts.pop(), set: makeArray(seed) } :
3433 				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
3434 			set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
3435 
3436 			if ( parts.length > 0 ) {
3437 				checkSet = makeArray(set);
3438 			} else {
3439 				prune = false;
3440 			}
3441 
3442 			while ( parts.length ) {
3443 				var cur = parts.pop(), pop = cur;
3444 
3445 				if ( !Expr.relative[ cur ] ) {
3446 					cur = "";
3447 				} else {
3448 					pop = parts.pop();
3449 				}
3450 
3451 				if ( pop == null ) {
3452 					pop = context;
3453 				}
3454 
3455 				Expr.relative[ cur ]( checkSet, pop, contextXML );
3456 			}
3457 		} else {
3458 			checkSet = parts = [];
3459 		}
3460 	}
3461 
3462 	if ( !checkSet ) {
3463 		checkSet = set;
3464 	}
3465 
3466 	if ( !checkSet ) {
3467 		throw "Syntax error, unrecognized expression: " + (cur || selector);
3468 	}
3469 
3470 	if ( toString.call(checkSet) === "[object Array]" ) {
3471 		if ( !prune ) {
3472 			results.push.apply( results, checkSet );
3473 		} else if ( context && context.nodeType === 1 ) {
3474 			for ( var i = 0; checkSet[i] != null; i++ ) {
3475 				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
3476 					results.push( set[i] );
3477 				}
3478 			}
3479 		} else {
3480 			for ( var i = 0; checkSet[i] != null; i++ ) {
3481 				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
3482 					results.push( set[i] );
3483 				}
3484 			}
3485 		}
3486 	} else {
3487 		makeArray( checkSet, results );
3488 	}
3489 
3490 	if ( extra ) {
3491 		Sizzle( extra, origContext, results, seed );
3492 		Sizzle.uniqueSort( results );
3493 	}
3494 
3495 	return results;
3496 };
3497 
3498 Sizzle.uniqueSort = function(results){
3499 	if ( sortOrder ) {
3500 		hasDuplicate = baseHasDuplicate;
3501 		results.sort(sortOrder);
3502 
3503 		if ( hasDuplicate ) {
3504 			for ( var i = 1; i < results.length; i++ ) {
3505 				if ( results[i] === results[i-1] ) {
3506 					results.splice(i--, 1);
3507 				}
3508 			}
3509 		}
3510 	}
3511 
3512 	return results;
3513 };
3514 
3515 Sizzle.matches = function(expr, set){
3516 	return Sizzle(expr, null, null, set);
3517 };
3518 
3519 Sizzle.find = function(expr, context, isXML){
3520 	var set, match;
3521 
3522 	if ( !expr ) {
3523 		return [];
3524 	}
3525 
3526 	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
3527 		var type = Expr.order[i], match;
3528 		
3529 		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
3530 			var left = match[1];
3531 			match.splice(1,1);
3532 
3533 			if ( left.substr( left.length - 1 ) !== "\\" ) {
3534 				match[1] = (match[1] || "").replace(/\\/g, "");
3535 				set = Expr.find[ type ]( match, context, isXML );
3536 				if ( set != null ) {
3537 					expr = expr.replace( Expr.match[ type ], "" );
3538 					break;
3539 				}
3540 			}
3541 		}
3542 	}
3543 
3544 	if ( !set ) {
3545 		set = context.getElementsByTagName("*");
3546 	}
3547 
3548 	return {set: set, expr: expr};
3549 };
3550 
3551 Sizzle.filter = function(expr, set, inplace, not){
3552 	var old = expr, result = [], curLoop = set, match, anyFound,
3553 		isXMLFilter = set && set[0] && isXML(set[0]);
3554 
3555 	while ( expr && set.length ) {
3556 		for ( var type in Expr.filter ) {
3557 			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
3558 				var filter = Expr.filter[ type ], found, item;
3559 				anyFound = false;
3560 
3561 				if ( curLoop === result ) {
3562 					result = [];
3563 				}
3564 
3565 				if ( Expr.preFilter[ type ] ) {
3566 					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
3567 
3568 					if ( !match ) {
3569 						anyFound = found = true;
3570 					} else if ( match === true ) {
3571 						continue;
3572 					}
3573 				}
3574 
3575 				if ( match ) {
3576 					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
3577 						if ( item ) {
3578 							found = filter( item, match, i, curLoop );
3579 							var pass = not ^ !!found;
3580 
3581 							if ( inplace && found != null ) {
3582 								if ( pass ) {
3583 									anyFound = true;
3584 								} else {
3585 									curLoop[i] = false;
3586 								}
3587 							} else if ( pass ) {
3588 								result.push( item );
3589 								anyFound = true;
3590 							}
3591 						}
3592 					}
3593 				}
3594 
3595 				if ( found !== undefined ) {
3596 					if ( !inplace ) {
3597 						curLoop = result;
3598 					}
3599 
3600 					expr = expr.replace( Expr.match[ type ], "" );
3601 
3602 					if ( !anyFound ) {
3603 						return [];
3604 					}
3605 
3606 					break;
3607 				}
3608 			}
3609 		}
3610 
3611 		// Improper expression
3612 		if ( expr === old ) {
3613 			if ( anyFound == null ) {
3614 				throw "Syntax error, unrecognized expression: " + expr;
3615 			} else {
3616 				break;
3617 			}
3618 		}
3619 
3620 		old = expr;
3621 	}
3622 
3623 	return curLoop;
3624 };
3625 
3626 var Expr = Sizzle.selectors = {
3627 	order: [ "ID", "NAME", "TAG" ],
3628 	match: {
3629 		ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
3630 		CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
3631 		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
3632 		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
3633 		TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
3634 		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
3635 		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
3636 		PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
3637 	},
3638 	leftMatch: {},
3639 	attrMap: {
3640 		"class": "className",
3641 		"for": "htmlFor"
3642 	},
3643 	attrHandle: {
3644 		href: function(elem){
3645 			return elem.getAttribute("href");
3646 		}
3647 	},
3648 	relative: {
3649 		"+": function(checkSet, part){
3650 			var isPartStr = typeof part === "string",
3651 				isTag = isPartStr && !/\W/.test(part),
3652 				isPartStrNotTag = isPartStr && !isTag;
3653 
3654 			if ( isTag ) {
3655 				part = part.toLowerCase();
3656 			}
3657 
3658 			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
3659 				if ( (elem = checkSet[i]) ) {
3660 					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
3661 
3662 					checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
3663 						elem || false :
3664 						elem === part;
3665 				}
3666 			}
3667 
3668 			if ( isPartStrNotTag ) {
3669 				Sizzle.filter( part, checkSet, true );
3670 			}
3671 		},
3672 		">": function(checkSet, part){
3673 			var isPartStr = typeof part === "string";
3674 
3675 			if ( isPartStr && !/\W/.test(part) ) {
3676 				part = part.toLowerCase();
3677 
3678 				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
3679 					var elem = checkSet[i];
3680 					if ( elem ) {
3681 						var parent = elem.parentNode;
3682 						checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
3683 					}
3684 				}
3685 			} else {
3686 				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
3687 					var elem = checkSet[i];
3688 					if ( elem ) {
3689 						checkSet[i] = isPartStr ?
3690 							elem.parentNode :
3691 							elem.parentNode === part;
3692 					}
3693 				}
3694 
3695 				if ( isPartStr ) {
3696 					Sizzle.filter( part, checkSet, true );
3697 				}
3698 			}
3699 		},
3700 		"": function(checkSet, part, isXML){
3701 			var doneName = done++, checkFn = dirCheck;
3702 
3703 			if ( typeof part === "string" && !/\W/.test(part) ) {
3704 				var nodeCheck = part = part.toLowerCase();
3705 				checkFn = dirNodeCheck;
3706 			}
3707 
3708 			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
3709 		},
3710 		"~": function(checkSet, part, isXML){
3711 			var doneName = done++, checkFn = dirCheck;
3712 
3713 			if ( typeof part === "string" && !/\W/.test(part) ) {
3714 				var nodeCheck = part = part.toLowerCase();
3715 				checkFn = dirNodeCheck;
3716 			}
3717 
3718 			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
3719 		}
3720 	},
3721 	find: {
3722 		ID: function(match, context, isXML){
3723 			if ( typeof context.getElementById !== "undefined" && !isXML ) {
3724 				var m = context.getElementById(match[1]);
3725 				return m ? [m] : [];
3726 			}
3727 		},
3728 		NAME: function(match, context){
3729 			if ( typeof context.getElementsByName !== "undefined" ) {
3730 				var ret = [], results = context.getElementsByName(match[1]);
3731 
3732 				for ( var i = 0, l = results.length; i < l; i++ ) {
3733 					if ( results[i].getAttribute("name") === match[1] ) {
3734 						ret.push( results[i] );
3735 					}
3736 				}
3737 
3738 				return ret.length === 0 ? null : ret;
3739 			}
3740 		},
3741 		TAG: function(match, context){
3742 			return context.getElementsByTagName(match[1]);
3743 		}
3744 	},
3745 	preFilter: {
3746 		CLASS: function(match, curLoop, inplace, result, not, isXML){
3747 			match = " " + match[1].replace(/\\/g, "") + " ";
3748 
3749 			if ( isXML ) {
3750 				return match;
3751 			}
3752 
3753 			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
3754 				if ( elem ) {
3755 					if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
3756 						if ( !inplace ) {
3757 							result.push( elem );
3758 						}
3759 					} else if ( inplace ) {
3760 						curLoop[i] = false;
3761 					}
3762 				}
3763 			}
3764 
3765 			return false;
3766 		},
3767 		ID: function(match){
3768 			return match[1].replace(/\\/g, "");
3769 		},
3770 		TAG: function(match, curLoop){
3771 			return match[1].toLowerCase();
3772 		},
3773 		CHILD: function(match){
3774 			if ( match[1] === "nth" ) {
3775 				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
3776 				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
3777 					match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
3778 					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
3779 
3780 				// calculate the numbers (first)n+(last) including if they are negative
3781 				match[2] = (test[1] + (test[2] || 1)) - 0;
3782 				match[3] = test[3] - 0;
3783 			}
3784 
3785 			// TODO: Move to normal caching system
3786 			match[0] = done++;
3787 
3788 			return match;
3789 		},
3790 		ATTR: function(match, curLoop, inplace, result, not, isXML){
3791 			var name = match[1].replace(/\\/g, "");
3792 			
3793 			if ( !isXML && Expr.attrMap[name] ) {
3794 				match[1] = Expr.attrMap[name];
3795 			}
3796 
3797 			if ( match[2] === "~=" ) {
3798 				match[4] = " " + match[4] + " ";
3799 			}
3800 
3801 			return match;
3802 		},
3803 		PSEUDO: function(match, curLoop, inplace, result, not){
3804 			if ( match[1] === "not" ) {
3805 				// If we're dealing with a complex expression, or a simple one
3806 				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
3807 					match[3] = Sizzle(match[3], null, null, curLoop);
3808 				} else {
3809 					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
3810 					if ( !inplace ) {
3811 						result.push.apply( result, ret );
3812 					}
3813 					return false;
3814 				}
3815 			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
3816 				return true;
3817 			}
3818 			
3819 			return match;
3820 		},
3821 		POS: function(match){
3822 			match.unshift( true );
3823 			return match;
3824 		}
3825 	},
3826 	filters: {
3827 		enabled: function(elem){
3828 			return elem.disabled === false && elem.type !== "hidden";
3829 		},
3830 		disabled: function(elem){
3831 			return elem.disabled === true;
3832 		},
3833 		checked: function(elem){
3834 			return elem.checked === true;
3835 		},
3836 		selected: function(elem){
3837 			// Accessing this property makes selected-by-default
3838 			// options in Safari work properly
3839 			elem.parentNode.selectedIndex;
3840 			return elem.selected === true;
3841 		},
3842 		parent: function(elem){
3843 			return !!elem.firstChild;
3844 		},
3845 		empty: function(elem){
3846 			return !elem.firstChild;
3847 		},
3848 		has: function(elem, i, match){
3849 			return !!Sizzle( match[3], elem ).length;
3850 		},
3851 		header: function(elem){
3852 			return /h\d/i.test( elem.nodeName );
3853 		},
3854 		text: function(elem){
3855 			return "text" === elem.type;
3856 		},
3857 		radio: function(elem){
3858 			return "radio" === elem.type;
3859 		},
3860 		checkbox: function(elem){
3861 			return "checkbox" === elem.type;
3862 		},
3863 		file: function(elem){
3864 			return "file" === elem.type;
3865 		},
3866 		password: function(elem){
3867 			return "password" === elem.type;
3868 		},
3869 		submit: function(elem){
3870 			return "submit" === elem.type;
3871 		},
3872 		image: function(elem){
3873 			return "image" === elem.type;
3874 		},
3875 		reset: function(elem){
3876 			return "reset" === elem.type;
3877 		},
3878 		button: function(elem){
3879 			return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
3880 		},
3881 		input: function(elem){
3882 			return /input|select|textarea|button/i.test(elem.nodeName);
3883 		}
3884 	},
3885 	setFilters: {
3886 		first: function(elem, i){
3887 			return i === 0;
3888 		},
3889 		last: function(elem, i, match, array){
3890 			return i === array.length - 1;
3891 		},
3892 		even: function(elem, i){
3893 			return i % 2 === 0;
3894 		},
3895 		odd: function(elem, i){
3896 			return i % 2 === 1;
3897 		},
3898 		lt: function(elem, i, match){
3899 			return i < match[3] - 0;
3900 		},
3901 		gt: function(elem, i, match){
3902 			return i > match[3] - 0;
3903 		},
3904 		nth: function(elem, i, match){
3905 			return match[3] - 0 === i;
3906 		},
3907 		eq: function(elem, i, match){
3908 			return match[3] - 0 === i;
3909 		}
3910 	},
3911 	filter: {
3912 		PSEUDO: function(elem, match, i, array){
3913 			var name = match[1], filter = Expr.filters[ name ];
3914 
3915 			if ( filter ) {
3916 				return filter( elem, i, match, array );
3917 			} else if ( name === "contains" ) {
3918 				return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
3919 			} else if ( name === "not" ) {
3920 				var not = match[3];
3921 
3922 				for ( var i = 0, l = not.length; i < l; i++ ) {
3923 					if ( not[i] === elem ) {
3924 						return false;
3925 					}
3926 				}
3927 
3928 				return true;
3929 			} else {
3930 				throw "Syntax error, unrecognized expression: " + name;
3931 			}
3932 		},
3933 		CHILD: function(elem, match){
3934 			var type = match[1], node = elem;
3935 			switch (type) {
3936 				case 'only':
3937 				case 'first':
3938 					while ( (node = node.previousSibling) )	 {
3939 						if ( node.nodeType === 1 ) { 
3940 							return false; 
3941 						}
3942 					}
3943 					if ( type === "first" ) { 
3944 						return true; 
3945 					}
3946 					node = elem;
3947 				case 'last':
3948 					while ( (node = node.nextSibling) )	 {
3949 						if ( node.nodeType === 1 ) { 
3950 							return false; 
3951 						}
3952 					}
3953 					return true;
3954 				case 'nth':
3955 					var first = match[2], last = match[3];
3956 
3957 					if ( first === 1 && last === 0 ) {
3958 						return true;
3959 					}
3960 					
3961 					var doneName = match[0],
3962 						parent = elem.parentNode;
3963 	
3964 					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
3965 						var count = 0;
3966 						for ( node = parent.firstChild; node; node = node.nextSibling ) {
3967 							if ( node.nodeType === 1 ) {
3968 								node.nodeIndex = ++count;
3969 							}
3970 						} 
3971 						parent.sizcache = doneName;
3972 					}
3973 					
3974 					var diff = elem.nodeIndex - last;
3975 					if ( first === 0 ) {
3976 						return diff === 0;
3977 					} else {
3978 						return ( diff % first === 0 && diff / first >= 0 );
3979 					}
3980 			}
3981 		},
3982 		ID: function(elem, match){
3983 			return elem.nodeType === 1 && elem.getAttribute("id") === match;
3984 		},
3985 		TAG: function(elem, match){
3986 			return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
3987 		},
3988 		CLASS: function(elem, match){
3989 			return (" " + (elem.className || elem.getAttribute("class")) + " ")
3990 				.indexOf( match ) > -1;
3991 		},
3992 		ATTR: function(elem, match){
3993 			var name = match[1],
3994 				result = Expr.attrHandle[ name ] ?
3995 					Expr.attrHandle[ name ]( elem ) :
3996 					elem[ name ] != null ?
3997 						elem[ name ] :
3998 						elem.getAttribute( name ),
3999 				value = result + "",
4000 				type = match[2],
4001 				check = match[4];
4002 
4003 			return result == null ?
4004 				type === "!=" :
4005 				type === "=" ?
4006 				value === check :
4007 				type === "*=" ?
4008 				value.indexOf(check) >= 0 :
4009 				type === "~=" ?
4010 				(" " + value + " ").indexOf(check) >= 0 :
4011 				!check ?
4012 				value && result !== false :
4013 				type === "!=" ?
4014 				value !== check :
4015 				type === "^=" ?
4016 				value.indexOf(check) === 0 :
4017 				type === "$=" ?
4018 				value.substr(value.length - check.length) === check :
4019 				type === "|=" ?
4020 				value === check || value.substr(0, check.length + 1) === check + "-" :
4021 				false;
4022 		},
4023 		POS: function(elem, match, i, array){
4024 			var name = match[2], filter = Expr.setFilters[ name ];
4025 
4026 			if ( filter ) {
4027 				return filter( elem, i, match, array );
4028 			}
4029 		}
4030 	}
4031 };
4032 
4033 var origPOS = Expr.match.POS;
4034 
4035 for ( var type in Expr.match ) {
4036 	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
4037 	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
4038 }
4039 
4040 var makeArray = function(array, results) {
4041 	array = Array.prototype.slice.call( array, 0 );
4042 
4043 	if ( results ) {
4044 		results.push.apply( results, array );
4045 		return results;
4046 	}
4047 	
4048 	return array;
4049 };
4050 
4051 // Perform a simple check to determine if the browser is capable of
4052 // converting a NodeList to an array using builtin methods.
4053 try {
4054 	Array.prototype.slice.call( document.documentElement.childNodes, 0 );
4055 
4056 // Provide a fallback method if it does not work
4057 } catch(e){
4058 	makeArray = function(array, results) {
4059 		var ret = results || [];
4060 
4061 		if ( toString.call(array) === "[object Array]" ) {
4062 			Array.prototype.push.apply( ret, array );
4063 		} else {
4064 			if ( typeof array.length === "number" ) {
4065 				for ( var i = 0, l = array.length; i < l; i++ ) {
4066 					ret.push( array[i] );
4067 				}
4068 			} else {
4069 				for ( var i = 0; array[i]; i++ ) {
4070 					ret.push( array[i] );
4071 				}
4072 			}
4073 		}
4074 
4075 		return ret;
4076 	};
4077 }
4078 
4079 var sortOrder;
4080 
4081 if ( document.documentElement.compareDocumentPosition ) {
4082 	sortOrder = function( a, b ) {
4083 		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
4084 			if ( a == b ) {
4085 				hasDuplicate = true;
4086 			}
4087 			return a.compareDocumentPosition ? -1 : 1;
4088 		}
4089 
4090 		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
4091 		if ( ret === 0 ) {
4092 			hasDuplicate = true;
4093 		}
4094 		return ret;
4095 	};
4096 } else if ( "sourceIndex" in document.documentElement ) {
4097 	sortOrder = function( a, b ) {
4098 		if ( !a.sourceIndex || !b.sourceIndex ) {
4099 			if ( a == b ) {
4100 				hasDuplicate = true;
4101 			}
4102 			return a.sourceIndex ? -1 : 1;
4103 		}
4104 
4105 		var ret = a.sourceIndex - b.sourceIndex;
4106 		if ( ret === 0 ) {
4107 			hasDuplicate = true;
4108 		}
4109 		return ret;
4110 	};
4111 } else if ( document.createRange ) {
4112 	sortOrder = function( a, b ) {
4113 		if ( !a.ownerDocument || !b.ownerDocument ) {
4114 			if ( a == b ) {
4115 				hasDuplicate = true;
4116 			}
4117 			return a.ownerDocument ? -1 : 1;
4118 		}
4119 
4120 		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
4121 		aRange.setStart(a, 0);
4122 		aRange.setEnd(a, 0);
4123 		bRange.setStart(b, 0);
4124 		bRange.setEnd(b, 0);
4125 		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
4126 		if ( ret === 0 ) {
4127 			hasDuplicate = true;
4128 		}
4129 		return ret;
4130 	};
4131 }
4132 
4133 // Utility function for retreiving the text value of an array of DOM nodes
4134 function getText( elems ) {
4135 	var ret = "", elem;
4136 
4137 	for ( var i = 0; elems[i]; i++ ) {
4138 		elem = elems[i];
4139 
4140 		// Get the text from text nodes and CDATA nodes
4141 		if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
4142 			ret += elem.nodeValue;
4143 
4144 		// Traverse everything else, except comment nodes
4145 		} else if ( elem.nodeType !== 8 ) {
4146 			ret += getText( elem.childNodes );
4147 		}
4148 	}
4149 
4150 	return ret;
4151 }
4152 
4153 // Check to see if the browser returns elements by name when
4154 // querying by getElementById (and provide a workaround)
4155 (function(){
4156 	// We're going to inject a fake input element with a specified name
4157 	var form = document.createElement("div"),
4158 		id = "script" + (new Date).getTime();
4159 	form.innerHTML = "<a name='" + id + "'/>";
4160 
4161 	// Inject it into the root element, check its status, and remove it quickly
4162 	var root = document.documentElement;
4163 	root.insertBefore( form, root.firstChild );
4164 
4165 	// The workaround has to do additional checks after a getElementById
4166 	// Which slows things down for other browsers (hence the branching)
4167 	if ( document.getElementById( id ) ) {
4168 		Expr.find.ID = function(match, context, isXML){
4169 			if ( typeof context.getElementById !== "undefined" && !isXML ) {
4170 				var m = context.getElementById(match[1]);
4171 				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
4172 			}
4173 		};
4174 
4175 		Expr.filter.ID = function(elem, match){
4176 			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
4177 			return elem.nodeType === 1 && node && node.nodeValue === match;
4178 		};
4179 	}
4180 
4181 	root.removeChild( form );
4182 	root = form = null; // release memory in IE
4183 })();
4184 
4185 (function(){
4186 	// Check to see if the browser returns only elements
4187 	// when doing getElementsByTagName("*")
4188 
4189 	// Create a fake element
4190 	var div = document.createElement("div");
4191 	div.appendChild( document.createComment("") );
4192 
4193 	// Make sure no comments are found
4194 	if ( div.getElementsByTagName("*").length > 0 ) {
4195 		Expr.find.TAG = function(match, context){
4196 			var results = context.getElementsByTagName(match[1]);
4197 
4198 			// Filter out possible comments
4199 			if ( match[1] === "*" ) {
4200 				var tmp = [];
4201 
4202 				for ( var i = 0; results[i]; i++ ) {
4203 					if ( results[i].nodeType === 1 ) {
4204 						tmp.push( results[i] );
4205 					}
4206 				}
4207 
4208 				results = tmp;
4209 			}
4210 
4211 			return results;
4212 		};
4213 	}
4214 
4215 	// Check to see if an attribute returns normalized href attributes
4216 	div.innerHTML = "<a href='#'></a>";
4217 	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
4218 			div.firstChild.getAttribute("href") !== "#" ) {
4219 		Expr.attrHandle.href = function(elem){
4220 			return elem.getAttribute("href", 2);
4221 		};
4222 	}
4223 
4224 	div = null; // release memory in IE
4225 })();
4226 
4227 if ( document.querySelectorAll ) {
4228 	(function(){
4229 		var oldSizzle = Sizzle, div = document.createElement("div");
4230 		div.innerHTML = "<p class='TEST'></p>";
4231 
4232 		// Safari can't handle uppercase or unicode characters when
4233 		// in quirks mode.
4234 		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
4235 			return;
4236 		}
4237 	
4238 		Sizzle = function(query, context, extra, seed){
4239 			context = context || document;
4240 
4241 			// Only use querySelectorAll on non-XML documents
4242 			// (ID selectors don't work in non-HTML documents)
4243 			if ( !seed && context.nodeType === 9 && !isXML(context) ) {
4244 				try {
4245 					return makeArray( context.querySelectorAll(query), extra );
4246 				} catch(e){}
4247 			}
4248 		
4249 			return oldSizzle(query, context, extra, seed);
4250 		};
4251 
4252 		for ( var prop in oldSizzle ) {
4253 			Sizzle[ prop ] = oldSizzle[ prop ];
4254 		}
4255 
4256 		div = null; // release memory in IE
4257 	})();
4258 }
4259 
4260 (function(){
4261 	var div = document.createElement("div");
4262 
4263 	div.innerHTML = "<div class='test e'></div><div class='test'></div>";
4264 
4265 	// Opera can't find a second classname (in 9.6)
4266 	// Also, make sure that getElementsByClassName actually exists
4267 	if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
4268 		return;
4269 	}
4270 
4271 	// Safari caches class attributes, doesn't catch changes (in 3.2)
4272 	div.lastChild.className = "e";
4273 
4274 	if ( div.getElementsByClassName("e").length === 1 ) {
4275 		return;
4276 	}
4277 	
4278 	Expr.order.splice(1, 0, "CLASS");
4279 	Expr.find.CLASS = function(match, context, isXML) {
4280 		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
4281 			return context.getElementsByClassName(match[1]);
4282 		}
4283 	};
4284 
4285 	div = null; // release memory in IE
4286 })();
4287 
4288 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4289 	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4290 		var elem = checkSet[i];
4291 		if ( elem ) {
4292 			elem = elem[dir];
4293 			var match = false;
4294 
4295 			while ( elem ) {
4296 				if ( elem.sizcache === doneName ) {
4297 					match = checkSet[elem.sizset];
4298 					break;
4299 				}
4300 
4301 				if ( elem.nodeType === 1 && !isXML ){
4302 					elem.sizcache = doneName;
4303 					elem.sizset = i;
4304 				}
4305 
4306 				if ( elem.nodeName.toLowerCase() === cur ) {
4307 					match = elem;
4308 					break;
4309 				}
4310 
4311 				elem = elem[dir];
4312 			}
4313 
4314 			checkSet[i] = match;
4315 		}
4316 	}
4317 }
4318 
4319 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
4320 	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
4321 		var elem = checkSet[i];
4322 		if ( elem ) {
4323 			elem = elem[dir];
4324 			var match = false;
4325 
4326 			while ( elem ) {
4327 				if ( elem.sizcache === doneName ) {
4328 					match = checkSet[elem.sizset];
4329 					break;
4330 				}
4331 
4332 				if ( elem.nodeType === 1 ) {
4333 					if ( !isXML ) {
4334 						elem.sizcache = doneName;
4335 						elem.sizset = i;
4336 					}
4337 					if ( typeof cur !== "string" ) {
4338 						if ( elem === cur ) {
4339 							match = true;
4340 							break;
4341 						}
4342 
4343 					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
4344 						match = elem;
4345 						break;
4346 					}
4347 				}
4348 
4349 				elem = elem[dir];
4350 			}
4351 
4352 			checkSet[i] = match;
4353 		}
4354 	}
4355 }
4356 
4357 var contains = document.compareDocumentPosition ? function(a, b){
4358 	return a.compareDocumentPosition(b) & 16;
4359 } : function(a, b){
4360 	return a !== b && (a.contains ? a.contains(b) : true);
4361 };
4362 
4363 var isXML = function(elem){
4364 	// documentElement is verified for cases where it doesn't yet exist
4365 	// (such as loading iframes in IE - #4833) 
4366 	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
4367 	return documentElement ? documentElement.nodeName !== "HTML" : false;
4368 };
4369 
4370 var posProcess = function(selector, context){
4371 	var tmpSet = [], later = "", match,
4372 		root = context.nodeType ? [context] : context;
4373 
4374 	// Position selectors must be done after the filter
4375 	// And so must :not(positional) so we move all PSEUDOs to the end
4376 	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
4377 		later += match[0];
4378 		selector = selector.replace( Expr.match.PSEUDO, "" );
4379 	}
4380 
4381 	selector = Expr.relative[selector] ? selector + "*" : selector;
4382 
4383 	for ( var i = 0, l = root.length; i < l; i++ ) {
4384 		Sizzle( selector, root[i], tmpSet );
4385 	}
4386 
4387 	return Sizzle.filter( later, tmpSet );
4388 };
4389 
4390 // Add Sizzle to Glow
4391 // This file is injected into sizzle.js by the ant "deps" target
4392 Glow.provide(function(glow) {
4393 	glow._sizzle = Sizzle;
4394 });
4395 
4396 return;
4397 
4398 
4399 window.Sizzle = Sizzle;
4400 
4401 })();
4402 Glow.provide(function(glow) {
4403 	var NodeListProto = glow.NodeList.prototype
4404 	/*
4405 		PrivateVar: ucheck
4406 			Used by unique(), increased by 1 on each use
4407 		*/
4408 		,	ucheck = 1
4409 	/*
4410 		PrivateVar: ucheckPropName
4411 			This is the property name used by unique checks
4412 		*/
4413 
4414 	, ucheckPropName = "_unique" + glow.UID;
4415 	/*
4416 		PrivateMethod: unique
4417 			Get an array of nodes without duplicate nodes from an array of nodes.
4418 
4419 		Arguments:
4420 			aNodes - (Array|<NodeList>)
4421 
4422 		Returns:
4423 			An array of nodes without duplicates.
4424 		*/
4425 		//worth checking if it's an XML document?
4426 		if (glow.env.ie) {
4427 			var unique = function(aNodes) {
4428 				if (aNodes.length == 1) { return aNodes; }
4429 
4430 				//remove duplicates
4431 				var r = [],
4432 					ri = 0,
4433 					i = 0;
4434 
4435 				for (; aNodes[i]; i++) {
4436 					if (aNodes[i].getAttribute(ucheckPropName) != ucheck && aNodes[i].nodeType == 1) {
4437 						r[ri++] = aNodes[i];
4438 					}
4439 					aNodes[i].setAttribute(ucheckPropName, ucheck);
4440 				}
4441 				for (i=0; aNodes[i]; i++) {
4442 					aNodes[i].removeAttribute(ucheckPropName);
4443 				}
4444 				ucheck++;
4445 				return r;
4446 			}
4447 		} else {
4448 			var unique = function(aNodes) {
4449 				if (aNodes.length == 1) { return aNodes; }
4450 
4451 				//remove duplicates
4452 				var r = [],
4453 					ri = 0,
4454 					i = 0;
4455 
4456 				for (; aNodes[i]; i++) {
4457 					if (aNodes[i][ucheckPropName] != ucheck && aNodes[i].nodeType == 1) {
4458 						r[ri++] = aNodes[i];
4459 					}
4460 					aNodes[i][ucheckPropName] = ucheck;
4461 				}
4462 				ucheck++;
4463 				return r;
4464 			}
4465 		};
4466 	/**
4467 	@name glow.NodeList#parent
4468 	@function
4469 	@description Gets the unique parent nodes of each node as a new NodeList.
4470 	@param {string | HTMLElement | NodeList} [search] Search value
4471 		If provided, will seek the next parent element until a match is found
4472 	@returns {glow.NodeList}
4473 
4474 		Returns a new NodeList containing the parent nodes, with
4475 		duplicates removed
4476 
4477 	@example
4478 		// elements which contain links
4479 		var parents = glow.dom.get("a").parent();
4480 	*/
4481 	NodeListProto.parent = function(search) {
4482 		var ret = [],
4483 			ri = 0,
4484 			i = this.length,
4485 			node;
4486 			
4487 		while (i--) {				
4488 			node = this[i];
4489 			if (node.nodeType == 1) {
4490 				if(search){						
4491 					while(node = node.parentNode){											
4492 						if (glow._sizzle.filter(search, [node]).length) {
4493 							ret[ri++] = node;							
4494 							break;
4495 						}							
4496 					}
4497 				}
4498 			
4499 				else if(node = node.parentNode){
4500 						ret[ri++] = node;						
4501 				}
4502 
4503 			}
4504 
4505 		}
4506 				
4507 		return new glow.NodeList(unique(ret));			
4508 	};
4509 	
4510 	/* Private method for prev() and next() */
4511 	function getNextOrPrev(nodelist, dir, search) {
4512 		var ret = [],
4513 			ri = 0,
4514 			node,
4515 			i = 0,
4516 			length = nodelist.length;
4517 
4518 		while (i < length) {			
4519 			node = nodelist[i];			
4520 			if(search){
4521 				while (node = node[dir + 'Sibling']) {					
4522 					if (node.nodeType == 1 && node.nodeName != '!') {						
4523 						if (glow._sizzle.filter(search, [node]).length) {
4524 							ret[ri++] = node;							
4525 							break;
4526 						}					
4527 					}					
4528 				}
4529 			}
4530 			else{
4531 				while (node = node[dir + 'Sibling']) {					
4532 					if (node.nodeType == 1 && node.nodeName != '!') {
4533 							ret[ri++] = node;							
4534 							 break;					
4535 					}					
4536 				}	
4537 			}
4538 		i++;
4539 		}
4540 		return new glow.NodeList(ret);
4541 	}
4542 	
4543 	/**
4544 	@name glow.NodeList#prev
4545 	@function
4546 	@description Gets the previous sibling element for each node in the ElementList.
4547 		If a filter is provided, the previous item that matches the filter is returned, or
4548 		none if no match is found.
4549 	@param {string | HTMLElement | NodeList} [search] Search value
4550 		If provided, will seek the previous sibling element until a match is found
4551 	@returns {glow.ElementList}
4552 		A new ElementList containing the previous sibling elements that match the (optional)
4553 		filter.
4554 	@example
4555 		// gets the element before #myLink (if there is one)
4556 		var next = glow.get("#myLink").prev();
4557 	@example
4558 		// get the previous sibling link element before #skipLink
4559 		glow.get('#skipLink').prev('a')
4560 	*/
4561 	NodeListProto.prev = function(search) {
4562 		return getNextOrPrev(this, 'previous', search);
4563 	};
4564 	
4565 	/**
4566 	@name glow.NodeList#next
4567 	@function
4568 	@description Gets the next sibling element for each node in the ElementList.
4569 		If a filter is provided, the next item that matches the filter is returned, or
4570 		none if no match is found.
4571 	@param {string | HTMLElement | NodeList} [search] Search value
4572 		If provided, will seek the next sibling element until a match is found
4573 	@returns {glow.ElementList}
4574 		A new ElementList containing the next sibling elements that match the (optional)
4575 		filter.
4576 	@example
4577 		// gets the element following #myLink (if there is one)
4578 		var next = glow.get("#myLink").next();
4579 	@example
4580 		// get the next sibling link element after #skipLink
4581 		glow.get('#skipLink').next('a')
4582 	*/
4583 	NodeListProto.next = function(search) {
4584 		return getNextOrPrev(this, 'next', search);	
4585 	};
4586 	
4587 	
4588 	/**
4589 	@name glow.NodeList#get
4590 	@function
4591 	@description Gets decendents of nodes that match a CSS selector.
4592 
4593 	@param {String} selector CSS selector
4594 
4595 	@returns {glow.NodeList}
4596 		Returns a new NodeList containing matched elements
4597 
4598 	@example
4599 		// create a new NodeList
4600 		var myNodeList = glow.dom.create("<div><a href='s.html'>Link</a></div>");
4601 
4602 		// get 'a' tags that are decendants of the NodeList nodes
4603 		myNewNodeList = myNodeList.get("a");
4604 	*/
4605 	NodeListProto.get = function(selector) {
4606 		var ret = [],
4607 			i = this.length;
4608 
4609 		while (i--) {			
4610 			glow._sizzle(selector, this[i], ret);
4611 			
4612 		}
4613 		// need to remove uniqueSorts because they're slow. Replace with own method for unique.
4614 		return new glow.NodeList(unique(ret));
4615 	};
4616 	
4617 	
4618 	
4619 	/**
4620 	@name glow.NodeList#ancestors
4621 	@function
4622 	@description Gets the unique ancestor nodes of each node as a new NodeList.
4623 	@param {Function|string} [filter] Filter test
4624 		If a string is provided, it is used in a call to {@link glow.ElementList#is ElementList#is}.
4625 		If a function is provided it will be passed 2 arguments, the index of the current item,
4626 		and the ElementList being itterated over.
4627 		Inside the function 'this' refers to the HTMLElement.
4628 		Return true to keep the node, or false to remove it.
4629 	@returns {glow.dom.NodeList}
4630 		Returns NodeList
4631 
4632 		@example
4633 		// get ancestor elements for anchor elements 
4634 		var ancestors = glow.dom.get("a").ancestors();
4635 	*/
4636 	NodeListProto.ancestors = function(filter) {
4637 		var ret = [],
4638 			ri = 0,
4639 			i = 0,
4640 			length = this.length,
4641 			node;
4642 					
4643 		while (i < length) {
4644 			node = this[i].parentNode;
4645 					
4646 			while (node && node.nodeType == 1) {							
4647 				ret[ri++] = node;
4648 				node = node.parentNode;
4649 			}								
4650 		i++;
4651 		}
4652 		if(filter){
4653             ret = new glow.NodeList(ret);
4654 			ret = ret.filter(filter);
4655 		}
4656 		return new glow.NodeList(unique(ret));
4657 	};
4658 	
4659 	/*
4660 		Private method to get the child elements for an html node (used by children())
4661 	*/
4662 		function getChildElms(node) {
4663 			var r = [],
4664 				childNodes = node.childNodes,
4665 				i = 0,
4666 				ri = 0;
4667 			
4668 			for (; childNodes[i]; i++) {
4669 				if (childNodes[i].nodeType == 1 && childNodes[i].nodeName != '!') {
4670 					r[ri++] = childNodes[i];
4671 				}
4672 			}
4673 			return r;
4674 		}
4675 	
4676 	/**
4677 	@name glow.NodeList#children
4678 	@function
4679 	@description Gets the child elements of each node as a new NodeList.
4680 
4681 	@returns {glow.dom.NodeList}
4682 
4683 		Returns a new NodeList containing all the child nodes
4684 				
4685 	@example
4686 		// get all list items
4687 		var items = glow.dom.get("ul, ol").children();
4688 	*/
4689 	NodeListProto.children = function() {
4690 		var ret = [],
4691 			i = this.length;
4692 				
4693 		while(i--) {
4694 			ret = ret.concat( getChildElms(this[i]) );
4695 		}
4696 		return new glow.NodeList(ret);	
4697 	};
4698 	
4699 	/**
4700 	@name glow.NodeList#contains
4701 	@function
4702 	@description Find if this NodeList contains the given element
4703 		
4704 	@param {string | HTMLELement | NodeList} Single element to check for
4705 
4706 	@returns {boolean}
4707 		myElementList.contains(elm)
4708 		// Returns true if an element in myElementList contains elm, or IS elm.
4709 	*/
4710 	NodeListProto.contains = function(elm) {
4711 		var i = 0,
4712 			node = new glow.NodeList(elm)[0],
4713 			length = this.length,
4714 			newNodes,
4715 			toTest;
4716 
4717 		// missing some nodes? Return false
4718 		if ( !node || !this.length ) {
4719 			return false;
4720 		}
4721 	
4722 		if (this[0].compareDocumentPosition) { //w3 method
4723 			while (i < length) {
4724 				//break out if the two are teh same
4725 				if(this[i] == node){
4726 					break;
4727 				}
4728 				//check against bitwise to see if node is contained in this
4729 				else if (!(this[i].compareDocumentPosition(node) & 16)) {								
4730 					return false;
4731 				}
4732 			i++;
4733 			}
4734 		}
4735 		else if(node.contains){					
4736 			for (; i < length; i++) {
4737 				if ( !( this[i].contains( node  ) ) ) {
4738 					return false;
4739 				}
4740 			}
4741 		}				
4742 		else { //manual method for last chance corale
4743 			while (i < length) {
4744 				toTest = node;
4745 				while (toTest = toTest.parentNode) {
4746 					if (this[i] == toTest) { break; }
4747 				}
4748 				if (!toTest) {
4749 					return false;
4750 				}
4751 			i++;
4752 			}
4753 		}
4754 			
4755 		return true;
4756 	};
4757 });
4758 Glow.provide(function(glow) {
4759 	var NodeListProto = glow.NodeList.prototype,
4760 		document = window.document,
4761 		undefined;
4762 	
4763 	// create a fragment and insert a set of nodes into it
4764 	function createFragment(nodes) {
4765 		var fragment = document.createDocumentFragment(),
4766 			i = 0,
4767 			node;
4768 		
4769 		while ( node = nodes[i++] ) {
4770 			fragment.appendChild(node);
4771 		}
4772 		
4773 		return fragment;
4774 	}
4775 	
4776 	// generate the #before and #after methods
4777 	// after: 1 for #(insert)after, 0 for #(insert)before
4778 	// insert: 1 for #insert(After|Before), 0 for #(after|before)
4779 	function afterAndBefore(after, insert) {
4780 		return function(elements) {
4781 			var toAddList,
4782 				toAddToList,
4783 				fragmentToAdd,
4784 				nextFragmentToAdd,
4785 				item,
4786 				itemParent;
4787 			
4788 			if (!this.length) { return this; }
4789 			
4790 			// normalise 'elements'
4791 			// if we're dealing with append/prepend then strings are always treated as HTML strings
4792 			if (!insert && typeof elements === 'string') {
4793 				elements = new glow.NodeList( glow.NodeList._strToNodes(elements) );
4794 			}
4795 			else {
4796 				elements = new glow.NodeList(elements);
4797 			}
4798 			
4799 			// set the element we're going to add to, and the elements we're going to add
4800 			if (insert) {
4801 				toAddToList = elements;
4802 				toAddList = new glow.NodeList(this);
4803 			}
4804 			else {
4805 				toAddToList = this;
4806 				toAddList = elements;
4807 			}
4808 			
4809 			nextFragmentToAdd = createFragment(toAddList);
4810 			
4811 			for (var i = 0, leni = toAddToList.length, lasti = leni - 1; i < leni; i++) {
4812 				item = toAddToList[i];
4813 				fragmentToAdd = nextFragmentToAdd;
4814 				
4815 				// we can only append after if the element has a parent right?
4816 				if (itemParent = item.parentNode) {
4817 					if (i !== lasti) { // if not the last item
4818 						nextFragmentToAdd = fragmentToAdd.cloneNode(true);
4819 						insert && toAddList.push(nextFragmentToAdd.childNodes);
4820 					}
4821 					itemParent.insertBefore(fragmentToAdd, after ? item.nextSibling : item);
4822 				}
4823 			}
4824 			
4825 			return insert ? toAddList : toAddToList;
4826 		}
4827 	}
4828 	
4829 	// generate the #append, #appendTo, #prepend and #prependTo methods
4830 	// append: 1 for #append(To), 0 for #prepend(To)
4831 	// to: 1 for #(append|prepend)To, 0 for #(append|prepend)
4832 	function appendAndPrepend(append, to) {
4833 		return function(elements) {
4834 			var toAddList,
4835 				toAddToList,
4836 				fragmentToAdd,
4837 				nextFragmentToAdd,
4838 				item;
4839 			
4840 			if (!this.length) { return this; }
4841 			
4842 			// normalise 'elements'
4843 			// if we're dealing with append/prepend then strings are always treated as HTML strings
4844 			if (!to && typeof elements === 'string') {
4845 				elements = new glow.NodeList( glow.NodeList._strToNodes(elements) );
4846 			}
4847 			else {
4848 				elements = new glow.NodeList(elements);
4849 			}
4850 				
4851 			// set the element we're going to add to, and the elements we're going to add
4852 			if (to) {
4853 				toAddToList = elements;
4854 				toAddList = new glow.NodeList(this);
4855 			}
4856 			else {
4857 				toAddToList = this;
4858 				toAddList = elements;
4859 			}
4860 			
4861 			nextFragmentToAdd = createFragment(toAddList);
4862 			
4863 			for (var i = 0, leni = toAddToList.length, lasti = leni - 1; i < leni; i++) {
4864 				item = toAddToList[i];
4865 				fragmentToAdd = nextFragmentToAdd;
4866 				
4867 				// avoid trying to append to non-elements
4868 				if (item.nodeType === 1) {
4869 					if (i !== lasti) { // if not the last item
4870 						nextFragmentToAdd = fragmentToAdd.cloneNode(true);
4871 						// add the clones to the return element for appendTo / prependTo
4872 						to && toAddList.push(nextFragmentToAdd.childNodes);
4873 					}
4874 					item.insertBefore(fragmentToAdd, append ? null : item.firstChild);
4875 				}
4876 			}
4877 			
4878 			return to ? toAddList : toAddToList;
4879 		}
4880 	}
4881 	
4882 	/**
4883 		@name glow.NodeList#after
4884 		@function
4885 		@description Insert node(s) after each node in this NodeList.
4886 			If there is more than one node in this NodeList, 'nodes'
4887 			will be inserted after the first element and clones will be
4888 			inserted after each subsequent element.
4889 			
4890 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert
4891 			Strings will be treated as HTML strings.
4892 		
4893 		@returns {glow.NodeList} Original NodeList
4894 		
4895 		@example
4896 			// adds a paragraph after each heading
4897 			glow('h1, h2, h3').after('<p>That was a nice heading.</p>');
4898 	*/
4899 	NodeListProto.after = afterAndBefore(1);
4900 	
4901 	/**
4902 		@name glow.NodeList#before
4903 		@function
4904 		@description Insert node(s) before each node in this NodeList.
4905 			If there is more than one node in this NodeList, 'nodes'
4906 			will be inserted before the first element and clones will be
4907 			inserted before each subsequent element.
4908 			
4909 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert
4910 			Strings will be treated as HTML strings.
4911 		
4912 		@returns {glow.NodeList} Original NodeList
4913 		
4914 		@example
4915 			// adds a div before each paragraph
4916 			glow('p').before('<div>Here comes a paragraph!</div>');
4917 	*/
4918 	NodeListProto.before = afterAndBefore(0);
4919 	
4920 	/**
4921 		@name glow.NodeList#append
4922 		@function
4923 		@description Appends node to each node in this NodeList.
4924 			If there is more than one node in this NodeList, then the given nodes
4925 			are appended to the first node and clones are appended to the other
4926 			nodes.
4927 			
4928 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Nodes(s) to append
4929 			Strings will be treated as HTML strings.
4930 		
4931 		@returns {glow.NodeList} Original NodeList
4932 		
4933 		@example
4934 			// ends every paragraph with '...'
4935 			glow('p').append('<span>...</span>');
4936 	*/
4937 	NodeListProto.append = appendAndPrepend(1);
4938 	
4939 	/**
4940 		@name glow.NodeList#prepend
4941 		@function
4942 		@description Prepends nodes to each node in this NodeList.
4943 			If there is more than one node in this NodeList, then the given nodes
4944 			are prepended to the first node and clones are prepended to the other
4945 			nodes.
4946 			
4947 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Nodes(s) to prepend
4948 			Strings will be treated as HTML strings.
4949 		
4950 		@returns {glow.NodeList} Original NodeList
4951 		
4952 		@example
4953 			// prepends every paragraph with 'Paragraph: '
4954 			glow('p').prepend('<span>Paragraph: </span>');
4955 	*/
4956 	NodeListProto.prepend = appendAndPrepend(0);
4957 	
4958 	/**
4959 		@name glow.NodeList#appendTo
4960 		@function
4961 		@description Appends nodes in this NodeList to given node(s)
4962 			If appending to more than one node, the NodeList is appended
4963 			to the first node and clones are appended to the others.
4964 			
4965 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} node Node(s) to append to.
4966 			Strings will be treated as CSS selectors or HTML strings.
4967 		
4968 		@returns {glow.NodeList} The appended nodes.
4969 		
4970 		@example
4971 			// appends '...' to every paragraph
4972 			glow('<span>...</span>').appendTo('p');
4973 	*/
4974 	NodeListProto.appendTo = appendAndPrepend(1, 1);
4975 
4976 	/**
4977 		@name glow.NodeList#prependTo
4978 		@function
4979 		@description Prepends nodes in this NodeList to given node(s)
4980 			If prepending to more than one node, the NodeList is prepended
4981 			to the first node and clones are prepended to the others.
4982 			
4983 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} node Node(s) to prepend to
4984 			Strings will be treated as CSS selectors or HTML strings.
4985 		
4986 		@returns {glow.NodeList} The prepended nodes.
4987 		
4988 		@example
4989 			// prepends 'Paragraph: ' to every paragraph
4990 			glow('<span>Paragraph: </span>').prependTo('p');
4991 	*/
4992 	NodeListProto.prependTo = appendAndPrepend(0, 1);
4993 	
4994 	/**
4995 		@name glow.NodeList#insertAfter
4996 		@function
4997 		@description Insert this NodeList after the given nodes
4998 			If inserting after more than one node, the NodeList is inserted
4999 			after the first node and clones are inserted after the others.
5000 			
5001 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert after
5002 			Strings will be treated as CSS selectors.
5003 			
5004 		@returns {glow.NodeList} Inserted nodes.
5005 		
5006 		@example
5007 			// adds a paragraph after each heading
5008 			glow('<p>HAI!</p>').insertAfter('h1, h2, h3');
5009 	*/
5010 	NodeListProto.insertAfter = afterAndBefore(1, 1);
5011 	
5012 	/**
5013 		@name glow.NodeList#insertBefore
5014 		@function
5015 		@description Insert this NodeList before the given nodes
5016 			If inserting before more than one node, the NodeList is inserted
5017 			before the first node and clones are inserted before the others.
5018 			
5019 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert before
5020 			Strings will be treated as CSS selectors.
5021 			
5022 		@returns {glow.NodeList} Inserted nodes.
5023 		
5024 		@example
5025 			// adds a div before each paragraph
5026 			glow('<div>Here comes a paragraph!</div>').insertBefore('p');
5027 	*/
5028 	NodeListProto.insertBefore = afterAndBefore(0, 1);
5029 	
5030 	/**
5031 		@name glow.NodeList#destroy
5032 		@function
5033 		@description Removes each element from the document
5034 			The element, attached listeners & attached data will be
5035 			destroyed to free up memory.
5036 			
5037 			Detroyed elements may not be reused in some browsers.
5038 			
5039 		@returns {glow.NodeList} An empty NodeList
5040 		
5041 		@example
5042 			// destroy all links in the document
5043 			glow("a").destroy();
5044 	*/
5045 	var tmpDiv = document.createElement('div');
5046 	
5047 	NodeListProto.destroy = function() {
5048 		glow.NodeList._destroyData(this);
5049 		this.appendTo(tmpDiv);
5050 		tmpDiv.innerHTML = '';
5051 		return new glow.NodeList();
5052 	};
5053 	
5054 	/**
5055 		@name glow.NodeList#remove
5056 		@function
5057 		@description Removes each element from the document
5058 			If you no longer need the elements, consider using
5059 			{@link glow.NodeList#destroy destroy}
5060 			
5061 		@returns {glow.NodeList} The removed elements
5062 
5063 		@example
5064 			// take all the links out of a document
5065 			glow("a").remove();
5066 	*/
5067 	NodeListProto.remove = function() {
5068 		var parent,
5069 			node,
5070 			i = this.length;
5071 		
5072 		while (i--) {
5073 			node = this[i];
5074 			if (parent = node.parentNode) {
5075 				parent.removeChild(node);
5076 			}
5077 		}
5078 		
5079 		return this;
5080 	};
5081 	
5082 	/**
5083 		@name glow.NodeList#empty
5084 		@function
5085 		@description Removes the nodes' contents
5086 
5087 		@returns {glow.NodeList} Original nodes
5088 
5089 		@example
5090 			// remove the contents of all textareas
5091 			glow("textarea").empty();
5092 	*/
5093 	// TODO: is this shortcut worth doing?
5094 	NodeListProto.empty = glow.env.ie ?
5095 		// When you clean an element out using innerHTML it destroys its inner text nodes in IE8 and below
5096 		// Here's an alternative method for IE:
5097 		function() {
5098 			var i = this.length, node, child;
5099 			
5100 			while (i--) {
5101 				node = this[i];
5102 				while (child = node.firstChild) {
5103 					node.removeChild(child);
5104 				}
5105 			}
5106 			
5107 			return this;
5108 		} :
5109 		// method for most browsers
5110 		function() {
5111 			var i = this.length;
5112 			
5113 			while (i--) {
5114 				this[i].innerHTML = '';
5115 			}
5116 			
5117 			return this;
5118 		}
5119 
5120 	/**
5121 		@name glow.NodeList#replaceWith
5122 		@function
5123 		@description Replace elements with another
5124 		
5125 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} elements Element(s) to insert into the document
5126 			If there is more than one element in the NodeList, then the given elements
5127 			replace the first element, clones are appended to the other	elements.
5128 			
5129 		@returns {glow.NodeList} The replaced elements
5130 			Call {@link glow.NodeList#destroy destroy} on these if you
5131 			no longer need them.
5132 	*/
5133 	NodeListProto.replaceWith = function(elements) {
5134 		return this.after(elements).remove();
5135 	};
5136 	
5137 	/**
5138 		@name glow.NodeList#wrap
5139 		@function
5140 		@description Wraps the given NodeList with the specified element(s).
5141 			The given NodeList items will always be placed in the first
5142 			child element that contains no further elements.
5143 			
5144 			Each item in a given NodeList will be wrapped individually.
5145 		
5146 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} wrapper Element to use as a wrapper
5147 			Strings will be treated as HTML strings if they begin with <, else
5148 			they'll be treated as a CSS selector.
5149 		
5150 		@returns {glow.NodeList} The NodeList with new wrapper parents
5151 			
5152 		@example
5153 			// <span id="mySpan">Hello</span>
5154 			glow("#mySpan").wrap("<div><p></p></div>");
5155 			// Makes:
5156 			// <div>
5157 			//     <p>
5158 			//         <span id="mySpan">Hello</span>
5159 			//     </p>
5160 			// </div>
5161 			
5162 	*/
5163 	// get first child element node of an element, otherwise undefined
5164 	function getFirstChildElm(parent) {					
5165 		for (var child = parent.firstChild; child; child = child.nextSibling) {
5166 			if (child.nodeType == 1) {
5167 				return child;
5168 			}			
5169 		}			
5170 		return undefined;			
5171 	}
5172 	
5173 	NodeListProto.wrap = function(wrapper) {
5174 		// normalise input
5175 		wrapper = new glow.NodeList(wrapper);
5176 		
5177 		// escape if the wraper is non-existant or not an element
5178 		if (!wrapper[0] || wrapper[0].nodeType != 1) {
5179 			return this;
5180 		}
5181 		
5182 		var toWrap,
5183 			toWrapTarget,
5184 			firstChildElm;
5185 		
5186 		for (var i = 0, leni = this.length; i<leni; i++) {
5187 			toWrap = this[i];
5188 			// get target element to insert toWrap in
5189 			toWrapTarget = wrapper[0];
5190 			
5191 			while (toWrapTarget) {
5192 				firstChildElm = getFirstChildElm(toWrapTarget);
5193 					
5194 				if (!firstChildElm) {
5195 					break;
5196 				}
5197 				toWrapTarget = firstChildElm;
5198 			}
5199 			
5200 			if (toWrap.parentNode) {						
5201 				wrapper.insertBefore(toWrap);													
5202 			}
5203 			
5204 			// If wrapping multiple nodes, we need to take a clean copy of the wrapping nodes
5205 			if (i != leni-1) {
5206 				wrapper = wrapper.clone();
5207 			}
5208 			
5209 			toWrapTarget.appendChild(toWrap);
5210 		}
5211 		
5212 		return this;
5213 	};
5214 	
5215 	/**
5216 		@name glow.NodeList#unwrap
5217 		@function
5218 		@description Removes the parent of each item in the list
5219 		
5220 		@returns {glow.NodeList} The now unwrapped elements
5221 		
5222 		@example
5223 			// Before: <div><p><span id="mySpan">Hello</span></p></div>
5224 			// unwrap the given element
5225 			glow("#mySpan").unwrap();
5226 			// After: <div><span id="mySpan">Hello</span></div>
5227 	*/
5228 	NodeListProto.unwrap = function() {
5229 		var parentToRemove,
5230 			childNodes,
5231 			// get unique parents
5232 			parentsToRemove = this.parent();
5233 		
5234 		for (var i = 0, leni = parentsToRemove.length; i < leni; i++) {				
5235 			parentToRemove = parentsToRemove.slice(i, i+1);
5236 			// make sure we get all children, including text nodes
5237 			childNodes = new glow.NodeList( parentToRemove[0].childNodes );
5238 			
5239 			// if the item we're removing has no new parent (i.e. is not in document), then we just remove the child and destroy the old parent
5240 			if (!parentToRemove[0].parentNode){
5241 				childNodes.remove();
5242 				parentToRemove.destroy();
5243 			}
5244 			else {
5245 				childNodes.insertBefore(parentToRemove);
5246 				parentToRemove.destroy();							
5247 			}
5248 		}
5249 		return this;
5250 	};
5251 	
5252 	/**
5253 		@name glow.NodeList#clone
5254 		@function
5255 		@description Clones each node in the NodeList, along with data & event listeners
5256 		
5257 		@returns {glow.NodeList}
5258 			Returns a new NodeList containing clones of all the nodes in
5259 			the NodeList
5260 		
5261 		@example
5262 			// get a copy of all heading elements
5263 			var myClones = glow.get("h1, h2, h3, h4, h5, h6").clone();
5264 	*/
5265 	NodeListProto.clone = function() {
5266 		var nodes = [],
5267 			eventIdProp = '__eventId' + glow.UID,
5268 			i = this.length;
5269 		
5270 		
5271 		while (i--) {
5272 			nodes[i] = this[i].cloneNode(true);
5273 			// some browsers (ie) also clone node properties as attributes
5274 			// we need to get rid of the eventId.
5275 			allCloneElms = new glow.NodeList( nodes ).get("*").push( nodes );
5276 			j = allCloneElms.length;
5277 				while(j--) {
5278 					nodes[i][eventIdProp] = null;
5279 					
5280 					// now copy over the data and events
5281 					glow.events._copyEvent(this[i], nodes[i]);
5282 					glow.NodeList._copyData(this[i], nodes[i]);
5283 				}
5284 			
5285 		}
5286 		// some browsers (ie) also clone node properties as attributes
5287 		// we need to get rid of the eventId.
5288 		//allCloneElms = new glow.NodeList( nodes ).get("*").push( nodes );
5289 		//j = allCloneElms.length;
5290 		
5291 		//while(j--) {
5292 			//allCloneElms[j][eventIdProp] = null;
5293 		//}
5294 		
5295 		
5296 				
5297 		// copy data from base elements to clone elements
5298 		/*allBaseElms = this.get("*").push( this );
5299 		i = allCloneElms.length;
5300 		while (i--) {
5301 			allCloneElms[i].removeAttribute(dataPropName);
5302 			glow.dom.get(allCloneElms[i]).data(
5303 			glow.dom.get(allBaseElms[i]).data()
5304 			);
5305 		}*/
5306 		
5307 		return new glow.NodeList(nodes);
5308 	};
5309 	
5310 	
5311 	/**
5312 		@name glow.NodeList#copy
5313 		@function
5314 		@description Copies each node in the NodeList, excluding data & event listeners
5315 		
5316 		@returns {glow.NodeList}
5317 			Returns a new NodeList containing copies of all the nodes in
5318 			the NodeList
5319 		
5320 		@example
5321 			// get a copy of all heading elements
5322 			var myCopies = glow.get("h1, h2, h3, h4, h5, h6").copy();
5323 	*/
5324 	NodeListProto.copy = function() {
5325 		var nodes = [],
5326 			i = this.length;
5327 		
5328 		while (i--) {
5329 			nodes[i] = this[i].cloneNode(true);
5330 		}
5331 		
5332 		return new glow.NodeList(nodes);
5333 	};
5334 	
5335 	/**
5336 		@name glow.NodeList#html
5337 		@function
5338 		@description Gets / sets HTML content
5339 			Either gets content of the first element, or sets the content
5340 			for all elements in the list
5341 			
5342 		@param {String} [htmlString] String to set as the HTML of elements
5343 			If omitted, the html for the first element in the list is
5344 			returned.
5345 		
5346 		@returns {glow.NodeList | string}
5347 			Returns the original NodeList when setting,
5348 			or the HTML content when getting.
5349 			
5350 		@example
5351 			// get the html in #footer
5352 			var footerContents = glow("#footer").html();
5353 			
5354 		@example
5355 			// set a new footer
5356 			glow("#footer").html("<strong>Hello World!</strong>");
5357 	*/
5358 	NodeListProto.html = function(htmlString) {
5359 		// getting
5360 		if (!arguments.length) {
5361 			return this[0] ? this[0].innerHTML : '';
5362 		}
5363 		
5364 		// setting
5365 		var i = this.length,
5366 			node;
5367 		
5368 		// normalise the string
5369 		htmlString = htmlString ? String(htmlString): '';
5370 		
5371 		while (i--) {
5372 			node = this[i];
5373 			if (node.nodeType == 1) {
5374 				try {
5375 					// this has a habit of failing in IE for some elements
5376 					node.innerHTML = htmlString;
5377 				}
5378 				catch (e) {
5379 					new glow.NodeList(node).empty().append(htmlString);
5380 				}
5381 			}
5382 		}
5383 		
5384 		return this;
5385 	};
5386 	
5387 	/**
5388 		@name glow.NodeList#text
5389 		@function
5390 		@description Gets / set the text content
5391 			Either gets content of the first element, or sets the content
5392 			for all elements in the list
5393 		
5394 		@param {String} [text] String to set as the text of elements
5395 			If omitted, the test for the first element in the list is
5396 			returned.
5397 		
5398 		@returns {glow.NodeList | String}
5399 			Returns the original NodeList when setting,
5400 			or the text content when getting.
5401 
5402 		@example
5403 			// set text
5404 			var div = glow("<div></div>").text("Fun & games!");
5405 			// <div>Func & games!</div>
5406 			
5407 		@example
5408 			// get text
5409 			var mainHeading = glow('#mainHeading').text();
5410 	*/
5411 	NodeListProto.text = function(textString) {
5412 		var firstNode = this[0],
5413 			i = this.length,
5414 			node;
5415 		
5416 		// getting
5417 		if (!arguments.length) {
5418 			// get the text by checking a load of properties in priority order
5419 			return firstNode ?
5420 				firstNode.textContent ||
5421 				firstNode.innerText ||
5422 				firstNode.nodeValue || '' // nodeValue for comment & text nodes
5423 				: '';
5424 		}
5425 		
5426 		// setting
5427 		// normalise the string
5428 		textString = textString ? String(textString): '';
5429 		
5430 		this.empty();
5431 		while (i--) {
5432 			node = this[i];
5433 			if (node.nodeType == 1) {
5434 				node.appendChild( document.createTextNode(textString) );
5435 			}
5436 			else {
5437 				node.nodeValue = textString;
5438 			}
5439 		}
5440 		
5441 		return this;
5442 	};
5443 });
5444 Glow.provide(function(glow) {
5445 	var NodeListProto = glow.NodeList.prototype,
5446 		doc = document,	
5447 		win = window;
5448 			
5449 	/********************************PRIVATE METHODS*****************************************/
5450 		
5451 	/*
5452 	PrivateMethod: toStyleProp
5453 		Converts a css property name into its javascript name, such as "background-color" to "backgroundColor".
5454 
5455 	Arguments: prop - (String) CSS Property name
5456 
5457 	Returns: String, javascript style property name
5458 	*/
5459 	
5460 	function toStyleProp(prop) {
5461 		if (prop == 'float') {
5462 			return glow.env.ie ? 'styleFloat' : 'cssFloat';
5463 		}
5464 		return prop.replace(/-(\w)/g, function(match, p1) {
5465 			return p1.toUpperCase();
5466 		});
5467 	}
5468 	/*
5469 	PrivateMethod: getCssValue
5470 		Get a computed css property
5471 		
5472 	Arguments:
5473 		elm - element
5474 		prop - css property or array of properties to add together
5475 
5476 	Returns:	String, value
5477 	*/
5478 	function getCssValue(elm, prop) {
5479 		var r, //return value
5480 			total = 0,
5481 			i = 0,
5482 			/*regex for detecting which css properties need to be calculated relative to the y axis*/
5483 			usesYAxis = /height|top/,
5484 			propLen = prop.length,
5485 			cssPropRegex = /^(?:(width|height)|(border-(top|bottom|left|right)-width))$/,
5486 			compStyle = doc.defaultView && (doc.defaultView.getComputedStyle(elm, null) || doc.defaultView.getComputedStyle),
5487 			elmCurrentStyle = elm.currentStyle,
5488 			oldDisplay,
5489 			match,
5490 			propTest = prop.push || cssPropRegex.exec(prop) || [];
5491 
5492 
5493 		if (prop.push) { //multiple properties, add them up
5494 			for (; i < propLen; i++) {
5495 				total += parseInt( getCssValue(elm, prop[i]), 10 ) || 0;
5496 			}
5497 			return total + 'px';
5498 		}
5499 			
5500 		if (propTest[1]) { // is width / height
5501 			if (!isVisible(elm)) { //element may be display: none
5502 				return tempBlock(elm, function() {
5503 					return getElmDimension(elm, propTest[1]) + 'px';
5504 				});
5505 			}
5506 			return getElmDimension(elm, propTest[1]) + 'px';
5507 		}
5508 		else if (propTest[2] //is border-*-width
5509 			&& glow.env.ie
5510 			&& getCssValue(elm, 'border-' + propTest[3] + '-style') == 'none'
5511 		) {
5512 			return '0';
5513 		}
5514 		else if (compStyle) { //W3 Method
5515 			//this returns computed values
5516 			if (typeof compStyle == 'function') {
5517 			//safari returns null for compStyle when element is display:non
5518 			oldDisplay = elm.style.display;
5519 			r = tempBlock(elm, function() {
5520 			if (prop == 'display') { //get true value for display, since we've just fudged it
5521 				elm.style.display = oldDisplay;
5522 				if (!doc.defaultView.getComputedStyle(elm, null)) {
5523 					return 'none';
5524 				}
5525 				elm.style.display = 'block';
5526 			}
5527 			return getCssValue(elm, prop);
5528 			});
5529 			} else {
5530 				// assume equal horizontal margins in safari 3
5531 				// http://bugs.webkit.org/show_bug.cgi?id=13343
5532 				// The above bug doesn't appear to be closed, but it works fine in Safari 4
5533 				if (glow.env.webkit > 500 && glow.env.webkit < 526 && prop == 'margin-right' && compStyle.getPropertyValue('position') != 'absolute') {
5534 					prop = 'margin-left';
5535 				}
5536 				r = compStyle.getPropertyValue(prop);
5537 			}
5538 		} else if (elmCurrentStyle) { //IE method
5539 				if (prop == 'opacity') {
5540 					match = /alpha\(opacity=([^\)]+)\)/.exec(elmCurrentStyle.filter);
5541 					return match ? String(parseInt(match[1], 10) / 100) : '1';
5542 				}
5543 				//this returns cascaded values so needs fixing
5544 				r = String(elmCurrentStyle[toStyleProp(prop)]);
5545 				if (/^-?[\d\.]+(?!px)[%a-z]+$/i.test(r) && prop != 'font-size') {
5546 					r = getPixelValue(elm, r, usesYAxis.test(prop)) + 'px';
5547 				}
5548 			}
5549 			//some results need post processing
5550 			if (prop.indexOf('color') != -1) { //deal with colour values
5551 				r = normaliseCssColor(r).toString();
5552 			} else if (r.indexOf('url') == 0) { //some browsers put quotes around the url, get rid
5553 				r = r.replace(/\"/g,'');
5554 			}
5555 			return r;
5556 		}
5557 	/*
5558 	PrivateMethod: isVisible
5559 		Is the element visible?
5560 	*/
5561 	function isVisible(elm) {
5562 		//this is a bit of a guess, if there's a better way to do this I'm interested!
5563 		return elm.offsetWidth ||
5564 			elm.offsetHeight;
5565 	}
5566 	/*
5567 	PrivateMethod: normaliseCssColor
5568 		Converts a CSS colour into "rgb(255, 255, 255)" or "transparent" format
5569 	*/
5570 
5571 	function normaliseCssColor(val) {
5572 		if (/^(transparent|rgba\(0, ?0, ?0, ?0\))$/.test(val)) { return 'transparent'; }
5573 			var match, //tmp regex match holder
5574 				r, g, b, //final colour vals
5575 				hex, //tmp hex holder
5576 				mathRound = Math.round,
5577 				parseIntFunc = parseInt,
5578 				parseFloatFunc = parseFloat,
5579 					htmlColorNames = {
5580 					black: 0,
5581 					silver: 0xc0c0c0,
5582 					gray: 0x808080,
5583 					white: 0xffffff,
5584 					maroon: 0x800000,
5585 					red: 0xff0000,
5586 					purple: 0x800080,
5587 					fuchsia: 0xff00ff,
5588 					green: 0x8000,
5589 					lime: 0xff00,
5590 					olive: 0x808000,
5591 					yellow: 0xffff00,
5592 					navy: 128,
5593 					blue: 255,
5594 					teal: 0x8080,
5595 					aqua: 0xffff,
5596 					orange: 0xffa500
5597 				},
5598 				colorRegex = /^rgb\(([\d\.]+)(%?),\s*([\d\.]+)(%?),\s*([\d\.]+)(%?)/i;
5599 
5600 			if (match = colorRegex.exec(val)) { //rgb() format, cater for percentages
5601 				r = match[2] ? mathRound(((parseFloatFunc(match[1]) / 100) * 255)) : parseIntFunc(match[1]);
5602 				g = match[4] ? mathRound(((parseFloatFunc(match[3]) / 100) * 255)) : parseIntFunc(match[3]);
5603 				b = match[6] ? mathRound(((parseFloatFunc(match[5]) / 100) * 255)) : parseIntFunc(match[5]);
5604 			} else {
5605 				if (typeof val == 'number') {
5606 					hex = val;
5607 				} else if (val.charAt(0) == '#') {
5608 					if (val.length == '4') { //deal with #fff shortcut
5609 						val = '#' + val.charAt(1) + val.charAt(1) + val.charAt(2) + val.charAt(2) + val.charAt(3) + val.charAt(3);
5610 					}
5611 					hex = parseIntFunc(val.slice(1), 16);
5612 				} else {
5613 					hex = htmlColorNames[val];
5614 				}
5615 
5616 				r = (hex) >> 16;
5617 				g = (hex & 0x00ff00) >> 8;
5618 				b = (hex & 0x0000ff);
5619 			}
5620 
5621 			val = new String('rgb(' + r + ', ' + g + ', ' + b + ')');
5622 			val.r = r;
5623 			val.g = g;
5624 			val.b = b;
5625 			return val;
5626 		}
5627 	/*
5628 	PrivateMethod: getElmDimension
5629 		Gets the size of an element as an integer, not including padding or border
5630 	*/
5631 	var horizontalBorderPadding = [
5632 				'border-left-width',
5633 				'border-right-width',
5634 				'padding-left',
5635 				'padding-right'
5636 			],
5637 			verticalBorderPadding = [
5638 				'border-top-width',
5639 				'border-bottom-width',
5640 				'padding-top',
5641 				'padding-bottom'
5642 			];
5643 		function getElmDimension(elm, cssProp /* (width|height) */) {
5644 			var r,
5645 			doc = document,
5646 			docElm = doc.documentElement,
5647 			docBody = document.body,
5648 			docElmOrBody = glow.env.standardsMode ? docElm : docBody,
5649 			isWidth = (cssProp == 'width'),
5650 			cssPropCaps = isWidth ? 'Width' : 'Height',
5651 			cssBorderPadding;
5652 
5653 			if (elm.window) { // is window
5654 				r = glow.env.webkit < 522.11 ? (isWidth ? elm.innerWidth				: elm.innerHeight) :
5655 					glow.env.webkit			? (isWidth ? docBody.clientWidth		: elm.innerHeight) :
5656 					glow.env.opera < 9.5		? (isWidth ? docBody.clientWidth		: docBody.clientHeight) :
5657 					/* else */			  (isWidth ? docElmOrBody.clientWidth	: docElmOrBody.clientHeight);
5658 
5659 			}
5660 			else if (elm.getElementById) { // is document
5661 				// we previously checked offsetWidth & clientWidth here
5662 				// but they returned values too large in IE6 scrollWidth seems enough
5663 				r = Math.max(
5664 					docBody['scroll' + cssPropCaps],
5665 					docElm['scroll' + cssPropCaps]
5666 				)
5667 			}
5668 			else {
5669 				// get an array of css borders & padding
5670 				cssBorderPadding = isWidth ? horizontalBorderPadding : verticalBorderPadding;
5671 				r = elm['offset' + cssPropCaps] - parseInt( getCssValue(elm, cssBorderPadding) );
5672 			}
5673 			return r;
5674 		}
5675 		
5676 	/*
5677 	PrivateMethod: setElmsSize
5678 		Set element's size
5679 
5680 	Arguments:
5681 		elms - (<NodeList>) Elements
5682 		val - (Mixed) Set element height / width. In px unless stated
5683 		type - (String) "height" or "width"
5684 
5685 	Returns:
5686 		Nowt.
5687 	*/
5688 	function setElmsSize(elms, val, type) {
5689 		if (typeof val == 'number' || /\d$/.test(val)) {
5690 			val += 'px';
5691 		}
5692 		for (var i = 0, len = elms.length; i < len; i++) {
5693 			elms[i].style[type] = val;
5694 		}
5695 	}
5696 	
5697 	/*
5698 	PrivateMethod: tempBlock
5699 		Gives an element display:block (but keeps it hidden) and runs a function, then sets the element back how it was
5700 
5701 	Arguments:
5702 		elm - element
5703 		func - function to run
5704 
5705 	Returns:
5706 		Return value of the function
5707 	*/
5708 	function tempBlock(elm, func) {
5709 		//TODO: rather than recording individual style properties, just cache cssText? This was faster for getting the element size
5710 		var r,
5711 			elmStyle = elm.style,
5712 			oldDisp = elmStyle.display,
5713 			oldVis = elmStyle.visibility,
5714 			oldPos = elmStyle.position;
5715 
5716 			elmStyle.visibility = 'hidden';
5717 			elmStyle.position = 'absolute';
5718 			elmStyle.display = 'block';
5719 		if (!isVisible(elm)) {
5720 			elmStyle.position = oldPos;
5721 			r = tempBlock(elm.parentNode, func);
5722 			elmStyle.display = oldDisp;
5723 			elmStyle.visibility = oldVis;
5724 		} else {
5725 			r = func();
5726 			elmStyle.display = oldDisp;
5727 			elmStyle.position = oldPos;
5728 			elmStyle.visibility = oldVis;
5729 		}
5730 		return r;
5731 	}
5732 	
5733 	/*
5734 	PrivateMethod: getPixelValue
5735 		Converts a relative value into an absolute pixel value. Only works in IE with Dimension value (not stuff like relative font-size).
5736 		Based on some Dean Edwards' code
5737 
5738 	Arguments:
5739 		element - element used to calculate relative values
5740 		value - (string) relative value
5741 		useYAxis - (string) calulate relative values to the y axis rather than x
5742 
5743 	Returns:
5744 		Number
5745 	*/
5746 	function getPixelValue(element, value, useYAxis) {
5747 		// Remember the original values
5748 		var axisPos = useYAxis ? 'top' : 'left',
5749 			axisPosUpper = useYAxis ? 'Top' : 'Left',
5750 			elmStyle = element.style,
5751 			positionVal = elmStyle[axisPos],
5752 			runtimePositionVal = element.runtimeStyle[axisPos],
5753 			r;
5754 			
5755 		// copy to the runtime type to prevent changes to the display
5756 		element.runtimeStyle[axisPos] = element.currentStyle[axisPos];
5757 			// set value to left / top
5758 		elmStyle[axisPos] = value;
5759 		// get the pixel value
5760 		r = elmStyle['pixel' + axisPosUpper];
5761 			
5762 		// revert values
5763 		elmStyle[axisPos] = positionVal;
5764 		element.runtimeStyle[axisPos] = runtimePositionVal;
5765 			
5766 		return r;
5767 	}
5768 	
5769 	/*************************************** API METHODS ******************************************/
5770 	/**
5771 	@name glow.NodeList#css
5772 	@function
5773 	@description Get / set a CSS property value
5774 		
5775 	@param {string | Object} property The CSS property name, or object of property-value pairs to set
5776 		
5777 	@param {string | number} [value] The value to apply
5778 		Number values will be treated as 'px' unless the CSS property
5779 		accepts a unitless value.
5780 		
5781 		If value is omitted, the value for the given property will be returned
5782 			
5783 	@returns {glow.NodeList | string} Returns the NodeList when setting value, or the CSS value when getting values.
5784 		CSS values are strings. For instance, "height" will return
5785 		"25px" for an element 25 pixels high. You can use
5786 		parseInt to convert these values.
5787 		
5788 	@example
5789 		// get value from first node
5790 		glow("#subNav").css("display");
5791 		
5792 	@example
5793 		// set left padding to 10px on all nodes
5794 		glow("#subNav li").css("padding-left", "2em");
5795 		
5796 	@example
5797 		// where appropriate, px is assumed when no unit is passed
5798 		glow("#mainPromo").css("margin-top", 300);
5799 		
5800 	@example
5801 		// set multiple CSS values at once
5802 		// NOTE: Property names containing a hyphen such as font-weight must be quoted
5803 		glow("#myDiv").css({
5804 			'font-weight': 'bold',
5805 			'padding'	 : '10px',
5806 			'color'		 : '#00cc99'
5807 		});
5808 	*/
5809 	
5810 	
5811 	NodeListProto.css = function(prop, val) {
5812 		var thisStyle,
5813 			i = this.length,
5814 			len = this.length,
5815 			originalProp = prop,
5816 			hasUnits = /width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/,
5817 			style;
5818 
5819 			if (prop.constructor === Object) { // set multiple values
5820 				for (style in prop) {
5821 					this.css(style, prop[style]);
5822 				}
5823 				return this;
5824 			}
5825 			else if (val != undefined) { //set one CSS value
5826 				prop = toStyleProp(prop);
5827 				while (i--) {
5828 					thisStyle = this[i].style;
5829 						
5830 					if (typeof val == 'number' && hasUnits.test(originalProp)) {
5831 						val = val.toString() + 'px';
5832 					}
5833 					if (prop == 'opacity' && glow.env.ie) {
5834 						//in IE the element needs hasLayout for opacity to work
5835 						thisStyle.zoom = '1';
5836 						if (val === '') {
5837 							thisStyle.filter = '';
5838 						} else {
5839 							thisStyle.filter = 'alpha(opacity=' + Math.round(Number(val, 10) * 100) + ')';
5840 						}
5841 					} else {
5842 						thisStyle[prop] = val;
5843 					}
5844 				}
5845 					return this;
5846 			} else { //getting stuff
5847 				if (!len) { return; }
5848 				return getCssValue(this[0], prop);
5849 			}	
5850 	};
5851 	
5852 	/**
5853 	@name glow.NodeList#height
5854 	@function
5855 	@description Gets / set element height
5856 		Return value does not include the padding or border of the element in
5857 		browsers supporting the correct box model.
5858 			
5859 		You can use this to easily get the height of the document or
5860 		window, see example below.
5861 		
5862 	@param {Number} [height] New height in pixels for each element in the list
5863 		If ommited, the height of the first element is returned
5864 		
5865 	@returns {glow.NodeList | number}
5866 		Height of first element, or original NodeList when setting heights.
5867 		
5868 	@example
5869 		// get the height of #myDiv
5870 		glow("#myDiv").height();
5871 		
5872 	@example
5873 		// set the height of list items in #myList to 200 pixels
5874 		glow("#myList > li").height(200);
5875 		
5876 	@example
5877 		// get the height of the document
5878 		glow(document).height();
5879 		
5880 	@example
5881 		// get the height of the window
5882 		glow(window).height();
5883 	*/
5884 	NodeListProto.height = function(height) {
5885 		if (height == undefined) {
5886 			return getElmDimension(this[0], 'height');
5887 		}
5888 		setElmsSize(this, height, 'height');
5889 		return this;	
5890 	};
5891 	
5892 	/**
5893 	@name glow.NodeList#width
5894 	@function
5895 	@description Gets / set element width
5896 		Return value does not include the padding or border of the element in
5897 		browsers supporting the correct box model.
5898 			
5899 		You can use this to easily get the width of the document or
5900 		window, see example below.
5901 		
5902 	@param {Number} [width] New width in pixels for each element in the list
5903 		If ommited, the width of the first element is returned
5904 		
5905 	@returns {glow.NodeList | number}
5906 		width of first element, or original NodeList when setting widths.
5907 		
5908 	@example
5909 		// get the width of #myDiv
5910 		glow("#myDiv").width();
5911 		
5912 	@example
5913 		// set the width of list items in #myList to 200 pixels
5914 		glow("#myList > li").width(200);
5915 		
5916 	@example
5917 		// get the width of the document
5918 		glow(document).width();
5919 		
5920 	@example
5921 		// get the width of the window
5922 		glow(window).width();
5923 	*/
5924 	NodeListProto.width = function(width) {
5925 		if (width == undefined) {
5926 			return getElmDimension(this[0], 'width');
5927 		}
5928 		setElmsSize(this, width, 'width');
5929 		return this;
5930 	};
5931 	
5932 	/**
5933 	@name glow.NodeList#scrollLeft
5934 	@function
5935 	@description Gets/sets the number of pixels the element has scrolled horizontally
5936 		To get/set the scroll position of the window, use this method on
5937 		a nodelist containing the window object.
5938 			
5939 	@param {Number} [val] New left scroll position
5940 		Omit this to get the current scroll position
5941 			
5942 	@returns {glow.NodeList | number}
5943 		Current scrollLeft value, or NodeList when setting scroll position.
5944 			
5945 	@example
5946 		// get the scroll left value of #myDiv
5947 		var scrollPos = glow("#myDiv").scrollLeft();
5948 		// scrollPos is a number, eg: 45
5949 
5950 	@example
5951 		// set the scroll left value of #myDiv to 20
5952 		glow("#myDiv").scrollLeft(20);
5953 
5954 	@example
5955 		// get the scrollLeft of the window
5956 		glow(window).scrollLeft();
5957 		// scrollPos is a number, eg: 45
5958 	*/
5959 	NodeListProto.scrollLeft = function(val) {
5960 		return scrollOffset(this, true, val);	
5961 	};
5962 	
5963 	/**
5964 	@name glow.NodeList#scrollTop
5965 	@function
5966 	@description Gets/sets the number of pixels the element has scrolled vertically
5967 		To get/set the scroll position of the window, use this method on
5968 		a nodelist containing the window object.
5969 		
5970 	@param {Number} [val] New top scroll position
5971 		Omit this to get the current scroll position
5972 			
5973 	@returns {glow.NodeList | number}
5974 		Current scrollTop value, or NodeList when setting scroll position.
5975 
5976 	@example
5977 		// get the scroll top value of #myDiv
5978 		var scrollPos = glow("#myDiv").scrollTop();
5979 		// scrollPos is a number, eg: 45
5980 
5981 	@example
5982 		// set the scroll top value of #myDiv to 20
5983 		glow("#myDiv").scrollTop(20);
5984 
5985 	@example
5986 		// get the scrollTop of the window
5987 		glow(window).scrollTop();
5988 		// scrollPos is a number, eg: 45
5989 	*/
5990 	NodeListProto.scrollTop = function(val) {
5991 		return scrollOffset(this, false, val);	
5992 	};
5993 	/**
5994 	@name glow.dom-getScrollOffset
5995 	@private
5996 	@description Get the scrollTop / scrollLeft of a particular element
5997 	@param {Element} elm Element (or window object) to get the scroll position of
5998 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
5999 	*/
6000 	function getScrollOffset(elm, isLeft) {
6001 		var r,			
6002 			scrollProp = 'scroll' + (isLeft ? 'Left' : 'Top');
6003 			
6004 		// are we dealing with the window object or the document object?
6005 		if (elm.window) {
6006 			// get the scroll of the documentElement or the pageX/Yoffset
6007 			// - some browsers use one but not the other
6008 			r = elm.document.documentElement[scrollProp]
6009 				|| (isLeft ? elm.pageXOffset : elm.pageYOffset)
6010 				|| 0;
6011 		} else {
6012 			r = elm[scrollProp];
6013 		}
6014 		return r;
6015 	}
6016 		
6017 	/**
6018 	@name glow.dom-setScrollOffset
6019 	@private
6020 	@description Set the scrollTop / scrollLeft of a particular element
6021 	@param {Element} elm Element (or window object) to get the scroll position of
6022 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
6023 	@param {Number} newVal New scroll value
6024 	*/
6025 	function setScrollOffset(elm, isLeft, newVal) {
6026 	// are we dealing with the window object or the document object?
6027 		if (elm.window) {
6028 			// we need to get whichever value we're not setting
6029 			elm.scrollTo(
6030 				isLeft  ? newVal : getScrollOffset(elm, true),
6031 				!isLeft ? newVal : getScrollOffset(elm, false)
6032 			);
6033 		} else {
6034 			elm['scroll' + (isLeft ? 'Left' : 'Top')] = newVal;
6035 		}
6036 	}
6037 	
6038 	/**
6039 	@name glow.dom-scrollOffset
6040 	@private
6041 	@description Set/get the scrollTop / scrollLeft of a NodeList
6042 	@param {glow.dom.NodeList} nodeList Elements to get / set the position of
6043 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
6044 	@param {Number} [val] Val to set (if not provided, we'll get the value)
6045 	@returns NodeList for sets, Number for gets
6046 	*/
6047 	function scrollOffset(nodeList, isLeft, val) {
6048 		var i = nodeList.length;
6049 			
6050 		if (val !== undefined) {
6051 			while (i--) {
6052 				setScrollOffset(nodeList[i], isLeft, val);
6053 			}
6054 			return nodeList;
6055 		} else {
6056 			return getScrollOffset(nodeList[0], isLeft);
6057 		}
6058 	}
6059 	/**
6060 	@name glow.NodeList#hide
6061 	@function
6062 	@description Hides all items in the NodeList.
6063 		
6064 	@returns {glow.NodeList}
6065 		
6066 	@example
6067 		// Hides all list items within #myList
6068 		glow("#myList li").hide();
6069 	*/
6070 	NodeListProto.hide = function() {
6071 		return this.css('display', 'none').css('visibility', 'hidden');	
6072 	};
6073 	
6074 	/**
6075 	@name glow.NodeList#show
6076 	@function
6077 	@description Shows all hidden items in the NodeList.
6078 		
6079 	@returns {glow.NodeList}
6080 		
6081 	@example
6082 		// Show element with ID myDiv
6083 		glow("#myDiv").show();
6084 			
6085 	@example
6086 		// Show all list items within #myList
6087 		glow("#myList li").show();
6088 	*/
6089 	NodeListProto.show = function() {
6090 		var i = this.length,
6091 			len = this.length,
6092 			currItem,
6093 			itemStyle;
6094 		while (i--) {
6095 			/* Create a NodeList for the current item */
6096 			currItem = new glow.NodeList(this[i]);
6097 			itemStyle = currItem[0].style;
6098 			if (currItem.css('display') == 'none') {
6099 				itemStyle.display = '';
6100 				itemStyle.visibility = 'visible';
6101 			/* If display is still none, set to block */
6102 			if (currItem.css('display') == 'none') {
6103 				itemStyle.display = 'block';
6104 				}
6105 			}	
6106 		}
6107 		return this;	
6108 	};
6109 
6110 	/**
6111 	@name glow.NodeList#offset
6112 	@function
6113 	@description Gets the offset from the top left of the document.
6114 		If the NodeList contains multiple items, the offset of the
6115 		first item is returned.
6116 			
6117 	@returns {Object}
6118 		Returns an object with "top" & "left" properties in pixels
6119 			
6120 	@example
6121 		glow("#myDiv").offset().top
6122 	*/
6123 	NodeListProto.offset = function() {
6124 				// http://weblogs.asp.net/bleroy/archive/2008/01/29/getting-absolute-coordinates-from-a-dom-element.aspx - great bit of research, most bugfixes identified here (and also jquery trac)
6125 		var elm = this[0],
6126 		doc = document,
6127 		docElm = doc.documentElement,
6128 			docScrollPos = {
6129 				x: getScrollOffset(window, true),
6130 				y: getScrollOffset(window, false)
6131 			}
6132 
6133 		//this is simple(r) if we can use 'getBoundingClientRect'
6134 		// Sorry but the sooper dooper simple(r) way is not accurate in Safari 4
6135 		if (!glow.env.webkit && elm.getBoundingClientRect) {
6136 			var rect = elm.getBoundingClientRect();
6137 			return {
6138 				top: Math.floor(rect.top)
6139 				/*
6140 				 getBoundingClientRect is realive to top left of
6141 				 the viewport, so we need to sort out scrolling offset
6142 				*/
6143 				+ docScrollPos.y
6144 				/*
6145 				IE adds the html element's border to the value. We can
6146 				deduct this value using client(Top|Left). However, if
6147 				the user has done html{border:0} clientTop will still
6148 				report a 2px border in IE quirksmode so offset will be off by 2.
6149 				Hopefully this is an edge case but we may have to revisit this
6150 				in future
6151 				*/
6152 				- docElm.clientTop,
6153 
6154 				left: Math.floor(rect.left) //see above for docs on all this stuff
6155 				+ docScrollPos.x
6156 				- docElm.clientLeft
6157 			};
6158 		} else { //damnit, let's go the long way around
6159 			var top = elm.offsetTop,
6160 			left = elm.offsetLeft,
6161 			originalElm = elm,
6162 			nodeNameLower,
6163 			docBody = document.body,
6164 			//does the parent chain contain a position:fixed element
6165 			involvesFixedElement = false,
6166 			offsetParentBeforeBody = elm;
6167 
6168 			//add up all the offset positions
6169 			while (elm = elm.offsetParent) {
6170 				left += elm.offsetLeft;
6171 				top += elm.offsetTop;
6172 
6173 				//if css position is fixed, we need to add in the scroll offset too, catch it here
6174 				if (getCssValue(elm, 'position') == 'fixed') {
6175 					involvesFixedElement = true;
6176 				}
6177 
6178 				//gecko & webkit (safari 3) don't add on the border for positioned items
6179 				if (glow.env.gecko || glow.env.webkit > 500) {
6180 					left += parseInt(getCssValue(elm, 'border-left-width')) || 0;
6181 					top  += parseInt(getCssValue(elm, 'border-top-width'))  || 0;
6182 				}
6183 				
6184 				//we need the offset parent (before body) later
6185 				if (elm.nodeName.toLowerCase() != 'body') {
6186 					offsetParentBeforeBody = elm;
6187 				}
6188 			}
6189 
6190 			//deduct all the scroll offsets
6191 			elm = originalElm;
6192 			while ((elm = elm.parentNode) && (elm != docBody) && (elm != docElm)) {
6193 				left -= elm.scrollLeft;
6194 				top -= elm.scrollTop;
6195 
6196 				//FIXES
6197 				//gecko doesn't add the border of contained elements to the offset (overflow!=visible)
6198 				if (glow.env.gecko && getCssValue(elm, 'overflow') != 'visible') {
6199 					left += parseInt(getCssValue(elm, 'border-left-width'));
6200 					top += parseInt(getCssValue(elm, 'border-top-width'));
6201 				}
6202 			}
6203 
6204 			//if we found a fixed position element we need to add the scroll offsets
6205 			if (involvesFixedElement) {
6206 				left += docScrollPos.x;
6207 				top += docScrollPos.y;
6208 			}
6209 
6210 			//FIXES
6211 			// Webkit < 500 body's offset gets counted twice for absolutely-positioned elements (or if there's a fixed element)
6212 			// Gecko - non-absolutely positioned elements that are direct children of body get the body offset counted twice
6213 			if (
6214 				(glow.env.webkit < 500 && (involvesFixedElement || getCssValue(offsetParentBeforeBody, 'position') == 'absolute')) ||
6215 				(glow.env.gecko && getCssValue(offsetParentBeforeBody, 'position') != 'absolute')
6216 			) {
6217 				left -= docBody.offsetLeft;
6218 				top -= docBody.offsetTop;
6219 			}
6220 
6221 			return {left:left, top:top};
6222 		}
6223 	};
6224 	
6225 	/**
6226 	@name glow.NodeList#position
6227 	@function
6228 	@description Get the top & left position of an element relative to its positioned parent
6229 		This is useful if you want to make a position:static element position:absolute
6230 		and retain the original position of the element
6231 			
6232 	@returns {Object}
6233 		An object with 'top' and 'left' number properties
6234 		
6235 	@example
6236 		// get the top distance from the positioned parent
6237 		glow("#elm").position().top
6238 	*/
6239 	NodeListProto.position = function() {
6240 		var positionedParent = new glow.NodeList( getPositionedParent(this[0]) ),
6241 			hasPositionedParent = !!positionedParent[0],
6242 					
6243 			// element margins to deduct
6244 			marginLeft = parseInt( this.css('margin-left') ) || 0,
6245 			marginTop  = parseInt( this.css('margin-top')  ) || 0,
6246 					
6247 			// offset parent borders to deduct, set to zero if there's no positioned parent
6248 			positionedParentBorderLeft = ( hasPositionedParent && parseInt( positionedParent.css('border-left-width') ) ) || 0,
6249 			positionedParentBorderTop  = ( hasPositionedParent && parseInt( positionedParent.css('border-top-width')  ) ) || 0,
6250 					
6251 			// element offsets
6252 		elOffset = this.offset(),
6253 		positionedParentOffset = hasPositionedParent ? positionedParent.offset() : {top: 0, left: 0};
6254 				
6255 		return {
6256 			left: elOffset.left - positionedParentOffset.left - marginLeft - positionedParentBorderLeft,
6257 			top:  elOffset.top  - positionedParentOffset.top  - marginTop  - positionedParentBorderTop
6258 		}	
6259 	};
6260 	/*
6261 		Get the 'real' positioned parent for an element, otherwise return null.
6262 	*/
6263 	function getPositionedParent(elm) {
6264 		var offsetParent = elm.offsetParent,
6265 		doc = document,
6266 		docElm = doc.documentElement;
6267 			
6268 		// get the real positioned parent
6269 		// IE places elements with hasLayout in the offsetParent chain even if they're position:static
6270 		// Also, <body> and <html> can appear in the offsetParent chain, but we don't want to return them if they're position:static
6271 		while (offsetParent && new glow.NodeList(offsetParent).css('position') == 'static') {	
6272 			offsetParent = offsetParent.offsetParent;
6273 		}
6274 			
6275 		// sometimes the <html> element doesn't appear in the offsetParent chain, even if it has position:relative
6276 		if (!offsetParent && new glow.NodeList(docElm).css('position') != 'static') {
6277 			offsetParent = docElm;
6278 		}
6279 			
6280 		return offsetParent || null;
6281 	}
6282 });
6283 Glow.provide(function(glow) {
6284 	var NodeListProto = glow.NodeList.prototype,
6285 		document = window.document,
6286 		undefined,
6287 		keyEventNames = ' keypress keydown keyup ';
6288 	
6289 	/**
6290 		@name glow.NodeList#on
6291 		@function
6292 		@description Listen for an event.
6293 		   This will listen for a particular event on each dom node
6294 		   in the NodeList.
6295 		   
6296 		   If you're listening to many children of a particular item,
6297 		   you may get better performance from {@link glow.NodeList#delegate}.
6298 		
6299 		@param {String} eventName Name of the event to listen for.
6300 		   This can be any regular DOM event ('click', 'mouseover' etc) or
6301 		   a special event of NodeList.
6302 		   
6303 		@param {Function} callback Function to call when the event fires.
6304 		   The callback is passed a single event object. The type of this
6305 		   object is {@link glow.DomEvent} unless otherwise stated.
6306 		   
6307 		@param {Object} [thisVal] Value of 'this' within the callback.
6308 		   By default, this is the dom node being listened to.
6309 		
6310 		@returns this
6311 		
6312 		@example
6313 		   glow.get('#testLink').on('click', function(domEvent) {
6314 			   // do stuff
6315 			   
6316 			   // if you want to cancel the default action (following the link)...
6317 			   return false;
6318 		   });
6319 	*/
6320 	NodeListProto.on = function(eventName, callback, thisVal) {
6321 		var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
6322 			
6323 		if (isKeyEvent) {
6324 			glow.events._addKeyListener(this, eventName, callback, thisVal);
6325 		}
6326 		else { // assume it's a DOM event
6327 			glow.events._addDomEventListener(this, eventName, callback, thisVal);
6328 		}
6329 		
6330 		return this;
6331 	}
6332 	
6333 	/**
6334 		@name glow.NodeList#detach
6335 		@function
6336 		@description detach a listener from elements
6337 		   This will detach the listener from each dom node in the NodeList.
6338 		
6339 		@param {String} eventName Name of the event to detach the listener from
6340 		   
6341 		@param {Function} callback Listener callback to detach
6342 		
6343 		@returns this
6344 		
6345 		@example
6346 			function clickListener(domEvent) {
6347 				// ...
6348 			}
6349 			
6350 			// adding listeners
6351 			glow.get('a').on('click', clickListener);
6352 			
6353 			// removing listeners
6354 			glow.get('a').detach('click', clickListener);
6355 	*/
6356 	NodeListProto.detach = function(eventName, callback, thisVal) {
6357 		var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
6358 		if (isKeyEvent) {
6359 			glow.events._removeKeyListener(this, eventName, callback);
6360 		}
6361 		else { // assume it's a DOM event
6362 			glow.events._removeDomEventListener(this, eventName, callback, thisVal);
6363 		}
6364 		
6365 		return this;
6366 	}
6367 	
6368 	/**
6369 		@name glow.NodeList#delegate
6370 		@function
6371 		@description Listen for an event occurring on child elements matching a selector.
6372 			'delegate' will catch events which occur on matching items created after
6373 			the listener was added. 
6374 		
6375 		@param {String} eventName Name of the event to listen for.
6376 			This can be any regular DOM event ('click', 'mouseover' etc) or
6377 			a special event of NodeList.
6378 		
6379 		@param {String} selector CSS selector of child elements to listen for events on
6380 			For example, if you were wanting to hear events from links, this
6381 			would be 'a'.
6382 		
6383 		@param {Function} callback Function to call when the event fires.
6384 			The callback is passed a single event object. The type of this
6385 			object is {@link glow.DomEvent} unless otherwise stated.
6386 		
6387 		@param {Object} [thisVal] Value of 'this' within the callback.
6388 			By default, this is the dom node matched by 'selector'.
6389 		
6390 		@returns this
6391 		
6392 		@example
6393 			// Using 'on' to catch clicks on links in a list
6394 			glow.get('#nav a').on('click', function() {
6395 				// do stuff
6396 			});
6397 			
6398 			// The above adds a listener to each link, any links created later
6399 			// will not have this listener, so we won't hear about them.
6400 			
6401 			// Using 'delegate' to catch clicks on links in a list
6402 			glow.get('#nav').delegate('click', 'a', function() {
6403 				// do stuff
6404 			});
6405 			
6406 			// The above only adds one listener to #nav which tracks clicks
6407 			// to any links within. This includes elements created after 'delegate'
6408 			// was called.
6409 		
6410 		@example
6411 			// Using delegate to change class names on table rows so :hover
6412 			// behaviour can be emulated in IE6
6413 			glow.get('#contactData').delegate('mouseover', 'tr', function() {
6414 				glow.get(this).addClass('hover');
6415 			});
6416 			
6417 			glow.get('#contactData').delegate('mouseout', 'tr', function() {
6418 				glow.get(this).removeClass('hover');
6419 			});
6420 	*/
6421 	NodeListProto.delegate = function(eventName, selector, callback, thisVal) {
6422 		var i = this.length,
6423 			attachTo,
6424 			isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
6425 		
6426 		while (i--) {
6427 			attachTo = this[i];
6428 			
6429 			if (isKeyEvent) {
6430 // 					glow.events._addKeyListener([attachTo], eventName, handler);
6431 			}
6432 			else { // assume it's a DOM event
6433 				glow.events._addDomEventListener([attachTo], eventName, callback, thisVal, selector);
6434 			}
6435 		}
6436 		
6437 		return this;
6438 	}
6439 	
6440 	/**
6441 		@name glow.NodeList#detachDelegate
6442 		@function
6443 		@description detach a delegated listener from elements
6444 		   This will detach the listener from each dom node in the NodeList.
6445 		
6446 		@param {String} eventName Name of the event to detach the listener from
6447 		
6448 		@param {String} selector CSS selector of child elements the listener is listening to
6449 		
6450 		@param {Function} callback Listener callback to detach
6451 		
6452 		@returns this
6453 		
6454 		@example
6455 			function clickListener(domEvent) {
6456 				// ...
6457 			}
6458 			
6459 			// adding listeners
6460 			glow.get('#nav').delegate('click', 'a', clickListener);
6461 			
6462 			// removing listeners
6463 			glow.get('#nav').detachDelegate('click', 'a', clickListener);
6464 	*/
6465 	NodeListProto.detachDelegate = function(eventName, selector, callback, thisVal) {
6466 		var i = this.length,
6467 			attachTo,
6468 			isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1),
6469 			handler;
6470 		
6471 		while (i--) {
6472 			attachTo = this[i];
6473 			
6474 			if (isKeyEvent) {
6475 // 				glow.events._removeKeyListener([attachTo], eventName, handler);
6476  			}
6477  			else {
6478  				glow.events._removeDomEventListener([attachTo], eventName, callback, selector);
6479  			}
6480 		}
6481 		
6482 		return this;
6483 	}
6484 	
6485 	/**
6486 		@name glow.NodeList#fire
6487 		@function
6488 		@param {String} eventName Name of the event to fire
6489 		@param {glow.events.Event} [event] Event object to pass into listeners.
6490 		   You can provide a simple object of key / value pairs which will
6491 		   be added as properties of a glow.events.Event instance.
6492 		
6493 		@description Fire an event on dom nodes within the NodeList
6494 		   Note, this will only trigger event listeners to be called, it won't
6495 		   for example, move the mouse or click a link on the page.
6496 		
6497 		@returns glow.events.Event
6498 		
6499 		@example
6500 		   glow.get('#testLink').on('click', function() {
6501 			   alert('Link clicked!');
6502 		   });
6503 		   
6504 		   // The following causes 'Link clicked!' to be alerted, but doesn't
6505 		   // cause the browser to follow the link
6506 		   glow.get('#testLink').fire('click');
6507 	*/
6508 	NodeListProto.fire = function(eventName, event) {
6509 		return glow.events.fire(this, eventName, event);
6510 	}
6511 	
6512 	/**
6513 		@name glow.NodeList#event:mouseenter
6514 		@event
6515 		@description Fires when the mouse enters the element specifically, does not bubble
6516 		
6517 		@param {glow.events.DomEvent} event Event Object
6518 	*/
6519 	
6520 	/**
6521 		@name glow.NodeList#event:mouseleave
6522 		@event
6523 		@description Fires when the mouse leaves the element specifically, does not bubble
6524 		
6525 		@param {glow.events.DomEvent} event Event Object
6526 	*/
6527 	
6528 	/**
6529 		@name glow.NodeList#event:keydown
6530 		@event
6531 		@description Fires when the user presses a key
6532 			Only fires if the element has focus, listen for this event on
6533 			the document to catch all keydowns.
6534 			
6535 			This event related to the user pressing a key on the keyboard,
6536 			if you're more concerned about the character entered, see the
6537 			{@link glow.NodeList#event:keypress keypress} event.
6538 			
6539 			keydown will only fire once, when the user presses the key.
6540 			
6541 			The order of events is keydown, keypress*, keyup. keypress may
6542 			fire many times if the user holds the key down.
6543 		
6544 		@param {glow.events.KeyboardEvent} event Event Object
6545 	*/
6546 	
6547 	/**
6548 		@name glow.NodeList#event:keypress
6549 		@event
6550 		@description Fires when a key's command executes.
6551 			For instance, if you hold down a key, it's action will occur many
6552 			times. This event will fire on each action.
6553 			
6554 			This event is useful when you want to react to keyboard repeating, or
6555 			to detect when a character is entered into a field.
6556 			
6557 			The order of events is keydown, keypress*, keyup. keypress may
6558 			fire many times if the user holds the key down.
6559 		
6560 		@param {glow.events.KeyboardEvent} event Event Object
6561 	*/
6562 	
6563 	/**
6564 		@name glow.NodeList#event:keyup
6565 		@event
6566 		@description Fires when the user releases a key
6567 			Only fires if the element has focus, listen for this event on
6568 			the document to catch all keyups.
6569 			
6570 			This event related to the user pressing a key on the keyboard,
6571 			if you're more concerned about the character entered, see the
6572 			{@link glow.NodeList#event:keypress keypress} event.
6573 			
6574 			The order of events is keydown, keypress*, keyup. keypress may
6575 			fire many times if the user holds the key down.
6576 		
6577 		@param {glow.events.KeyboardEvent} event Event Object
6578 	*/
6579 });
6580 Glow.complete('core', '2.0.0-alpha1');
6581