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 	glow.UID = 'glow' + Math.floor(Math.random() * (1<<30));
 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 		/**
337 			@private
338 			@function
339 			@name bindReady
340 			@description Add listener to document to detect when page is ready.
341 		 */
342 		var bindReady = function() { // use `var bindReady= function` form instead of `function bindReady()` to prevent FireBug 'cannot access optimized closure' error
343 			//don't do this stuff if the dom is already ready
344 			if (glow.isDomReady) { return; }
345 			glow._addReadyBlock('glow_domReady'); // wait for dom to be ready
346 			
347 			function onReady() { /*debug*///log.info('onReady()');
348 				runReadyQueue();
349 				glow._removeReadyBlock('glow_domReady');
350 			}
351 					
352 			if (document.readyState == 'complete') { // already here!
353 				 /*debug*///log.info('already complete');
354 				onReady();
355 			}
356 			else if (glow.env.ie && document.attachEvent) { /*debug*///log.info('bindready() - document.attachEvent');
357 				// like IE
358 				
359 				// not an iframe...
360 				if (document.documentElement.doScroll && window == top) {
361 					(function() {  /*debug*///log.info('doScroll');
362 						try {
363 							document.documentElement.doScroll('left');
364 						}
365 						catch(error) {
366 							setTimeout(arguments.callee, 0);
367 							return;
368 						}
369 				
370 						// and execute any waiting functions
371 						onReady();
372 					})();
373 				}
374 				else {
375 					// an iframe...
376 					document.attachEvent(
377 						'onreadystatechange',
378 						function() { /*debug*///log.info('onreadystatechange');
379 							if (document.readyState == 'complete') {
380 								document.detachEvent('onreadystatechange', arguments.callee);
381 								onReady();
382 							}
383 						}
384 					);
385 				}
386 			}
387 			else if (document.readyState) { /*debug*///log.info('bindready() - document.readyState');
388 				// like pre Safari
389 				(function() { /*debug*///log.info('loaded|complete');
390 					if ( /loaded|complete/.test(document.readyState) ) {
391 						onReady();
392 					}
393 					else {
394 						setTimeout(arguments.callee, 0);
395 					}
396 				})();
397 			}
398 			else if (document.addEventListener) {/*debug*///log.info('bindready() - document.addEventListener');
399 				// like Mozilla, Opera and recent webkit
400 				document.addEventListener( 
401 					'DOMContentLoaded',
402 					function(){ /*debug*///log.info('glow DOMContentLoaded');
403 						document.removeEventListener('DOMContentLoaded', arguments.callee, false);
404 						onReady();
405 					},
406 					false
407 				);
408 			}
409 			else {
410 				throw new Error('Unable to bind glow ready listener to document.');
411 			}
412 		};
413 	
414 		glow.notSupported = ( // here are the browsers we don't support
415 			glow.env.ie < 6 ||
416 			(glow.env.gecko < 1.9 && !/^1\.8\.1/.test(glow.env.version)) ||
417 			glow.env.opera < 9 ||
418 			glow.env.webkit < 412
419 		);
420 		// deprecated
421 		glow.isSupported = !glow.notSupported;
422 		
423 		// block 'ready' if browser isn't supported
424 		if (glow.notSupported) {
425 			glow._addReadyBlock('glow_browserSupport');
426 		}
427 		
428 		bindReady();
429 	}
430 );
431 // end-source: core/ready.js
432 /**
433 	@name glow.util
434 	@namespace
435 	@description Core JavaScript helpers
436 */
437 Glow.provide(function(glow) {
438 	var util = {},
439 		undefined,
440 		TYPES = {
441 			UNDEFINED : "undefined",
442 			OBJECT    : "object",
443 			NUMBER    : "number",
444 			BOOLEAN   : "boolean",
445 			STRING    : "string",
446 			ARRAY     : "array",
447 			FUNCTION  : "function",
448 			NULL      : "null"
449 		},
450 		/*
451 		PrivateProperty: TEXT
452 			hash of strings used in encoding/decoding
453 		*/
454 		TEXT = {
455 			AT    : "@",
456 			EQ    : "=",
457 			DOT   : ".",
458 			EMPTY : "",
459 			AND   : "&",
460 			OPEN  : "(",
461 			CLOSE : ")"
462 		},
463 		/*
464 		PrivateProperty: JSON
465 			nested hash of strings and regular expressions used in encoding/decoding Json
466 		*/
467 		JSON = {
468 			HASH : {
469 				START     : "{",
470 				END       : "}",
471 				SHOW_KEYS : true
472 			},
473 
474 			ARRAY : {
475 				START     : "[",
476 				END       : "]",
477 				SHOW_KEYS : false
478 			},
479 
480 			DATA_SEPARATOR   : ",",
481 			KEY_SEPARATOR    : ":",
482 			KEY_DELIMITER    : "\"",
483 			STRING_DELIMITER : "\"",
484 
485 			SAFE_PT1 : /^[\],:{}\s]*$/,
486 			SAFE_PT2 : /\\./g,
487 			SAFE_PT3 : /\"[^\"\\\n\r]*\"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g,
488 			SAFE_PT4 : /(?:^|:|,)(?:\s*\[)+/g
489 		};
490 	/**
491 		@private
492 		@name glow.util-_getType
493 		@param {Object} object The object to be tested.
494 		@returns {string} The data type of the object.
495 	*/
496 	function _getType(object) {
497 		var typeOfObject = typeof object,
498 			constructorStr,
499 			type;
500 
501 		if (object === null) { return 'null'; } // warn: won't work across frames?
502 		else if (isFunction(object)) { return 'Function'; }
503 		else if (isArray(object)) { return 'Array'; }
504 		else if (typeOfObject === 'object') {
505 			
506 			constructorStr = object.constructor.toString();
507 
508 			if ( /^function (\S+?)\(/.test(constructorStr) ) {
509 				type = RegExp.$1;
510 				if (type === 'Object') { return 'object'; }
511 				else { return type; }
512 			}
513 		}
514 
515 		return typeOfObject;
516 	}
517 
518 	function isArray(o) {
519 		return {}.toString.call(o) === '[object Array]';
520 	}
521 	
522 	function isFunction(o) {
523 		return {}.toString.call(o) === '[object Function]';
524 	}
525 
526 	/**
527 		@name glow.util.getType
528 		@function
529 		@description Get the native type or constructor name of an object.
530 			This allows you to safely get the type of an object, even
531 			if it came from another frame.
532 			
533 		@param {Object} object Object to get the type of.
534 			
535 		@example
536 			glow.util.getType( null ); // 'null'
537 			glow.util.getType( undefined ); // 'undefined'
538 			glow.util.getType('Hello'); // 'string'
539 			glow.util.getType( {} ); // 'Object'
540 			glow.util.getType(12); // 'number'
541 			glow.util.getType( [] ); // 'Array'
542 			glow.util.getType( function(){} ); // 'Function'
543 			glow.util.getType( glow('#whatever') ); // 'NodeList'
544 			
545 		@example
546 			var MyConstructor = function() {},
547 				obj = new MyConstructor;
548 			
549 			glow.util.getType(obj); // ''
550 			// The above returns an empty string as the constructor
551 			// is an anonymous function and therefore has no name
552 	*/
553 	util.getType = _getType;
554 	
555 	/**
556 		@name glow.util.apply
557 		@function
558 		@description Copies properties from one object to another
559 			All properties from 'source' will be copied onto
560 			'destination', potentially overwriting existing properties
561 			on 'destination'.
562 			
563 			Properties from 'source's prototype chain will not be copied.
564 		
565 		@param {Object} [destination] Destination object.
566 			If this object is undefined or falsey, a new object will be created.
567 		
568 		@param {Object} [source] Properties of this object will be copied onto the destination
569 			If this object is undefined or falsey, a new object will be created.
570 		
571 		@returns {Object} The destination object.
572 		
573 		@example
574 			var obj = glow.util.apply({foo: "hello", bar: "world"}, {bar: "everyone"});
575 			//results in {foo: "hello", bar: "everyone"}
576 	*/
577 	util.apply = function(destination, source) {
578 		destination = destination || {};
579 		source = source || {};
580 		
581 		/*!debug*/
582 			if (typeof destination != 'object') {
583 				glow.debug.warn('[wrong type] glow.util.apply expects argument "destination" to be of type object, not ' + typeof destination + '.');
584 			}
585 			if (typeof source != 'object') {
586 				glow.debug.warn('[wrong type] glow.util.apply expects argument "source" to be of type object, not ' + typeof source + '.');
587 			}
588 		/*gubed!*/
589 		for (var i in source) {
590 			if ( source.hasOwnProperty(i) ) {
591 				destination[i] = source[i];
592 			}
593 		}
594 		return destination;
595 	};
596 	
597 	/**
598 		@name glow.util.extend
599 		@function
600 		@description Copies the prototype of one object to another
601 			The 'subclass' can also access the 'base class' via subclass.base
602 
603 		@param {Function} sub Class which inherits properties.
604 		@param {Function} base Class to inherit from.
605 		@param {Object} additionalProperties An object of properties and methods to add to the subclass.
606 
607 		@example
608 			function MyClass(arg) {
609 				this.prop = arg;
610 			}
611 			MyClass.prototype.showProp = function() {
612 				alert(this.prop);
613 			};
614 			function MyOtherClass(arg) {
615 				//call the base class's constructor
616 				MyOtherClass.base.apply(this, arguments);
617 			}
618 			glow.util.extend(MyOtherClass, MyClass, {
619 				setProp: function(newProp) {
620 					this.prop = newProp;
621 				}
622 			});
623 
624 			var test = new MyOtherClass("hello");
625 			test.showProp(); // alerts "hello"
626 			test.setProp("world");
627 			test.showProp(); // alerts "world"
628 	*/
629 	util.extend = function(sub, base, additionalProperties) {
630 		/*!debug*/
631 			if (arguments.length < 2) {
632 				glow.debug.warn('[wrong count] glow.util.extend expects at least 2 arguments, not '+arguments.length+'.');
633 			}
634 			if (typeof sub != 'function') {
635 				glow.debug.error('[wrong type] glow.util.extend expects argument "sub" to be of type function, not ' + typeof sub + '.');
636 			}
637 			if (typeof base != 'function') {
638 				glow.debug.error('[wrong type] glow.util.extend expects argument "base" to be of type function, not ' + typeof base + '.');
639 			}
640 		/*gubed!*/
641 		var f = function () {}, p;
642 		f.prototype = base.prototype;
643 		p = new f();
644 		sub.prototype = p;
645 		p.constructor = sub;
646 		sub.base = base;
647 		if (additionalProperties) {
648 			util.apply(sub.prototype, additionalProperties);
649 		}
650 	};
651 	
652 	/**
653 		@name glow.util.escapeRegex
654 		@function
655 		@description Escape special regex chars from a string
656 
657 		@param {string} str String to escape
658 		
659 		@returns {string} Escaped string
660 		
661 		@example
662 			var str = glow.util.escapeRegex('[Hello. Is this escaped?]');
663 			// Outputs:
664 			// \[Hello\. Is this escaped\?\]
665 	*/
666 	util.escapeRegex = function(str) {
667 		/*!debug*/
668 			if (arguments.length !== 1) {
669 				glow.debug.warn('[wrong count] glow.util.escapeRegex expects 1 argument, not '+arguments.length+'.');
670 			}
671 		/*gubed!*/
672 		return String(str).replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&');
673 	};
674 	
675 	/**
676 		@name glow.util.encodeUrl
677 		@function
678 		@description Encodes an object for use as a query string.
679 		
680 			Returns a string representing the object suitable for use 
681 			as a query string, with all values suitably escaped.
682 			It does not include the initial question mark. Where the 
683 			input field was an array, the key is repeated in the output.
684 		
685 		@param {Object} object The object to be encoded.
686 		
687 			This must be a hash whose values can only be primitives or 
688 			arrays of primitives.
689 		
690 		@returns {String}
691 		
692 		@example
693 			var getRef = glow.util.encodeUrl({foo: "Foo", bar: ["Bar 1", "Bar2"]});
694 			// will return "foo=Foo&bar=Bar%201&bar=Bar2"
695 	*/
696 	util.encodeUrl = function (object) {
697 		var type = _getType(object),
698 			paramsList = [],
699 			listLength = 0;
700 		
701 		/*!debug*/
702 			if (typeof object !== 'object') {
703 				throw new Error('glow.util.encodeUrl: cannot encode item');
704 			}
705 		/*gubed!*/
706 		
707 		for (var key in object) {
708 			type = _getType( object[key] );
709 
710 			/*!debug*/
711 				if (type !== 'Array' || type !== 'string') {
712 					glow.debug.warn('[wrong type] glow.util.encodeUrl expected Array or String value for "' + key + '", not ' + type + '.');
713 				}
714 			/*gubed!*/
715 			if (type === 'Array') {
716 				for(var i = 0, l = object[key].length; i < l; i++) {
717 					/*!debug*/
718 						if (_getType(object[key])[i] !== 'string') {
719 							glow.debug.warn('[wrong type] glow.util.encodeUrl expected string value for "' + key + '" value at index ' + i + ', not ' + _getType(object[key])[i] + '.');
720 						}
721 					/*gubed!*/
722 					paramsList[listLength++] = key + '=' + encodeURIComponent(object[key][i]);
723 				}
724 			}
725 			else { // assume string
726 				paramsList[listLength++] = key + '=' + encodeURIComponent(object[key]);
727 			}
728 		}
729 
730 		return paramsList.join('&');
731 	};
732 	
733 	/**
734 		@name glow.util.decodeUrl
735 		@function
736 		@description Decodes a query string into an object.
737 		
738 			Returns an object representing the data given by the query 
739 			string, with all values suitably unescaped. All keys in the 
740 			query string are keys of the object. Repeated keys result 
741 			in an array.
742 		
743 		@param {String} string The query string to be decoded.
744 		
745 			It should not include the initial question mark.
746 		
747 		@returns {Object}
748 		
749 		@example
750 			var getRef = glow.util.decodeUrl("foo=Foo&bar=Bar%201&bar=Bar2");
751 			// will return the object {foo: "Foo", bar: ["Bar 1", "Bar2"]}
752 	*/
753 	util.decodeUrl = function(text) {
754 		/*!debug*/
755 			if (arguments.length !== 1) {
756 				glow.debug.warn('[wrong count] glow.util.decodeUrl expects 1 argument, not '+arguments.length+'.');
757 			}
758 			if (typeof text !== 'string') {
759 				glow.debug.warn('[wrong type] glow.util.decodeUrl expects argument "text" to be of type string, not ' + typeof text + '.');
760 			}
761 		/*gubed!*/
762 		
763 		var result = {},
764 			keyValues = text.split(/[&;]/),
765 			thisPair,
766 			key,
767 			value;
768 
769 		for(var i = 0, leni = keyValues.length; i < leni; i++) {
770 			thisPair = keyValues[i].split('=');
771 			
772 			if (thisPair.length < 2) {
773 				key = keyValues[i];
774 				value = '';
775 			}
776 			else {
777 				key   = '' + decodeURIComponent(thisPair[0]);
778 				value = '' + decodeURIComponent(thisPair[1]);
779 			}
780 			
781 			// will be either: undefined, string or [object Array]
782 			switch (typeof result[key]) {
783 				case 'string':
784 					result[key] = [result[key], value];
785 					break;
786 				case 'undefined':
787 					result[key] = value;
788 					break;
789 				default:
790 					result[key].push(value);
791 			}
792 		}
793 
794 		return result;
795 	};
796 	
797 	/**
798 			@name glow.util.encodeJson
799 			@function
800 			@description Encodes an object into a string JSON representation.
801 			
802 				Returns a string representing the object as JSON.
803 			
804 			@param {Object} object The object to be encoded.
805 			 
806 				This can be arbitrarily nested, but must not contain 
807 				functions or cyclical structures.
808 			
809 			@returns {Object}
810 			
811 			@example
812 				var myObj = {foo: "Foo", bar: ["Bar 1", "Bar2"]};
813 				var getRef = glow.util.encodeJson(myObj);
814 				// will return '{"foo": "Foo", "bar": ["Bar 1", "Bar2"]}'
815 			*/
816 	util.encodeJson = function(object, options){
817 		function _encode(object, options)
818 				{
819 					if(_getType(object) == TYPES.ARRAY) {
820 						var type = JSON.ARRAY;
821 					} else {
822 						var type = JSON.HASH;
823 					}
824 
825 					var serial = [type.START];
826 					var len = 1;
827 					var dataType;
828 					var notFirst = false;
829 
830 					for(var key in object) {
831 						dataType = _getType(object[key]);
832 
833 						if(dataType != TYPES.UNDEFINED) { /* ignore undefined data */
834 							if(notFirst) {
835 								serial[len++] = JSON.DATA_SEPARATOR;
836 							}
837 							notFirst = true;
838 
839 							if(type.SHOW_KEYS) {
840 								serial[len++] = JSON.KEY_DELIMITER;
841 								serial[len++] = key;
842 								serial[len++] = JSON.KEY_DELIMITER;
843 								serial[len++] = JSON.KEY_SEPARATOR;
844 							}
845 
846 							switch(dataType) {
847 								case TYPES.FUNCTION:
848 									throw new Error("glow.data.encodeJson: cannot encode item");
849 									break;
850 								case TYPES.STRING:
851 								default:
852 									serial[len++] = JSON.STRING_DELIMITER;
853 									serial[len++] = glow.lang.replace(object[key], SLASHES.TEST, _replaceSlashes);
854 									serial[len++] = JSON.STRING_DELIMITER;
855 									break;
856 								case TYPES.NUMBER:
857 								case TYPES.BOOLEAN:
858 									serial[len++] = object[key];
859 									break;
860 								case TYPES.OBJECT:
861 								case TYPES.ARRAY:
862 									serial[len++] = _encode(object[key], options);
863 									break;
864 								case TYPES.NULL:
865 									serial[len++] = TYPES.NULL;
866 									break;
867 							}
868 						}
869 					}
870 					serial[len++] = type.END;
871 
872 					return serial.join(TEXT.EMPTY);
873 				}
874 
875 				options = options || {};
876 				var type = _getType(object);
877 
878 				if((type == TYPES.OBJECT) || (type == TYPES.ARRAY)) {
879 					return _encode(object, options);
880 				} else {
881 					throw new Error("glow.data.encodeJson: cannot encode item");
882 				}
883 		
884 	};
885 	/**
886 			@name glow.util.decodeJson
887 			@function
888 			@description Decodes a string JSON representation into an object.
889 				
890 				Returns a JavaScript object that mirrors the data given.
891 			
892 			@param {String} string The string to be decoded.
893 				Must be valid JSON. 
894 			
895 			@param {Object} opts
896 			
897 					Zero or more of the following as properties of an object:
898 					@param {Boolean} [opts.safeMode=false] Whether the string should only be decoded if it is  deemed "safe". 
899 					The json.org regular expression checks are used. 
900 			
901 			@returns {Object}
902 			
903 			@example
904 				var getRef = glow.util.decodeJson('{foo: "Foo", bar: ["Bar 1", "Bar2"]}');
905 				// will return {foo: "Foo", bar: ["Bar 1", "Bar2"]}
906 			
907 				var getRef = glow.util.decodeJson('foobar', {safeMode: true});
908 				// will throw an error
909 			*/
910 	util.decodeJson = function(text, options){
911 		if(_getType(text) != TYPES.STRING) {
912 					throw new Error("glow.data.decodeJson: cannot decode item");
913 				}
914 
915 				options = options || {};
916 				options.safeMode = options.safeMode || false;
917 
918 				var canEval = true;
919 
920 				if(options.safeMode) {
921 					canEval = (JSON.SAFE_PT1.test(text.replace(JSON.SAFE_PT2, TEXT.AT).replace(JSON.SAFE_PT3, JSON.ARRAY.END).replace(JSON.SAFE_PT4, TEXT.EMPTY)));
922 				}
923 
924 				if(canEval) {
925 					try {
926 						return eval(TEXT.OPEN + text + TEXT.CLOSE);
927 					}
928 					catch(e) {/* continue to error */}
929 				}
930 
931 				throw new Error("glow.data.decodeJson: cannot decode item");
932 	};
933 	/**
934 		@name glow.util.trim
935 		@function
936 		@description Removes leading and trailing whitespace from a string
937 	
938 		@param {string} str String to trim
939 	
940 		@returns {String}
941 	
942 			String without leading and trailing whitespace
943 	
944 		@example
945 			glow.util.trim("  Hello World  "); // "Hello World"
946 	*/
947 	util.trim = function(str) {
948 		//this optimisation from http://blog.stevenlevithan.com/archives/faster-trim-javascript
949 		return str.trim ? str.trim() : str.replace(/^\s*((?:[\S\s]*\S)?)\s*$/, '$1');
950 	};
951 	
952 	/**
953 		@name glow.util.interpolate
954 		@function
955 		@description Replaces placeholders in a string with data from an object
956 		
957 		@param {String} template The string containing {placeholders}
958 		@param {Object} data Object containing the data to be merged in to the template
959 			<p>The object can contain nested data objects and arrays, with nested object properties and array elements are accessed using dot notation. eg foo.bar or foo.0.</p>
960 			<p>The data labels in the object cannot contain characters used in the template delimiters, so if the data must be allowed to contain the default { and } delimiters, the delimters must be changed using the option below.</p>
961 		@param {Object} opts Options object
962 			@param {String} [opts.delimiter="{}"] Alternative label delimiter(s) for the template
963 				The first character supplied will be the opening delimiter, and the second the closing. If only one character is supplied, it will be used for both ends.
964 			@param {Boolean} [opts.escapeHtml=false] Escape any special html characters found in the data object
965 				Use this to safely inject data from the user into an HTML template. The glow.dom module
966 				must be present for this feature to work (an error will be thrown otherwise).
967 		
968 		@returns {String}
969 		
970 		@example
971 			var data = {
972 				name: "Domino",
973 				colours: ["black", "white"],
974 				family: {
975 					mum: "Spot",
976 					dad: "Patch",
977 					siblings: []
978 				}
979 			};
980 			var template = "My cat's name is {name}. His colours are {colours.0} & {colours.1}. His mum is {family.mum}, his dad is {family.dad} and he has {family.siblings.length} brothers or sisters.";
981 			var result = glow.util.interpolate(template, data);
982 			// result == "My cat's name is Domino. His colours are black & white. His mum is Spot, his dad is Patch and he has 0 brothers or sisters."
983 		
984 		@example
985 			var data = {
986 				name: 'Haxors!!1 <script src="hackhackhack.js"></script>'
987 			}
988 			var template = '<p>Hello, my name is {name}</p>';
989 			var result = glow.util.interpolate(template, data, {
990 				escapeHtml: true
991 			});
992 			// result == '<p>Hello, my name is Haxors!!1 <script src="hackhackhack.js"></script></p>'
993 	*/
994 	util.interpolate = function(template, data, opts) {
995 		var placeHolderRx,
996 			leftDelimiter,
997 			rightDelimiter,
998 			// div used for html escaping
999 			div;
1000 	
1001 		opts = opts || {};
1002 		
1003 		// make sure the dom module is around
1004 		if (opts.escapeHtml) {
1005 			div = glow('<div></div>');
1006 		}
1007 	
1008 		if (opts.delimiter == undefined) {
1009 			placeHolderRx = /\{[^{}]+\}/g;
1010 		}
1011 		else {
1012 			leftDelimiter = opts.delimiter.substr(0, 1).replace(regexEscape, "\\$1");
1013 			rightDelimiter = opts.delimiter.substr(1, 1).replace(regexEscape, "\\$1") || leftDelimiter;
1014 			placeHolderRx = new RegExp(leftDelimiter + "[^" + leftDelimiter + rightDelimiter + "]+" + rightDelimiter, "g");
1015 		}
1016 	
1017 		return template.replace(placeHolderRx, function (placeholder) {
1018 			var key = placeholder.slice(1, -1),
1019 				keyParts = key.split("."),
1020 				val,
1021 				i = 0,
1022 				len = keyParts.length;
1023 			
1024 			if (key in data) {
1025 				// need to be backwards compatible with "flattened" data.
1026 				val = data[key]; 
1027 			}
1028 			else {
1029 				// look up the chain
1030 				val = data;
1031 				for (; i < len; i++) {
1032 					if (keyParts[i] in val) {
1033 						val = val[ keyParts[i] ];
1034 					}
1035 					else {
1036 						return placeholder;
1037 					}
1038 				}
1039 			}
1040 			
1041 			if (opts.escapeHtml) {
1042 				val = div.text(val).html();
1043 			}
1044 			return val;
1045 		});
1046 	};
1047 	
1048 	/**
1049 		@example
1050 			glow.util.cookie(key); // get value for key
1051 			glow.util.cookie({key: val, key2: val2}, opts); // set all keys, vals
1052 			glow.util.cookie(key, val, opts); // set key, val
1053 			glow.util.cookie(); // get all keys, vals
1054 			
1055 			// use value of undefined
1056 	*/
1057 	util.cookie = function(key, value, opts) {
1058 		/*!debug*/
1059 			if (arguments.length > 3) {
1060 				glow.debug.warn('[wrong count] glow.util.cookie expects 3 or less arguments, not '+arguments.length+'.');
1061 			}
1062 			
1063 			if (arguments.length === 1 && _getType(key) !== 'string' && _getType(key) !== 'object') {
1064 				glow.debug.warn('[wrong type] glow.util.cookie expects argument "key" to be of type string or object, not ' + _getType(key) + '.');
1065 			}
1066 			
1067 			if (
1068 				arguments.length === 2
1069 				&&
1070 				(
1071 					! (_getType(key) === 'string' && _getType(value) === 'string')
1072 					||
1073 					! (_getType(key) === 'object' && _getType(value) === 'object')
1074 				)
1075 			) {
1076 				glow.debug.warn('[wrong type] glow.util.cookie expects arguments to be (key, val) or (keyVals, opts).');
1077 			}
1078 			
1079 			if (arguments.length === 3 && _getType(key) !== 'string' && _getType(value) !== 'string' && _getType(opts) !== 'object') {
1080 				glow.debug.warn('[wrong type] glow.util.cookie expects argument "key" and "value" to be strings and "options" to be an object.');
1081 			}
1082 			
1083 			if (opts && opts.debug && (typeof opts.expires !== 'number' || !opts.expires.toUTCString)) {
1084 				glow.debug.warn('[wrong type] glow.util.cookie expects opts.expires to be a number or a Date.');
1085 			}
1086 		/*gubed!*/
1087 		
1088 		
1089 		var date = '',
1090 			expires = '',
1091 			path = '',
1092 			domain = '',
1093 			secure = '',
1094 			keyValues,
1095 			thisPair,
1096 			key,
1097 			val,
1098 			cookieValues;
1099 		
1100 		if (opts) {
1101 			if (opts.expires) {
1102 				if (typeof opts.expires === 'number') {
1103 					date = new Date();
1104 					date.setTime(date.getTime() + (opts.expires * 24 * 60 * 60 * 1000)); // opts.expires days
1105 				}
1106 				else { // is already a Date
1107 					date = opts.expires;
1108 				}
1109 				expires = '; expires=' + date.toUTCString();
1110 			}
1111 		   
1112 			path = opts.path ? '; path=' + (opts.path) : '';
1113 			domain = opts.domain ? '; domain=' + (opts.domain) : '';
1114 			secure = opts.secure ? '; secure' : '';
1115 		}
1116 		else {
1117 			opts = {};
1118 		}
1119 		
1120 		if (typeof key === 'string' && typeof value === 'string') { // a single setter
1121 			document.cookie = key + '=' + encodeURIComponent(value) + expires + path + domain + secure;
1122 		}
1123 		else if (typeof key === 'object') { // an all setter
1124 			for (var p in key) {
1125 				document.cookie = p + '=' + encodeURIComponent(key[p]) + expires + path + domain + secure;
1126 			}
1127 		}
1128 		else { // a getter
1129 			cookieValues = {};
1130 			if (document.cookie && document.cookie != '') {
1131 				keyValues = document.cookie.split(/; ?/);
1132 				for (var i = 0, leni = keyValues.length; i < leni; i++) {
1133 					thisPair = keyValues[i].split('=');
1134 					
1135 					cookieValues[thisPair[0]] = decodeURIComponent(thisPair[1]);
1136 				}
1137 			}
1138 			
1139 			if (typeof key === 'string') { // a single getter
1140 				return cookieValues[key];
1141 			}
1142 			else if (typeof key === 'undefined') { // an all getter
1143 				return cookieValues;
1144 			}
1145 		}
1146 	};
1147 	
1148 	util.removeCookie = function(key) {
1149 		util.cookie(key, '', {expires: -1});
1150 	};
1151 	
1152 	// export
1153 	glow.util = util;
1154 });
1155 Glow.provide(function(glow) {
1156 	/**
1157 	@name glow.events
1158 	@namespace
1159 	@description Handling custom events
1160 	*/
1161 	var events = {};
1162 		
1163 	/* storage variables */
1164 	
1165 	var eventListeners = {}, // eventName: [ [callback, thisVal], ... ] 
1166 		eventId = 1,
1167 		objIdCounter = 1, 
1168 		eventKey = '__eventId' + glow.UID; 
1169 
1170 	
1171 	/**
1172 	@name glow.events.addListeners
1173 	@function
1174 	@param {Object[]} attachTo Array of objects to add listeners to.
1175 	@param {string} name Name of the event to listen for.
1176 		Event names are case sensitive.
1177 	@param {function} callback Function to call when the event is fired.
1178 		The callback will be passed a single event object. The type of this
1179 		object depends on the event (see documentation for the event
1180 		you're listening to).
1181 	@param {Object} [thisVal] Value of 'this' within the callback.
1182 		By default, this is the object being listened to.
1183 	@see glow.events.Target#fire
1184 	@description Convenience method to add listeners to many objects at once.
1185 		If you want to add a listener to a single object, use its
1186 		'on' method.
1187 	*/
1188 	events.addListeners = function (attachTo, name, callback, thisVal) {
1189 		var listenerIds = [],
1190 			objIdent,
1191 			listener,
1192 			eventsOnObject,
1193 			currentListeners;
1194 	
1195 		//attach the event for each element, return an array of listener ids
1196 		var i = attachTo.length;
1197 		while (i--) {
1198 			objIdent = attachTo[i][eventKey];
1199 			if (!objIdent){
1200 				objIdent = attachTo[i][eventKey] = objIdCounter++;
1201 			}
1202 					
1203 			listener = [ callback, thisVal ];
1204 			eventsOnObject = eventListeners[objIdent];
1205 			if(!eventsOnObject){
1206 				eventsOnObject = eventListeners[objIdent] = {};
1207 			}
1208 					
1209 			currentListeners = eventsOnObject[name];
1210 			if(!currentListeners){
1211 				eventsOnObject[name] = [listener];
1212 			}
1213 			else{
1214 				currentListeners[currentListeners.length] = listener;
1215 			}							
1216 		}
1217 		return events;
1218 	};
1219 	
1220 	events._getPrivateEventKey = function(node) {
1221 		if (!node[eventKey]) {
1222 			node[eventKey] = objIdCounter++;
1223 		}
1224 		
1225 		return node[eventKey];
1226 	}
1227 	
1228 	/**
1229 	@name glow.events.fire
1230 	@function
1231 	@param {Object[]} items      Array of objects to add listeners to
1232 	@param {string}   eventName  Name of the event to fire
1233 	@param {glow.events.Event|Object} [event] Event object to pass into listeners.
1234        You can provide a simple object of key-value pairs which will
1235        be added as properties on the glow.events.Event instance.
1236 		
1237 	@description Convenience method to fire events on multiple items at once.
1238 		If you want to fire events on a single object, use its
1239 		'fire' method.
1240 	*/
1241 		
1242 	events.fire = function (items, eventName, event) {
1243 		if (! event) {
1244 			event = new events.Event();
1245 		}
1246 		else if ( event.constructor === Object ) {
1247 			event = new events.Event( event )
1248 		}
1249 		
1250 		// for loop, because order matters!
1251 		for(var i = 0, len = items.length; i < len; i++) { 
1252 			callListeners(items[i], eventName, event);
1253 		}
1254 			
1255 		return event;
1256 	};
1257 
1258 	
1259 	/**
1260 	 @name glow.events-callListeners
1261 	 @private
1262 	*/
1263 	function callListeners(item, eventName, event, thisVal) {
1264 		var objIdent = item[eventKey],
1265 			listenersForEvent,
1266 			returnedVal;			
1267 		
1268 		// set the attachedTo value for this event
1269 		event.attachedTo = event.attachedTo || item;
1270 		
1271 		if (!objIdent || !eventListeners[objIdent]) {
1272 			return event;
1273 		}
1274 		
1275 		listenersForEvent = eventListeners[objIdent][eventName];
1276 			
1277 		if (!listenersForEvent) {
1278 			return event;
1279 		}
1280 		// Slice to make sure we get a unique copy.
1281 		listenersForEvent = listenersForEvent.slice(0);
1282 		for (var i = 0, len = listenersForEvent.length; i < len; i++){
1283 			returnedVal = listenersForEvent[i][0].call((listenersForEvent[i][1] || thisVal || item), event);
1284 			if (returnedVal === false){
1285 				event.preventDefault();
1286 			}
1287 		}
1288 			
1289 		return event;
1290 	}
1291 	events._callListeners = callListeners;
1292 		
1293 		
1294 	/**
1295 	@name glow.events.removeAllListeners
1296 	@function
1297 	@param {Object[]} items Items to remove events from		    
1298 	@description Removes all listeners attached to a given object.
1299 		This removes not only listeners you added, but listeners others
1300 		added too. For this reason it should only be used as part of a cleanup
1301 		operation on objects that are about to be destroyed.
1302 	*/
1303 	
1304 	events.removeAllListeners = function (items) {
1305 		var objIdent,
1306 		i = items.length;		
1307 		
1308 		while(i--){
1309 			
1310 			objIdent = items[i][eventKey];
1311 			
1312 			if (!objIdent) {
1313 				return false;
1314 			}
1315 			else {
1316 				delete eventListeners[objIdent];
1317 			}
1318 		}
1319 
1320 		return true;
1321 	};
1322 
1323 
1324 	/**
1325 	@name glow.events.removeListeners
1326 	@function
1327 	@param {Object[]} items Items to remove events from.
1328 	@param {string} eventName Name of the event to remove.
1329 	@param {function} callback A reference to the original callback used when the listener was added.
1330 	@decription Removes listeners for an event.
1331 	*/
1332 	events.removeListeners = function (item, eventName, callback) { /* TODO: items! */
1333 		var objIdent,
1334 			listenersForEvent,
1335 			i = item.length;
1336 		
1337 	
1338 		while(i--){
1339 			
1340 			objIdent = item[i][eventKey];
1341 				
1342 			if(!objIdent || !eventListeners[objIdent]){
1343 				return events;
1344 			}
1345 			
1346 		
1347 			listenersForEvent = eventListeners[objIdent][eventName];
1348 			if(!listenersForEvent){
1349 				return events;
1350 			}
1351 			
1352 			// for loop, because order matters
1353 			for(var j = 0, lenj = listenersForEvent.length; j < lenj; j++){						
1354 				if (listenersForEvent[j][0] === callback){
1355 					listenersForEvent.splice(j, 1);
1356 					break;
1357 				}
1358 		
1359 			}
1360 		}
1361 		
1362 		return events;			
1363 	};
1364 	
1365 	/**
1366 		Copies the events from one NodeList to another
1367 		@private
1368 		@name glow.events._copyEvents
1369 		@see glow.NodeList#clone
1370 		@function
1371 	*/
1372 	events._copyDomEvents = function(from, to){
1373 		var objIdent,
1374 			i = from.length,
1375 			j, jLen,
1376 			listeners,
1377 			listenersForEvent,
1378 			eventName,
1379 			toItem;
1380 		
1381 		// loop over elements
1382 		while(i--){
1383 			objIdent = from[i][eventKey];
1384 			listeners = eventListeners[objIdent];
1385 			
1386 			if (objIdent){
1387 				toItem = to.slice(i, i+1);
1388 				
1389 				// loop over event names (of listeners attached)
1390 				for ( eventName in listeners ) {
1391 					listenersForEvent = listeners[eventName];
1392 					
1393 					// loop over individual listeners and add them to the 'to' item
1394 					// loop forward to preserve event order
1395 					for (j = 0, jLen = listenersForEvent.length; j < jLen; j++) {
1396 						// add listener
1397 						toItem.on( eventName, listenersForEvent[j][0], listenersForEvent[j][1] );
1398 					}
1399 				}
1400 			}
1401 		}
1402 	}
1403 	/**
1404 	@name glow.events._getListeners
1405 	@private
1406 	@function
1407 	@param {Object} item Item to find events for
1408 	@decription Returns a list of listeners attached for the given item.
1409 	
1410 	*/	
1411 	events._getListeners = function(item){
1412 		var objIdent = item[eventKey];
1413 			
1414 		if (!objIdent) {
1415 			return {};
1416 		}
1417 		else {
1418 			// todo: need to return listeners in a sensible format
1419 			return eventListeners[objIdent];
1420 		}
1421 			
1422 	};
1423 	
1424 	///**
1425 	//@name glow.events.hasListener
1426 	//@function
1427 	//@param {Object[]} item  Item to find events for
1428 	//@param {String}   eventName  Name of the event to match
1429 	//@decription Returns true if an event is found for the item supplied
1430 	//
1431 	//*/
1432 	//
1433 	//glow.events.hasListener = function (item, eventName) {
1434 	//	var objIdent,
1435 	//		listenersForEvent;
1436 	//		
1437 	//	for (var i = 0, len = item.length; i < len; i++) {	
1438 	//		objIdent = item[i][eventKey];
1439 	//			
1440 	//		if (!objIdent || !eventListeners[objIdent]) {
1441 	//			return false;
1442 	//		}
1443 	//				
1444 	//		listenersForEvent = eventListeners[objIdent][eventName];
1445 	//		if (!listenersForEvent) {
1446 	//			return false;
1447 	//		}
1448 	//		else {
1449 	//			return true;							
1450 	//		}					
1451 	//	}
1452 	//	
1453 	//	return false;			
1454 	//};
1455 	
1456 	/**
1457 	@name glow.events.Target
1458 	@class
1459 	@description An object that can have event listeners and fire events.
1460 		Extend this class to make your own objects have 'on' and 'fire'
1461 		methods.
1462 		
1463 	@example
1464 		// Ball is our constructor
1465 		function Ball() {
1466 			// ...
1467 		}
1468 		       
1469 		// make Ball inherit from Target
1470 		glow.util.extend(Ball, glow.events.Target, {
1471 			// additional methods for Ball here, eg:
1472 			bowl: function() {
1473 				// ...
1474 			}
1475 		});
1476 		       
1477 		// now instances of Ball can receive event listeners
1478 		var myBall = new Ball();
1479 		myBall.on('bounce', function() {
1480 			alert('BOING!');
1481 		});
1482 		       
1483 		// and events can be fired from Ball instances
1484 		myBall.fire('bounce');
1485 	*/
1486 	
1487 	events.Target = function () {
1488 			
1489 	};
1490 	var targetProto = events.Target.prototype;
1491 		
1492 	/**
1493 	@name glow.events.Target.extend
1494 	@function
1495 	@param {Object} obj Object to add Target instance methods to.
1496 		
1497 	@description Convenience method to add Target instance methods onto an object.
1498 		If you want to add Target methods to a class, extend glow.events.Target instead.
1499 		       
1500 	@example
1501 		var myApplication = {};
1502 		       
1503 		glow.events.Target.extend(myApplication);
1504 		       
1505 		// now myApplication can fire events...
1506 		myApplication.fire('load');
1507 		       
1508 		// and other objects can listen for those events
1509 		myApplication.on('load', function(e) {
1510 			alert('App loaded');
1511 		});
1512 	*/
1513 	
1514 	events.Target.extend = function (obj) {
1515 		glow.util.apply( obj, glow.events.Target.prototype );
1516 	};
1517 		
1518 	/**
1519 	@name glow.events.Target#on
1520 	@function
1521 	@param {string} eventName Name of the event to listen for.
1522 	@param {function} callback Function to call when the event fires.
1523 		The callback is passed a single event object. The type of this
1524 		object depends on the event (see documentation for the event
1525 		you're listening to).
1526 	@param {Object} [thisVal] Value of 'this' within the callback.
1527 		By default, this is the object being listened to.
1528 		
1529 	@description Listen for an event
1530 		
1531 	@returns this
1532 		
1533 	@example
1534 		myObj.on('show', function() {
1535 		    // do stuff
1536 		});
1537 	*/
1538 	
1539 	targetProto.on = function(eventName, callback, thisVal) {
1540 		glow.events.addListeners([this], eventName, callback, thisVal);
1541 		return this;
1542 	}
1543 		
1544 	/**
1545 	@name glow.events.Target#detach
1546 	@function
1547 	@param {string} eventName Name of the event to remove.
1548 	@param {function} callback Callback to detach.
1549 	@param {Object} [thisVal] Value of 'this' within the callback.
1550 		By default, this is the object being listened to.
1551 	@description Remove an event listener.
1552 		
1553 	@returns this Target object
1554 		
1555 	@example
1556 		function showListener() {
1557 		    // ...
1558 		}
1559 		       
1560 		// add listener
1561 		myObj.on('show', showListener);
1562 		       
1563 		// remove listener
1564 		myObj.detach('show', showListener);
1565 		       
1566 	@example
1567 		// note the following WILL NOT WORK
1568 		       
1569 		// add listener
1570 		myObj.on('show', function() {
1571 		    alert('hi');
1572 		});
1573 		       
1574 		// remove listener
1575 		myObj.detach('show', function() {
1576 			alert('hi');
1577 		});
1578 		       
1579 		// this is because both callbacks are different function instances
1580 	
1581 	*/
1582 		
1583 	targetProto.detach = function(eventName, callback) {
1584 		glow.events.removeListeners(this, eventName, callback);
1585 		return this;
1586 	}
1587 		
1588 	/**
1589 	@name glow.events.Target#fire
1590 	@function
1591 	@param {string} eventName Name of the event to fire.
1592 	@param {glow.events.Event|Object} [event] Event object to pass into listeners.
1593 		    You can provide a simple object of key-value pairs which will
1594 		    be added as properties of a glow.events.Event instance.
1595 		
1596 	@description Fire an event.
1597 		
1598 	@returns glow.events.Event
1599 		
1600 	@example
1601 		myObj.fire('show');
1602 		       
1603 	@example
1604 		// adding properties to the event object
1605 		myBall.fire('bounce', {
1606 		    velocity: 30
1607 		});
1608 	       
1609 	@example
1610 		// BallBounceEvent extends glow.events.Event but has extra methods
1611 		myBall.fire( 'bounce', new BallBounceEvent(myBall) );
1612 	*/
1613 	
1614 	targetProto.fire = function(eventName, event) {
1615 		if (! event) {
1616 			event = new events.Event();
1617 		}
1618 		else if ( event.constructor === Object ) {
1619 			event = new events.Event( event )
1620 		}
1621 		
1622 		return callListeners(this, eventName, event);
1623 	}
1624 		
1625 	/**
1626 	@name glow.events.Event
1627 	@class
1628 	@param {Object} [properties] Properties to add to the Event instance.
1629 		Each key-value pair in the object will be added to the Event as
1630 		properties.
1631 	       
1632 	@description Describes an event that occurred.
1633 		You don't need to create instances of this class if you're simply
1634 		listening to events. One will be provided as the first argument
1635 		in your callback.
1636 	       
1637 	@example
1638 		// creating a simple event object
1639 		var event = new glow.events.Event({
1640 			velocity: 50,
1641 			direction: 180
1642 		});
1643 		       
1644 		// 'velocity' and 'direction' are simple made-up properties
1645 		// you may want to add to your event object
1646 		       
1647 	@example
1648 		// inheriting from glow.events.Event to make a more
1649 		// specialised event object
1650 		       
1651 		function RocketEvent() {
1652 			// ...
1653 		}
1654 		       
1655 		// inherit from glow.events.Event
1656 		glow.util.extend(RocketEvent, glow.events.Event, {
1657 			getVector: function() {
1658 				return // ...
1659 			}
1660 		});
1661 		       
1662 		// firing the event
1663 		rocketInstance.fire( 'landingGearDown', new RocketEvent() );
1664 		       
1665 		// how a user would listen to the event
1666 		rocketInstance.on('landingGearDown', function(rocketEvent) {
1667 			var vector = rocketEvent.getVector();
1668 		});
1669 	*/
1670 		
1671 	events.Event = function(obj) {			
1672 		if (obj) {
1673 			glow.util.apply(this, obj);
1674 		}
1675 	};
1676 	var eventProto = events.Event.prototype;
1677 	/**
1678 	@name glow.events.Event#attachedTo
1679 	@type {Object}
1680 	@description The object the listener was attached or delegated to.
1681 	*/
1682 
1683 		
1684 	/**
1685 	@name glow.events.Event#preventDefault
1686 	@function
1687 	@description Prevent the default action of the event.
1688 		Eg, if the click event on a link is cancelled, the link
1689 		is not followed.
1690 		       
1691 		Returning false from an event listener has the same effect
1692 		as calling this function.
1693 		       
1694 		For custom events, it's down to whatever fired the event
1695 		to decide what to do in this case. See {@link glow.events.Event#defaultPrevented defaultPrevented}
1696 		       
1697 	@example
1698 		myLinks.on('click', function(event) {
1699 			event.preventDefault();
1700 		});
1701 		       
1702 		// same as...
1703 		       
1704 		myLinks.on('click', function(event) {
1705 			return false;
1706 		});
1707 	*/
1708 	
1709 	eventProto.preventDefault = function () {	
1710 		this._defaultPrevented = true;		
1711 	};
1712 
1713 		
1714 	/**
1715 	@name glow.events.Event#defaultPrevented
1716 	@function
1717 	@description Has the default been prevented for this event?
1718 		This should be used by whatever fires the event to determine if it should
1719 		carry out of the default action.
1720 		
1721 	@returns {Boolean} Returns true if {@link glow.events.Event#preventDefault preventDefault} has been called for this event.
1722 		
1723 	@example
1724 		// fire the 'show' event
1725 		// read if the default action has been prevented
1726 		if ( overlayInstance.fire('show').defaultPrevented() == false ) {
1727 		    // go ahead and show
1728 		}
1729 	*/
1730 	
1731 	eventProto.defaultPrevented = function () {
1732 		return this._defaultPrevented;
1733 	};
1734 
1735 	
1736 	/* Export */
1737 	glow.events = events;
1738 });
1739 Glow.provide(function(glow) {
1740 	var document = window.document,
1741 		undef = undefined,
1742 		domEventHandlers = [], // like: domEventHandlers[uniqueId][eventName].count, domEventHandlers[uniqueId][eventName].callback
1743 		// shortcuts to aim compression
1744 		events = glow.events,
1745 		_callListeners = events._callListeners,
1746 		_getPrivateEventKey = events._getPrivateEventKey,
1747 		// used for feature detection
1748 		supportsActivateDeactivate = (document.createElement('div').onactivate !== undefined);
1749 	
1750 	/** 
1751 		@name glow.events.DomEvent
1752 		@constructor
1753 		@extends glow.events.Event
1754 		
1755 		@param {Event|string} nativeEvent A native browser event read properties from, or the name of a native event.
1756 		
1757 		@param {Object} [properties] Properties to add to the Event instance.
1758 		   Each key-value pair in the object will be added to the Event as
1759 		   properties
1760 		
1761 		@description Describes a DOM event that occurred
1762 		   You don't need to create instances of this class if you're simply
1763 		   listening to events. One will be provided as the first argument
1764 		   in your callback.
1765 	*/
1766 	function DomEvent(e, properties) {
1767 		/** 
1768 			@name glow.events.DomEvent#nativeEvent
1769 			@type {Event | MouseEvent | UIEvent}
1770 			@description The native event object provided by the browser.
1771 		 */
1772 		this.nativeEvent = e;
1773 		
1774 		/** 
1775 			@name glow.events.DomEvent#type
1776 			@type {string}
1777 			@description The native type of the event, like 'click' or 'keydown'.
1778 		 */
1779 		this.type = e.type;
1780 		
1781 		/** 
1782 			@name glow.events.DomEvent#source
1783 			@type {HTMLElement}
1784 			@description The element that the event originated from.
1785 				For example, you could attach a listener to an <ol> element to listen for
1786 				clicks. If the user clicked on an <li> the source property would be the
1787 				<li> element, and {@link glow.DomEvent#attachedTo attachedTo} would be
1788 				the <ol>.
1789 		*/
1790 		this.source = e.target || e.srcElement || undefined;
1791 		
1792 		// some rare cases crop up in Firefox where the source is a text node
1793 		if (this.source && this.source.nodeType === 3) {
1794 			this.source = this.source.parentNode;
1795 		}
1796 		
1797 		/** 
1798 			@name glow.events.DomEvent#related
1799 			@type {HTMLElement}
1800 			@description A related HTMLElement
1801 				For mouseover / mouseenter events, this will refer to the previous element
1802 				the mouse was over.
1803 				
1804 				For mouseout / mouseleave events, this will refer to the element the mouse
1805 				is now over.
1806 		*/
1807 		this.related = e.relatedTarget || (this.type == 'mouseover' ? e.fromElement : e.toElement);
1808 		
1809 		/** 
1810 			@name glow.events.DomEvent#shiftKey
1811 			@type {boolean | undefined}
1812 			@description Was the shift key pressed during the event?
1813 		*/
1814 		this.shiftKey = (e.shiftKey === undef)? undef : !!e.shiftKey;
1815 		
1816 		/** 
1817 			@name glow.events.DomEvent#altKey
1818 			@type {boolean | undefined}
1819 			@description Was the alt key pressed during the event?
1820 		*/
1821 		this.altKey = (e.altKey === undef)? undef : !!e.altKey;
1822 		
1823 		/** 
1824 			@name glow.events.DomEvent#ctrlKey
1825 			@type {boolean | undefined}
1826 			@description Was the ctrl key pressed during the event?
1827 		*/
1828 		this.ctrlKey = (e.ctrlKey === undef)? undef : !!e.ctrlKey;
1829 		
1830 		/**
1831 			@name glow.events.DomEvent#button
1832 			@type {number | undefined}
1833 			@description A number representing which button was pressed.
1834 				0 for the left button, 1 for the middle button or 2 for the right button.
1835 		*/
1836 		this.button = glow.env.ie ? (e.button & 1 ? 0 : e.button & 2 ? 2 : 1) : e.button;
1837 		
1838 		/** 
1839 			@name glow.events.DomEvent#mouseTop
1840 			@type {number}
1841 			@description The vertical position of the mouse pointer in the page in pixels.
1842 		*/
1843 		/** 
1844 			@name glow.events.DomEvent#mouseLeft
1845 			@type {number}
1846 			@description The horizontal position of the mouse pointer in the page in pixels.
1847 		*/
1848 		if (e.pageX !== undef || e.pageY !== undef) {
1849 			this.mouseTop = e.pageY;
1850 			this.mouseLeft = e.pageX;
1851 		}
1852 		else if (e.clientX !== undef || e.clientY !== undef) {
1853 			this.mouseTop = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
1854 			this.mouseLeft = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
1855 		}
1856 		
1857 		/** 
1858 			@name glow.events.DomEvent#wheelData
1859 			@type {number}
1860 			@description The number of clicks the mouse wheel moved.
1861 				Up values are positive, down values are negative.
1862 		*/
1863 		if (this.type == 'mousewheel') {
1864 			// this works in latest opera, but have read that it needs to be switched in direction
1865 			// if there was an opera bug, I can't find which version it was fixed in
1866 			this.wheelDelta =
1867 				e.wheelDelta ? e.wheelDelta / 120 :
1868 				e.detail ? - e.detail / 3 :
1869 				0;
1870 		}
1871 		
1872 		for (var key in properties) {
1873 			this[key] = properties[key];
1874 		}
1875 	}
1876 	
1877 	glow.util.extend(DomEvent, events.Event, {
1878 		// no docs for this as it simply adds DOM behaviour to glow.events.Event#preventDefault
1879 		preventDefault: function() {
1880 			var nativeEvent = this.nativeEvent;
1881 			if (nativeEvent) {
1882 				nativeEvent.preventDefault && nativeEvent.preventDefault();
1883 				nativeEvent.returnValue = false;
1884 			}
1885 			// call the original method
1886 			events.Event.prototype.preventDefault.call(this);
1887 			return this;
1888 		},
1889 		/**
1890 			@name glow.events.DomEvent#stopPropagation
1891 			@function
1892 			@description Stop an event bubbling any further.
1893 				For instance, if you had 2 click listeners, one on a link and
1894 				one on a parent element, if you stopped the event propogating in the
1895 				link listener, the event will never be fired on the parent element.
1896 			
1897 			@returns this
1898 		*/
1899 		stopPropagation: function() {
1900 			var nativeEvent = this.nativeEvent;
1901 			
1902 			if (nativeEvent) {
1903 				// the ie way
1904 				nativeEvent.cancelBubble = true;
1905 				// the proper way
1906 				nativeEvent.stopPropagation && nativeEvent.stopPropagation();
1907 			}
1908 			return this;
1909 		}
1910 	});
1911 	
1912 	/**
1913 		Add listener for an event fired by the browser.
1914 		@private
1915 		@name glow.events._addDomEventListener
1916 		@see glow.NodeList#on
1917 		@function
1918 	*/
1919 	events._addDomEventListener = function(nodeList, eventName) {
1920 		var i = nodeList.length, // TODO: should we check that this nodeList is deduped?
1921 			attachTo,
1922 			id;
1923 	
1924 		while (i--) {
1925 			attachTo = nodeList[i];
1926 
1927 			id = _getPrivateEventKey(attachTo);
1928 
1929 			// check if there is already a handler for this kind of event attached
1930 			// to this node (which will run all associated callbacks in Glow)
1931 			if (!domEventHandlers[id]) { domEventHandlers[id] = {}; }
1932 
1933 			if (domEventHandlers[id][eventName] && domEventHandlers[id][eventName].count > 0) { // already have handler in place
1934 				domEventHandlers[id][eventName].count++;
1935 				continue;
1936 			}
1937 
1938 			// no bridge in place yet
1939 			domEventHandlers[id][eventName] = { count:1 };
1940 			
1941 			// attach a handler to tell Glow to run all the associated callbacks
1942 			(function(attachTo) {
1943 				var handler = domHandle(attachTo, eventName);
1944 				
1945 				if (attachTo.addEventListener) { // like DOM2 browsers	
1946 					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
1947 				}
1948 				else if (attachTo.attachEvent) { // like IE
1949 					attachTo.attachEvent('on' + handler.domName, handler);
1950 				}
1951 				// older browsers?
1952 				
1953 				domEventHandlers[id][eventName].callback = handler;
1954 			})(attachTo);
1955 		}
1956 	}
1957 	
1958 	function domHandle(attachTo, eventName) {
1959 		var handler;
1960 		
1961 		if (eventName === 'mouseenter' || eventName === 'mouseleave') {
1962 			// mousenter and mouseleave handle their own delegation as its non-standard
1963 			handler = function(nativeEvent) {
1964 				var domEvent = new DomEvent(nativeEvent),
1965 					container,
1966 					selector,
1967 					elementsToTest = _getDelegateMatches(attachTo, eventName, domEvent);
1968 				
1969 				// add this element to the delegates
1970 				elementsToTest.push( [attachTo] );
1971 				
1972 				for (var i = 0, leni = elementsToTest.length; i < leni; i++) {
1973 					container = elementsToTest[i][0];
1974 					selector = elementsToTest[i][1];
1975 					
1976 					if (!new glow.NodeList(container).contains(domEvent.related)) {
1977 						_callListeners(attachTo, selector ? eventName + '/' + selector : eventName, domEvent, container); // fire() returns result of callback
1978 					}
1979 				}
1980 				return !domEvent.defaultPrevented();
1981 			};
1982 			
1983 			handler.domName = (eventName === 'mouseenter') ? 'mouseover' : 'mouseout';
1984 		}
1985 		// handle blur & focus differently for IE so it bubbles
1986 		else if ( supportsActivateDeactivate && (eventName === 'focus' || eventName === 'blur') ) {
1987 			// activate and deactivate are like focus and blur but bubble
1988 			// However, <body> and <html> also activate so we need to fix that
1989 			handler = function(nativeEvent) {
1990 				var nodeName = nativeEvent.srcElement.nodeName;
1991 				if (nodeName !== 'HTML' && nodeName !== 'BODY') {
1992 					_callDomListeners( attachTo, eventName, new DomEvent(nativeEvent) );
1993 				}
1994 			}
1995 			
1996 			handler.domName = (eventName === 'focus') ? 'activate' : 'deactivate';
1997 		}
1998 		else {
1999 			handler = function(nativeEvent) {
2000 				var domEvent = new DomEvent(nativeEvent);
2001 				_callDomListeners(attachTo, eventName, domEvent); // fire() returns result of callback
2002 				
2003 				return !domEvent.defaultPrevented();
2004 			};
2005 			
2006 			handler.domName = eventName;
2007 		}
2008 		
2009 		return handler;
2010 	}
2011 	
2012 	
2013 	/**
2014 		Remove listener for an event fired by the browser.
2015 		@private
2016 		@name glow.events._removeDomEventListener
2017 		@see glow.NodeList#detach
2018 		@function
2019 	*/
2020 	events._removeDomEventListener = function(nodeList, eventName) {
2021 		var i = nodeList.length,
2022 			attachTo,
2023 			id,
2024 			bridge,
2025 			handler;
2026 			
2027 		while (i--) {
2028 			attachTo = nodeList[i];
2029 			
2030 			// skip if there is no bridge for this kind of event attached
2031 			id = _getPrivateEventKey(attachTo);
2032 			if (!domEventHandlers[id] || !domEventHandlers[id][eventName]) { continue; }
2033 
2034 			bridge = domEventHandlers[id][eventName];
2035 			
2036 			// one less listener associated with this event
2037 			if ( !--bridge.count ) {
2038 				// no more listeners associated with this event
2039 				handler = bridge.callback;
2040 				
2041 				if (attachTo.removeEventListener) { // like DOM2 browsers	
2042 					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
2043 				}
2044 				else if (attachTo.detachEvent) { // like IE
2045 					attachTo.detachEvent('on' + handler.domName, handler);
2046 				}
2047 				domEventHandlers[id][eventName] = undefined;
2048 			}
2049 		}
2050 	}
2051 
2052 // see: http://developer.yahoo.com/yui/3/event/#eventsimulation
2053 // see: http://developer.yahoo.com/yui/docs/YAHOO.util.UserAction.html
2054 // 	function simulateDomEvent(nodeList, domEvent) {
2055 // 		var i = nodeList.length,
2056 // 			eventName = domEvent.type,
2057 // 			nativeEvent,
2058 // 			node,
2059 // 			fire;
2060 // 		
2061 // 		if (document.createEvent) {
2062 // 			var nativeEvent = document.createEvent('MouseEvent'); // see: 
2063 // 			nativeEvent.initEvent(eventName, true, true);
2064 // 			
2065 // 			fire = function(el) {
2066 // 				return !el.dispatchEvent(nativeEvent);
2067 // 			}
2068 // 		}
2069 // 		else {
2070 // 			fire = function(el) {
2071 // 				var nativeEvent = document.createEventObject(); 
2072 // 				return el.fireEvent('on'+eventName, nativeEvent);
2073 // 			}
2074 // 		}
2075 // 		
2076 // 		while (i--) {
2077 // 			node = nodeList[i];
2078 // 			if (node.nodeType !== 1) { continue; }
2079 // 			fire(node);
2080 // 		}
2081 // 	}
2082 
2083 	/*
2084 		The following is a proposal for dealing with event delegation without
2085 		multiple bridges. This allows us to have only one listener per element per event
2086 		therefore only one search for delegates per event.
2087 	*/
2088 	
2089 	// structure:
2090 	// delegates[eventId][eventName][selector] = number of delegates listening for that selector (for that event on that element)
2091 	var delegates = {}
2092 	
2093 	/**
2094 		@name glow.events._registerDelegate
2095 		@private
2096 		@function
2097 		@description Register a delegated event
2098 			This allows selectors for a given element & eventName to be retrieved later
2099 		
2100 		@param {glow.NodeList} nodeList Elements to register
2101 		@param {string} eventName
2102 		@param {string} selector Selector to match for the delegate
2103 	*/
2104 	events._registerDelegate = function(nodeList, eventName, selector) {
2105 		var id,
2106 			i = nodeList.length,
2107 			delegatesForEvent;
2108 		
2109 		while (i--) {
2110 			id = _getPrivateEventKey( nodeList[i] );
2111 			delegates[id] = delegates[id] || {};
2112 			delegatesForEvent = delegates[id][eventName] = delegates[id][eventName] || {};
2113 			// increment the count or set it to 1
2114 			delegatesForEvent[selector] = delegatesForEvent[selector] + 1 || 1;
2115 		}
2116 	};
2117 	
2118 	/**
2119 		@name glow.events._unregisterDelegate
2120 		@private
2121 		@function
2122 		@description Unregister a delegated event
2123 		
2124 		@param {glow.NodeList} nodeList Elements to unregister
2125 		@param {string} eventName
2126 		@param {string} selector Selector to match for the delegate
2127 	*/
2128 	events._unregisterDelegate = function(nodeList, eventName, selector) {
2129 		var id,
2130 			selectorCounts,
2131 			i = nodeList.length;
2132 		
2133 		while (i--) {
2134 			id = _getPrivateEventKey( nodeList[i] );
2135 			if ( !delegates[id] || !( selectorCounts = delegates[id][eventName] ) ) { continue; }
2136 			
2137 			// either decrement the count or delete the entry
2138 			if ( selectorCounts[selector] && --selectorCounts[selector] === 0 ) {
2139 				delete selectorCounts[selector];
2140 			}
2141 		}
2142 	};
2143 	
2144 	/**
2145 		@name glow.events._getDelegateMatches
2146 		@private
2147 		@function
2148 		@description Get the elements which qualify for a delegated event
2149 		
2150 		@param {HTMLElement} element Element the listener is attached to
2151 		@param {string} eventName
2152 		@param {glow.events.DomEvent} event DOM event for the original event
2153 			The events source will be used as a place to start searching
2154 			
2155 		@returns {Array[]} An array of arrays like [matchedNode, selectorMatched]
2156 	*/
2157 	var _getDelegateMatches = events._getDelegateMatches = function(element, eventName, event) {
2158 		var id = _getPrivateEventKey(element),
2159 			selectorCounts,
2160 			selector,
2161 			node,
2162 			r = [];
2163 		
2164 		// get delegated listeners
2165 		if ( delegates[id] && ( selectorCounts = delegates[id][eventName] ) ) {
2166 			for (selector in selectorCounts) {
2167 				node = event.source;
2168 				// if the source matches the selector
2169 				while (node && node !== element) {
2170 					if (glow._sizzle.matches( selector, [node] ).length) {
2171 						r.push( [node, selector] );
2172 						break;
2173 					}
2174 					
2175 					node = node.parentNode;
2176 				}
2177 			}
2178 		}
2179 		return r;
2180 	}
2181 	
2182 	/**
2183 		@name glow.events._callDomListeners
2184 		@private
2185 		@function
2186 		@description Call delegated listeners and normal listeners for an event
2187 			Events that don't bubble (like mouseenter and mouseleave) need
2188 			to handle their own delegation rather than use this.
2189 		
2190 		@param {HTMLElement} element Element to fire event on
2191 		@param {string} eventName
2192 		@param {glow.events.DomEvent} event
2193 			
2194 		@returns {glow.events.DomEvent} Original event passed in
2195 	*/
2196 	var _callDomListeners = events._callDomListeners = function(element, eventName, event) {
2197 		var delegateMatches = _getDelegateMatches(element, eventName, event);
2198 		
2199 		// call delegated listeners
2200 		for (var i = 0, leni = delegateMatches.length; i < leni; i++) {
2201 			event.attachedTo = delegateMatches[i][0];
2202 			_callListeners( element, eventName + '/' + delegateMatches[i][1], event, delegateMatches[i][0] );
2203 		}
2204 		
2205 		// call non-delegated listeners
2206 		event.attachedTo = element;
2207 		_callListeners(element, eventName, event);
2208 		
2209 		return event;
2210 	}
2211 	
2212 	// export
2213 	events.DomEvent = DomEvent;
2214 });
2215 Glow.provide(function(glow) {
2216 	var document = window.document,
2217 		undefined,
2218         keyboardEventProto,
2219 		env = glow.env,
2220 		// the keyCode for the last keydown (returned to undefined on keyup)
2221 		activeKey,
2222 		// the charCode for the last keypress (returned to undefined on keyup & keydown)
2223 		activeChar,
2224 		DomEvent = glow.events.DomEvent,
2225 		_callDomListeners = glow.events._callDomListeners,
2226 		_getPrivateEventKey = glow.events._getPrivateEventKey,
2227 		// object of event names & listeners, eg:
2228 		// {
2229 		//    eventId: [
2230 		//        2, // the number of glow listeners added for this node
2231 		//        keydownListener,
2232 		//        keypressListener,
2233 		//        keyupListener
2234 		//    ]
2235 		// }
2236 		// This lets us remove these DOM listeners from the node when the glow listeners reaches zero
2237 		eventKeysRegistered = {}; 
2238 	
2239 	/** 
2240 		@name glow.events.KeyboardEvent
2241 		@constructor
2242 		@extends glow.events.DomEvent
2243 		
2244 		@description Describes a keyboard event.
2245 		   You don't need to create instances of this class if you're simply
2246 		   listening to events. One will be provided as the first argument
2247 		   in your callback.
2248 		   
2249 		@param {Event} nativeEvent A native browser event read properties from.
2250 		
2251 		@param {Object} [properties] Properties to add to the Event instance.
2252 		   Each key-value pair in the object will be added to the Event as
2253 		   properties.
2254 	*/
2255 	function KeyboardEvent(nativeEvent) {
2256 		if (activeKey) {
2257 			this.key = keyCodeToId(activeKey);
2258 		}
2259 		if (activeChar) {
2260 			this.keyChar = String.fromCharCode(activeChar);
2261 		}
2262 		DomEvent.call(this, nativeEvent);
2263 	}
2264     
2265     glow.util.extend(KeyboardEvent, DomEvent, {
2266         /** 
2267             @name glow.events.KeyboardEvent#key
2268             @type {string}
2269             @description The key pressed
2270 				This is a string representing the key pressed.
2271 				
2272 				Alphanumeric keys are represented by 0-9 and a-z (always lowercase). Other safe cross-browser values are:
2273 				
2274 				<ul>
2275 					<li>backspace</li>
2276 					<li>tab</li>
2277 					<li>return</li>
2278 					<li>shift</li>
2279 					<li>alt</li>
2280 					<li>escape</li>
2281 					<li>space</li>
2282 					<li>pageup</li>
2283 					<li>pagedown</li>
2284 					<li>end</li>
2285 					<li>home</li>
2286 					<li>left</li>
2287 					<li>up</li>
2288 					<li>right</li>
2289 					<li>down</li>
2290 					<li>insert</li>
2291 					<li>delete</li>
2292 					<li>;</li>
2293 					<li>=</li>
2294 					<li>-</li>
2295 					<li>f1</li>
2296 					<li>f2</li>
2297 					<li>f3</li>
2298 					<li>f4</li>
2299 					<li>f5</li>
2300 					<li>f6</li>
2301 					<li>f7</li>
2302 					<li>f8</li>
2303 					<li>f9</li>
2304 					<li>f10</li>
2305 					<li>f11</li>
2306 					<li>f12</li>
2307 					<li>numlock</li>
2308 					<li>scrolllock</li>
2309 					<li>pause</li>
2310 					<li>,</li>
2311 					<li>.</li>
2312 					<li>/</li>
2313 					<li>[</li>
2314 					<li>\</li>
2315 					<li>]</li>
2316 				</ul>
2317 				
2318 				Some keys may trigger actions in your browser and operating system, some
2319 				are not cancelable.
2320                 
2321             @example
2322 				glow(document).on('keypress', function(event) {
2323 					switch (event.key) {
2324 						case 'up':
2325 							// do stuff
2326 							break;
2327 						case 'down':
2328 							// do stuff
2329 							break;
2330 					}
2331 				});
2332         */
2333         key: '',
2334         /** 
2335             @name glow.events.KeyboardEvent#keyChar
2336             @type {string}
2337             @description The character entered.
2338                 This is only available during 'keypress' events.
2339                 
2340                 If the user presses shift and 1, event.key will be "1", but event.keyChar
2341                 will be "!".
2342                 
2343             @example
2344 				// only allow numbers to be entered into the ageInput field
2345 				glow('#ageInput').on('keypress', function(event) {
2346 					// Convert keyChar to a number and see if we get
2347 					// a valid number back
2348 					return !isNaN( Number(event.keyChar) );
2349 				});
2350         */
2351         keyChar: ''
2352     });
2353 	
2354 	/** 
2355 		@private
2356 		@description Add a listener onto a DOM element
2357 		
2358 		@param {HTMLElement} elm
2359 		@param {string} name Event name, without 'on' at the start
2360 		@param {function} callback Callback for the event
2361 	*/
2362 	function addListener(elm, name, callback) {
2363 		if (elm.addEventListener) { // like DOM2 browsers	
2364 			elm.addEventListener(name, callback, false);
2365 		}
2366 		else if (elm.attachEvent) { // like IE
2367 			elm.attachEvent('on' + name, callback);
2368 		}
2369 	}
2370 	
2371 	/** 
2372 		@private
2373 		@description Removes a listener onto a DOM element
2374 		
2375 		@param {HTMLElement} elm
2376 		@param {string} name Event name, without 'on' at the start
2377 		@param {function} callback Callback for the event
2378 	*/
2379 	function removeListener(elm, name, callback) {
2380 		if (elm.removeEventListener) { // like DOM2 browsers	
2381 			elm.removeEventListener(name, callback, false);
2382 		}
2383 		else if (elm.detachEvent) { // like IE
2384 			elm.detachEvent('on' + name, callback);
2385 		}
2386 	}
2387 	
2388 	/** 
2389 		@private
2390 		@description Do we expect the browser to fire a keypress after a given keydown?
2391 			Also fills in activeChar for webkit.
2392 
2393 		@param {number} keyCode The keyCode from a keydown listener.
2394 		@param {boolean} defaultPrevented Was the keydown prevented?
2395 	*/
2396 	function expectKeypress(keyCode, defaultPrevented) {
2397 		var keyName;
2398 		
2399 		// for browsers that fire keypress for the majority of keys
2400 		if (env.gecko || env.opera || env.webkit < 525) {
2401 			return !noKeyPress[keyCode];
2402 		}
2403 		
2404 		// for browsers that only fire keypress for printable chars
2405 		keyName = keyCodeToId(keyCode);
2406 		
2407 		// is this a printable char?
2408 		if (keyName.length === 1 || keyName === 'tab' || keyName === 'space') {
2409 			// webkit doesn't fire keypress if the keydown has been prevented
2410 			// take a good guess at the active char for webkit
2411 			activeChar = ( keyNameToChar[keyName] || keyName ).charCodeAt(0);
2412 			return !(env.webkit && defaultPrevented);
2413 		}
2414 		return false;
2415 	}
2416 	
2417 	/** 
2418 		@private
2419 		@description Add the key listeners for firing glow's normalised key events.
2420 		
2421 		@param {HTMLElement} attachTo Element to attach listeners to.
2422 		
2423 		@returns {Object[]} An entry for eventKeysRegistered.
2424 	*/
2425 	function addDomKeyListeners(attachTo) {
2426 		var keydownHandler,
2427 			keypressHandler,
2428 			keyupHandler,
2429 			// Even though the user may only be interested in one key event,
2430 			// we need all 3 listeners to normalise any of them.
2431 			// Hash of which keys are down, keyed by keyCode
2432 			// Like: {123: true, 124: false}
2433 			keysDown = {};
2434 		
2435 		keydownHandler = function(nativeEvent) {
2436 			var keyCode = nativeEvent.keyCode,
2437 				preventDefault,
2438 				preventDefaultKeyPress;
2439 			
2440 			// some browsers repeat this event while a key is held down, we don't want to do that
2441 			if ( !keysDown[keyCode] ) {
2442 				activeKey = keyCode;
2443 				activeChar = undefined;
2444 				preventDefault = _callDomListeners( attachTo, 'keydown', new KeyboardEvent(nativeEvent) ).defaultPrevented();
2445 				keysDown[keyCode] = true;
2446 			}
2447 			// we want to fire a keyPress event here if the browser isn't going to fire one itself
2448 			if ( !expectKeypress(keyCode, preventDefault) ) {
2449 				preventDefaultKeyPress = _callDomListeners( attachTo, 'keypress', new KeyboardEvent(nativeEvent) ).defaultPrevented();
2450 			}
2451 			// return false if either the keydown or fake keypress event was cancelled
2452 			return !(preventDefault || preventDefaultKeyPress);
2453 		};
2454 		
2455 		keypressHandler = function(nativeEvent) {
2456 			var keyName, preventDefault;
2457 			// some browsers store the charCode in .charCode, some in .keyCode
2458 			activeChar = nativeEvent.charCode || nativeEvent.keyCode;
2459 			keyName = keyCodeToId(activeKey);
2460 			
2461 			// some browsers fire this event for non-printable chars, look at the previous keydown and see if we're expecting a printable char
2462 			if ( keyName.length > 1 && keyName !== 'tab' && keyName !== 'space' ) {
2463 				// non-printable chars usually have an ID length greater than 1
2464 				activeChar = undefined;
2465 			}
2466 
2467 			preventDefault = _callDomListeners( attachTo, 'keypress', new KeyboardEvent(nativeEvent) ).defaultPrevented();
2468 			return !preventDefault;
2469 		};
2470 		
2471 		keyupHandler = function(nativeEvent) {
2472 			var keyCode = nativeEvent.keyCode,
2473 				preventDefault;
2474 			
2475 			// set the active key so KeyboardEvent picks it up
2476 			activeKey = keyCode;
2477 			activeChar = undefined;
2478 			preventDefault = _callDomListeners( attachTo, 'keyup', new KeyboardEvent(nativeEvent) ).defaultPrevented();
2479 			keysDown[keyCode] = false;
2480 			activeKey = undefined;
2481 			return !preventDefault;
2482 		};
2483 		
2484 		// add listeners to the dom
2485 		addListener(attachTo, 'keydown',  keydownHandler);
2486 		addListener(attachTo, 'keypress', keypressHandler);
2487 		addListener(attachTo, 'keyup',    keyupHandler);
2488 		
2489 		return [1, keydownHandler, keypressHandler, keyupHandler];
2490 	}
2491 	
2492 	/**
2493 		@name glow.events._addKeyListener
2494 		@private
2495 		@function
2496 		@description Add DOM listeners for key events fired by the browser.
2497 			Won't add more than one.
2498 		
2499 		@param {glow.NodeList} nodeList Elements to add listeners to.
2500 		
2501 		@see glow.NodeList#on
2502 	*/
2503 	glow.events._addKeyListener = function(nodeList) {
2504 		var i = nodeList.length,
2505 			attachTo,
2506 			eventKey;
2507 	
2508 		while (i--) {
2509 			attachTo = nodeList[i];
2510 
2511 			// get the ID for this event
2512 			eventKey = _getPrivateEventKey(attachTo);
2513 			
2514 			// if we've already attached DOM listeners for this, don't add them again
2515 			if ( eventKeysRegistered[eventKey] ) {
2516 				// increment the number of things listening to this
2517 				// This lets us remove these DOM listeners from the node when
2518 				// the glow listeners reaches zero
2519 				eventKeysRegistered[eventKey][0]++;
2520 				continue;
2521 			}
2522 			else {
2523 				eventKeysRegistered[eventKey] = addDomKeyListeners(attachTo);
2524 			}
2525 		}
2526 	}
2527 	
2528 	/**
2529 		@name glow.events._removeKeyListener
2530 		@private
2531 		@function
2532 		@description Remove DOM listeners for key events fired by the browser
2533 			Avoids removing DOM listeners until all Glow listeners have been removed
2534 		
2535 		@param {glow.NodeList} nodeList Elements to remove listeners from
2536 		
2537 		@see glow.NodeList#detach
2538 	*/
2539 	glow.events._removeKeyListener = function(nodeList) {
2540 		var i = nodeList.length,
2541 			attachTo,
2542 			eventKey,
2543 			eventRegistry;
2544 		
2545 		while (i--) {
2546 			attachTo = nodeList[i];
2547 			
2548 			// get the ID for this event
2549 			eventKey = _getPrivateEventKey(attachTo);
2550 			eventRegistry = eventKeysRegistered[eventKey];
2551 			// exist if there are no key events registered for this node
2552 			if ( !eventRegistry ) {
2553 				continue;
2554 			}
2555 			if ( --eventRegistry[0] === 0 ) {
2556 				// our glow listener count is zero, we have no need for the dom listeners anymore
2557 				removeListener( attachTo, 'keydown',   eventRegistry[1] );
2558 				removeListener( attachTo, 'keypress',  eventRegistry[2] );
2559 				removeListener( attachTo, 'keyup',     eventRegistry[3] );
2560 				eventKeysRegistered[eventKey] = undefined;
2561 			}
2562 		}
2563 	}
2564 	/**
2565 		@private
2566 		@function
2567 		@description convert a keyCode to a string name for that key
2568 		
2569 		@param {number} keyCode
2570 		
2571 		@returns {string} ID for that key. Is a letter a-z, number 0-9, or id from 'keyIds'
2572 	*/
2573 	function keyCodeToId(keyCode) {
2574 		// key codes for 0-9 A-Z are the same as their char codes
2575 		if ( (keyCode >= keyCodeA && keyCode <= keyCodeZ) || (keyCode >= keyCode0 && keyCode <= keyCode9) ) {
2576 			return String.fromCharCode(keyCode).toLowerCase();
2577 		}
2578 		return keyIds[keyCode] || 'unknown' + keyCode;
2579 	}
2580 	
2581 	// keyCode to key name translation
2582 	var keyCodeA = 65,
2583 		keyCodeZ = 90,
2584 		keyCode0 = 48,
2585 		keyCode9 = 57,
2586 		// key codes for non-alphanumeric keys
2587 		keyIds = {
2588 			8: 'backspace',
2589 			9: 'tab',
2590 			13: 'return',
2591 			16: 'shift',
2592 			17: 'control',
2593 			18: 'alt',
2594 			19: 'pause',
2595 			27: 'escape',
2596 			32: 'space',
2597 			33: 'pageup',
2598 			34: 'pagedown',
2599 			35: 'end',
2600 			36: 'home',
2601 			37: 'left',
2602 			38: 'up',
2603 			39: 'right',
2604 			40: 'down',
2605 			//44: 'printscreen', // Only fires keyup in firefox, IE. Doesn't fire in webkit, opera.
2606 			45: 'insert',
2607 			46: 'delete',
2608 			59: ';',
2609 			61: '=',
2610 			//91: 'meta',
2611 			//93: 'menu', // no keycode in opera, doesn't fire in Chrome
2612 			
2613 			// these are number pad numbers, but Opera doesn't distinguish them from normal number keys so we normalise on that
2614 				96: '0', 
2615 				97: '1',
2616 				98: '2',
2617 				99: '3',
2618 				100: '4',
2619 				101: '5',
2620 				102: '6',
2621 				103: '7',
2622 				104: '8',
2623 				105: '9',
2624 				//106: '*', // opera fires 2 keypress events
2625 				//107: '+', // opera fires 2 keypress events
2626 				109: '-', // opera sees - as insert, but firefox 3.0 see the normal - key the same as the numpad one
2627 				//110: '.', // opera sees this as n
2628 				111: '/',
2629 			// end of numpad
2630 			
2631 			112: 'f1',
2632 			113: 'f2',
2633 			114: 'f3',
2634 			115: 'f4',
2635 			116: 'f5',
2636 			117: 'f6',
2637 			118: 'f7',
2638 			119: 'f8',
2639 			120: 'f9',
2640 			121: 'f10',
2641 			122: 'f11',
2642 			123: 'f12',
2643 			144: 'numlock',
2644 			145: 'scrolllock',
2645 			188: ',',
2646 			189: '-',
2647 			190: '.',
2648 			191: '/',
2649 			192: "'",
2650 			219: '[',
2651 			220: '\\',
2652 			221: ']',
2653 			222: '#', // opera sees # key as 3. Pah.
2654 			223: '`',
2655 			//224: 'meta', // same as [ in opera
2656 			226: '\\' // this key appears on a US layout in webkit windows
2657 		},
2658 		// converting key names to chars, for key names greater than 1 char
2659 		keyNameToChar = {
2660 			space: ' ',
2661 			tab: '\t'
2662 		}
2663 		noKeyPress = {};
2664 	
2665 	// corrections for particular browsers :(
2666 	if (env.gecko) {
2667 		keyIds[107] = '=';
2668 		
2669 		noKeyPress = {
2670 			16: 1,  // shift
2671 			17: 1,  // control
2672 			18: 1,  // alt
2673 			144: 1, // numlock
2674 			145: 1  // scrolllock
2675 		};
2676 	}
2677 	else if (env.opera) {
2678 		keyIds[42] = '*';
2679 		keyIds[43] = '+';
2680 		keyIds[47] = '/';
2681 		keyIds[222] = "'";
2682 		keyIds[192] = '`';
2683 		
2684 		noKeyPress = {
2685 			16: 1,  // shift
2686 			17: 1,  // control
2687 			18: 1   // alt
2688 		};
2689 	}
2690 	else if (env.webkit || env.ie) {
2691 		keyIds[186] = ';';
2692 		keyIds[187] = '=';
2693 	}
2694 	
2695 	// export
2696 	glow.events.KeyboardEvent = KeyboardEvent;
2697 });
2698 Glow.provide(function(glow) {
2699 	var NodeListProto, undefined,
2700 		// shortcuts to aid compression
2701 		document = window.document,
2702 		arraySlice = Array.prototype.slice,
2703 		arrayPush = Array.prototype.push;
2704 	
2705 	/**
2706 		@name glow.NodeList
2707 		@constructor
2708 		@description An array-like collection of DOM Nodes
2709 			It is recommended to create a NodeList using the shortcut function {@link glow}.
2710 			
2711 		@param {string | glow.NodeList | Node | Node[] | Window} contents Items to populate the NodeList with.
2712 			This parameter will be passed to {@link glow.NodeList#push}.
2713 			
2714 			Strings will be treated as CSS selectors unless they start with '<', in which
2715 			case they'll be treated as an HTML string.
2716 			
2717 		@example
2718 			// empty NodeList
2719 			var myNodeList = glow();
2720 
2721 		@example
2722 			// using glow to return a NodeList then chaining methods
2723 			glow('p').addClass('eg').append('<div>Hello!</div>');
2724 			
2725 		@example
2726 			// creating an element from a string
2727 			glow('<div>Hello!</div>').appendTo('body');
2728 		
2729 		@see <a href="http://wiki.github.com/jeresig/sizzle/">Supported CSS selectors</a>
2730 	*/
2731 	function NodeList(contents) {
2732 		// call push if we've been given stuff to add
2733 		contents && this.push(contents);
2734 	}
2735 	NodeListProto = NodeList.prototype;
2736 	
2737 	/**
2738 		@name glow.NodeList#length
2739 		@type Number
2740 		@description Number of nodes in the NodeList
2741 		@example
2742 			// get the number of paragraphs on the page
2743 			glow('p').length;
2744 	*/
2745 	NodeListProto.length = 0;
2746 	
2747 	/**
2748 		@name glow.NodeList._strToNodes
2749 		@private
2750 		@function
2751 		@description Converts a string to an array of nodes
2752 		
2753 		@param {string} str HTML string
2754 		
2755 		@returns {Node[]} Array of nodes (including text / comment nodes)
2756 	*/
2757 	NodeList._strToNodes = (function() {
2758 		var	tmpDiv = document.createElement('div'),
2759 			// these wraps are in the format [depth to children, opening html, closing html]
2760 			tableWrap = [1, '<table>', '</table>'],
2761 			emptyWrap = [0, '', ''],
2762 			// Easlier Webkits won't accept <link> & <style> elms to be the only child of an element,
2763 			// it steals them and hides them in the head for some reason. Using
2764 			// broken html fixes it for some reason
2765 			paddingWrap = glow.env.webkit < 526 ? [0, '', '</div>'] : [1, 'b<div>', '</div>'],
2766 			trWrap = [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2767 			wraps = {
2768 				caption: tableWrap,
2769 				thead: tableWrap,
2770 				th: trWrap,
2771 				colgroup: tableWrap,
2772 				tbody: tableWrap,
2773 				tr: [2, '<table><tbody>', '</tbody></table>'],
2774 				td: trWrap,
2775 				tfoot: tableWrap,
2776 				option: [1, '<select multiple="multiple">', '</select>'],
2777 				legend: [1, '<fieldset>', '</fieldset>'],
2778 				link: paddingWrap,
2779 				script: paddingWrap,
2780 				style: paddingWrap,
2781 				'!': paddingWrap
2782 			};
2783 
2784 		function strToNodes(str) {
2785 			var r = [],
2786 				tagName = ( /^<([\w!]+)/.exec(str) || [] )[1],
2787 				// This matches str content with potential elements that cannot
2788 				// be a child of <div>.  elmFilter declared at top of page.
2789 				wrap = wraps[tagName] || emptyWrap,
2790 				nodeDepth = wrap[0],
2791 				childElm = tmpDiv,
2792 				exceptTbody,
2793 				rLen = 0,
2794 				firstChild;
2795 
2796 			// Create the new element using the node tree contents available in filteredElm.
2797 			childElm.innerHTML = (wrap[1] + str + wrap[2]);
2798 
2799 			// Strip newElement down to just the required elements' parent
2800 			while(nodeDepth--) {
2801 				childElm = childElm.lastChild;
2802 			}
2803 
2804 			// pull nodes out of child
2805 			if (wrap === tableWrap && str.indexOf('<tbody') === -1) {
2806 				// IE7 (and earlier) sometimes gives us a <tbody> even though we didn't ask for one
2807 				while (firstChild = childElm.firstChild) {
2808 					if (firstChild.nodeName != 'TBODY') {
2809 						r[rLen++] = firstChild;
2810 					}
2811 					childElm.removeChild(firstChild);
2812 				}
2813 			}
2814 			else {
2815 				while (firstChild = childElm.firstChild) {
2816 					r[rLen++] = childElm.removeChild(firstChild);
2817 				}
2818 			}
2819 
2820 			return r;
2821 		}
2822 
2823 		return strToNodes;
2824 	})();
2825 	
2826 	// takes a collection and returns an array
2827 	var collectionToArray = function(collection) {
2828 		return arraySlice.call(collection, 0);
2829 	};
2830 	
2831 	try {
2832 		// look out for an IE bug
2833 		arraySlice.call( document.documentElement.childNodes, 0 );
2834 	}
2835 	catch(e) {
2836 		collectionToArray = function(collection) {
2837 			// We can't use this trick on IE collections that are com-based, like HTMLCollections
2838 			// Thankfully they don't have a constructor, so that's how we detect those
2839 			if (collection instanceof Object) {
2840 				return arraySlice.call(collection, 0);
2841 			}
2842 			var i   = collection.length,
2843 				arr = [];
2844 				
2845 			while (i--) {
2846 				arr[i] = collection[i];
2847 			}
2848 			return arr;
2849 		}
2850 	}
2851 	
2852 	/**
2853 		@name glow.NodeList#push
2854 		@function
2855 		@description Adds nodes to the NodeList
2856 		
2857 		@param {string | Node | Node[] | glow.NodeList} nodes Node(s) to add to the NodeList
2858 			Strings will be treated as CSS selectors or HTML strings.
2859 		
2860 		@returns {glow.NodeList}
2861 		
2862 		@example
2863 			myNodeList.push('<div>Foo</div>').push('h1');
2864 	*/
2865 	NodeListProto.push = function(nodes) {
2866 		/*!debug*/
2867 			if (arguments.length !== 1) {
2868 				glow.debug.warn('[wrong count] glow.NodeList#push expects 1 argument, not '+arguments.length+'.');
2869 			}
2870 		/*gubed!*/
2871 		
2872 		if (nodes) {
2873 			if (typeof nodes === 'string') {
2874 				// if the string begins <, treat it as html, otherwise it's a selector
2875 				if (nodes.charAt(0) === '<') {
2876 					nodes = NodeList._strToNodes(nodes);
2877 				}
2878 				else {
2879 					nodes = glow._sizzle(nodes)
2880 				}
2881 				arrayPush.apply(this, nodes);
2882 			}
2883 			
2884 			else if ( nodes.nodeType || nodes.window == nodes ) {
2885 				if (this.length) {
2886 					arrayPush.call(this, nodes);
2887 				}
2888 				else {
2889 					this[0] = nodes;
2890 					this.length = 1;
2891 				}
2892 			}
2893 			else if (nodes.length !== undefined) {
2894 				if (nodes.constructor != Array) {
2895 					// convert array-like objects into an array
2896 					nodes = collectionToArray(nodes);
2897 				}
2898 				arrayPush.apply(this, nodes);
2899 			}
2900 			/*!debug*/
2901 			else {
2902 				glow.debug.warn('[wrong type] glow.NodeList#push: Ignoring unexpected argument type, failing silently');
2903 			}
2904 			/*gubed!*/
2905 		}
2906 		/*!debug*/
2907 		else {
2908 			glow.debug.warn('[wrong type] glow.NodeList#push: Ignoring false argument type, failing silently');
2909 		}
2910 		/*gubed!*/
2911 		return this;
2912 	};
2913 	
2914 	/**
2915 		@name glow.NodeList#eq
2916 		@function
2917 		@description Compares this NodeList to another
2918 			Returns true if both NodeLists contain the same items in the same order
2919 		
2920 		@param {Node | Node[] | glow.NodeList} nodeList The NodeList to compare to.
2921 		
2922 		@returns {boolean}
2923 		
2924 		@see {@link glow.NodeList#is} for testing if a NodeList item matches a selector
2925 		
2926 		@example
2927 			// the following returns true
2928 			glow('#blah').eq( document.getElementById('blah') );
2929 	*/
2930 	NodeListProto.eq = function(nodeList) {
2931 		/*!debug*/
2932 			if (arguments.length !== 1) {
2933 				glow.debug.warn('[wrong count] glow.NodeList#eq expects 1 argument, not ' + arguments.length + '.');
2934 			}
2935 			if (typeof nodeList !== 'object') {
2936 				glow.debug.warn('[wrong type] glow.NodeList#eq expects object argument, not ' + typeof nodeList + '.');
2937 			}
2938 		/*gubed!*/
2939 		
2940 		var len = this.length,
2941 			i = len;
2942 		
2943 		// normalise param to NodeList
2944 		if ( !(nodeList instanceof NodeList) ) {
2945 			nodeList = new NodeList(nodeList);
2946 		}
2947 		
2948 		// quickly return false if lengths are different
2949 		if (len != nodeList.length) {
2950 			return false;
2951 		}
2952 		
2953 		// loop through and return false on inequality
2954 		while (i--) {
2955 			if (this[i] !== nodeList[i]) {
2956 				return false;
2957 			}
2958 		}
2959 		
2960 		return true;
2961 	};
2962 	
2963 	/**
2964 		@name glow.NodeList#slice
2965 		@function
2966 		@description Get a section of an NodeList
2967 			Operates in the same way as an Array's slice method
2968 		
2969 		@param {number} start Start index
2970 			If negative, it specifies a position measured from the end of the list
2971 		
2972 		@param {number} [end] End index
2973 			By default, this is the end of the list. A negative end specifies
2974 			a position measured from the end of the list.
2975 		
2976 		@returns {glow.NodeList} A new sliced NodeList
2977 		
2978 		@example
2979 		var myNodeList = glow("<div></div><p></p>");
2980 		myNodeList.slice(1, 2); // selects the paragraph
2981 		myNodeList.slice(-1); // same thing, selects the paragraph
2982 	*/
2983 	NodeListProto.slice = function(/*start, end*/) {
2984 		return new NodeList( arraySlice.apply(this, arguments) );
2985 	};
2986 	
2987 	/**
2988 		@name glow.NodeList#sort
2989 		@function
2990 		@description Sort the elements in the list.
2991 			Items will already be in document order if a CSS selector
2992 			was used to fetch them.
2993 		
2994 		@param {Function} [func] Function to determine sort order
2995 			This function will be passed 2 elements (elementA, elementB). The function
2996 			should return a number less than 0 to sort elementA lower than elementB
2997 			and greater than 0 to sort elementA higher than elementB.
2998 			
2999 			If no function is provided, elements will be sorted in document order.
3000 		
3001 		@returns {glow.NodeList} A new sorted NodeList
3002 		
3003 		@example
3004 			//get links in alphabetical (well, lexicographical) order
3005 			var links = glow("a").sort(function(elementA, elementB) {
3006 				return glow(elementA).text() < glow(elementB).text() ? -1 : 1;
3007 			})
3008 	*/
3009 	NodeListProto.sort = function(func) {
3010 		var items = collectionToArray(this),
3011 			sortedElms = func ? items.sort(func) : glow._sizzle.uniqueSort(items);
3012 		
3013 		return new NodeList(sortedElms);
3014 	};
3015 	
3016 	/**
3017 		@name glow.NodeList#item
3018 		@function
3019 		@description Get a single item from the list as an NodeList
3020 			Negative numbers can be used to get items from the end of the
3021 			list.
3022 		
3023 		@param {number} index The numeric index of the node to return.
3024 		
3025 		@returns {glow.NodeList} A new NodeList containing a single item
3026 		
3027 		@example
3028 			// get the html from the fourth element
3029 			myNodeList.item(3).html();
3030 			
3031 		@example
3032 			// add a class name to the last item
3033 			myNodeList.item(-1).addClass('last');
3034 	*/
3035 	NodeListProto.item = function(index) {
3036 		/*!debug*/
3037 			if ( arguments.length !== 1 ) {
3038 				glow.debug.warn('[wrong count] glow.NodeList#item expects 1 argument, got ' + arguments.length);
3039 			}
3040 		/*gubed!*/
3041 		// TODO: test which of these methods is faster (use the current one unless significantly slower)
3042 		return this.slice(index, (index + 1) || this.length);
3043 		// return new NodeList( index < 0 ? this[this.length + index] : this[index] );
3044 	};
3045 	
3046 	/**
3047 		@name glow.NodeList#each
3048 		@function
3049 		@description Calls a function for each node in the list.
3050 		
3051 		@param {Function} callback The function to call for each node.
3052 			The function will be passed 2 arguments, the index of the current item,
3053 			and the NodeList being iterated over.
3054 			
3055 			Inside the function 'this' refers to the Node.
3056 			
3057 			Returning false from this function stops further iterations
3058 		
3059 		@returns {glow.NodeList}
3060 		
3061 		@example
3062 			// add "link number: x" to each link, where x is the index of the link
3063 			glow("a").each(function(i, nodeList) {
3064 				glow(this).append(' link number: ' + i);
3065 			});
3066 		@example
3067 			// breaking out of an each loop
3068 			glow("a").each(function(i, nodeList) {
3069 				// do stuff
3070 				if ( glow(this).hasClass('whatever') ) {
3071 					// we don't want to process any more links
3072 					return false;
3073 				}
3074 			});
3075 	*/
3076 	NodeListProto.each = function(callback) {
3077 		/*!debug*/
3078 			if ( arguments.length !== 1 ) {
3079 				glow.debug.warn('[wrong count] glow.NodeList#each expects 1 argument, got ' + arguments.length);
3080 			}
3081 			if (typeof callback != 'function') {
3082 				glow.debug.warn('[wrong type] glow.NodeList#each expects "function", got ' + typeof callback);
3083 			}
3084 		/*gubed!*/
3085 		for (var i = 0, len = this.length; i<len; i++) {
3086 			if ( callback.call(this[i], i, this) === false ) {
3087 				break;
3088 			}
3089 		}
3090 		return this;
3091 	};
3092 	
3093 	/**
3094 		@name glow.NodeList#filter
3095 		@function
3096 		@description Filter the NodeList
3097 		 
3098 		@param {Function|string} test Filter test
3099 			If a string is provided it's treated as a CSS selector. Elements
3100 			which match the CSS selector are added to the new NodeList.
3101 			
3102 			If 'test' is a function, it will be called per node in the NodeList.
3103 			
3104 			The function is passed 2 arguments, the index of the current item,
3105 			and the ElementList being itterated over.
3106 			
3107 			Inside the function 'this' refers to the node.
3108 			Return true to add the element to the new NodeList.
3109 		 
3110 		@returns {glow.NodeList} A new NodeList containing the filtered nodes
3111 		 
3112 		@example
3113 			// return images with a width greater than 320
3114 			glow("img").filter(function () {
3115 				return glow(this).width() > 320;
3116 			});
3117 		
3118 		@example
3119 			// Get items that don't have an alt attribute
3120 			myElementList.filter(':not([alt])');
3121 	*/
3122 	NodeListProto.filter = function(test) {
3123 		/*!debug*/
3124 			if ( arguments.length !== 1 ) {
3125 				glow.debug.warn('[wrong count] glow.NodeList#filter expects 1 argument, got ' + arguments.length);
3126 			}
3127 			if ( !/^(function|string)$/.test(typeof test) ) {
3128 				glow.debug.warn('[wrong type] glow.NodeList#each expects function/string, got ' + typeof test);
3129 			}
3130 		/*gubed!*/
3131 		var r = [],
3132 			ri = 0;
3133 		
3134 		if (typeof test === 'string') {
3135 			r = glow._sizzle.matches(test, this);
3136 		}
3137 		else {	
3138 			for (var i = 0, len = this.length; i<len; i++) {
3139 				if ( test.call(this[i], i, this) ) {
3140 					r[ri++] = this[i];
3141 				}
3142 			}
3143 		}
3144 		
3145 		return new NodeList(r);
3146 	};
3147 
3148 	
3149 	/**
3150 		@name glow.NodeList#is
3151 		@function
3152 		@description Tests if the first element matches a CSS selector
3153 
3154 		@param {string} selector CSS selector
3155 		
3156 		@returns {boolean}
3157 		
3158 		@example
3159 			if ( myNodeList.is(':visible') ) {
3160 				// ...
3161 			}
3162 	*/
3163 	NodeListProto.is = function(selector) {
3164 		/*!debug*/
3165 			if ( arguments.length !== 1 ) {
3166 				glow.debug.warn('[wrong count] glow.NodeList#is expects 1 argument, got ' + arguments.length);
3167 			}
3168 			if ( typeof selector !== 'string' ) {
3169 				glow.debug.warn('[wrong type] glow.NodeList#is expects string, got ' + typeof selector);
3170 			}
3171 		/*gubed!*/
3172 		if ( !this[0] ) {
3173 			return false;
3174 		}
3175 		return !!glow._sizzle.matches( selector, [ this[0] ] ).length;
3176 	};
3177 	
3178 	// export
3179 	glow.NodeList = NodeList;
3180 });
3181 Glow.provide(function(glow) {
3182 	var undef
3183 		, NodeListProto = glow.NodeList.prototype
3184 	
3185 		/**
3186 			@private
3187 			@name glow.NodeList-dom0PropertyMapping
3188 			@description Mapping of HTML attribute names to DOM0 property names.
3189 		*/
3190 		, dom0PropertyMapping = { // keys must be lowercase
3191 			'class'     : 'className',
3192 			'for'       : 'htmlFor',
3193 			'maxlength' : 'maxLength'
3194 		}
3195 		
3196 		/**
3197 			@private
3198 			@name glow.NodeList-dataPropName
3199 			@type String
3200 			@description The property name added to the DomElement by the NodeList#data method.
3201 		*/
3202 		, dataPropName = '_uniqueData' + glow.UID
3203 		
3204 		/**
3205 			@private
3206 			@name glow.NodeList-dataIndex
3207 			@type String
3208 			@description The value of the dataPropName added by the NodeList#data method.
3209 		*/
3210 		, dataIndex = 1 // must be a truthy value
3211 			
3212 		/**
3213 			@private
3214 			@name glow.NodeList-dataCache
3215 			@type Object
3216 			@description Holds the data used by the NodeList#data method.
3217 			
3218 			The structure is like:
3219 			[
3220 				{
3221 					myKey: "my data"
3222 				}
3223 			]
3224 		*/
3225 		, dataCache = [];
3226 			
3227 	/**
3228 	@name glow.NodeList#addClass
3229 	@function
3230 	@description Adds a class to each node.
3231 
3232 	@param {string} name The name of the class to add.
3233 
3234 	@returns {glow.NodeList}
3235 
3236 	@example
3237 		glow("#login a").addClass("highlight");
3238 	*/
3239 	NodeListProto.addClass = function(name) {
3240 		var i = this.length;
3241 		
3242 		/*!debug*/
3243 			if (arguments.length !== 1) {
3244 				glow.debug.warn('[wrong count] glow.NodeList#addClass expects 1 argument, not '+arguments.length+'.');
3245 			}
3246 			else if (typeof arguments[0] !== 'string') {
3247 				glow.debug.warn('[wrong type] glow.NodeList#addClass expects argument 1 to be of type string, not '+typeof arguments[0]+'.');
3248 			}
3249 		/*gubed!*/
3250 		
3251 		while (i--) {
3252 			if (this[i].nodeType === 1) {
3253 				_addClass(this[i], name);
3254 			}
3255 		}
3256 		
3257 		return this;
3258 	};
3259 	
3260 	function _addClass(node, name) { // TODO: handle classnames separated by non-space characters?
3261 		if ( (' ' + node.className + ' ').indexOf(' ' + name + ' ') === -1 ) {
3262 			node.className += (node.className? ' ' : '') + name;
3263 		}
3264 	}
3265 	
3266 	/**
3267 	@name glow.NodeList#attr
3268 	@function
3269 	@description Gets or sets attributes.
3270 
3271 		When getting an attribute, it is retrieved from the first
3272 		node in this NodeList. Setting attributes applies the change
3273 		to each element in this NodeList.
3274 
3275 		To set an attribute, pass in the name as the first
3276 		parameter and the value as a second parameter.
3277 
3278 		To set multiple attributes in one call, pass in an object of
3279 		name/value pairs as a single parameter.
3280 
3281 		For browsers that don't support manipulating attributes
3282 		using the DOM, this method will try to do the right thing
3283 		(i.e. don't expect the semantics of this method to be
3284 		consistent across browsers as this is not possible with
3285 		currently supported browsers).
3286 
3287 	@param {string | Object} name The name of the attribute, or an object of name/value pairs
3288 	@param {string} [value] The value to set the attribute to.
3289 
3290 	@returns {string | undefined | glow.NodeList}
3291 
3292 		When setting attributes this method returns its own NodeList, otherwise
3293 		returns the attribute value. The attribute name is always treated as
3294 		case-insensitive. When getting, the returned value will be of type string unless
3295 		that particular attribute was never set and there is no default value, in which
3296 		case the returned value will be an empty string.
3297 
3298 	@example
3299 		var myNodeList = glow(".myImgClass");
3300 
3301 		// get an attribute
3302 		myNodeList.attr("class");
3303 
3304 		// set an attribute
3305 		myNodeList.attr("class", "anotherImgClass");
3306 
3307 		// set multiple attributes
3308 		myNodeList.attr({
3309 		  src: "a.png",
3310 		  alt: "Cat jumping through a field"
3311 		});
3312 	 */
3313 	 // see: http://tobielangel.com/2007/1/11/attribute-nightmare-in-ie/
3314 	NodeListProto.attr = function(/*arguments*/) {
3315 		var args = arguments,
3316 			argsLen = args.length,
3317 			thisLen = this.length,
3318 			keyvals,
3319 			name = keyvals = args[0], // using this API: attr(name) or attr({key: val}) ?
3320 			dom0Property = '',
3321 			node,
3322 			attrNode;
3323 		
3324 		/*!debug*/
3325 			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]+'.'); }
3326 			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.'); }
3327 			else if (arguments.length === 0 ||  arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#attr expects 1 or 2 arguments, not '+arguments.length+'.'); }
3328 		/*gubed!*/
3329 		
3330 		if (this.length === 0) { // is this an empty nodelist?
3331 			return (argsLen > 1)? this : undef;
3332 		}
3333 		
3334 		if (typeof keyvals === 'object') { // SETting value from {name: value} object
3335 			for (name in keyvals) {
3336 				if (!keyvals.hasOwnProperty(name)) { continue; }
3337 				
3338 				// in IE6 and IE7 the attribute name needs to be translated into dom property name
3339 				if (glow.env.ie < 8) {
3340 					dom0Property = dom0PropertyMapping[name.toLowerCase()];
3341 				}
3342 				
3343 				var i = thisLen;
3344 				while (i--) {
3345 					node = this[i];
3346 					
3347 					if (node.nodeType !== 1) { continue; }
3348 					
3349 					if (dom0Property) {
3350 						node[dom0Property] = keyvals[name];
3351 					}
3352 					else {
3353 						node.setAttribute(name, keyvals[name], 0); // IE flags, 0: case-insensitive
3354 					}
3355 				}
3356 			}
3357 			
3358 			return this;
3359 		}
3360 		else {
3361 			node = this[0];
3362 				
3363 			if (node.nodeType !== 1) {
3364 				return (argsLen > 1)? this : undef;
3365 			}
3366 
3367 			if (argsLen === 1) { // GETting value from name. see http://reference.sitepoint.com/javascript/Element/getAttribute
3368 				if ( glow.env.ie && (name === 'href' || name === 'src') ) {
3369 					value = node.getAttribute(name, 2); // IE flags, 0: case-insensitive + 2: exactly as set
3370 					return (value === null)? '' : value;
3371 				}
3372 				else if (node.attributes[name]) { // in IE node.getAttributeNode sometimes returns unspecified default values so we look for specified attributes if we can
3373 					return (!node.attributes[name].specified)? '' : node.attributes[name].value;
3374 				}
3375 				else if (node.getAttributeNode) { // in IE getAttribute() does not always work so we use getAttributeNode if we can
3376 					attrNode = node.getAttributeNode(name, 0);
3377 					return (attrNode === null)? '' : attrNode.value;
3378 				}
3379 				else {
3380 					value = node.getAttribute(name, 2); // IE flags, 0: case-insensitive + 2: exactly as set
3381 					return (value === null)? '' : value;
3382 				}	
3383 			}
3384 			else { // SETting a single value like attr(name, value), normalize to an keyval object
3385 				if (glow.env.ie < 8) {
3386 					dom0Property = dom0PropertyMapping[name.toLowerCase()];
3387 				}
3388 				
3389 				if (dom0Property) {
3390 					node[dom0Property] = args[1];
3391 				}
3392 				else {
3393 					node.setAttribute(name, args[1], 0); // IE flags, 0: case-insensitive
3394 				}
3395 				return this;
3396 			}
3397 		}
3398 	};
3399 	/**
3400 		Copies the data from one nodelist to another
3401 		@private
3402 		@name glow.NodeList._copyData
3403 		@see glow.NodeList#clone
3404 		@function
3405 	*/
3406 	glow.NodeList._copyData = function(from, to){
3407 		var i = to.length,
3408 			data;
3409 		
3410 		while (i--) {
3411 			data = dataCache[ from[i][dataPropName] ];
3412 			data && to.slice(i, i+1).data(data);
3413 		}
3414 	}
3415 
3416 	/**
3417 	@name glow.NodeList#data
3418 	@function
3419 	@description Use this to safely attach arbitrary data to any DOM Element.
3420 	
3421 	This method is useful when you wish to avoid memory leaks that are possible when adding your own data directly to DOM Elements.
3422 	
3423 	When called with no arguments, will return glow's entire data store for the first node in this NodeList.
3424 	
3425 	Otherwise, when given a name, will return the associated value from the first node in this NodeList.
3426 	
3427 	When given both a name and a value, will store that data on every node in this NodeList.
3428 	
3429 	Optionally you can pass in a single object composed of multiple name, value pairs.
3430 	
3431 	@param {string|Object} [key] The name of the value in glow's data store.
3432 	@param {Object} [val] The value you wish to associate with the given name.
3433 	@see glow.NodeList#removeData
3434 	@example
3435 	
3436 	glow("p").data("tea", "milky");
3437 	var colour = glow("p").data("tea"); // milky
3438 	@returns {Object} When setting a value this method can be chained, as in that case it will return itself.
3439 	@see glow.NodeList#removeData
3440 	*/
3441 	NodeListProto.data = function (key, val) { /*debug*///console.log("data("+key+", "+val+")");
3442 		var args = arguments,
3443 			argsLen = args.length,
3444 			keyvals = key, // like: data({key: val}) or data(key, val)
3445 			index,
3446 			node;
3447 			
3448 		/*!debug*/
3449 			if (arguments.length === 2 && typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#data expects name argument to be of type string.'); }
3450 			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.'); }
3451 			else if (arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#data expects 0, 1 or 2 arguments.'); }
3452 		/*gubed!*/
3453 		
3454 		if (argsLen > 1) { // SET key, val on every node
3455 			var i = this.length;
3456 			while (i--) {
3457 				node = this[i];
3458 				if (node.nodeType !== 1) { continue; }
3459 				
3460 				index = node[''+dataPropName];
3461 
3462 				if (!index) { // assumes index is always > 0
3463 					index = dataIndex++;
3464 					
3465 					node[dataPropName] = index;
3466 					dataCache[index] = {};
3467 				}
3468 				dataCache[index][key] = val;
3469 			}
3470 			
3471 			return this; // chainable with (key, val) signature
3472 		}
3473 		else if (typeof keyvals === 'object') { // SET keyvals on every node
3474 			var i = this.length;
3475 			while (i--) {
3476 				node = this[i];
3477 				if (node.nodeType !== 1) { continue; }
3478 				
3479 				index = node[dataPropName];
3480 				if (!index) { // assumes index is always > 0
3481 					index = dataIndex++;
3482 					
3483 					node[dataPropName] = index;
3484 					dataCache[index] = {};
3485 				}
3486 				for (key in keyvals) {
3487 					dataCache[index][key] = keyvals[key];
3488 				}
3489 			}
3490 			
3491 			return this; // chainable with ({key, val}) signature
3492 		}
3493 		else { // GET from first node
3494 			node = this[0];
3495 			if (node === undef || node.nodeType !== 1) { return undef; }
3496 				
3497 			if ( !(index = node[dataPropName]) ) {
3498 				return undef;
3499 			}
3500 
3501 			if (key !== undef) {
3502 				return dataCache[index][key];
3503 			}
3504 			
3505 			// get the entire data cache object for this node
3506 			return dataCache[index];
3507 		}
3508 	};
3509 	
3510 	/**
3511 	@name glow.NodeList#hasAttr
3512 	@function
3513 	@description Does the node have a particular attribute?
3514 		
3515 		The first node in this NodeList is tested.
3516 		
3517 	@param {string} name The name of the attribute to test for.
3518 
3519 	@returns {boolean|undefined} Returns undefined if the first node is not an element,
3520 	or if the NodeList is empty, otherwise returns true/false to indicate if that attribute exists
3521 	on the first element.
3522 
3523 	@example
3524 		if ( glow("#myImg").hasAttr("alt") ){
3525 			// ...
3526 		}
3527 	*/
3528 	NodeListProto.hasAttr = function(name) {
3529 		var node;
3530 		
3531 		/*!debug*/
3532 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#hasAttr expects 1 argument.'); }
3533 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#hasAttr expects argument 1 to be of type string.'); }
3534 		/*gubed!*/
3535 		
3536 		node = this[0];
3537 		
3538 		if (this.length && node.nodeType === 1) {
3539 			if (node.attributes[name]) { // is an object in  IE, or else: undefined in IE < 8, null in IE 8
3540 				return !!node.attributes[name].specified;
3541 			}
3542 			
3543 			if (node.hasAttribute) { return node.hasAttribute(name); } // like FF, Safari, etc
3544 			else { return node.attributes[name] !== undef; } // like IE7
3545 		}
3546 	};
3547 	
3548 	/**
3549 	@name glow.NodeList#hasClass
3550 	@function
3551 	@description Does the node have a particular class?
3552 
3553 		The first node in this NodeList is tested.
3554 
3555 	@param {string} name The name of the class to test for.
3556 
3557 	@returns {boolean}
3558 
3559 	@example
3560 		if ( glow("#myInput").hasClass("errored") ){
3561 			// ...
3562 		}
3563 	*/
3564 	NodeListProto.hasClass = function (name) {
3565 		/*!debug*/
3566 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#hasClass expects 1 argument.'); }
3567 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#hasClass expects argument 1 to be of type string.'); }
3568 		/*gubed!*/
3569 		
3570 		if (this.length && this[0].nodeType === 1) {
3571 			return ( (' ' + this[0].className + ' ').indexOf(' ' + name + ' ') > -1 );
3572 		}
3573 	};
3574 	
3575 	/**
3576 	@name glow.NodeList#prop
3577 	@function
3578 	@description Gets or sets node properties.
3579 	
3580 		This function gets / sets node properties, to get attributes,
3581 		see {@link glow.NodeList#attr NodeList#attr}.
3582 		
3583 		When getting a property, it is retrieved from the first
3584 		node in this NodeList. Setting properties to each element in
3585 		this NodeList.
3586 		
3587 		To set multiple properties in one call, pass in an object of
3588 		name/value pairs.
3589 		
3590 	@param {string | Object} name The name of the property, or an object of name/value pairs
3591 	@param {string} [value] The value to set the property to.
3592 
3593 	@returns {string | glow.NodeList}
3594 
3595 		When setting properties it returns the NodeList, otherwise
3596 		returns the property value.
3597 
3598 	@example
3599 		var myNodeList = glow("#formElement");
3600 
3601 		// get the node name
3602 		myNodeList.prop("nodeName");
3603 
3604 		// set a property
3605 		myNodeList.prop("_secretValue", 10);
3606 
3607 		// set multiple properties
3608 		myNodeList.prop({
3609 			checked: true,
3610 			_secretValue: 10
3611 		});
3612 	*/
3613 	NodeListProto.prop = function(name, val) {
3614 		var hash = name,
3615 			argsLen = arguments.length;
3616 		
3617 		/*!debug*/
3618 			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.'); }
3619 			else if (arguments.length === 2 && typeof name !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#prop expects name to be of type string.'); }
3620 			else if (arguments.length === 0 || arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#prop expects 1 or 2 arguments.'); }
3621 		/*gubed!*/
3622 		
3623 		if (this.length === 0) return;
3624 		
3625 		if (argsLen === 2 && typeof name === 'string') {
3626 			for (var i = 0, ilen = this.length; i < ilen; i++) {
3627 				if (this[i].nodeType === 1) { this[i][name] = val; }
3628 			}
3629 			return this;
3630 		}
3631 		else if (argsLen === 1 && hash.constructor === Object) {
3632 			for (var key in hash) {
3633 				for (var i = 0, ilen = this.length; i < ilen; i++) {
3634 					if (this[i].nodeType === 1) { this[i][key] = hash[key]; }
3635 				}
3636 			}
3637 			return this;
3638 		}
3639 		else if (argsLen === 1 && typeof name === 'string') {
3640 			if (this[0].nodeType === 1) { return this[0][name]; }
3641 		}
3642 		else {
3643 			throw new Error('Invalid parameters.');
3644 		}
3645 	};
3646 	
3647 	/**
3648 	@name glow.NodeList#removeAttr
3649 	@function
3650 	@description Removes an attribute from each node.
3651 
3652 	@param {string} name The name of the attribute to remove.
3653 
3654 	@returns {glow.NodeList}
3655 
3656 	@example
3657 		glow("a").removeAttr("target");
3658 	*/
3659 	NodeListProto.removeAttr = function (name) {
3660 		var dom0Property;
3661 		
3662 		/*!debug*/
3663 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#removeAttr expects 1 argument.'); }
3664 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeAttr expects argument 1 to be of type string.'); }
3665 		/*gubed!*/
3666 	
3667 		for (var i = 0, leni = this.length; i < leni; i++) {
3668 			if (this[i].nodeType === 1) {
3669 				if (glow.env.ie < 8) {
3670 					if ( (dom0Property = dom0PropertyMapping[name.toLowerCase()]) ) {
3671 						this[i][dom0Property] = '';
3672 					}
3673 				}
3674 				
3675 				if (this[i].removeAttribute) this[i].removeAttribute(name);
3676 			}
3677 		}
3678 		return this;
3679 	};
3680 	
3681 	/**
3682 	@name glow.NodeList#removeClass
3683 	@function
3684 	@description Removes a class from each node.
3685 
3686 	@param {string} name The name of the class to remove.
3687 
3688 	@returns {glow.NodeList}
3689 
3690 	@example
3691 		glow("#footer #login a").removeClass("highlight");
3692 	*/
3693 	NodeListProto.removeClass = function(name) {
3694 		var node;
3695 					
3696 		/*!debug*/
3697 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#removeClass() expects 1 argument.'); }
3698 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeClass() expects argument 1 to be of type string.'); }
3699 		/*gubed!*/
3700 		
3701 		var i = this.length;
3702 		while (i--) {
3703 			node = this[i];
3704 			if (node.className) {
3705 				_removeClass(node, name);
3706 			}
3707 		}
3708 		return this;
3709 	};
3710 	
3711 	function _removeClass(node, name) {
3712 		var oldClasses = node.className.split(' '),
3713 			newClasses = [];
3714 			
3715 		oldClasses = node.className.split(' ');
3716 		newClasses = [];
3717 		
3718 		var i = oldClasses.length;
3719 		while (i--) {
3720 			if (oldClasses[i] !== name) {
3721 				oldClasses[i] && newClasses.unshift(oldClasses[i]); // unshift to maintain original order
3722 			}
3723 		}
3724 		node.className = (newClasses.length)? newClasses.join(' ') : '';
3725 	}
3726 	
3727 	/**
3728 	@name glow.NodeList#removeData
3729 	@function
3730 	@description Removes data previously added by {@link glow.NodeList#data} from each node in this NodeList.
3731 	
3732 	When called with no arguments, will delete glow's entire data store for each node in this NodeList.
3733 	
3734 	Otherwise, when given a name, will delete the associated value from each node in this NodeList.
3735 	
3736 	@param {string} [key] The name of the value in glow's data store.
3737 	@see glow.NodeList#data
3738 	*/
3739 	NodeListProto.removeData = function(key) {
3740 		var elm,
3741 			i = this.length,
3742 			index;
3743 			// uses private scoped variables: dataCache, dataPropName
3744 		
3745 		/*!debug*/
3746 			if (arguments.length > 1) { glow.debug.warn('[wrong count] glow.NodeList#removeData expects 0 or 1 arguments.'); }
3747 			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.'); }
3748 		/*gubed!*/
3749 		
3750 		while (i--) {
3751 			elm = this[i];
3752 			index = elm[dataPropName];
3753 			
3754 			if (index !== undef) {
3755 				switch (arguments.length) {
3756 					case 0:
3757 						dataCache[index] = undef;
3758 						elm[dataPropName] = undef;
3759 						try {
3760 							delete elm[dataPropName]; // IE 6 goes wobbly here
3761 						}
3762 						catch(e) { // remove expando from IE 6
3763 							elm.removeAttribute && elm.removeAttribute(dataPropName);
3764 						}
3765 						break;
3766 					case 1:
3767 						dataCache[index][key] = undef;
3768 						delete dataCache[index][key];
3769 						break;
3770 				}
3771 			}
3772 		}
3773 		
3774 		return this; // chainable
3775 	};
3776 	
3777 	/**
3778 	@name glow.NodeList#toggleClass
3779 	@function
3780 	@description Toggles a class on each node.
3781 
3782 	@param {string} name The name of the class to toggle.
3783 
3784 	@returns {glow.NodeList}
3785 
3786 	@example
3787 		glow(".onOffSwitch").toggleClass("on");
3788 	 */
3789 	NodeListProto.toggleClass = function(name) {
3790 		var node;
3791 		
3792 		/*!debug*/
3793 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#toggleClass() expects 1 argument.'); }
3794 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#toggleClass() expects argument 1 to be of type string.'); }
3795 		/*gubed!*/
3796 		
3797 		for (var i = 0, leni = this.length; i < leni; i++) {
3798 			node = this[i];
3799 			if (node.className) {
3800 				if ( (' ' + node.className + ' ').indexOf(' ' + name + ' ') > -1 ) {
3801 					_removeClass(node, name);
3802 				}
3803 				else {
3804 					_addClass(node, name);
3805 				}
3806 			}
3807 		}
3808 		
3809 		return this;
3810 	};
3811 	
3812 	/**
3813 	@name glow.NodeList#val
3814 	@function
3815 	@description Gets or sets form values for the first node.
3816 		The returned value depends on the type of element, see below:
3817 
3818 		<dl>
3819 		<dt>Radio button or checkbox</dt>
3820 		<dd>If checked, then the contents of the value property, otherwise an empty string.</dd>
3821 		<dt>Select</dt>
3822 		<dd>The contents of value property of the selected option</dd>
3823 		<dt>Select (multiple)</dt>
3824 		<dd>An array of selected option values.</dd>
3825 		<dt>Other form elements</dt>
3826 		<dd>The value of the input.</dd>
3827 		</dl>
3828 
3829 		Getting values from a form:
3830 
3831 		If the first element in the NodeList is a form, then an
3832 		object is returned containing the form data. Each item
3833 		property of the object is a value as above, apart from when
3834 		multiple elements of the same name exist, in which case the
3835 		it will contain an array of values.
3836 
3837 		Setting values for form elements:
3838 
3839 		If a value is passed and the first element of the NodeList
3840 		is a form element, then the form element is given that value.
3841 		For select elements, this means that the first option that
3842 		matches the value will be selected. For selects that allow
3843 		multiple selection, the options which have a value that
3844 		exists in the array of values/match the value will be
3845 		selected and others will be deselected.
3846 
3847 		Checkboxes and radio buttons will be checked only if the value is the same
3848 		as the one you provide.
3849 
3850 		Setting values for forms:
3851 
3852 		If the first element in the NodeList is a form and the
3853 		value is an object, then each element of the form has its
3854 		value set to the corresponding property of the object, using
3855 		the method described above.
3856 
3857 	@param {string | Object} [value] The value to set the form element/elements to.
3858 
3859 	@returns {glow.NodeList | string | Object}
3860 
3861 		When used to set a value it returns the NodeList, otherwise
3862 		returns the value as described above.
3863 
3864 	@example
3865 		// get a value from an input with the id 'username'
3866 		var username = glow("#username").val();
3867 
3868 	@example			
3869 		// get values from a form
3870 		var userDetails = glow("form").val();
3871 
3872 	@example
3873 		// set a value
3874 		glow("#username").val("example username");
3875 
3876 	@example
3877 		// set values in a form
3878 		glow("form").val({
3879 			username : "another",
3880 			name     : "A N Other"
3881 		});
3882 	*/
3883 	NodeListProto.val = function(){		
3884 		var args = arguments,
3885 			val = args[0],
3886 			i = 0,
3887 			length = this.length;
3888 
3889 		if (args.length === 0) {
3890 			return this[0].nodeName == 'FORM' ?
3891 				formValues(this[0]) :
3892 				elementValue(this[0]);
3893 		}
3894 		if (this[0].nodeName == 'FORM') {
3895 			if (! typeof val == 'object') {
3896 				throw 'value for FORM must be object';
3897 			}
3898 			setFormValues(this[0], val);
3899 		} else {
3900 			for (; i < length; i++) {
3901 				setValue(this[i], val);
3902 			}
3903 		}
3904 		return this;		
3905 	};
3906 	
3907 	/*
3908 	 @name elementValue
3909 	 @private
3910 	 @returns the value of the form element
3911 	*/
3912 	function elementValue (el) {
3913 		var elType = el.type,
3914 			elChecked = el.checked,
3915 			elValue = el.value,
3916 			vals = [],
3917 			i = 0;
3918 
3919 		if (elType == 'radio') {
3920 			return elChecked ? elValue : '';
3921 		}
3922 			
3923 		else if (elType == 'checkbox') {
3924 			return elChecked ? elValue : '';
3925 		}
3926 			
3927 		else if (elType == 'select-one') {
3928 			return el.selectedIndex > -1 ? el.options[el.selectedIndex].value : '';
3929 		}
3930 			
3931 		else if (elType == 'select-multiple') {
3932 			for (var length = el.options.length; i < length; i++) {
3933 				if (el.options[i].selected) {
3934 					vals[vals.length] = el.options[i].value;
3935 				}
3936 			}
3937 			return vals;
3938 		}
3939 			
3940 		else {
3941 			return elValue;
3942 		}
3943 	}
3944 		
3945 	/*
3946 	@name: setValue
3947 	@description Set the value of a form element.  Returns values that weren't able to set if array of vals passed (for multi select). Otherwise true if val set, false if not
3948 	@returns val or bool
3949 	@private
3950 	*/
3951 	function setValue (el, val) {
3952 		var i = 0,
3953 			length,
3954 			n = 0,
3955 			nlen,
3956 			elOption,
3957 			optionVal;
3958 
3959 		if (el.type == 'select-one') {
3960 			for (length = el.options.length; i < length; i++) {
3961 				if (el.options[i].value == val) {
3962 					el.selectedIndex = i;
3963 					return true;
3964 				}
3965 			}
3966 			return false;
3967 		} else if (el.type == 'select-multiple') {
3968 			var isArray = !!val.push;
3969 			for (i = 0, length = el.options.length; i < length; i++) {
3970 				elOption = el.options[i];
3971 				optionVal = elOption.value;
3972 				if (isArray) {
3973 					elOption.selected = false;
3974 					for (nlen = val.length; n < nlen; n++) {
3975 						if (optionVal == val[n]) {
3976 							elOption.selected = true;
3977 							val.splice(n, 1);
3978 							break;
3979 						}
3980 					}
3981 				} else {
3982 					return elOption.selected = val == optionVal;
3983 				}
3984 			}
3985 			return false;
3986 		} else if (el.type == 'radio' || el.type == 'checkbox') {
3987 			el.checked = val == el.value;
3988 			return val == el.value;
3989 		} else {
3990 			el.value = val;
3991 			return true;
3992 		}
3993 	}
3994 		
3995 	/*
3996 	@name setFormValues
3997 	@description Set values of a form to those in passed in object.
3998 	@private
3999 	*/
4000 	function setFormValues (form, vals) {
4001 		var prop, currentField,
4002 			fields = {},
4003 			storeType, i = 0, n, len, foundOne, currentFieldType;
4004 
4005 		for (prop in vals) {
4006 			currentField = form[prop];
4007 			if (currentField && currentField[0] && !currentField.options) { // is array of fields
4008 				//normalise values to array of vals
4009 				vals[prop] = vals[prop] && vals[prop].push ? vals[prop] : [vals[prop]];
4010 				//order the fields by types that matter
4011 				fields.radios = [];
4012 				fields.checkboxesSelects = [];
4013 				fields.multiSelects = [];
4014 				fields.other = [];
4015 
4016 				for (i = 0; currentField[i]; i++) {
4017 					currentFieldType = currentField[i].type;
4018 					if (currentFieldType == 'radio') {
4019 						storeType = 'radios';
4020 				} else if (currentFieldType == 'select-one' || currentFieldType == 'checkbox') {
4021 						storeType = 'checkboxesSelects';
4022 					} else if (currentFieldType == 'select-multiple') {
4023 						storeType = 'multiSelects';
4024 					} else {
4025 						storeType = 'other';
4026 					}
4027 					//add it to the correct array
4028 					fields[storeType][fields[storeType].length] = currentField[i];
4029 				}
4030 
4031 				for (i = 0; fields.multiSelects[i]; i++) {
4032 					vals[prop] = setValue(fields.multiSelects[i], vals[prop]);
4033 				}
4034 				for (i = 0; fields.checkboxesSelects[i]; i++) {
4035 					setValue(fields.checkboxesSelects[i], '');
4036 					for (n = 0, len = vals[prop].length; n < len; n++) {
4037 						if (setValue(fields.checkboxesSelects[i], vals[prop][n])) {
4038 							vals[prop].slice(n, 1);
4039 							break;
4040 						}
4041 					}
4042 				}
4043 				for (i = 0; fields.radios[i]; i++) {
4044 					fields.radios[i].checked = false;
4045 					foundOne = false;
4046 					for (n = 0, len = vals[prop].length; n < len; n++) {
4047 						if (setValue(fields.radios[i], vals[prop][n])) {
4048 							vals[prop].slice(n, 1);
4049 							foundOne = true;
4050 							break;
4051 						}
4052 						if (foundOne) { break; }
4053 					}
4054 				}
4055 				for (i = 0; fields.other[i] && vals[prop][i] !== undefined; i++) {
4056 					setValue(fields.other[i], vals[prop][i]);
4057 				}
4058 			} else if (currentField && currentField.nodeName) { // is single field, easy
4059 				setValue(currentField, vals[prop]);
4060 			}
4061 		}
4062 	}
4063 
4064 	/*
4065 	@name formValues
4066 	@description Get an object containing form data.
4067 	@private
4068 	*/
4069 	function formValues (form) {
4070 		var vals = {},
4071 			radios = {},
4072 			formElements = form.elements,
4073 			i = formElements.length,
4074 			name,
4075 			formElement,
4076 			j,
4077 			radio,
4078 			nodeName;
4079 		while (i--) {
4080 			formElement = formElements[i];
4081 			nodeName = formElement.nodeName.toLowerCase();
4082 			name = formElement.name;
4083 				
4084 			// fieldsets & objects come back as form elements, but we don't care about these
4085 			// we don't bother with fields that don't have a name
4086 			// switch to whitelist?
4087 			if (
4088 				nodeName == 'fieldset' ||
4089 				nodeName == 'object' ||
4090 				!name
4091 			) { continue; }
4092 			if (formElement.type == 'checkbox' && ! formElement.checked) {
4093 				if (! name in vals) {
4094 					vals[name] = undefined;
4095 				}
4096 			} else if (formElement.type == 'radio') {
4097 				
4098 				if (radios[name]) {
4099 					radios[name][radios[name].length] = formElement;
4100 				} else {
4101 					radios[name] = [formElement];
4102 				}
4103 			} else {
4104 				var value = elementValue(formElement);
4105 				if (name in vals) {
4106 					if (vals[name].push) {
4107 						vals[name][vals[name].length] = value;
4108 					} else {
4109 						vals[name] = [vals[name], value];
4110 					}
4111 				} else {
4112 					vals[name] = value;
4113 				}
4114 			}
4115 		}
4116 		for (i in radios) {
4117 			var length,
4118 			j = 0;
4119 			for (length = radios[i].length; j < length; j++) {
4120 				radio = radios[i][j];
4121 				name = radio.name;
4122 				if (radio.checked) {
4123 					vals[radio.name] = radio.value;
4124 					break;
4125 				}
4126 			}
4127 			if (! name in vals) { alert('15 if name in vals'); vals[name] = undefined; }
4128 		}
4129 		return vals;
4130 	}
4131 });
4132 /*!
4133  * Sizzle CSS Selector Engine - v1.0
4134  *  Copyright 2009, The Dojo Foundation
4135  *  Released under the MIT, BSD, and GPL Licenses.
4136  *  More information: http://sizzlejs.com/
4137  */
4138 (function(){
4139 
4140 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
4141 	done = 0,
4142 	toString = Object.prototype.toString,
4143 	hasDuplicate = false,
4144 	baseHasDuplicate = true;
4145 
4146 // Here we check if the JavaScript engine is using some sort of
4147 // optimization where it does not always call our comparision
4148 // function. If that is the case, discard the hasDuplicate value.
4149 //   Thus far that includes Google Chrome.
4150 [0, 0].sort(function(){
4151 	baseHasDuplicate = false;
4152 	return 0;
4153 });
4154 
4155 var Sizzle = function(selector, context, results, seed) {
4156 	results = results || [];
4157 	context = context || document;
4158 
4159 	var origContext = context;
4160 
4161 	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
4162 		return [];
4163 	}
4164 	
4165 	if ( !selector || typeof selector !== "string" ) {
4166 		return results;
4167 	}
4168 
4169 	var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),
4170 		soFar = selector, ret, cur, pop, i;
4171 	
4172 	// Reset the position of the chunker regexp (start from head)
4173 	do {
4174 		chunker.exec("");
4175 		m = chunker.exec(soFar);
4176 
4177 		if ( m ) {
4178 			soFar = m[3];
4179 		
4180 			parts.push( m[1] );
4181 		
4182 			if ( m[2] ) {
4183 				extra = m[3];
4184 				break;
4185 			}
4186 		}
4187 	} while ( m );
4188 
4189 	if ( parts.length > 1 && origPOS.exec( selector ) ) {
4190 		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
4191 			set = posProcess( parts[0] + parts[1], context );
4192 		} else {
4193 			set = Expr.relative[ parts[0] ] ?
4194 				[ context ] :
4195 				Sizzle( parts.shift(), context );
4196 
4197 			while ( parts.length ) {
4198 				selector = parts.shift();
4199 
4200 				if ( Expr.relative[ selector ] ) {
4201 					selector += parts.shift();
4202 				}
4203 				
4204 				set = posProcess( selector, set );
4205 			}
4206 		}
4207 	} else {
4208 		// Take a shortcut and set the context if the root selector is an ID
4209 		// (but not if it'll be faster if the inner selector is an ID)
4210 		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
4211 				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
4212 			ret = Sizzle.find( parts.shift(), context, contextXML );
4213 			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
4214 		}
4215 
4216 		if ( context ) {
4217 			ret = seed ?
4218 				{ expr: parts.pop(), set: makeArray(seed) } :
4219 				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
4220 			set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
4221 
4222 			if ( parts.length > 0 ) {
4223 				checkSet = makeArray(set);
4224 			} else {
4225 				prune = false;
4226 			}
4227 
4228 			while ( parts.length ) {
4229 				cur = parts.pop();
4230 				pop = cur;
4231 
4232 				if ( !Expr.relative[ cur ] ) {
4233 					cur = "";
4234 				} else {
4235 					pop = parts.pop();
4236 				}
4237 
4238 				if ( pop == null ) {
4239 					pop = context;
4240 				}
4241 
4242 				Expr.relative[ cur ]( checkSet, pop, contextXML );
4243 			}
4244 		} else {
4245 			checkSet = parts = [];
4246 		}
4247 	}
4248 
4249 	if ( !checkSet ) {
4250 		checkSet = set;
4251 	}
4252 
4253 	if ( !checkSet ) {
4254 		Sizzle.error( cur || selector );
4255 	}
4256 
4257 	if ( toString.call(checkSet) === "[object Array]" ) {
4258 		if ( !prune ) {
4259 			results.push.apply( results, checkSet );
4260 		} else if ( context && context.nodeType === 1 ) {
4261 			for ( i = 0; checkSet[i] != null; i++ ) {
4262 				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
4263 					results.push( set[i] );
4264 				}
4265 			}
4266 		} else {
4267 			for ( i = 0; checkSet[i] != null; i++ ) {
4268 				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
4269 					results.push( set[i] );
4270 				}
4271 			}
4272 		}
4273 	} else {
4274 		makeArray( checkSet, results );
4275 	}
4276 
4277 	if ( extra ) {
4278 		Sizzle( extra, origContext, results, seed );
4279 		Sizzle.uniqueSort( results );
4280 	}
4281 
4282 	return results;
4283 };
4284 
4285 Sizzle.uniqueSort = function(results){
4286 	if ( sortOrder ) {
4287 		hasDuplicate = baseHasDuplicate;
4288 		results.sort(sortOrder);
4289 
4290 		if ( hasDuplicate ) {
4291 			for ( var i = 1; i < results.length; i++ ) {
4292 				if ( results[i] === results[i-1] ) {
4293 					results.splice(i--, 1);
4294 				}
4295 			}
4296 		}
4297 	}
4298 
4299 	return results;
4300 };
4301 
4302 Sizzle.matches = function(expr, set){
4303 	return Sizzle(expr, null, null, set);
4304 };
4305 
4306 Sizzle.find = function(expr, context, isXML){
4307 	var set;
4308 
4309 	if ( !expr ) {
4310 		return [];
4311 	}
4312 
4313 	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
4314 		var type = Expr.order[i], match;
4315 		
4316 		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
4317 			var left = match[1];
4318 			match.splice(1,1);
4319 
4320 			if ( left.substr( left.length - 1 ) !== "\\" ) {
4321 				match[1] = (match[1] || "").replace(/\\/g, "");
4322 				set = Expr.find[ type ]( match, context, isXML );
4323 				if ( set != null ) {
4324 					expr = expr.replace( Expr.match[ type ], "" );
4325 					break;
4326 				}
4327 			}
4328 		}
4329 	}
4330 
4331 	if ( !set ) {
4332 		set = context.getElementsByTagName("*");
4333 	}
4334 
4335 	return {set: set, expr: expr};
4336 };
4337 
4338 Sizzle.filter = function(expr, set, inplace, not){
4339 	var old = expr, result = [], curLoop = set, match, anyFound,
4340 		isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);
4341 
4342 	while ( expr && set.length ) {
4343 		for ( var type in Expr.filter ) {
4344 			if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
4345 				var filter = Expr.filter[ type ], found, item, left = match[1];
4346 				anyFound = false;
4347 
4348 				match.splice(1,1);
4349 
4350 				if ( left.substr( left.length - 1 ) === "\\" ) {
4351 					continue;
4352 				}
4353 
4354 				if ( curLoop === result ) {
4355 					result = [];
4356 				}
4357 
4358 				if ( Expr.preFilter[ type ] ) {
4359 					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
4360 
4361 					if ( !match ) {
4362 						anyFound = found = true;
4363 					} else if ( match === true ) {
4364 						continue;
4365 					}
4366 				}
4367 
4368 				if ( match ) {
4369 					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
4370 						if ( item ) {
4371 							found = filter( item, match, i, curLoop );
4372 							var pass = not ^ !!found;
4373 
4374 							if ( inplace && found != null ) {
4375 								if ( pass ) {
4376 									anyFound = true;
4377 								} else {
4378 									curLoop[i] = false;
4379 								}
4380 							} else if ( pass ) {
4381 								result.push( item );
4382 								anyFound = true;
4383 							}
4384 						}
4385 					}
4386 				}
4387 
4388 				if ( found !== undefined ) {
4389 					if ( !inplace ) {
4390 						curLoop = result;
4391 					}
4392 
4393 					expr = expr.replace( Expr.match[ type ], "" );
4394 
4395 					if ( !anyFound ) {
4396 						return [];
4397 					}
4398 
4399 					break;
4400 				}
4401 			}
4402 		}
4403 
4404 		// Improper expression
4405 		if ( expr === old ) {
4406 			if ( anyFound == null ) {
4407 				Sizzle.error( expr );
4408 			} else {
4409 				break;
4410 			}
4411 		}
4412 
4413 		old = expr;
4414 	}
4415 
4416 	return curLoop;
4417 };
4418 
4419 Sizzle.error = function( msg ) {
4420 	throw "Syntax error, unrecognized expression: " + msg;
4421 };
4422 
4423 var Expr = Sizzle.selectors = {
4424 	order: [ "ID", "NAME", "TAG" ],
4425 	match: {
4426 		ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
4427 		CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
4428 		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
4429 		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
4430 		TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
4431 		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
4432 		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
4433 		PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
4434 	},
4435 	leftMatch: {},
4436 	attrMap: {
4437 		"class": "className",
4438 		"for": "htmlFor"
4439 	},
4440 	attrHandle: {
4441 		href: function(elem){
4442 			return elem.getAttribute("href");
4443 		}
4444 	},
4445 	relative: {
4446 		"+": function(checkSet, part){
4447 			var isPartStr = typeof part === "string",
4448 				isTag = isPartStr && !/\W/.test(part),
4449 				isPartStrNotTag = isPartStr && !isTag;
4450 
4451 			if ( isTag ) {
4452 				part = part.toLowerCase();
4453 			}
4454 
4455 			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
4456 				if ( (elem = checkSet[i]) ) {
4457 					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
4458 
4459 					checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
4460 						elem || false :
4461 						elem === part;
4462 				}
4463 			}
4464 
4465 			if ( isPartStrNotTag ) {
4466 				Sizzle.filter( part, checkSet, true );
4467 			}
4468 		},
4469 		">": function(checkSet, part){
4470 			var isPartStr = typeof part === "string",
4471 				elem, i = 0, l = checkSet.length;
4472 
4473 			if ( isPartStr && !/\W/.test(part) ) {
4474 				part = part.toLowerCase();
4475 
4476 				for ( ; i < l; i++ ) {
4477 					elem = checkSet[i];
4478 					if ( elem ) {
4479 						var parent = elem.parentNode;
4480 						checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
4481 					}
4482 				}
4483 			} else {
4484 				for ( ; i < l; i++ ) {
4485 					elem = checkSet[i];
4486 					if ( elem ) {
4487 						checkSet[i] = isPartStr ?
4488 							elem.parentNode :
4489 							elem.parentNode === part;
4490 					}
4491 				}
4492 
4493 				if ( isPartStr ) {
4494 					Sizzle.filter( part, checkSet, true );
4495 				}
4496 			}
4497 		},
4498 		"": function(checkSet, part, isXML){
4499 			var doneName = done++, checkFn = dirCheck, nodeCheck;
4500 
4501 			if ( typeof part === "string" && !/\W/.test(part) ) {
4502 				part = part.toLowerCase();
4503 				nodeCheck = part;
4504 				checkFn = dirNodeCheck;
4505 			}
4506 
4507 			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
4508 		},
4509 		"~": function(checkSet, part, isXML){
4510 			var doneName = done++, checkFn = dirCheck, nodeCheck;
4511 
4512 			if ( typeof part === "string" && !/\W/.test(part) ) {
4513 				part = part.toLowerCase();
4514 				nodeCheck = part;
4515 				checkFn = dirNodeCheck;
4516 			}
4517 
4518 			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
4519 		}
4520 	},
4521 	find: {
4522 		ID: function(match, context, isXML){
4523 			if ( typeof context.getElementById !== "undefined" && !isXML ) {
4524 				var m = context.getElementById(match[1]);
4525 				return m ? [m] : [];
4526 			}
4527 		},
4528 		NAME: function(match, context){
4529 			if ( typeof context.getElementsByName !== "undefined" ) {
4530 				var ret = [], results = context.getElementsByName(match[1]);
4531 
4532 				for ( var i = 0, l = results.length; i < l; i++ ) {
4533 					if ( results[i].getAttribute("name") === match[1] ) {
4534 						ret.push( results[i] );
4535 					}
4536 				}
4537 
4538 				return ret.length === 0 ? null : ret;
4539 			}
4540 		},
4541 		TAG: function(match, context){
4542 			return context.getElementsByTagName(match[1]);
4543 		}
4544 	},
4545 	preFilter: {
4546 		CLASS: function(match, curLoop, inplace, result, not, isXML){
4547 			match = " " + match[1].replace(/\\/g, "") + " ";
4548 
4549 			if ( isXML ) {
4550 				return match;
4551 			}
4552 
4553 			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
4554 				if ( elem ) {
4555 					if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
4556 						if ( !inplace ) {
4557 							result.push( elem );
4558 						}
4559 					} else if ( inplace ) {
4560 						curLoop[i] = false;
4561 					}
4562 				}
4563 			}
4564 
4565 			return false;
4566 		},
4567 		ID: function(match){
4568 			return match[1].replace(/\\/g, "");
4569 		},
4570 		TAG: function(match, curLoop){
4571 			return match[1].toLowerCase();
4572 		},
4573 		CHILD: function(match){
4574 			if ( match[1] === "nth" ) {
4575 				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
4576 				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
4577 					match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
4578 					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
4579 
4580 				// calculate the numbers (first)n+(last) including if they are negative
4581 				match[2] = (test[1] + (test[2] || 1)) - 0;
4582 				match[3] = test[3] - 0;
4583 			}
4584 
4585 			// TODO: Move to normal caching system
4586 			match[0] = done++;
4587 
4588 			return match;
4589 		},
4590 		ATTR: function(match, curLoop, inplace, result, not, isXML){
4591 			var name = match[1].replace(/\\/g, "");
4592 			
4593 			if ( !isXML && Expr.attrMap[name] ) {
4594 				match[1] = Expr.attrMap[name];
4595 			}
4596 
4597 			if ( match[2] === "~=" ) {
4598 				match[4] = " " + match[4] + " ";
4599 			}
4600 
4601 			return match;
4602 		},
4603 		PSEUDO: function(match, curLoop, inplace, result, not){
4604 			if ( match[1] === "not" ) {
4605 				// If we're dealing with a complex expression, or a simple one
4606 				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
4607 					match[3] = Sizzle(match[3], null, null, curLoop);
4608 				} else {
4609 					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
4610 					if ( !inplace ) {
4611 						result.push.apply( result, ret );
4612 					}
4613 					return false;
4614 				}
4615 			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
4616 				return true;
4617 			}
4618 			
4619 			return match;
4620 		},
4621 		POS: function(match){
4622 			match.unshift( true );
4623 			return match;
4624 		}
4625 	},
4626 	filters: {
4627 		enabled: function(elem){
4628 			return elem.disabled === false && elem.type !== "hidden";
4629 		},
4630 		disabled: function(elem){
4631 			return elem.disabled === true;
4632 		},
4633 		checked: function(elem){
4634 			return elem.checked === true;
4635 		},
4636 		selected: function(elem){
4637 			// Accessing this property makes selected-by-default
4638 			// options in Safari work properly
4639 			elem.parentNode.selectedIndex;
4640 			return elem.selected === true;
4641 		},
4642 		parent: function(elem){
4643 			return !!elem.firstChild;
4644 		},
4645 		empty: function(elem){
4646 			return !elem.firstChild;
4647 		},
4648 		has: function(elem, i, match){
4649 			return !!Sizzle( match[3], elem ).length;
4650 		},
4651 		header: function(elem){
4652 			return (/h\d/i).test( elem.nodeName );
4653 		},
4654 		text: function(elem){
4655 			return "text" === elem.type;
4656 		},
4657 		radio: function(elem){
4658 			return "radio" === elem.type;
4659 		},
4660 		checkbox: function(elem){
4661 			return "checkbox" === elem.type;
4662 		},
4663 		file: function(elem){
4664 			return "file" === elem.type;
4665 		},
4666 		password: function(elem){
4667 			return "password" === elem.type;
4668 		},
4669 		submit: function(elem){
4670 			return "submit" === elem.type;
4671 		},
4672 		image: function(elem){
4673 			return "image" === elem.type;
4674 		},
4675 		reset: function(elem){
4676 			return "reset" === elem.type;
4677 		},
4678 		button: function(elem){
4679 			return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
4680 		},
4681 		input: function(elem){
4682 			return (/input|select|textarea|button/i).test(elem.nodeName);
4683 		}
4684 	},
4685 	setFilters: {
4686 		first: function(elem, i){
4687 			return i === 0;
4688 		},
4689 		last: function(elem, i, match, array){
4690 			return i === array.length - 1;
4691 		},
4692 		even: function(elem, i){
4693 			return i % 2 === 0;
4694 		},
4695 		odd: function(elem, i){
4696 			return i % 2 === 1;
4697 		},
4698 		lt: function(elem, i, match){
4699 			return i < match[3] - 0;
4700 		},
4701 		gt: function(elem, i, match){
4702 			return i > match[3] - 0;
4703 		},
4704 		nth: function(elem, i, match){
4705 			return match[3] - 0 === i;
4706 		},
4707 		eq: function(elem, i, match){
4708 			return match[3] - 0 === i;
4709 		}
4710 	},
4711 	filter: {
4712 		PSEUDO: function(elem, match, i, array){
4713 			var name = match[1], filter = Expr.filters[ name ];
4714 
4715 			if ( filter ) {
4716 				return filter( elem, i, match, array );
4717 			} else if ( name === "contains" ) {
4718 				return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
4719 			} else if ( name === "not" ) {
4720 				var not = match[3];
4721 
4722 				for ( var j = 0, l = not.length; j < l; j++ ) {
4723 					if ( not[j] === elem ) {
4724 						return false;
4725 					}
4726 				}
4727 
4728 				return true;
4729 			} else {
4730 				Sizzle.error( "Syntax error, unrecognized expression: " + name );
4731 			}
4732 		},
4733 		CHILD: function(elem, match){
4734 			var type = match[1], node = elem;
4735 			switch (type) {
4736 				case 'only':
4737 				case 'first':
4738 					while ( (node = node.previousSibling) )	 {
4739 						if ( node.nodeType === 1 ) { 
4740 							return false; 
4741 						}
4742 					}
4743 					if ( type === "first" ) { 
4744 						return true; 
4745 					}
4746 					node = elem;
4747 				case 'last':
4748 					while ( (node = node.nextSibling) )	 {
4749 						if ( node.nodeType === 1 ) { 
4750 							return false; 
4751 						}
4752 					}
4753 					return true;
4754 				case 'nth':
4755 					var first = match[2], last = match[3];
4756 
4757 					if ( first === 1 && last === 0 ) {
4758 						return true;
4759 					}
4760 					
4761 					var doneName = match[0],
4762 						parent = elem.parentNode;
4763 	
4764 					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
4765 						var count = 0;
4766 						for ( node = parent.firstChild; node; node = node.nextSibling ) {
4767 							if ( node.nodeType === 1 ) {
4768 								node.nodeIndex = ++count;
4769 							}
4770 						} 
4771 						parent.sizcache = doneName;
4772 					}
4773 					
4774 					var diff = elem.nodeIndex - last;
4775 					if ( first === 0 ) {
4776 						return diff === 0;
4777 					} else {
4778 						return ( diff % first === 0 && diff / first >= 0 );
4779 					}
4780 			}
4781 		},
4782 		ID: function(elem, match){
4783 			return elem.nodeType === 1 && elem.getAttribute("id") === match;
4784 		},
4785 		TAG: function(elem, match){
4786 			return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
4787 		},
4788 		CLASS: function(elem, match){
4789 			return (" " + (elem.className || elem.getAttribute("class")) + " ")
4790 				.indexOf( match ) > -1;
4791 		},
4792 		ATTR: function(elem, match){
4793 			var name = match[1],
4794 				result = Expr.attrHandle[ name ] ?
4795 					Expr.attrHandle[ name ]( elem ) :
4796 					elem[ name ] != null ?
4797 						elem[ name ] :
4798 						elem.getAttribute( name ),
4799 				value = result + "",
4800 				type = match[2],
4801 				check = match[4];
4802 
4803 			return result == null ?
4804 				type === "!=" :
4805 				type === "=" ?
4806 				value === check :
4807 				type === "*=" ?
4808 				value.indexOf(check) >= 0 :
4809 				type === "~=" ?
4810 				(" " + value + " ").indexOf(check) >= 0 :
4811 				!check ?
4812 				value && result !== false :
4813 				type === "!=" ?
4814 				value !== check :
4815 				type === "^=" ?
4816 				value.indexOf(check) === 0 :
4817 				type === "$=" ?
4818 				value.substr(value.length - check.length) === check :
4819 				type === "|=" ?
4820 				value === check || value.substr(0, check.length + 1) === check + "-" :
4821 				false;
4822 		},
4823 		POS: function(elem, match, i, array){
4824 			var name = match[2], filter = Expr.setFilters[ name ];
4825 
4826 			if ( filter ) {
4827 				return filter( elem, i, match, array );
4828 			}
4829 		}
4830 	}
4831 };
4832 
4833 var origPOS = Expr.match.POS,
4834 	fescape = function(all, num){
4835 		return "\\" + (num - 0 + 1);
4836 	};
4837 
4838 for ( var type in Expr.match ) {
4839 	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
4840 	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
4841 }
4842 
4843 var makeArray = function(array, results) {
4844 	array = Array.prototype.slice.call( array, 0 );
4845 
4846 	if ( results ) {
4847 		results.push.apply( results, array );
4848 		return results;
4849 	}
4850 	
4851 	return array;
4852 };
4853 
4854 // Perform a simple check to determine if the browser is capable of
4855 // converting a NodeList to an array using builtin methods.
4856 // Also verifies that the returned array holds DOM nodes
4857 // (which is not the case in the Blackberry browser)
4858 try {
4859 	Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
4860 
4861 // Provide a fallback method if it does not work
4862 } catch(e){
4863 	makeArray = function(array, results) {
4864 		var ret = results || [], i = 0;
4865 
4866 		if ( toString.call(array) === "[object Array]" ) {
4867 			Array.prototype.push.apply( ret, array );
4868 		} else {
4869 			if ( typeof array.length === "number" ) {
4870 				for ( var l = array.length; i < l; i++ ) {
4871 					ret.push( array[i] );
4872 				}
4873 			} else {
4874 				for ( ; array[i]; i++ ) {
4875 					ret.push( array[i] );
4876 				}
4877 			}
4878 		}
4879 
4880 		return ret;
4881 	};
4882 }
4883 
4884 var sortOrder;
4885 
4886 if ( document.documentElement.compareDocumentPosition ) {
4887 	sortOrder = function( a, b ) {
4888 		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
4889 			if ( a == b ) {
4890 				hasDuplicate = true;
4891 			}
4892 			return a.compareDocumentPosition ? -1 : 1;
4893 		}
4894 
4895 		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
4896 		if ( ret === 0 ) {
4897 			hasDuplicate = true;
4898 		}
4899 		return ret;
4900 	};
4901 } else if ( "sourceIndex" in document.documentElement ) {
4902 	sortOrder = function( a, b ) {
4903 		if ( !a.sourceIndex || !b.sourceIndex ) {
4904 			if ( a == b ) {
4905 				hasDuplicate = true;
4906 			}
4907 			return a.sourceIndex ? -1 : 1;
4908 		}
4909 
4910 		var ret = a.sourceIndex - b.sourceIndex;
4911 		if ( ret === 0 ) {
4912 			hasDuplicate = true;
4913 		}
4914 		return ret;
4915 	};
4916 } else if ( document.createRange ) {
4917 	sortOrder = function( a, b ) {
4918 		if ( !a.ownerDocument || !b.ownerDocument ) {
4919 			if ( a == b ) {
4920 				hasDuplicate = true;
4921 			}
4922 			return a.ownerDocument ? -1 : 1;
4923 		}
4924 
4925 		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
4926 		aRange.setStart(a, 0);
4927 		aRange.setEnd(a, 0);
4928 		bRange.setStart(b, 0);
4929 		bRange.setEnd(b, 0);
4930 		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
4931 		if ( ret === 0 ) {
4932 			hasDuplicate = true;
4933 		}
4934 		return ret;
4935 	};
4936 }
4937 
4938 // Utility function for retreiving the text value of an array of DOM nodes
4939 Sizzle.getText = function( elems ) {
4940 	var ret = "", elem;
4941 
4942 	for ( var i = 0; elems[i]; i++ ) {
4943 		elem = elems[i];
4944 
4945 		// Get the text from text nodes and CDATA nodes
4946 		if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
4947 			ret += elem.nodeValue;
4948 
4949 		// Traverse everything else, except comment nodes
4950 		} else if ( elem.nodeType !== 8 ) {
4951 			ret += Sizzle.getText( elem.childNodes );
4952 		}
4953 	}
4954 
4955 	return ret;
4956 };
4957 
4958 // Check to see if the browser returns elements by name when
4959 // querying by getElementById (and provide a workaround)
4960 (function(){
4961 	// We're going to inject a fake input element with a specified name
4962 	var form = document.createElement("div"),
4963 		id = "script" + (new Date()).getTime();
4964 	form.innerHTML = "<a name='" + id + "'/>";
4965 
4966 	// Inject it into the root element, check its status, and remove it quickly
4967 	var root = document.documentElement;
4968 	root.insertBefore( form, root.firstChild );
4969 
4970 	// The workaround has to do additional checks after a getElementById
4971 	// Which slows things down for other browsers (hence the branching)
4972 	if ( document.getElementById( id ) ) {
4973 		Expr.find.ID = function(match, context, isXML){
4974 			if ( typeof context.getElementById !== "undefined" && !isXML ) {
4975 				var m = context.getElementById(match[1]);
4976 				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
4977 			}
4978 		};
4979 
4980 		Expr.filter.ID = function(elem, match){
4981 			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
4982 			return elem.nodeType === 1 && node && node.nodeValue === match;
4983 		};
4984 	}
4985 
4986 	root.removeChild( form );
4987 	root = form = null; // release memory in IE
4988 })();
4989 
4990 (function(){
4991 	// Check to see if the browser returns only elements
4992 	// when doing getElementsByTagName("*")
4993 
4994 	// Create a fake element
4995 	var div = document.createElement("div");
4996 	div.appendChild( document.createComment("") );
4997 
4998 	// Make sure no comments are found
4999 	if ( div.getElementsByTagName("*").length > 0 ) {
5000 		Expr.find.TAG = function(match, context){
5001 			var results = context.getElementsByTagName(match[1]);
5002 
5003 			// Filter out possible comments
5004 			if ( match[1] === "*" ) {
5005 				var tmp = [];
5006 
5007 				for ( var i = 0; results[i]; i++ ) {
5008 					if ( results[i].nodeType === 1 ) {
5009 						tmp.push( results[i] );
5010 					}
5011 				}
5012 
5013 				results = tmp;
5014 			}
5015 
5016 			return results;
5017 		};
5018 	}
5019 
5020 	// Check to see if an attribute returns normalized href attributes
5021 	div.innerHTML = "<a href='#'></a>";
5022 	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
5023 			div.firstChild.getAttribute("href") !== "#" ) {
5024 		Expr.attrHandle.href = function(elem){
5025 			return elem.getAttribute("href", 2);
5026 		};
5027 	}
5028 
5029 	div = null; // release memory in IE
5030 })();
5031 
5032 if ( document.querySelectorAll ) {
5033 	(function(){
5034 		var oldSizzle = Sizzle, div = document.createElement("div");
5035 		div.innerHTML = "<p class='TEST'></p>";
5036 
5037 		// Safari can't handle uppercase or unicode characters when
5038 		// in quirks mode.
5039 		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
5040 			return;
5041 		}
5042 	
5043 		Sizzle = function(query, context, extra, seed){
5044 			context = context || document;
5045 
5046 			// Only use querySelectorAll on non-XML documents
5047 			// (ID selectors don't work in non-HTML documents)
5048 			if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {
5049 				try {
5050 					return makeArray( context.querySelectorAll(query), extra );
5051 				} catch(e){}
5052 			}
5053 		
5054 			return oldSizzle(query, context, extra, seed);
5055 		};
5056 
5057 		for ( var prop in oldSizzle ) {
5058 			Sizzle[ prop ] = oldSizzle[ prop ];
5059 		}
5060 
5061 		div = null; // release memory in IE
5062 	})();
5063 }
5064 
5065 (function(){
5066 	var div = document.createElement("div");
5067 
5068 	div.innerHTML = "<div class='test e'></div><div class='test'></div>";
5069 
5070 	// Opera can't find a second classname (in 9.6)
5071 	// Also, make sure that getElementsByClassName actually exists
5072 	if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
5073 		return;
5074 	}
5075 
5076 	// Safari caches class attributes, doesn't catch changes (in 3.2)
5077 	div.lastChild.className = "e";
5078 
5079 	if ( div.getElementsByClassName("e").length === 1 ) {
5080 		return;
5081 	}
5082 	
5083 	Expr.order.splice(1, 0, "CLASS");
5084 	Expr.find.CLASS = function(match, context, isXML) {
5085 		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
5086 			return context.getElementsByClassName(match[1]);
5087 		}
5088 	};
5089 
5090 	div = null; // release memory in IE
5091 })();
5092 
5093 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
5094 	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
5095 		var elem = checkSet[i];
5096 		if ( elem ) {
5097 			elem = elem[dir];
5098 			var match = false;
5099 
5100 			while ( elem ) {
5101 				if ( elem.sizcache === doneName ) {
5102 					match = checkSet[elem.sizset];
5103 					break;
5104 				}
5105 
5106 				if ( elem.nodeType === 1 && !isXML ){
5107 					elem.sizcache = doneName;
5108 					elem.sizset = i;
5109 				}
5110 
5111 				if ( elem.nodeName.toLowerCase() === cur ) {
5112 					match = elem;
5113 					break;
5114 				}
5115 
5116 				elem = elem[dir];
5117 			}
5118 
5119 			checkSet[i] = match;
5120 		}
5121 	}
5122 }
5123 
5124 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
5125 	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
5126 		var elem = checkSet[i];
5127 		if ( elem ) {
5128 			elem = elem[dir];
5129 			var match = false;
5130 
5131 			while ( elem ) {
5132 				if ( elem.sizcache === doneName ) {
5133 					match = checkSet[elem.sizset];
5134 					break;
5135 				}
5136 
5137 				if ( elem.nodeType === 1 ) {
5138 					if ( !isXML ) {
5139 						elem.sizcache = doneName;
5140 						elem.sizset = i;
5141 					}
5142 					if ( typeof cur !== "string" ) {
5143 						if ( elem === cur ) {
5144 							match = true;
5145 							break;
5146 						}
5147 
5148 					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
5149 						match = elem;
5150 						break;
5151 					}
5152 				}
5153 
5154 				elem = elem[dir];
5155 			}
5156 
5157 			checkSet[i] = match;
5158 		}
5159 	}
5160 }
5161 
5162 Sizzle.contains = document.compareDocumentPosition ? function(a, b){
5163 	return !!(a.compareDocumentPosition(b) & 16);
5164 } : function(a, b){
5165 	return a !== b && (a.contains ? a.contains(b) : true);
5166 };
5167 
5168 Sizzle.isXML = function(elem){
5169 	// documentElement is verified for cases where it doesn't yet exist
5170 	// (such as loading iframes in IE - #4833) 
5171 	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
5172 	return documentElement ? documentElement.nodeName !== "HTML" : false;
5173 };
5174 
5175 var posProcess = function(selector, context){
5176 	var tmpSet = [], later = "", match,
5177 		root = context.nodeType ? [context] : context;
5178 
5179 	// Position selectors must be done after the filter
5180 	// And so must :not(positional) so we move all PSEUDOs to the end
5181 	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
5182 		later += match[0];
5183 		selector = selector.replace( Expr.match.PSEUDO, "" );
5184 	}
5185 
5186 	selector = Expr.relative[selector] ? selector + "*" : selector;
5187 
5188 	for ( var i = 0, l = root.length; i < l; i++ ) {
5189 		Sizzle( selector, root[i], tmpSet );
5190 	}
5191 
5192 	return Sizzle.filter( later, tmpSet );
5193 };
5194 
5195 // Add Sizzle to Glow
5196 // This file is injected into sizzle.js by the ant "deps" target
5197 Glow.provide(function(glow) {
5198 	glow._sizzle = Sizzle;
5199 });
5200 
5201 return;
5202 
5203 
5204 window.Sizzle = Sizzle;
5205 
5206 })();
5207 Glow.provide(function(glow) {
5208 	var NodeListProto = glow.NodeList.prototype
5209 	/*
5210 		PrivateVar: ucheck
5211 			Used by unique(), increased by 1 on each use
5212 		*/
5213 		,	ucheck = 1
5214 	/*
5215 		PrivateVar: ucheckPropName
5216 			This is the property name used by unique checks
5217 		*/
5218 
5219 	, ucheckPropName = "_unique" + glow.UID;
5220 	/*
5221 		PrivateMethod: unique
5222 			Get an array of nodes without duplicate nodes from an array of nodes.
5223 
5224 		Arguments:
5225 			aNodes - (Array|<NodeList>)
5226 
5227 		Returns:
5228 			An array of nodes without duplicates.
5229 		*/
5230 		//worth checking if it's an XML document?
5231 		if (glow.env.ie) {
5232 			var unique = function(aNodes) {
5233 				if (aNodes.length == 1) { return aNodes; }
5234 
5235 				//remove duplicates
5236 				var r = [],
5237 					ri = 0,
5238 					i = 0;
5239 
5240 				for (; aNodes[i]; i++) {
5241 					if (aNodes[i].getAttribute(ucheckPropName) != ucheck && aNodes[i].nodeType == 1) {
5242 						r[ri++] = aNodes[i];
5243 					}
5244 					aNodes[i].setAttribute(ucheckPropName, ucheck);
5245 				}
5246 				for (i=0; aNodes[i]; i++) {
5247 					aNodes[i].removeAttribute(ucheckPropName);
5248 				}
5249 				ucheck++;
5250 				return r;
5251 			}
5252 		} else {
5253 			var unique = function(aNodes) {
5254 				if (aNodes.length == 1) { return aNodes; }
5255 
5256 				//remove duplicates
5257 				var r = [],
5258 					ri = 0,
5259 					i = 0;
5260 
5261 				for (; aNodes[i]; i++) {
5262 					if (aNodes[i][ucheckPropName] != ucheck && aNodes[i].nodeType == 1) {
5263 						r[ri++] = aNodes[i];
5264 					}
5265 					aNodes[i][ucheckPropName] = ucheck;
5266 				}
5267 				ucheck++;
5268 				return r;
5269 			}
5270 		};
5271 	/**
5272 	@name glow.NodeList#parent
5273 	@function
5274 	@description Gets the unique parent nodes of each node as a new NodeList.
5275 	@param {string | HTMLElement | NodeList} [search] Search value
5276 		If provided, will seek the next parent element until a match is found
5277 	@returns {glow.NodeList}
5278 
5279 		Returns a new NodeList containing the parent nodes, with
5280 		duplicates removed
5281 
5282 	@example
5283 		// elements which contain links
5284 		var parents = glow.dom.get("a").parent();
5285 	*/
5286 	NodeListProto.parent = function(search) {
5287 		var ret = [],
5288 			ri = 0,
5289 			i = this.length,
5290 			node;
5291 			
5292 		while (i--) {				
5293 			node = this[i];
5294 			if (node.nodeType == 1) {
5295 				if(search){						
5296 					while(node = node.parentNode){											
5297 						if (glow._sizzle.filter(search, [node]).length) {
5298 							ret[ri++] = node;							
5299 							break;
5300 						}							
5301 					}
5302 				}
5303 			
5304 				else if(node = node.parentNode){
5305 						ret[ri++] = node;						
5306 				}
5307 
5308 			}
5309 
5310 		}
5311 				
5312 		return new glow.NodeList(unique(ret));			
5313 	};
5314 	
5315 	/* Private method for prev() and next() */
5316 	function getNextOrPrev(nodelist, dir, search) {
5317 		var ret = [],
5318 			ri = 0,
5319 			node,
5320 			i = 0,
5321 			length = nodelist.length;
5322 
5323 		while (i < length) {			
5324 			node = nodelist[i];			
5325 			if(search){
5326 				while (node = node[dir + 'Sibling']) {					
5327 					if (node.nodeType == 1 && node.nodeName != '!') {						
5328 						if (glow._sizzle.filter(search, [node]).length) {
5329 							ret[ri++] = node;							
5330 							break;
5331 						}					
5332 					}					
5333 				}
5334 			}
5335 			else{
5336 				while (node = node[dir + 'Sibling']) {					
5337 					if (node.nodeType == 1 && node.nodeName != '!') {
5338 							ret[ri++] = node;							
5339 							 break;					
5340 					}					
5341 				}	
5342 			}
5343 		i++;
5344 		}
5345 		return new glow.NodeList(ret);
5346 	}
5347 	
5348 	/**
5349 	@name glow.NodeList#prev
5350 	@function
5351 	@description Gets the previous sibling element for each node in the ElementList.
5352 		If a filter is provided, the previous item that matches the filter is returned, or
5353 		none if no match is found.
5354 	@param {string | HTMLElement | NodeList} [search] Search value
5355 		If provided, will seek the previous sibling element until a match is found
5356 	@returns {glow.ElementList}
5357 		A new ElementList containing the previous sibling elements that match the (optional)
5358 		filter.
5359 	@example
5360 		// gets the element before #myLink (if there is one)
5361 		var next = glow.get("#myLink").prev();
5362 	@example
5363 		// get the previous sibling link element before #skipLink
5364 		glow.get('#skipLink').prev('a')
5365 	*/
5366 	NodeListProto.prev = function(search) {
5367 		return getNextOrPrev(this, 'previous', search);
5368 	};
5369 	
5370 	/**
5371 	@name glow.NodeList#next
5372 	@function
5373 	@description Gets the next sibling element for each node in the ElementList.
5374 		If a filter is provided, the next item that matches the filter is returned, or
5375 		none if no match is found.
5376 	@param {string | HTMLElement | NodeList} [search] Search value
5377 		If provided, will seek the next sibling element until a match is found
5378 	@returns {glow.ElementList}
5379 		A new ElementList containing the next sibling elements that match the (optional)
5380 		filter.
5381 	@example
5382 		// gets the element following #myLink (if there is one)
5383 		var next = glow.get("#myLink").next();
5384 	@example
5385 		// get the next sibling link element after #skipLink
5386 		glow.get('#skipLink').next('a')
5387 	*/
5388 	NodeListProto.next = function(search) {
5389 		return getNextOrPrev(this, 'next', search);	
5390 	};
5391 	
5392 	
5393 	/**
5394 	@name glow.NodeList#get
5395 	@function
5396 	@description Gets decendents of nodes that match a CSS selector.
5397 
5398 	@param {String} selector CSS selector
5399 
5400 	@returns {glow.NodeList}
5401 		Returns a new NodeList containing matched elements
5402 
5403 	@example
5404 		// create a new NodeList
5405 		var myNodeList = glow.dom.create("<div><a href='s.html'>Link</a></div>");
5406 
5407 		// get 'a' tags that are decendants of the NodeList nodes
5408 		myNewNodeList = myNodeList.get("a");
5409 	*/
5410 	NodeListProto.get = function(selector) {
5411 		var ret = [],
5412 			i = this.length;
5413 
5414 		while (i--) {			
5415 			glow._sizzle(selector, this[i], ret);
5416 			
5417 		}
5418 		// need to remove uniqueSorts because they're slow. Replace with own method for unique.
5419 		return new glow.NodeList(unique(ret));
5420 	};
5421 	
5422 	
5423 	
5424 	/**
5425 	@name glow.NodeList#ancestors
5426 	@function
5427 	@description Gets the unique ancestor nodes of each node as a new NodeList.
5428 	@param {Function|string} [filter] Filter test
5429 		If a string is provided, it is used in a call to {@link glow.ElementList#is ElementList#is}.
5430 		If a function is provided it will be passed 2 arguments, the index of the current item,
5431 		and the ElementList being itterated over.
5432 		Inside the function 'this' refers to the HTMLElement.
5433 		Return true to keep the node, or false to remove it.
5434 	@returns {glow.dom.NodeList}
5435 		Returns NodeList
5436 
5437 		@example
5438 		// get ancestor elements for anchor elements 
5439 		var ancestors = glow.dom.get("a").ancestors();
5440 	*/
5441 	NodeListProto.ancestors = function(filter) {
5442 		var ret = [],
5443 			ri = 0,
5444 			i = 0,
5445 			length = this.length,
5446 			node;
5447 					
5448 		while (i < length) {
5449 			node = this[i].parentNode;
5450 					
5451 			while (node && node.nodeType == 1) {							
5452 				ret[ri++] = node;
5453 				node = node.parentNode;
5454 			}								
5455 		i++;
5456 		}
5457 		if(filter){
5458             ret = new glow.NodeList(ret);
5459 			ret = ret.filter(filter);
5460 		}
5461 		return new glow.NodeList(unique(ret));
5462 	};
5463 	
5464 	/*
5465 		Private method to get the child elements for an html node (used by children())
5466 	*/
5467 		function getChildElms(node) {
5468 			var r = [],
5469 				childNodes = node.childNodes,
5470 				i = 0,
5471 				ri = 0;
5472 			
5473 			for (; childNodes[i]; i++) {
5474 				if (childNodes[i].nodeType == 1 && childNodes[i].nodeName != '!') {
5475 					r[ri++] = childNodes[i];
5476 				}
5477 			}
5478 			return r;
5479 		}
5480 	
5481 	/**
5482 	@name glow.NodeList#children
5483 	@function
5484 	@description Gets the child elements of each node as a new NodeList.
5485 
5486 	@returns {glow.dom.NodeList}
5487 
5488 		Returns a new NodeList containing all the child nodes
5489 				
5490 	@example
5491 		// get all list items
5492 		var items = glow.dom.get("ul, ol").children();
5493 	*/
5494 	NodeListProto.children = function() {
5495 		var ret = [],
5496 			i = this.length;
5497 				
5498 		while(i--) {
5499 			ret = ret.concat( getChildElms(this[i]) );
5500 		}
5501 		return new glow.NodeList(ret);	
5502 	};
5503 	
5504 	/**
5505 	@name glow.NodeList#contains
5506 	@function
5507 	@description Find if this NodeList contains the given element
5508 		
5509 	@param {string | HTMLELement | NodeList} Single element to check for
5510 
5511 	@returns {boolean}
5512 		myElementList.contains(elm)
5513 		// Returns true if an element in myElementList contains elm, or IS elm.
5514 	*/
5515 	NodeListProto.contains = function(elm) {
5516 		var i = 0,
5517 			node = new glow.NodeList(elm)[0],
5518 			length = this.length,
5519 			newNodes,
5520 			toTest;
5521 
5522 		// missing some nodes? Return false
5523 		if ( !node || !this.length ) {
5524 			return false;
5525 		}
5526 	
5527 		if (this[0].compareDocumentPosition) { //w3 method
5528 			while (i < length) {
5529 				//break out if the two are teh same
5530 				if(this[i] == node){
5531 					break;
5532 				}
5533 				//check against bitwise to see if node is contained in this
5534 				else if (!(this[i].compareDocumentPosition(node) & 16)) {								
5535 					return false;
5536 				}
5537 			i++;
5538 			}
5539 		}
5540 		else if(node.contains){					
5541 			for (; i < length; i++) {
5542 				if ( !( this[i].contains( node  ) ) ) {
5543 					return false;
5544 				}
5545 			}
5546 		}				
5547 		else { //manual method for last chance corale
5548 			while (i < length) {
5549 				toTest = node;
5550 				while (toTest = toTest.parentNode) {
5551 					if (this[i] == toTest) { break; }
5552 				}
5553 				if (!toTest) {
5554 					return false;
5555 				}
5556 			i++;
5557 			}
5558 		}
5559 			
5560 		return true;
5561 	};
5562 });
5563 Glow.provide(function(glow) {
5564 	var NodeListProto = glow.NodeList.prototype,
5565 		document = window.document,
5566 		undefined;
5567 	
5568 	// create a fragment and insert a set of nodes into it
5569 	function createFragment(nodes) {
5570 		var fragment = document.createDocumentFragment(),
5571 			i = 0,
5572 			node;
5573 		
5574 		while ( node = nodes[i++] ) {
5575 			fragment.appendChild(node);
5576 		}
5577 		
5578 		return fragment;
5579 	}
5580 	
5581 	// generate the #before and #after methods
5582 	// after: 1 for #(insert)after, 0 for #(insert)before
5583 	// insert: 1 for #insert(After|Before), 0 for #(after|before)
5584 	function afterAndBefore(after, insert) {
5585 		return function(elements) {
5586 			var toAddList,
5587 				toAddToList,
5588 				fragmentToAdd,
5589 				nextFragmentToAdd,
5590 				item,
5591 				itemParent;
5592 			
5593 			if (!this.length) { return this; }
5594 			
5595 			// normalise 'elements'
5596 			// if we're dealing with append/prepend then strings are always treated as HTML strings
5597 			if (!insert && typeof elements === 'string') {
5598 				elements = new glow.NodeList( glow.NodeList._strToNodes(elements) );
5599 			}
5600 			else {
5601 				elements = new glow.NodeList(elements);
5602 			}
5603 			
5604 			// set the element we're going to add to, and the elements we're going to add
5605 			if (insert) {
5606 				toAddToList = elements;
5607 				toAddList = new glow.NodeList(this);
5608 			}
5609 			else {
5610 				toAddToList = this;
5611 				toAddList = elements;
5612 			}
5613 			
5614 			nextFragmentToAdd = createFragment(toAddList);
5615 			
5616 			for (var i = 0, leni = toAddToList.length, lasti = leni - 1; i < leni; i++) {
5617 				item = toAddToList[i];
5618 				fragmentToAdd = nextFragmentToAdd;
5619 				
5620 				// we can only append after if the element has a parent right?
5621 				if (itemParent = item.parentNode) {
5622 					if (i !== lasti) { // if not the last item
5623 						nextFragmentToAdd = fragmentToAdd.cloneNode(true);
5624 						insert && toAddList.push(nextFragmentToAdd.childNodes);
5625 					}
5626 					itemParent.insertBefore(fragmentToAdd, after ? item.nextSibling : item);
5627 				}
5628 			}
5629 			
5630 			return insert ? toAddList : toAddToList;
5631 		}
5632 	}
5633 	
5634 	// generate the #append, #appendTo, #prepend and #prependTo methods
5635 	// append: 1 for #append(To), 0 for #prepend(To)
5636 	// to: 1 for #(append|prepend)To, 0 for #(append|prepend)
5637 	function appendAndPrepend(append, to) {
5638 		return function(elements) {
5639 			var toAddList,
5640 				toAddToList,
5641 				fragmentToAdd,
5642 				nextFragmentToAdd,
5643 				item;
5644 			
5645 			if (!this.length) { return this; }
5646 			
5647 			// normalise 'elements'
5648 			// if we're dealing with append/prepend then strings are always treated as HTML strings
5649 			if (!to && typeof elements === 'string') {
5650 				elements = new glow.NodeList( glow.NodeList._strToNodes(elements) );
5651 			}
5652 			else {
5653 				elements = new glow.NodeList(elements);
5654 			}
5655 				
5656 			// set the element we're going to add to, and the elements we're going to add
5657 			if (to) {
5658 				toAddToList = elements;
5659 				toAddList = new glow.NodeList(this);
5660 			}
5661 			else {
5662 				toAddToList = this;
5663 				toAddList = elements;
5664 			}
5665 			
5666 			nextFragmentToAdd = createFragment(toAddList);
5667 			
5668 			for (var i = 0, leni = toAddToList.length, lasti = leni - 1; i < leni; i++) {
5669 				item = toAddToList[i];
5670 				fragmentToAdd = nextFragmentToAdd;
5671 				
5672 				// avoid trying to append to non-elements
5673 				if (item.nodeType === 1) {
5674 					if (i !== lasti) { // if not the last item
5675 						nextFragmentToAdd = fragmentToAdd.cloneNode(true);
5676 						// add the clones to the return element for appendTo / prependTo
5677 						to && toAddList.push(nextFragmentToAdd.childNodes);
5678 					}
5679 					item.insertBefore(fragmentToAdd, append ? null : item.firstChild);
5680 				}
5681 			}
5682 			
5683 			return to ? toAddList : toAddToList;
5684 		}
5685 	}
5686 	
5687 	/**
5688 		@name glow.NodeList#after
5689 		@function
5690 		@description Insert node(s) after each node in this NodeList.
5691 			If there is more than one node in this NodeList, 'nodes'
5692 			will be inserted after the first element and clones will be
5693 			inserted after each subsequent element.
5694 			
5695 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert
5696 			Strings will be treated as HTML strings.
5697 		
5698 		@returns {glow.NodeList} Original NodeList
5699 		
5700 		@example
5701 			// adds a paragraph after each heading
5702 			glow('h1, h2, h3').after('<p>That was a nice heading.</p>');
5703 	*/
5704 	NodeListProto.after = afterAndBefore(1);
5705 	
5706 	/**
5707 		@name glow.NodeList#before
5708 		@function
5709 		@description Insert node(s) before each node in this NodeList.
5710 			If there is more than one node in this NodeList, 'nodes'
5711 			will be inserted before the first element and clones will be
5712 			inserted before each subsequent element.
5713 			
5714 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert
5715 			Strings will be treated as HTML strings.
5716 		
5717 		@returns {glow.NodeList} Original NodeList
5718 		
5719 		@example
5720 			// adds a div before each paragraph
5721 			glow('p').before('<div>Here comes a paragraph!</div>');
5722 	*/
5723 	NodeListProto.before = afterAndBefore(0);
5724 	
5725 	/**
5726 		@name glow.NodeList#append
5727 		@function
5728 		@description Appends node to each node in this NodeList.
5729 			If there is more than one node in this NodeList, then the given nodes
5730 			are appended to the first node and clones are appended to the other
5731 			nodes.
5732 			
5733 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Nodes(s) to append
5734 			Strings will be treated as HTML strings.
5735 		
5736 		@returns {glow.NodeList} Original NodeList
5737 		
5738 		@example
5739 			// ends every paragraph with '...'
5740 			glow('p').append('<span>...</span>');
5741 	*/
5742 	NodeListProto.append = appendAndPrepend(1);
5743 	
5744 	/**
5745 		@name glow.NodeList#prepend
5746 		@function
5747 		@description Prepends nodes to each node in this NodeList.
5748 			If there is more than one node in this NodeList, then the given nodes
5749 			are prepended to the first node and clones are prepended to the other
5750 			nodes.
5751 			
5752 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Nodes(s) to prepend
5753 			Strings will be treated as HTML strings.
5754 		
5755 		@returns {glow.NodeList} Original NodeList
5756 		
5757 		@example
5758 			// prepends every paragraph with 'Paragraph: '
5759 			glow('p').prepend('<span>Paragraph: </span>');
5760 	*/
5761 	NodeListProto.prepend = appendAndPrepend(0);
5762 	
5763 	/**
5764 		@name glow.NodeList#appendTo
5765 		@function
5766 		@description Appends nodes in this NodeList to given node(s)
5767 			If appending to more than one node, the NodeList is appended
5768 			to the first node and clones are appended to the others.
5769 			
5770 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} node Node(s) to append to.
5771 			Strings will be treated as CSS selectors or HTML strings.
5772 		
5773 		@returns {glow.NodeList} The appended nodes.
5774 		
5775 		@example
5776 			// appends '...' to every paragraph
5777 			glow('<span>...</span>').appendTo('p');
5778 	*/
5779 	NodeListProto.appendTo = appendAndPrepend(1, 1);
5780 
5781 	/**
5782 		@name glow.NodeList#prependTo
5783 		@function
5784 		@description Prepends nodes in this NodeList to given node(s)
5785 			If prepending to more than one node, the NodeList is prepended
5786 			to the first node and clones are prepended to the others.
5787 			
5788 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} node Node(s) to prepend to
5789 			Strings will be treated as CSS selectors or HTML strings.
5790 		
5791 		@returns {glow.NodeList} The prepended nodes.
5792 		
5793 		@example
5794 			// prepends 'Paragraph: ' to every paragraph
5795 			glow('<span>Paragraph: </span>').prependTo('p');
5796 	*/
5797 	NodeListProto.prependTo = appendAndPrepend(0, 1);
5798 	
5799 	/**
5800 		@name glow.NodeList#insertAfter
5801 		@function
5802 		@description Insert this NodeList after the given nodes
5803 			If inserting after more than one node, the NodeList is inserted
5804 			after the first node and clones are inserted after the others.
5805 			
5806 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert after
5807 			Strings will be treated as CSS selectors.
5808 			
5809 		@returns {glow.NodeList} Inserted nodes.
5810 		
5811 		@example
5812 			// adds a paragraph after each heading
5813 			glow('<p>HAI!</p>').insertAfter('h1, h2, h3');
5814 	*/
5815 	NodeListProto.insertAfter = afterAndBefore(1, 1);
5816 	
5817 	/**
5818 		@name glow.NodeList#insertBefore
5819 		@function
5820 		@description Insert this NodeList before the given nodes
5821 			If inserting before more than one node, the NodeList is inserted
5822 			before the first node and clones are inserted before the others.
5823 			
5824 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert before
5825 			Strings will be treated as CSS selectors.
5826 			
5827 		@returns {glow.NodeList} Inserted nodes.
5828 		
5829 		@example
5830 			// adds a div before each paragraph
5831 			glow('<div>Here comes a paragraph!</div>').insertBefore('p');
5832 	*/
5833 	NodeListProto.insertBefore = afterAndBefore(0, 1);
5834 	
5835 	/**
5836 		@name glow.NodeList#destroy
5837 		@function
5838 		@description Removes each element from the document
5839 			The element, attached listeners & attached data will be
5840 			destroyed to free up memory.
5841 			
5842 			Detroyed elements may not be reused in some browsers.
5843 			
5844 		@returns undefined
5845 		
5846 		@example
5847 			// destroy all links in the document
5848 			glow("a").destroy();
5849 	*/
5850 	var tmpDiv = document.createElement('div');
5851 	
5852 	NodeListProto.destroy = function() {
5853 		var allElements = this.get('*').push(this);
5854 		
5855 		// remove data and listeners
5856 		glow.events.removeAllListeners( allElements.removeData() );
5857 		
5858 		this.appendTo(tmpDiv);
5859 		tmpDiv.innerHTML = '';
5860 	};
5861 	
5862 	/**
5863 		@name glow.NodeList#remove
5864 		@function
5865 		@description Removes each element from the document
5866 			If you no longer need the elements, consider using
5867 			{@link glow.NodeList#destroy destroy}
5868 			
5869 		@returns {glow.NodeList} The removed elements
5870 
5871 		@example
5872 			// take all the links out of a document
5873 			glow("a").remove();
5874 	*/
5875 	NodeListProto.remove = function() {
5876 		var parent,
5877 			node,
5878 			i = this.length;
5879 		
5880 		while (i--) {
5881 			node = this[i];
5882 			if (parent = node.parentNode) {
5883 				parent.removeChild(node);
5884 			}
5885 		}
5886 		
5887 		return this;
5888 	};
5889 	
5890 	/**
5891 		@name glow.NodeList#empty
5892 		@function
5893 		@description Removes the nodes' contents
5894 
5895 		@returns {glow.NodeList} Original nodes
5896 
5897 		@example
5898 			// remove the contents of all textareas
5899 			glow("textarea").empty();
5900 	*/
5901 	// TODO: is this shortcut worth doing?
5902 	NodeListProto.empty = glow.env.ie ?
5903 		// When you clean an element out using innerHTML it destroys its inner text nodes in IE8 and below
5904 		// Here's an alternative method for IE:
5905 		function() {
5906 			var i = this.length, node, child;
5907 			
5908 			while (i--) {
5909 				node = this[i];
5910 				while (child = node.firstChild) {
5911 					node.removeChild(child);
5912 				}
5913 			}
5914 			
5915 			return this;
5916 		} :
5917 		// method for most browsers
5918 		function() {
5919 			var i = this.length;
5920 			
5921 			while (i--) {
5922 				this[i].innerHTML = '';
5923 			}
5924 			
5925 			return this;
5926 		}
5927 
5928 	/**
5929 		@name glow.NodeList#replaceWith
5930 		@function
5931 		@description Replace elements with another
5932 		
5933 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} elements Element(s) to insert into the document
5934 			If there is more than one element in the NodeList, then the given elements
5935 			replace the first element, clones are appended to the other	elements.
5936 			
5937 		@returns {glow.NodeList} The replaced elements
5938 			Call {@link glow.NodeList#destroy destroy} on these if you
5939 			no longer need them.
5940 	*/
5941 	NodeListProto.replaceWith = function(elements) {
5942 		return this.after(elements).remove();
5943 	};
5944 	
5945 	/**
5946 		@name glow.NodeList#wrap
5947 		@function
5948 		@description Wraps the given NodeList with the specified element(s).
5949 			The given NodeList items will always be placed in the first
5950 			child element that contains no further elements.
5951 			
5952 			Each item in a given NodeList will be wrapped individually.
5953 		
5954 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} wrapper Element to use as a wrapper
5955 			Strings will be treated as HTML strings if they begin with <, else
5956 			they'll be treated as a CSS selector.
5957 		
5958 		@returns {glow.NodeList} The NodeList with new wrapper parents
5959 			
5960 		@example
5961 			// <span id="mySpan">Hello</span>
5962 			glow("#mySpan").wrap("<div><p></p></div>");
5963 			// Makes:
5964 			// <div>
5965 			//     <p>
5966 			//         <span id="mySpan">Hello</span>
5967 			//     </p>
5968 			// </div>
5969 			
5970 	*/
5971 	// get first child element node of an element, otherwise undefined
5972 	function getFirstChildElm(parent) {					
5973 		for (var child = parent.firstChild; child; child = child.nextSibling) {
5974 			if (child.nodeType == 1) {
5975 				return child;
5976 			}			
5977 		}			
5978 		return undefined;			
5979 	}
5980 	
5981 	NodeListProto.wrap = function(wrapper) {
5982 		// normalise input
5983 		wrapper = new glow.NodeList(wrapper);
5984 		
5985 		// escape if the wraper is non-existant or not an element
5986 		if (!wrapper[0] || wrapper[0].nodeType != 1) {
5987 			return this;
5988 		}
5989 		
5990 		var toWrap,
5991 			toWrapTarget,
5992 			firstChildElm;
5993 		
5994 		for (var i = 0, leni = this.length; i<leni; i++) {
5995 			toWrap = this[i];
5996 			// get target element to insert toWrap in
5997 			toWrapTarget = wrapper[0];
5998 			
5999 			while (toWrapTarget) {
6000 				firstChildElm = getFirstChildElm(toWrapTarget);
6001 					
6002 				if (!firstChildElm) {
6003 					break;
6004 				}
6005 				toWrapTarget = firstChildElm;
6006 			}
6007 			
6008 			if (toWrap.parentNode) {						
6009 				wrapper.insertBefore(toWrap);													
6010 			}
6011 			
6012 			// If wrapping multiple nodes, we need to take a clean copy of the wrapping nodes
6013 			if (i != leni-1) {
6014 				wrapper = wrapper.clone();
6015 			}
6016 			
6017 			toWrapTarget.appendChild(toWrap);
6018 		}
6019 		
6020 		return this;
6021 	};
6022 	
6023 	/**
6024 		@name glow.NodeList#unwrap
6025 		@function
6026 		@description Removes the parent of each item in the list
6027 		
6028 		@returns {glow.NodeList} The now unwrapped elements
6029 		
6030 		@example
6031 			// Before: <div><p><span id="mySpan">Hello</span></p></div>
6032 			// unwrap the given element
6033 			glow("#mySpan").unwrap();
6034 			// After: <div><span id="mySpan">Hello</span></div>
6035 	*/
6036 	NodeListProto.unwrap = function() {
6037 		var parentToRemove,
6038 			childNodes,
6039 			// get unique parents
6040 			parentsToRemove = this.parent();
6041 		
6042 		for (var i = 0, leni = parentsToRemove.length; i < leni; i++) {				
6043 			parentToRemove = parentsToRemove.slice(i, i+1);
6044 			// make sure we get all children, including text nodes
6045 			childNodes = new glow.NodeList( parentToRemove[0].childNodes );
6046 			
6047 			// 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
6048 			if (!parentToRemove[0].parentNode){
6049 				childNodes.remove();
6050 				parentToRemove.destroy();
6051 			}
6052 			else {
6053 				childNodes.insertBefore(parentToRemove);
6054 				parentToRemove.destroy();							
6055 			}
6056 		}
6057 		return this;
6058 	};
6059 	
6060 	/**
6061 		@name glow.NodeList#clone
6062 		@function
6063 		@description Clones each node in the NodeList, along with data & event listeners
6064 		
6065 		@returns {glow.NodeList}
6066 			Returns a new NodeList containing clones of all the nodes in
6067 			the NodeList
6068 		
6069 		@example
6070 			// get a copy of all heading elements
6071 			var myClones = glow.get("h1, h2, h3, h4, h5, h6").clone();
6072 	*/
6073 	
6074 	NodeListProto.clone = function() {
6075 		var clonedNodeList = this.copy(),
6076 			allCloneElms = clonedNodeList.get('*').push(clonedNodeList),
6077 			allElms = this.get('*').push(this);
6078 		
6079 		// now copy over the data and events for all cloned elements
6080 		glow.events._copyDomEvents(allElms, allCloneElms);
6081 		glow.NodeList._copyData(allElms, allCloneElms);
6082 		
6083 		return clonedNodeList;
6084 	};
6085 	
6086 	
6087 	/**
6088 		@name glow.NodeList#copy
6089 		@function
6090 		@description Copies each node in the NodeList, excluding data & event listeners
6091 		
6092 		@returns {glow.NodeList}
6093 			Returns a new NodeList containing copies of all the nodes in
6094 			the NodeList
6095 		
6096 		@example
6097 			// get a copy of all heading elements
6098 			var myCopies = glow.get("h1, h2, h3, h4, h5, h6").copy();
6099 	*/
6100 	NodeListProto.copy = function() {
6101 		var nodes = [],
6102 			i = this.length,
6103 			clonedNodeList,
6104 			allCloneElms,
6105 			eventIdProp = '__eventId' + glow.UID,
6106 			dataPropName = '_uniqueData' + glow.UID;
6107 		
6108 		while (i--) {
6109 			nodes[i] = this[i].cloneNode(true);
6110 		}
6111 		
6112 		clonedNodeList = new glow.NodeList(nodes);
6113 		
6114 		// IE also clones node properties as attributes
6115 		// we need to get rid of the eventId & dataId
6116 		if (glow.env.ie) {
6117 			allCloneElms = clonedNodeList.get('*').push(nodes);
6118 			i = allCloneElms.length;
6119 			while (i--) {
6120 				allCloneElms[i][dataPropName] = allCloneElms[i][eventIdProp] = undefined;
6121 			}
6122 		}
6123 		
6124 		return clonedNodeList;
6125 	};
6126 	
6127 	/**
6128 		@name glow.NodeList#html
6129 		@function
6130 		@description Gets / sets HTML content
6131 			Either gets content of the first element, or sets the content
6132 			for all elements in the list
6133 			
6134 		@param {String} [htmlString] String to set as the HTML of elements
6135 			If omitted, the html for the first element in the list is
6136 			returned.
6137 		
6138 		@returns {glow.NodeList | string}
6139 			Returns the original NodeList when setting,
6140 			or the HTML content when getting.
6141 			
6142 		@example
6143 			// get the html in #footer
6144 			var footerContents = glow("#footer").html();
6145 			
6146 		@example
6147 			// set a new footer
6148 			glow("#footer").html("<strong>Hello World!</strong>");
6149 	*/
6150 	NodeListProto.html = function(htmlString) {
6151 		// getting
6152 		if (!arguments.length) {
6153 			return this[0] ? this[0].innerHTML : '';
6154 		}
6155 		
6156 		// setting
6157 		var i = this.length,
6158 			node;
6159 		
6160 		// normalise the string
6161 		htmlString = htmlString === undefined? '' : String(htmlString);
6162 		
6163 		while (i--) {
6164 			node = this[i];
6165 			if (node.nodeType == 1) {
6166 				try {
6167 					// this has a habit of failing in IE for some elements
6168 					node.innerHTML = htmlString;
6169 				}
6170 				catch (e) {
6171 					new glow.NodeList(node).empty().append(htmlString);
6172 				}
6173 			}
6174 		}
6175 		
6176 		return this;
6177 	};
6178 	
6179 	/**
6180 		@name glow.NodeList#text
6181 		@function
6182 		@description Gets / set the text content
6183 			Either gets content of the first element, or sets the content
6184 			for all elements in the list
6185 		
6186 		@param {String} [text] String to set as the text of elements
6187 			If omitted, the test for the first element in the list is
6188 			returned.
6189 		
6190 		@returns {glow.NodeList | String}
6191 			Returns the original NodeList when setting,
6192 			or the text content when getting.
6193 
6194 		@example
6195 			// set text
6196 			var div = glow("<div></div>").text("Fun & games!");
6197 			// <div>Func & games!</div>
6198 			
6199 		@example
6200 			// get text
6201 			var mainHeading = glow('#mainHeading').text();
6202 	*/
6203 	NodeListProto.text = function(textString) {
6204 		var firstNode = this[0],
6205 			i = this.length,
6206 			node;
6207 		
6208 		// getting
6209 		if (!arguments.length) {
6210 			// get the text by checking a load of properties in priority order
6211 			return firstNode ?
6212 				firstNode.textContent ||
6213 				firstNode.innerText ||
6214 				firstNode.nodeValue || '' // nodeValue for comment & text nodes
6215 				: '';
6216 		}
6217 		
6218 		// setting
6219 		// normalise the string
6220 		textString = textString ? String(textString): '';
6221 		
6222 		this.empty();
6223 		while (i--) {
6224 			node = this[i];
6225 			if (node.nodeType == 1) {
6226 				node.appendChild( document.createTextNode(textString) );
6227 			}
6228 			else {
6229 				node.nodeValue = textString;
6230 			}
6231 		}
6232 		
6233 		return this;
6234 	};
6235 });
6236 Glow.provide(function(glow) {
6237 	var NodeList = glow.NodeList,
6238 		NodeListProto = NodeList.prototype,
6239 		win = window,
6240 		document = win.document,	
6241 		getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
6242 		// regex for toStyleProp
6243 		dashAlphaRe = /-(\w)/g,
6244 		// regex for getCssValue
6245 		isNumberButNotPx = /^-?[\d\.]+(?!px)[%a-z]+$/i,
6246 		ieOpacityRe = /alpha\(opacity=([^\)]+)\)/,
6247 		// regex for #css
6248 		hasUnits = /width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/;
6249 	
6250 	// replace function for toStyleProp
6251 	function toStylePropReplace(match, p1) {
6252 		return p1.toUpperCase();
6253 	}
6254 	
6255 	/**
6256 		@private
6257 		@function
6258 		@description Converts a css property name into its javascript name.
6259 			Such as "background-color" to "backgroundColor".
6260 		@param {string} prop CSS Property name.
6261 		@returns {string}
6262 	*/
6263 	function toStyleProp(prop) {
6264 		if (prop == 'float') {
6265 			return glow.env.ie ? 'styleFloat' : 'cssFloat';
6266 		}
6267 		return prop.replace(dashAlphaRe, toStylePropReplace);
6268 	}
6269 	
6270 	/**
6271 		@private
6272 		@function
6273 		@description Get a total value of multiple CSS properties
6274 		@param {HTMLElement} elm
6275 		@param {string[]} props CSS properties to get the total value of
6276 		@returns {number}
6277 	*/
6278 	function getTotalCssValue(elm, props) {
6279 		var total = 0,
6280 			i = props.length;
6281 			
6282 		while (i--) {
6283 			total += parseFloatFunc(
6284 				getCssValue( elm, props[i] )
6285 			) || 0;
6286 		}
6287 		
6288 		return total;
6289 	}
6290 	
6291 	/**
6292 		@private
6293 		@function
6294 		@description Get a computed css property
6295 		@param {HTMLElement} elm
6296 		@param {string} prop CSS property to get the value of
6297 		@returns {string}
6298 	*/
6299 	function getCssValue(elm, prop) {
6300 		var defaultView = elm.ownerDocument.defaultView,
6301 			computedStyle,
6302 			r,
6303 			currentStyle,
6304 			oldDisplay,
6305 			match;
6306 		
6307 		if (getComputedStyle) { // the W3 way
6308 			computedStyle = defaultView.getComputedStyle(elm, null);
6309 			
6310 			// http://bugs.webkit.org/show_bug.cgi?id=13343
6311 			// Webkit fails to get margin-right for rendered elements.
6312 			// margin-right is measured from the right of the element to the right of the parent
6313 			if (glow.env.webkit && prop === 'margin-right') {
6314 				oldDisplay = elm.style.display;
6315 				elm.style.display = 'none';
6316 				r = computedStyle[prop];
6317 				elm.style.display = oldDisplay;
6318 			}
6319 			else {
6320 				r = computedStyle.getPropertyValue(prop);
6321 			}
6322 		}
6323 		else if (currentStyle = elm.currentStyle) { // the IE<9 way
6324 			if (prop === 'opacity') { // opacity, the IE way
6325 				match = ieOpacityRe.exec(currentStyle.filter);
6326 				return match ? String(parseInt(match[1], 10) / 100) || '1' : '1';
6327 			}
6328 			// catch border-*-width. IE gets this wrong if the border style is none
6329 			else if (
6330 				prop.indexOf('border') === 0 &&
6331 				prop.slice(-5) === 'width' &&
6332 				getCssValue(elm, 'border-style') === 'none') {
6333 				
6334 				return '0px';
6335 			}
6336 			
6337 			r = currentStyle[ toStyleProp(prop) ];
6338 			
6339 			// font-size gives us incorrect values when put through getPixelValue, avoid
6340 			if (isNumberButNotPx.test(r) && prop != 'font-size') {
6341 				r = getPixelValue( elm, r, prop.indexOf('height') >= 0 || prop.indexOf('top') >= 0 ) + 'px';
6342 			}
6343 		}
6344 		
6345 		// post-process return value
6346 		if (prop === 'opacity') {
6347 			r = r || '1';
6348 		}
6349 		else if (prop.indexOf('color') != -1) { //deal with colour values
6350 			r = NodeList._parseColor(r).toString();
6351 		}
6352 		
6353 		return r;
6354 	}
6355 	
6356 	// vars used in _parseColor
6357 	var mathRound = Math.round,
6358 		parseIntFunc = parseInt,
6359 		parseFloatFunc = parseFloat,
6360 		htmlColorNames = {
6361 			black:   0x000000,
6362 			silver:  0xc0c0c0,
6363 			gray:    0x808080,
6364 			white:   0xffffff,
6365 			maroon:  0x800000,
6366 			red:     0xff0000,
6367 			purple:  0x800080,
6368 			fuchsia: 0xff00ff,
6369 			green:   0x008000,
6370 			lime:    0x00ff00,
6371 			olive:   0x808000,
6372 			yellow:  0xffff00,
6373 			navy:    0x000080,
6374 			blue:    0x0000ff,
6375 			teal:    0x008080,
6376 			aqua:    0x00ffff,
6377 			orange:  0xffa500
6378 		},
6379 		// match a string like rgba(10%, 10%, 10%, 0.5) where the % and alpha parts are optional
6380 		colorRegex = /^rgba?\(([\d\.]+)(%?),\s*([\d\.]+)(%?),\s*([\d\.]+)(%?)(?:,\s*([\d\.]+))?/i,
6381 		transColorRegex = /^(transparent|rgba\(0, ?0, ?0, ?0\))$/,
6382 		wordCharRegex = /\w/g;
6383 	
6384 	/**
6385 		@name glow.NodeList._parseColor
6386 		@private
6387 		@function
6388 		@description Convert a CSS colour string into a normalised format
6389 		@returns {string} String in format rgb(0, 0, 0)
6390 			Returned string also has r, g & b number properties
6391 	*/
6392 	NodeList._parseColor = function (val) {
6393 		if ( transColorRegex.test(val) ) {
6394 			return 'rgba(0, 0, 0, 0)';
6395 		}
6396 		
6397 		var match, //tmp regex match holder
6398 			r, g, b, a, //final colour vals
6399 			hex; //tmp hex holder
6400 
6401 		if ( match = colorRegex.exec(val) ) { //rgb() format, cater for percentages
6402 			r = match[2] ? mathRound( parseFloatFunc(match[1]) * 2.55 ) : parseIntFunc(match[1]);
6403 			g = match[4] ? mathRound( parseFloatFunc(match[3]) * 2.55 ) : parseIntFunc(match[3]);
6404 			b = match[6] ? mathRound( parseFloatFunc(match[5]) * 2.55 ) : parseIntFunc(match[5]);
6405 			a = parseFloatFunc( match[7] || '1' );
6406 		} else {
6407 			if (typeof val == 'number') {
6408 				hex = val;
6409 			}
6410 			else if (val.charAt(0) == '#') {
6411 				if (val.length === 4) { //deal with #fff shortcut
6412 					val = val.replace(wordCharRegex, '$&$&');
6413 				}
6414 				hex = parseIntFunc(val.slice(1), 16);
6415 			}
6416 			else {
6417 				hex = htmlColorNames[val];
6418 			}
6419 
6420 			r = (hex) >> 16;
6421 			g = (hex & 0x00ff00) >> 8;
6422 			b = (hex & 0x0000ff);
6423 			a = 1;
6424 		}
6425 
6426 		val = new String('rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')');
6427 		val.r = r;
6428 		val.g = g;
6429 		val.b = b;
6430 		val.a = a;
6431 		return val;
6432 	}
6433 	
6434 	// vars for generateWidthAndHeight
6435 	var horizontalBorderPadding = [
6436 			'border-left-width',
6437 			'border-right-width',
6438 			'padding-left',
6439 			'padding-right'
6440 		],
6441 		verticalBorderPadding = [
6442 			'border-top-width',
6443 			'border-bottom-width',
6444 			'padding-top',
6445 			'padding-bottom'
6446 		];
6447 	
6448 	/**
6449 		@private
6450 		@function
6451 		@description Get width or height of an element width/height.
6452 		@param {HTMLElement} elm Element to measure.
6453 		@param {string} 'Width' or 'Height'.
6454 	*/
6455 	function getDimension(elm, cssProp) {
6456 		// exit if there's no element, or it isn't an Element, window or document
6457 		if ( !elm || elm.nodeType === 3 || elm.nodeType === 8 ) {
6458 			return 0;
6459 		}
6460 		
6461 		var r,
6462 			document = elm.ownerDocument || elm.document || elm,
6463 			docElm = document.documentElement,
6464 			docBody = document.body,
6465 			docElmOrBody = glow.env.standardsMode ? docElm : docBody,
6466 			isWidth = (cssProp == 'Width'),
6467 			cssBorderPadding;
6468 
6469 		if (elm.window) { // is window
6470 			r = glow.env.webkit ? (isWidth ? docBody.clientWidth : elm.innerHeight) :
6471 				/* else */        docElmOrBody['client' + cssProp];
6472 		}
6473 		else if (elm.getElementById) { // is document
6474 			// we previously checked offsetWidth & clientWidth here
6475 			// but they returned values too large in IE6
6476 			r = Math.max(
6477 				docBody['scroll' + cssProp],
6478 				docElm['scroll' + cssProp]
6479 			)
6480 		}
6481 		else {
6482 			// get an array of css borders & padding
6483 			cssBorderPadding = isWidth ? horizontalBorderPadding : verticalBorderPadding;
6484 			r = elm['offset' + cssProp] - getTotalCssValue(elm, cssBorderPadding);
6485 		}
6486 		return r;
6487 	}
6488 	
6489 	/**
6490 		@private
6491 		@function
6492 		@description Converts a relative value into an absolute pixel value.
6493 			Only works in IE with Dimension value (not stuff like relative font-size).
6494 			Based on some Dean Edwards' code
6495 		
6496 		@param {HTMLElement} element Used to calculate relative values
6497 		@param {string} value Relative value
6498 		@param {boolean} useYAxis Calulate relative values to the y axis rather than x
6499 		@returns number
6500 	*/
6501 	function getPixelValue(element, value, useYAxis) {
6502 		// Remember the original values
6503 		var axisPos = useYAxis ? 'top' : 'left',
6504 			axisPosUpper = useYAxis ? 'Top' : 'Left',
6505 			elmStyle = element.style,
6506 			positionVal = elmStyle[axisPos],
6507 			runtimePositionVal = element.runtimeStyle[axisPos],
6508 			r;
6509 			
6510 		// copy to the runtime type to prevent changes to the display
6511 		element.runtimeStyle[axisPos] = element.currentStyle[axisPos];
6512 			// set value to left / top
6513 		elmStyle[axisPos] = value;
6514 		// get the pixel value
6515 		r = elmStyle['pixel' + axisPosUpper];
6516 			
6517 		// revert values
6518 		elmStyle[axisPos] = positionVal;
6519 		element.runtimeStyle[axisPos] = runtimePositionVal;
6520 			
6521 		return r;
6522 	}
6523 	
6524 	/**
6525 	@name glow.NodeList#css
6526 	@function
6527 	@description Get / set a CSS property value
6528 		
6529 	@param {string | Object} property The CSS property name, or object of property-value pairs to set
6530 		
6531 	@param {string | number} [value] The value to apply
6532 		Number values will be treated as 'px' unless the CSS property
6533 		accepts a unitless value.
6534 		
6535 		If value is omitted, the value for the given property will be returned
6536 			
6537 	@returns {glow.NodeList | string} Returns the NodeList when setting value, or the CSS value when getting values.
6538 		CSS values are strings. For instance, "height" will return
6539 		"25px" for an element 25 pixels high. You can use
6540 		parseInt to convert these values.
6541 		
6542 	@example
6543 		// get value from first node
6544 		glow('#subNav').css('display');
6545 		
6546 	@example
6547 		// set left padding to 10px on all nodes
6548 		glow('#subNav li').css('padding-left', '2em');
6549 		
6550 	@example
6551 		// where appropriate, px is assumed when no unit is passed
6552 		glow('#mainPromo').css('margin-top', 300);
6553 		
6554 	@example
6555 		// set multiple CSS values at once
6556 		// NOTE: Property names containing a hyphen such as font-weight must be quoted
6557 		glow('#myDiv').css({
6558 			'font-weight': 'bold',
6559 			'padding'	 : '10px',
6560 			'color'		 : '#00cc99'
6561 		});
6562 	*/
6563 	NodeListProto.css = function(prop, val) {
6564 		var thisStyle,
6565 			i = this.length,
6566 			styleProp,
6567 			style,
6568 			firstItem = this[0];
6569 
6570 		if (prop.constructor === Object) { // set multiple values
6571 			for (style in prop) {
6572 				this.css( style, prop[style] );
6573 			}
6574 			return this;
6575 		}
6576 		else if (val !== undefined) { //set one CSS value
6577 			styleProp = toStyleProp(prop);
6578 			while (i--) {
6579 				if (this[i].nodeType === 1) {
6580 					thisStyle = this[i].style;
6581 						
6582 					if ( !isNaN(val) && hasUnits.test(prop) ) {
6583 						val += 'px';
6584 					}
6585 					
6586 					if (prop === 'opacity' && glow.env.ie) {
6587 						val = parseFloatFunc(val);
6588 						//in IE the element needs hasLayout for opacity to work
6589 						thisStyle.zoom = '1';
6590 						thisStyle.filter = (val !== 1) ?
6591 							'alpha(opacity=' + mathRound(val * 100) + ')' :
6592 							'';
6593 					}
6594 					else {
6595 						thisStyle[styleProp] = val;
6596 					}
6597 				}
6598 			}
6599 			return this;
6600 		}
6601 		else { //getting stuff
6602 			if (prop === 'width' || prop === 'height') {
6603 				return this[prop]() + 'px';
6604 			}
6605 			return (firstItem && firstItem.nodeType === 1) ? getCssValue(firstItem, prop) : '';
6606 		}	
6607 	};
6608 	
6609 	/**
6610 	@name glow.NodeList#height
6611 	@function
6612 	@description Gets / set element height
6613 		Return value does not include the padding or border of the element in
6614 		browsers supporting the correct box model.
6615 			
6616 		You can use this to easily get the height of the document or
6617 		window, see example below.
6618 		
6619 	@param {Number} [height] New height in pixels for each element in the list
6620 		If ommited, the height of the first element is returned
6621 		
6622 	@returns {glow.NodeList | number}
6623 		Height of first element, or original NodeList when setting heights.
6624 		
6625 	@example
6626 		// get the height of #myDiv
6627 		glow("#myDiv").height();
6628 		
6629 	@example
6630 		// set the height of list items in #myList to 200 pixels
6631 		glow("#myList > li").height(200);
6632 		
6633 	@example
6634 		// get the height of the document
6635 		glow(document).height();
6636 		
6637 	@example
6638 		// get the height of the window
6639 		glow(win).height();
6640 	*/
6641 	NodeListProto.height = function(height) {
6642 		if (height === undefined) {
6643 			return getDimension(this[0], 'Height');
6644 		}
6645 		return this.css('height', height);	
6646 	};
6647 	
6648 	/**
6649 	@name glow.NodeList#width
6650 	@function
6651 	@description Gets / set element width
6652 		Return value does not include the padding or border of the element in
6653 		browsers supporting the correct box model.
6654 			
6655 		You can use this to easily get the width of the document or
6656 		window, see example below.
6657 		
6658 	@param {Number} [width] New width in pixels for each element in the list
6659 		If ommited, the width of the first element is returned
6660 		
6661 	@returns {glow.NodeList | number}
6662 		width of first element, or original NodeList when setting widths.
6663 		
6664 	@example
6665 		// get the width of #myDiv
6666 		glow("#myDiv").width();
6667 		
6668 	@example
6669 		// set the width of list items in #myList to 200 pixels
6670 		glow("#myList > li").width(200);
6671 		
6672 	@example
6673 		// get the width of the document
6674 		glow(document).width();
6675 		
6676 	@example
6677 		// get the width of the window
6678 		glow(window).width();
6679 	*/
6680 	NodeListProto.width = function(width) {
6681 		if (width === undefined) {
6682 			return getDimension(this[0], 'Width');
6683 		}
6684 		return this.css('width', width);
6685 	};
6686 	
6687 	/**
6688 	@name glow.NodeList#scrollLeft
6689 	@function
6690 	@description Gets/sets the number of pixels the element has scrolled horizontally
6691 		To get/set the scroll position of the window, use this method on
6692 		a nodelist containing the window object.
6693 			
6694 	@param {Number} [val] New left scroll position
6695 		Omit this to get the current scroll position
6696 			
6697 	@returns {glow.NodeList | number}
6698 		Current scrollLeft value, or NodeList when setting scroll position.
6699 			
6700 	@example
6701 		// get the scroll left value of #myDiv
6702 		var scrollPos = glow('#myDiv').scrollLeft();
6703 		// scrollPos is a number, eg: 45
6704 
6705 	@example
6706 		// set the scroll left value of #myDiv to 20
6707 		glow('#myDiv').scrollLeft(20);
6708 
6709 	@example
6710 		// get the scrollLeft of the window
6711 		glow(window).scrollLeft();
6712 		// scrollPos is a number, eg: 45
6713 	*/
6714 	NodeListProto.scrollLeft = function(val) {
6715 		return scrollOffset(this, true, val);	
6716 	};
6717 	
6718 	/**
6719 	@name glow.NodeList#scrollTop
6720 	@function
6721 	@description Gets/sets the number of pixels the element has scrolled vertically
6722 		To get/set the scroll position of the window, use this method on
6723 		a nodelist containing the window object.
6724 		
6725 	@param {Number} [val] New top scroll position
6726 		Omit this to get the current scroll position
6727 			
6728 	@returns {glow.NodeList | number}
6729 		Current scrollTop value, or NodeList when setting scroll position.
6730 
6731 	@example
6732 		// get the scroll top value of #myDiv
6733 		var scrollPos = glow("#myDiv").scrollTop();
6734 		// scrollPos is a number, eg: 45
6735 
6736 	@example
6737 		// set the scroll top value of #myDiv to 20
6738 		glow("#myDiv").scrollTop(20);
6739 
6740 	@example
6741 		// get the scrollTop of the window
6742 		glow(window).scrollTop();
6743 		// scrollPos is a number, eg: 45
6744 	*/
6745 	NodeListProto.scrollTop = function(val) {
6746 		return scrollOffset(this, false, val);	
6747 	};
6748 	/**
6749 	@name glow.dom-getScrollOffset
6750 	@private
6751 	@description Get the scrollTop / scrollLeft of a particular element
6752 	@param {Element} elm Element (or window object) to get the scroll position of
6753 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
6754 	*/
6755 	function getScrollOffset(elm, isLeft) {
6756 		var r,			
6757 			scrollProp = 'scroll' + (isLeft ? 'Left' : 'Top');
6758 			
6759 		// are we dealing with the window object or the document object?
6760 		if (elm.window) {
6761 			// get the scroll of the documentElement or the pageX/Yoffset
6762 			// - some browsers use one but not the other
6763 			r = elm.document.documentElement[scrollProp]
6764 				|| (isLeft ? elm.pageXOffset : elm.pageYOffset)
6765 				|| 0;
6766 		} else {
6767 			r = elm[scrollProp];
6768 		}
6769 		return r;
6770 	}
6771 		
6772 	/**
6773 	@name glow.dom-setScrollOffset
6774 	@private
6775 	@description Set the scrollTop / scrollLeft of a particular element
6776 	@param {Element} elm Element (or window object) to get the scroll position of
6777 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
6778 	@param {Number} newVal New scroll value
6779 	*/
6780 	function setScrollOffset(elm, isLeft, newVal) {
6781 	// are we dealing with the window object or the document object?
6782 		if (elm.window) {
6783 			// we need to get whichever value we're not setting
6784 			elm.scrollTo(
6785 				isLeft  ? newVal : getScrollOffset(elm, true),
6786 				!isLeft ? newVal : getScrollOffset(elm, false)
6787 			);
6788 		} else {
6789 			elm['scroll' + (isLeft ? 'Left' : 'Top')] = newVal;
6790 		}
6791 	}
6792 	
6793 	/**
6794 	@name glow.dom-scrollOffset
6795 	@private
6796 	@description Set/get the scrollTop / scrollLeft of a NodeList
6797 	@param {glow.dom.NodeList} nodeList Elements to get / set the position of
6798 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
6799 	@param {Number} [val] Val to set (if not provided, we'll get the value)
6800 	@returns NodeList for sets, Number for gets
6801 	*/
6802 	function scrollOffset(nodeList, isLeft, val) {
6803 		var i = nodeList.length;
6804 			
6805 		if (val !== undefined) {
6806 			while (i--) {
6807 				setScrollOffset(nodeList[i], isLeft, val);
6808 			}
6809 			return nodeList;
6810 		} else {
6811 			return getScrollOffset(nodeList[0], isLeft);
6812 		}
6813 	}
6814 	/**
6815 	@name glow.NodeList#hide
6816 	@function
6817 	@description Hides all items in the NodeList.
6818 		
6819 	@returns {glow.NodeList}
6820 		
6821 	@example
6822 		// Hides all list items within #myList
6823 		glow("#myList li").hide();
6824 	*/
6825 	NodeListProto.hide = function() {
6826 		return this.css('display', 'none').css('visibility', 'hidden');	
6827 	};
6828 	
6829 	/**
6830 	@name glow.NodeList#show
6831 	@function
6832 	@description Shows all hidden items in the NodeList.
6833 		
6834 	@returns {glow.NodeList}
6835 		
6836 	@example
6837 		// Show element with ID myDiv
6838 		glow("#myDiv").show();
6839 			
6840 	@example
6841 		// Show all list items within #myList
6842 		glow("#myList li").show();
6843 	*/
6844 	NodeListProto.show = function() {
6845 		var i = this.length,
6846 			currItem,
6847 			itemStyle;
6848 			
6849 		while (i--) {
6850 			/* Create a NodeList for the current item */
6851 			currItem = new glow.NodeList(this[i]);
6852 			itemStyle = currItem[0].style;
6853 			if (currItem.css('display') == 'none') {
6854 				itemStyle.display = '';
6855 				itemStyle.visibility = 'visible';
6856 				/* If display is still none, set to block */
6857 				if (currItem.css('display') == 'none') {
6858 					itemStyle.display = 'block';
6859 				}
6860 			}	
6861 		}
6862 		return this;	
6863 	};
6864 
6865 	/**
6866 	@name glow.NodeList#offset
6867 	@function
6868 	@description Gets the offset from the top left of the document.
6869 		If the NodeList contains multiple items, the offset of the
6870 		first item is returned.
6871 			
6872 	@returns {Object}
6873 		Returns an object with "top" & "left" properties in pixels
6874 			
6875 	@example
6876 		glow("#myDiv").offset().top
6877 	*/
6878 	NodeListProto.offset = function() {
6879 		if ( !this[0] || this[0].nodeType !== 1) {
6880 			return {top: 0, left: 0};
6881 		}
6882 		
6883 		// 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)
6884 		var elm = this[0],
6885 			doc = elm.ownerDocument,
6886 			docElm = doc.documentElement,
6887 			window = doc.defaultView || doc.parentWindow,
6888 			docScrollPos = {
6889 				x: getScrollOffset(window, true),
6890 				y: getScrollOffset(window, false)
6891 			};
6892 
6893 		//this is simple(r) if we can use 'getBoundingClientRect'
6894 		// Sorry but the sooper dooper simple(r) way is not accurate in Safari 4
6895 		if (!glow.env.webkit && elm.getBoundingClientRect) {
6896 			var rect = elm.getBoundingClientRect();
6897 			
6898 			return {
6899 				top: Math.floor(rect.top)
6900 					/*
6901 					 getBoundingClientRect is realive to top left of
6902 					 the viewport, so we need to sort out scrolling offset
6903 					*/
6904 					+ docScrollPos.y
6905 					/*
6906 					IE adds the html element's border to the value. We can
6907 					deduct this value using client(Top|Left). However, if
6908 					the user has done html{border:0} clientTop will still
6909 					report a 2px border in IE quirksmode so offset will be off by 2.
6910 					Hopefully this is an edge case but we may have to revisit this
6911 					in future
6912 					*/
6913 					- docElm.clientTop,
6914 
6915 				left: Math.floor(rect.left) //see above for docs on all this stuff
6916 					+ docScrollPos.x
6917 					- docElm.clientLeft
6918 			};
6919 		}
6920 		else { //damnit, let's go the long way around
6921 			var top = elm.offsetTop,
6922 				left = elm.offsetLeft,
6923 				originalElm = elm,
6924 				nodeNameLower,
6925 				docBody = document.body,
6926 				//does the parent chain contain a position:fixed element
6927 				involvesFixedElement = false,
6928 				offsetParentBeforeBody = elm;
6929 
6930 			//add up all the offset positions
6931 			while (elm = elm.offsetParent) {
6932 				left += elm.offsetLeft;
6933 				top += elm.offsetTop;
6934 
6935 				//if css position is fixed, we need to add in the scroll offset too, catch it here
6936 				if (getCssValue(elm, 'position') == 'fixed') {
6937 					involvesFixedElement = true;
6938 				}
6939 
6940 				//gecko & webkit (safari 3) don't add on the border for positioned items
6941 				if (glow.env.gecko || glow.env.webkit > 500) {
6942 					left += parseInt(getCssValue(elm, 'border-left-width')) || 0;
6943 					top  += parseInt(getCssValue(elm, 'border-top-width'))  || 0;
6944 				}
6945 				
6946 				//we need the offset parent (before body) later
6947 				if (elm.nodeName.toLowerCase() != 'body') {
6948 					offsetParentBeforeBody = elm;
6949 				}
6950 			}
6951 
6952 			//deduct all the scroll offsets
6953 			elm = originalElm;
6954 			
6955 			while ((elm = elm.parentNode) && (elm != docBody) && (elm != docElm)) {
6956 				left -= elm.scrollLeft;
6957 				top -= elm.scrollTop;
6958 
6959 				//FIXES
6960 				//gecko doesn't add the border of contained elements to the offset (overflow!=visible)
6961 				if (glow.env.gecko && getCssValue(elm, 'overflow') != 'visible') {
6962 					left += parseInt(getCssValue(elm, 'border-left-width'));
6963 					top += parseInt(getCssValue(elm, 'border-top-width'));
6964 				}
6965 			}
6966 
6967 			//if we found a fixed position element we need to add the scroll offsets
6968 			if (involvesFixedElement) {
6969 				left += docScrollPos.x;
6970 				top += docScrollPos.y;
6971 			}
6972 
6973 			//FIXES
6974 			// Gecko - non-absolutely positioned elements that are direct children of body get the body offset counted twice
6975 			if (
6976 				(glow.env.gecko && getCssValue(offsetParentBeforeBody, 'position') != 'absolute')
6977 			) {
6978 				left -= docBody.offsetLeft;
6979 				top -= docBody.offsetTop;
6980 			}
6981 
6982 			return {left:left, top:top};
6983 		}
6984 	};
6985 	
6986 	/**
6987 	@name glow.NodeList#position
6988 	@function
6989 	@description Get the top & left position of an element relative to its positioned parent
6990 		This is useful if you want to make a position:static element position:absolute
6991 		and retain the original position of the element
6992 			
6993 	@returns {Object}
6994 		An object with 'top' and 'left' number properties
6995 		
6996 	@example
6997 		// get the top distance from the positioned parent
6998 		glow("#elm").position().top
6999 	*/
7000 	NodeListProto.position = function() {
7001 		var positionedParent = new glow.NodeList( getPositionedParent(this[0]) ),
7002 			hasPositionedParent = !!positionedParent[0],
7003 					
7004 			// element margins to deduct
7005 			marginLeft = parseInt( this.css('margin-left') ) || 0,
7006 			marginTop  = parseInt( this.css('margin-top')  ) || 0,
7007 					
7008 			// offset parent borders to deduct, set to zero if there's no positioned parent
7009 			positionedParentBorderLeft = ( hasPositionedParent && parseInt( positionedParent.css('border-left-width') ) ) || 0,
7010 			positionedParentBorderTop  = ( hasPositionedParent && parseInt( positionedParent.css('border-top-width')  ) ) || 0,
7011 					
7012 			// element offsets
7013 		elOffset = this.offset(),
7014 		positionedParentOffset = hasPositionedParent ? positionedParent.offset() : {top: 0, left: 0};
7015 				
7016 		return {
7017 			left: elOffset.left - positionedParentOffset.left - marginLeft - positionedParentBorderLeft,
7018 			top:  elOffset.top  - positionedParentOffset.top  - marginTop  - positionedParentBorderTop
7019 		}	
7020 	};
7021 	/*
7022 		Get the 'real' positioned parent for an element, otherwise return null.
7023 	*/
7024 	function getPositionedParent(elm) {
7025 		var offsetParent = elm.offsetParent,
7026 		docElm = document.documentElement;
7027 			
7028 		// get the real positioned parent
7029 		// IE places elements with hasLayout in the offsetParent chain even if they're position:static
7030 		// Also, <body> and <html> can appear in the offsetParent chain, but we don't want to return them if they're position:static
7031 		while (offsetParent && new glow.NodeList(offsetParent).css('position') == 'static') {	
7032 			offsetParent = offsetParent.offsetParent;
7033 		}
7034 			
7035 		// sometimes the <html> element doesn't appear in the offsetParent chain, even if it has position:relative
7036 		if (!offsetParent && new glow.NodeList(docElm).css('position') != 'static') {
7037 			offsetParent = docElm;
7038 		}
7039 			
7040 		return offsetParent || null;
7041 	}
7042 });
7043 Glow.provide(function(glow) {
7044 	var NodeListProto = glow.NodeList.prototype,
7045 		document = window.document,
7046 		undefined,
7047 		keyEventNames = ' keypress keydown keyup ';
7048 	
7049 	/**
7050 		@name glow.NodeList#on
7051 		@function
7052 		@description Listen for an event.
7053 		   This will listen for a particular event on each dom node
7054 		   in the NodeList.
7055 		   
7056 		   If you're listening to many children of a particular item,
7057 		   you may get better performance from {@link glow.NodeList#delegate}.
7058 		
7059 		@param {String} eventName Name of the event to listen for.
7060 		   This can be any regular DOM event ('click', 'mouseover' etc) or
7061 		   a special event of NodeList.
7062 		   
7063 		@param {Function} callback Function to call when the event fires.
7064 		   The callback is passed a single event object. The type of this
7065 		   object is {@link glow.DomEvent} unless otherwise stated.
7066 		   
7067 		@param {Object} [thisVal] Value of 'this' within the callback.
7068 		   By default, this is the dom node being listened to.
7069 		
7070 		@returns this
7071 		
7072 		@example
7073 		   glow.get('#testLink').on('click', function(domEvent) {
7074 			   // do stuff
7075 			   
7076 			   // if you want to cancel the default action (following the link)...
7077 			   return false;
7078 		   });
7079 	*/
7080 	NodeListProto.on = function(eventName, callback, thisVal) {
7081 		var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
7082 		
7083 		// add standard glow listeners
7084 		glow.events.addListeners(this, eventName, callback, thisVal);
7085 		
7086 		// add the bridge functions if needed
7087 		if (isKeyEvent) {
7088 			glow.events._addKeyListener(this);
7089 		}
7090 		else { // assume it's a DOM event
7091 			glow.events._addDomEventListener(this, eventName);
7092 		}
7093 		
7094 		return this;
7095 	}
7096 	
7097 	/**
7098 		@name glow.NodeList#detach
7099 		@function
7100 		@description detach a listener from elements
7101 		   This will detach the listener from each dom node in the NodeList.
7102 		
7103 		@param {String} eventName Name of the event to detach the listener from
7104 		   
7105 		@param {Function} callback Listener callback to detach
7106 		
7107 		@returns this
7108 		
7109 		@example
7110 			function clickListener(domEvent) {
7111 				// ...
7112 			}
7113 			
7114 			// adding listeners
7115 			glow.get('a').on('click', clickListener);
7116 			
7117 			// removing listeners
7118 			glow.get('a').detach('click', clickListener);
7119 	*/
7120 	NodeListProto.detach = function(eventName, callback) {
7121 		var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
7122 		
7123 		// remove standard glow listeners
7124 		glow.events.removeListeners(this, eventName, callback);
7125 		
7126 		// remove the bridge functions if needed
7127 		if (isKeyEvent) {
7128 			glow.events._removeKeyListener(this);
7129 		}
7130 		else { // assume it's a DOM event
7131 			glow.events._removeDomEventListener(this, eventName);
7132 		}
7133 		
7134 		return this;
7135 	}
7136 	
7137 	/**
7138 		@name glow.NodeList#delegate
7139 		@function
7140 		@description Listen for an event occurring on child elements matching a selector.
7141 			'delegate' will catch events which occur on matching items created after
7142 			the listener was added. 
7143 		
7144 		@param {String} eventName Name of the event to listen for.
7145 			This can be any regular DOM event ('click', 'mouseover' etc) or
7146 			a special event of NodeList.
7147 		
7148 		@param {String} selector CSS selector of child elements to listen for events on
7149 			For example, if you were wanting to hear events from links, this
7150 			would be 'a'.
7151 		
7152 		@param {Function} callback Function to call when the event fires.
7153 			The callback is passed a single event object. The type of this
7154 			object is {@link glow.DomEvent} unless otherwise stated.
7155 		
7156 		@param {Object} [thisVal] Value of 'this' within the callback.
7157 			By default, this is the dom node matched by 'selector'.
7158 		
7159 		@returns this
7160 		
7161 		@example
7162 			// Using 'on' to catch clicks on links in a list
7163 			glow.get('#nav a').on('click', function() {
7164 				// do stuff
7165 			});
7166 			
7167 			// The above adds a listener to each link, any links created later
7168 			// will not have this listener, so we won't hear about them.
7169 			
7170 			// Using 'delegate' to catch clicks on links in a list
7171 			glow.get('#nav').delegate('click', 'a', function() {
7172 				// do stuff
7173 			});
7174 			
7175 			// The above only adds one listener to #nav which tracks clicks
7176 			// to any links within. This includes elements created after 'delegate'
7177 			// was called.
7178 		
7179 		@example
7180 			// Using delegate to change class names on table rows so :hover
7181 			// behaviour can be emulated in IE6
7182 			glow.get('#contactData').delegate('mouseover', 'tr', function() {
7183 				glow.get(this).addClass('hover');
7184 			});
7185 			
7186 			glow.get('#contactData').delegate('mouseout', 'tr', function() {
7187 				glow.get(this).removeClass('hover');
7188 			});
7189 	*/
7190 	NodeListProto.delegate = function(eventName, selector, callback, thisVal) {
7191 		var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
7192 		
7193 		// add standard glow listeners
7194 		glow.events.addListeners(this, eventName + '/' + selector, callback, thisVal);
7195 		
7196 		// register delegates
7197 		glow.events._registerDelegate(this, eventName, selector);
7198 		
7199 		// add the bridge functions if needed
7200 		if (isKeyEvent) {
7201 			glow.events._addKeyListener(this);
7202 		}
7203 		else { // assume it's a DOM event
7204 			glow.events._addDomEventListener(this, eventName);
7205 		}
7206 		
7207 		return this;
7208 	}
7209 	
7210 	/**
7211 		@name glow.NodeList#detachDelegate
7212 		@function
7213 		@description detach a delegated listener from elements
7214 		   This will detach the listener from each dom node in the NodeList.
7215 		
7216 		@param {String} eventName Name of the event to detach the listener from
7217 		
7218 		@param {String} selector CSS selector of child elements the listener is listening to
7219 		
7220 		@param {Function} callback Listener callback to detach
7221 		
7222 		@returns this
7223 		
7224 		@example
7225 			function clickListener(domEvent) {
7226 				// ...
7227 			}
7228 			
7229 			// adding listeners
7230 			glow.get('#nav').delegate('click', 'a', clickListener);
7231 			
7232 			// removing listeners
7233 			glow.get('#nav').detachDelegate('click', 'a', clickListener);
7234 	*/
7235 	NodeListProto.detachDelegate = function(eventName, selector, callback, thisVal) {
7236 		var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
7237 		
7238 		// remove standard glow listeners
7239 		glow.events.removeListeners(this, eventName + '/' + selector, callback);
7240 		
7241 		// unregister delegates
7242 		glow.events._unregisterDelegate(this, eventName, selector);
7243 		
7244 		// remove the bridge functions if needed
7245 		if (isKeyEvent) {
7246 			glow.events._removeKeyListener(this);
7247 		}
7248 		else { // assume it's a DOM event
7249 			glow.events._removeDomEventListener(this, eventName);
7250 		}
7251 		
7252 		return this;
7253 	}
7254 	
7255 	/**
7256 		@name glow.NodeList#fire
7257 		@function
7258 		@param {String} eventName Name of the event to fire
7259 		@param {glow.events.Event} [event] Event object to pass into listeners.
7260 		   You can provide a simple object of key / value pairs which will
7261 		   be added as properties of a glow.events.Event instance.
7262 		
7263 		@description Fire an event on dom nodes within the NodeList
7264 		   Note, this will only trigger event listeners to be called, it won't
7265 		   for example, move the mouse or click a link on the page.
7266 		
7267 		@returns glow.events.Event
7268 		
7269 		@example
7270 		   glow.get('#testLink').on('click', function() {
7271 			   alert('Link clicked!');
7272 		   });
7273 		   
7274 		   // The following causes 'Link clicked!' to be alerted, but doesn't
7275 		   // cause the browser to follow the link
7276 		   glow.get('#testLink').fire('click');
7277 	*/
7278 	NodeListProto.fire = function(eventName, event) {
7279 		return glow.events.fire(this, eventName, event);
7280 	}
7281 	
7282 	/**
7283 		@name glow.NodeList#event:mouseenter
7284 		@event
7285 		@description Fires when the mouse enters the element specifically, does not bubble
7286 		
7287 		@param {glow.events.DomEvent} event Event Object
7288 	*/
7289 	
7290 	/**
7291 		@name glow.NodeList#event:mouseleave
7292 		@event
7293 		@description Fires when the mouse leaves the element specifically, does not bubble
7294 		
7295 		@param {glow.events.DomEvent} event Event Object
7296 	*/
7297 	
7298 	/**
7299 		@name glow.NodeList#event:keydown
7300 		@event
7301 		@description Fires when the user presses a key
7302 			Only fires if the element has focus, listen for this event on
7303 			the document to catch all keydowns.
7304 			
7305 			This event related to the user pressing a key on the keyboard,
7306 			if you're more concerned about the character entered, see the
7307 			{@link glow.NodeList#event:keypress keypress} event.
7308 			
7309 			keydown will only fire once, when the user presses the key.
7310 			
7311 			The order of events is keydown, keypress*, keyup. keypress may
7312 			fire many times if the user holds the key down.
7313 		
7314 		@param {glow.events.KeyboardEvent} event Event Object
7315 	*/
7316 	
7317 	/**
7318 		@name glow.NodeList#event:keypress
7319 		@event
7320 		@description Fires when a key's command executes.
7321 			For instance, if you hold down a key, it's action will occur many
7322 			times. This event will fire on each action.
7323 			
7324 			This event is useful when you want to react to keyboard repeating, or
7325 			to detect when a character is entered into a field.
7326 			
7327 			The order of events is keydown, keypress*, keyup. keypress may
7328 			fire many times if the user holds the key down.
7329 		
7330 		@param {glow.events.KeyboardEvent} event Event Object
7331 	*/
7332 	
7333 	/**
7334 		@name glow.NodeList#event:keyup
7335 		@event
7336 		@description Fires when the user releases a key
7337 			Only fires if the element has focus, listen for this event on
7338 			the document to catch all keyups.
7339 			
7340 			This event related to the user pressing a key on the keyboard,
7341 			if you're more concerned about the character entered, see the
7342 			{@link glow.NodeList#event:keypress keypress} event.
7343 			
7344 			The order of events is keydown, keypress*, keyup. keypress may
7345 			fire many times if the user holds the key down.
7346 		
7347 		@param {glow.events.KeyboardEvent} event Event Object
7348 	*/
7349 });
7350 Glow.provide(function(glow) {
7351 	var NodeList = glow.NodeList,
7352 		NodeListProto = NodeList.prototype,
7353 		undefined,
7354 		parseFloat = window.parseFloat,
7355 		// used to detect which CSS properties require units
7356 		requiresUnitsRe = /width|height|top$|bottom$|left$|right$|spacing$|indent$|fontSize/i,
7357 		// which simple CSS values cannot be negative
7358 		noNegativeValsRe = /width|height|padding|opacity/,
7359 		getUnit = /\D+$/,
7360 		usesYAxis = /height|top/;
7361 	
7362 	// TODO: get this from appearence.js
7363 	function toStyleProp(prop) {
7364 		if (prop == 'float') {
7365 			return glow.env.ie ? 'styleFloat' : 'cssFloat';
7366 		}
7367 		return prop.replace(/-(\w)/g, function(match, p1) {
7368 			return p1.toUpperCase();
7369 		});
7370 	}
7371 	
7372 	/**
7373 		@private
7374 		@function
7375 		@param {nodelist} element
7376 		@param {string} toUnit (em|%|pt...)
7377 		@param {string} axis (x|y)
7378 		@description Converts a css unit.
7379 			We need to know the axis for calculating relative values, since they're
7380 			relative to the width / height of the parent element depending
7381 			on the situation.
7382 	*/
7383 	var testElement = glow('<div style="position:absolute;visibility:hidden;border:0;margin:0;padding:0"></div>');
7384 	
7385 	function convertCssUnit(element, value, toUnit, axis) {
7386 		var elmStyle = testElement[0].style,
7387 			axisProp = (axis === 'x') ? 'width' : 'height',
7388 			startPixelValue,
7389 			toUnitPixelValue;
7390 		
7391 		startPixelValue = testElement.css(axisProp, value).insertAfter(element)[axisProp]();
7392 		// using 10 of the unit then dividing by 10 to increase accuracy
7393 		toUnitPixelValue = testElement.css(axisProp, 10 + toUnit)[axisProp]() / 10;
7394 		testElement.remove();
7395 		return startPixelValue / toUnitPixelValue;
7396 	}
7397 	
7398 	/**
7399 		@private
7400 		@function
7401 		@description Animate a colour value
7402 	*/
7403 	function animateColor(anim, stylePropName, from, to) {
7404 		to = NodeList._parseColor(to);
7405 		to = [to.r, to.g, to.b];
7406 		from = NodeList._parseColor(from);
7407 		from = [from.r, from.g, from.b];
7408 		
7409 		anim.prop(stylePropName, {
7410 			// we only need a template if we have units
7411 			template: 'rgb(?,?,?)',
7412 			from: from,
7413 			to: to,
7414 			round: true,
7415 			min: 0,
7416 			max: 255
7417 		});
7418 	}
7419 	
7420 	/**
7421 		@private
7422 		@function
7423 		@description Animate opacity in IE's 'special' way
7424 	*/
7425 	function animateIeOpacity(elm, anim, from, to) {
7426 		to   = parseFloat(to)   * 100;
7427 		from = parseFloat(from) * 100;
7428 		
7429 		// give the element 'hasLayout'
7430 		elm.style.zoom = 1;
7431 		
7432 		anim.prop('filter', {
7433 			// we only need a template if we have units
7434 			template: 'alpha(opacity=?)',
7435 			from: from,
7436 			to: to,
7437 			allowNegative: false
7438 		});
7439 	}
7440 	
7441 	/**
7442 		@private
7443 		@function
7444 		@description Scroll positions
7445 	*/
7446 	function animateScroll(elm, anim, from, to, scrollTopOrLeft) {
7447 		var diff;
7448 		
7449 		to   = parseFloat(to);
7450 		from = parseFloat(from);
7451 		elm = glow(elm);
7452 		
7453 		// auto-get start value if there isn't one
7454 		if ( isNaN(from) ) {
7455 			from = elm[scrollTopOrLeft]();
7456 		}
7457 		
7458 		diff = to - from;
7459 		
7460 		anim.on('frame', function() {
7461 			elm[scrollTopOrLeft]( diff * this.value + from );
7462 		});
7463 	}
7464 	
7465 	/**
7466 		@private
7467 		@function
7468 		@description Animate simple values
7469 			This is a set of space-separated numbers (42) or numbers + unit (42em)
7470 			
7471 			Units can be mixed
7472 	*/
7473 	function animateValues(element, anim, stylePropName, from, to) {
7474 		var toUnit,
7475 			fromUnit,
7476 			round = [],
7477 			template = '',
7478 			requiresUnits = requiresUnitsRe.test(stylePropName),
7479 			minZero = noNegativeValsRe.test(stylePropName);
7480 		
7481 		from = String(from).split(' ');
7482 		to = String(to).split(' ');
7483 		
7484 		for (var i = 0, leni = to.length; i < leni; i++) {
7485 			toUnit   = ( getUnit.exec( to[i] )   || [''] )[0];
7486 			fromUnit = ( getUnit.exec( from[i] ) || [''] )[0];
7487 			
7488 			// create initial units if required
7489 			if (requiresUnits) {
7490 				toUnit = toUnit || 'px';
7491 				fromUnit = fromUnit || 'px';
7492 			}
7493 			
7494 			round[i] = (toUnit === 'px');
7495 			
7496 			// make the 'from' unit the same as the 'to' unit
7497 			if (toUnit !== fromUnit) {
7498 				from = convertCssUnit( element, from, toUnit, usesYAxis.test(stylePropName) ? 'y' : 'x' );
7499 			}
7500 			
7501 			template += ' ?' + toUnit;
7502 			from[i] = parseFloat( from[i] );
7503 			to[i]   = parseFloat( to[i] );
7504 		}
7505 		
7506 		anim.prop(stylePropName, {
7507 			template: template,
7508 			from: from,
7509 			to: to,
7510 			round: round,
7511 			min: minZero ? 0 : undefined
7512 		});
7513 	}
7514 	
7515 	/**
7516 		@private
7517 		@function
7518 		@description Makes an animtion adjust CSS values over time
7519 	*/
7520 	function addCssAnim(nodeList, anim, properties) {
7521 		var to, from, i,
7522 			property,
7523 			propertyIsArray,
7524 			stylePropName;
7525 		
7526 		for (var propName in properties) {
7527 			property = properties[propName];
7528 			propertyIsArray = property.push;
7529 			stylePropName = toStyleProp(propName);
7530 			to = propertyIsArray ? property[1] : property;
7531 			i = nodeList.length;
7532 			
7533 			// do this for each nodelist item
7534 			while (i--) {
7535 				// deal with special values, scrollTop and scrollLeft which aren't really CSS
7536 				// This is the only animation that can work on the window object too
7537 				if ( propName.indexOf('scroll') === 0 && (nodeList[i].scrollTo || nodeList[i].scrollTop !== undefined) ) {
7538 					animateScroll(nodeList[i], anim, propertyIsArray ? property[0] : undefined, to, propName);
7539 					continue;
7540 				}
7541 				
7542 				// skip non-element nodes
7543 				if ( nodeList[i].nodeType !== 1 ) { continue; }
7544 				
7545 				// set new target
7546 				anim.target( nodeList[i].style );
7547 				
7548 				from = propertyIsArray ? property[0] : nodeList.item(i).css(propName);
7549 				
7550 				// deal with colour values
7551 				if ( propName.indexOf('color') !== -1 ) {
7552 					animateColor(anim, stylePropName, from, to);
7553 				}
7554 				// nice special case for IE
7555 				else if (glow.env.ie && stylePropName === 'opacity') {
7556 					animateIeOpacity(nodeList[i], anim, from, to);
7557 				}
7558 				// assume we're dealing with simple numbers, or numbers + unit
7559 				// eg "5px", "5px 2em", "10px 5px 1em 4px"
7560 				else {
7561 					animateValues(nodeList[i], anim, stylePropName, from, to);
7562 				}
7563 			}
7564 		}
7565 	}
7566 	
7567 	/**
7568 		@name glow.NodeList#anim
7569 		@function
7570 		@description Animate properties of elements
7571 			All elements in the NodeList are animated
7572 			
7573 			All CSS values which are simple numbers (with optional unit)
7574 			are supported. Eg: width, margin-top, left
7575 			
7576 			All CSS values which are space-separated values are supported
7577 			(eg background-position, margin, padding), although a 'from'
7578 			value must be provided for short-hand properties like 'margin'.
7579 			
7580 			All CSS colour values are supported. Eg: color, background-color.
7581 			
7582 			'scrollLeft' and 'scrollTop' can be animated for elements and
7583 			the window object.
7584 			
7585 			Other properties, including CSS properties with limited support, can
7586 			be animated using {@link glow.anim.Anim#prop}.
7587 		
7588 		@param {number} duration Length of the animation in seconds.
7589 		@param {Object} properties Properties to animate.
7590 			This is an object where the key is the CSS property and the value
7591 			is the value to animate to.
7592 			
7593 			The value can also be an array, where the first item is the value to
7594 			animate from, and the second is the value to animate to.
7595 			
7596 			Numerical values will be treated as 'px' if the property requires units.
7597 		
7598 		@param {Object} [opts] Options object
7599 		@param {function|string} [opts.tween='easeBoth'] The motion of the animation.
7600 			Strings are treated as properties of {@link glow.tweens}, although
7601 			a tween function can be provided.
7602 		@param {boolean} [opts.destroyOnComplete=true] Destroy the animation once it completes (unless it loops).
7603 			This will free any DOM references the animation may have created. Once
7604 			the animation is destroyed, it cannot be started again.
7605 		@param {boolean} [opts.loop=true] Loop the animation.
7606 		@param {boolean} [opts.startNow=true] Start the animation straight away?
7607 			Animations can be started by calling {@link glow.anim.Anim#start}
7608 		
7609 		@returns {glow.anim.Anim}
7610 		
7611 		@example
7612 			// change the nav's background colour to white and the top position
7613 			// to 20px over a duration of 3 seconds
7614 			glow('#nav').anim(3, {
7615 				'background-color': '#fff',
7616 				'top': 20
7617 			});
7618 			
7619 		@example
7620 			// Fade an element out and alert 'done' when complete
7621 			glow('#nav').anim(3, {
7622 				'opacity': 0
7623 			}).on('complete', function() {
7624 				alert('done!');
7625 			});
7626 			
7627 		@example
7628 			// Scroll the window to the top
7629 			glow(window).anim(2, {
7630 				scrollTop: 0
7631 			});
7632 		
7633 		@see {@link glow.NodeList#queueAnim} - Queue an animation to run after the current anim
7634 		@see {@link glow.NodeList#fadeIn} - Shortcut to fade elements in
7635 		@see {@link glow.NodeList#fadeOut} - Shortcut to fade elements out
7636 		@see {@link glow.NodeList#fadeToggle} - Shortcut to toggle the fade of an element
7637 		@see {@link glow.NodeList#slideOpen} - Shortcut to slide an element open
7638 		@see {@link glow.NodeList#slideShut} - Shortcut to slide an element shut
7639 		@see {@link glow.NodeList#slideToggle} - Shortcut to toggle an element open / shut
7640 
7641 	*/
7642 	NodeListProto.anim = function(duration, properties, opts) {
7643 		/*!debug*/
7644 			if (arguments.length < 2 || arguments.length > 3) {
7645 				glow.debug.warn('[wrong count] glow.NodeList#anim expects 2 or 3 arguments, not ' + arguments.length + '.');
7646 			}
7647 			if (typeof duration !== 'number') {
7648 				glow.debug.warn('[wrong type] glow.NodeList#anim expects number as "duration" argument, not ' + typeof duration + '.');
7649 			}
7650 			if (typeof properties !== 'object') {
7651 				glow.debug.warn('[wrong type] glow.NodeList#anim expects object as "properties" argument, not ' + typeof properties + '.');
7652 			}
7653 			if (opts !== undefined && typeof opts !== 'object') {
7654 				glow.debug.warn('[wrong type] glow.NodeList#anim expects object as "opts" argument, not ' + typeof opts + '.');
7655 			}
7656 		/*gubed!*/
7657 		
7658 		opts = opts || {};
7659 		
7660 		var anim = new glow.anim.Anim(duration, opts);
7661 		
7662 		addCssAnim(this, anim, properties);
7663 		
7664 		// auto start
7665 		!(opts.startNow === false) && anim.start();
7666 		return anim;
7667 	};
7668 	
7669 	/**
7670 		@private
7671 		@function
7672 		@description Used as a listener for an animations's stop event.
7673 			'this' is a nodelist of the animating item
7674 			
7675 			Set in queueAnim
7676 	*/
7677 	function queueAnimStop() {
7678 		this.removeData('glow_lastQueuedAnim').removeData('glow_currentAnim');
7679 	}
7680 	
7681 	/**
7682 		@name glow.NodeList#queueAnim
7683 		@function
7684 		@description Queue an animation to run after the current animation
7685 			All elements in the NodeList are animated
7686 		
7687 			This supports the same CSS properties as {@link glow.NodeList#anim},
7688 			but the animation is not started until the previous animation (added
7689 			via {@link glow.NodeList#queueAnim queueAnim})
7690 			on that element ends.
7691 			
7692 			If there are no queued animations on the element, the animation starts
7693 			straight away.
7694 		
7695 		@param {number} duration Length of the animation in seconds.
7696 		@param {Object} Properties to animate.
7697 			This is an object where the key is the CSS property and the value
7698 			is the value to animate to.
7699 			
7700 			The value can also be an array, where the first item is the value to
7701 			animate from, and the second is the value to animate to.
7702 			
7703 			Numerical values will be treated as 'px' if the property requires units.
7704 		
7705 		@param {Object} [opts] Options object
7706 		@param {function|string} [opts.tween='easeBoth'] The motion of the animation.
7707 			Strings are treated as properties of {@link glow.tweens}, although
7708 			a tween function can be provided.
7709 		@param {boolean} [opts.destroyOnComplete=true] Destroy the animation once it completes (unless it loops).
7710 			This will free any DOM references the animation may have created. Once
7711 			the animation is destroyed, it cannot be started again.
7712 		
7713 		@returns {glow.NodeList}
7714 		
7715 		@example
7716 			// change a nav item's background colour from white to yellow
7717 			// when the mouse is over it, and back again when the mouse
7718 			// exits.
7719 			glow('#nav').delegate('mouseenter', 'li', function() {
7720 				glow(this).queueAnim(0.5, {
7721 					'background-color': 'yellow'
7722 				});
7723 			}).delegate('mouseleave', 'li', function() {
7724 				glow(this).queueAnim(0.5, {
7725 					'background-color': 'white'
7726 				});
7727 			});
7728 			
7729 		@example
7730 			// adding listeners to a queued anim
7731 			glow('#elementToAnimate').queueAnim(0.5, {
7732 				height: 0
7733 			}).lastQueuedAnim().on('complete', function() {
7734 				alert('Animation complete!');
7735 			});
7736 			
7737 		@example
7738 			// stopping and clearing current animation queue.
7739 			// The next animation created via queueAnim will start
7740 			// immediately
7741 			glow('#elementToAnimate').curentAnim().stop();
7742 		
7743 		@see {@link glow.NodeList#fadeIn} - Shortcut to fade elements in
7744 		@see {@link glow.NodeList#fadeOut} - Shortcut to fade elements out
7745 		@see {@link glow.NodeList#fadeToggle} - Shortcut to toggle the fade of an element
7746 		@see {@link glow.NodeList#slideOpen} - Shortcut to slide an element open
7747 		@see {@link glow.NodeList#slideShut} - Shortcut to slide an element shut
7748 		@see {@link glow.NodeList#slideToggle} - Shortcut to toggle an element open / shut
7749 
7750 	*/
7751 	NodeListProto.queueAnim = function(duration, properties, opts) {
7752 		/*!debug*/
7753 			if (arguments.length < 2 || arguments.length > 3) {
7754 				glow.debug.warn('[wrong count] glow.NodeList#queueAnim expects 2 or 3 arguments, not ' + arguments.length + '.');
7755 			}
7756 			if (typeof duration !== 'number') {
7757 				glow.debug.warn('[wrong type] glow.NodeList#queueAnim expects number as "duration" argument, not ' + typeof duration + '.');
7758 			}
7759 			if (typeof properties !== 'object') {
7760 				glow.debug.warn('[wrong type] glow.NodeList#queueAnim expects object as "properties" argument, not ' + typeof properties + '.');
7761 			}
7762 			if (opts !== undefined && typeof opts !== 'object') {
7763 				glow.debug.warn('[wrong type] glow.NodeList#queueAnim expects object as "opts" argument, not ' + typeof opts + '.');
7764 			}
7765 		/*gubed!*/
7766 		
7767 		opts = opts || {};
7768 		
7769 		var i = this.length,
7770 			item,
7771 			lastQueuedAnim,
7772 			anim,
7773 			startNextAnim;
7774 		
7775 		// we don't want animations starting now
7776 		opts.startNow = false;
7777 		
7778 		while (i--) {
7779 			item = this.item(i);
7780 			if (item[0].nodeType !== 1) { continue; }
7781 			lastQueuedAnim = item.data('glow_lastQueuedAnim');
7782 			// add a listener to 'stop', to clear the queue
7783 			anim = new glow.anim.Anim(duration, opts).on('stop', queueAnimStop, item);
7784 			item.data('glow_lastQueuedAnim', anim);
7785 			
7786 			// closure some properties
7787 			(function(item, properties, anim) {
7788 				startNextAnim = function() {
7789 					addCssAnim(item, anim, properties);
7790 					anim.start();
7791 					item.data('glow_currentAnim', anim);
7792 				}
7793 			})(item, properties, anim);
7794 			
7795 			// do we start the anim now, or after the next one?
7796 			if (lastQueuedAnim) {
7797 				lastQueuedAnim.on('complete', startNextAnim);
7798 			}
7799 			else {
7800 				startNextAnim();
7801 			}
7802 		}
7803 		
7804 		return this;
7805 	};
7806 	
7807 	/**
7808 		@name glow.NodeList#currentAnim
7809 		@function
7810 		@description Get the currently playing animation added via {@link glow.NodeList#queueAnim queueAnim} for this element
7811 			If no animation is currently playing, an empty animation is returned.
7812 			This means you don't need to check to see if the item is defined before
7813 			calling methods on it.
7814 			
7815 			This method acts on the first item in the NodeList.
7816 		
7817 		@returns {glow.anim.Anim}
7818 			
7819 		@example
7820 			// stopping and clearing current animation queue.
7821 			// The next animation created via queueAnim will start
7822 			// immediately
7823 			glow('#elementToAnimate').curentAnim().stop();
7824 		
7825 		@example
7826 			// Is the element animating as part of queueAnim?
7827 			glow('#elementToAnimate').curentAnim().playing; // true/false
7828 	*/
7829 	NodeListProto.currentAnim = function() {
7830 		/*!debug*/
7831 			if (arguments.length !== 0) {
7832 				glow.debug.warn('[wrong count] glow.NodeList#currentAnim expects 0 arguments, not ' + arguments.length + '.');
7833 			}
7834 		/*gubed!*/
7835 		return this.data('glow_currentAnim') || new glow.anim.Anim(0);
7836 	}
7837 	
7838 	/**
7839 		@name glow.NodeList#lastQueuedAnim
7840 		@function
7841 		@description Get the last animation added via {@link glow.NodeList#queueAnim queueAnim} for this element
7842 			If no animation has been added, an empty animation is returned.
7843 			This means you don't need to check to see if the item is defined before
7844 			calling methods on it.
7845 			
7846 			This method acts on the first item in the NodeList.
7847 		
7848 		@returns {glow.anim.Anim}
7849 	*/
7850 	NodeListProto.lastQueuedAnim = function() {
7851 		/*!debug*/
7852 			if (arguments.length !== 0) {
7853 				glow.debug.warn('[wrong count] glow.NodeList#lastQueuedAnim expects 0 arguments, not ' + arguments.length + '.');
7854 			}
7855 		/*gubed!*/
7856 		return this.data('glow_lastQueuedAnim') || new glow.anim.Anim(0);
7857 	}
7858 	
7859 	/**
7860 		@private
7861 		@function
7862 		@description This function generates the various anim shortcut functions
7863 	*/
7864 	function animShortcut(animName, animReverseName, animPropsFunc, defaultTween, onComplete, additionalFunc) {
7865 		return function(duration, opts) {
7866 			/*!debug*/
7867 				if (arguments.length > 2) {
7868 					glow.debug.warn('[wrong count] glow.NodeList animation shortcuts expect 0, 1 or 2 arguments, not ' + arguments.length + '.');
7869 				}
7870 				if (duration !== undefined && typeof duration !== 'number') {
7871 					glow.debug.warn('[wrong type] glow.NodeList animation shortcuts expect number as "duration" argument, not ' + typeof duration + '.');
7872 				}
7873 				if (opts !== undefined && typeof opts !== 'object') {
7874 					glow.debug.warn('[wrong type] glow.NodeList animation shortcuts expect object as "opts" argument, not ' + typeof opts + '.');
7875 				}
7876 			/*gubed!*/
7877 			
7878 			opts = opts || {};
7879 			
7880 			var item,
7881 				reverseAnim,
7882 				currentAnim,
7883 				calcDuration,
7884 				anim,
7885 				i = this.length;
7886 				
7887 			opts.tween = opts.tween || defaultTween;
7888 			
7889 			if (duration === undefined) {
7890 				duration = 1;
7891 			}
7892 			
7893 			calcDuration = duration;
7894 			
7895 			while (i--) {
7896 				item = this.item(i);
7897 				currentAnim = item.data('glow_' + animName);
7898 				// if this isn't an element ,or we're already animating it, skip
7899 				if ( item[0].nodeType !== 1 || (currentAnim && currentAnim.playing) ) { continue; }
7900 				
7901 				// if there's a reverse anim happening & it's playing, get rid
7902 				reverseAnim = item.data('glow_' + animReverseName);
7903 				if (reverseAnim && reverseAnim.playing) {
7904 					// reduce the duration if we're not fading out as much
7905 					calcDuration = duration * (reverseAnim.position / reverseAnim.duration);
7906 					
7907 					reverseAnim.stop().destroy();
7908 				}
7909 				
7910 				item.data('glow_' + animName,
7911 					anim = item.anim( calcDuration, animPropsFunc(item), opts ).on('complete', onComplete, item)
7912 				);
7913 				
7914 				additionalFunc && additionalFunc(anim, item, opts);
7915 			}
7916 			
7917 			return this;
7918 		}
7919 	};
7920 	
7921 	/**
7922 		@name glow.NodeList#fadeIn
7923 		@function
7924 		@description Fade elements in
7925 			If the element is currently fading out, the fadeOut animation will be automatically stopped.
7926 		
7927 		@param {number} [duration=1] Duration in seconds
7928 		@param {Object} [opts] Options object
7929 		@param {function|string} [opts.tween='easeOut'] The motion of the animation.
7930 			Strings are treated as properties of {@link glow.tweens}, although
7931 			a tween function can be provided.
7932 			
7933 		@returns {glow.NodeList}
7934 		
7935 		@example
7936 			// make a tooltip fade in & out
7937 			var tooltip = glow('#emailTooltip');
7938 			
7939 			glow('#emailInput').on('focus', function() {
7940 				tooltip.fadeIn();
7941 			}).on('blur', function() {
7942 				tooltip.fadeOut();
7943 			});
7944 	*/
7945 	NodeListProto.fadeIn = animShortcut('fadeIn', 'fadeOut', function(item) {
7946 		item.css('display', 'block');
7947 		return {opacity: 1};
7948 	}, 'easeOut', function() {
7949 		// on complete
7950 		// we remove the filter from IE to bring back cleartype
7951 		if (glow.env.ie) {
7952 			this[0].style.filter = '';
7953 		}
7954 	});
7955 	
7956 	/**
7957 		@name glow.NodeList#fadeOut
7958 		@function
7959 		@description Fade elements out
7960 			If the element is currently fading in, the fadeIn animation will be automatically stopped.
7961 		
7962 		@param {number} [duration=1] Duration in seconds
7963 		@param {Object} [opts] Options object
7964 		@param {function|string} [opts.tween='easeIn'] The motion of the animation.
7965 			Strings are treated as properties of {@link glow.tweens}, although
7966 			a tween function can be provided.
7967 			
7968 		@returns {glow.NodeList}
7969 		
7970 		@example
7971 			// make a tooltip fade in & out
7972 			var tooltip = glow('#emailTooltip');
7973 			
7974 			glow('#emailInput').on('focus', function() {
7975 				tooltip.fadeIn();
7976 			}).on('blur', function() {
7977 				tooltip.fadeOut();
7978 			});
7979 	*/
7980 	NodeListProto.fadeOut = animShortcut('fadeOut', 'fadeIn', function() {
7981 		return {opacity:0}
7982 	}, 'easeIn', function() {
7983 		this.css('display', 'none');
7984 	});
7985 	
7986 	/**
7987 		@name glow.NodeList#fadeToggle
7988 		@function
7989 		@description Fade elements in/out
7990 			If the element is currently fading in/out, the fadeIn/fadeOut animation
7991 			will be automatically stopped.
7992 			
7993 			// Implementation note: (delete me later)
7994 			If the element has an opactity of 0, then fade in, otherwise fade out.
7995 			UNLESS there's fadeOut animation currently happening on this element,
7996 			then fade in.
7997 			
7998 		@param {number} [duration=1] Duration in seconds
7999 		@param {Object} [opts] Options object
8000 		@param {function|string} [opts.tween] The motion of the animation.
8001 			Strings are treated as properties of {@link glow.tweens}, although
8002 			a tween function can be provided.
8003 			
8004 			By default, 'easeIn' is used for fading out, and 'easeOut' is
8005 			used for fading in.
8006 			
8007 		@returns {glow.NodeList}
8008 		
8009 		@example
8010 			// make a tooltip fade in & out
8011 			var tooltip = glow('#emailTooltip');
8012 			
8013 			glow('#toggleTooltip').on('click', function() {
8014 				tooltip.fadeToggle();
8015 			});
8016 	*/
8017 	NodeListProto.fadeToggle = function(duration, opts) {
8018 		var i = this.length,
8019 			item,
8020 			fadeOutAnim;
8021 		
8022 		while (i--) {
8023 			item = this.item(i);
8024 			if (item[0].nodeType === 1) {
8025 				// if the element has an opacity of 0, or is currently fading out
8026 				if ( item.css('opacity') === '0' || ((fadeOutAnim = item.data('glow_fadeOut')) && fadeOutAnim.playing) ) {
8027 					item.fadeIn(duration, opts);
8028 				}
8029 				else {
8030 					item.fadeOut(duration, opts);
8031 				}
8032 			}
8033 		}
8034 		
8035 		return this;
8036 	};
8037 	
8038 	/**
8039 		@name glow.NodeList#slideOpen
8040 		@function
8041 		@description Slide elements open
8042 			This animates an element's height from its current height to its
8043 			full auto-height size.
8044 			
8045 			If the element is currently sliding shut, the slideShut animation
8046 			will be automatically stopped.
8047 		
8048 		@param {number} [duration=1] Duration in seconds
8049 		@param {Object} [opts] Options object
8050 			@param {function|string} [opts.tween='easeBoth'] The motion of the animation.
8051 				Strings are treated as properties of {@link glow.tweens}, although
8052 				a tween function can be provided.
8053 			@param {boolean} [opts.lockToBottom=false] Lock the bottom of the content to the bottom of the element.
8054 				This means the bottom of the content is shown first, rather than the top.
8055 			
8056 		@returns {glow.NodeList}
8057 		
8058 		@example
8059 			var menuContent = glow('#menu div.content');
8060 			
8061 			glow('#menu').on('mouseenter', function() {
8062 				menuContent.slideOpen();
8063 			}).on('mouseleave', function() {
8064 				menuContent.slideShut();
8065 			});
8066 		
8067 		@example
8068 			glow('#furtherInfoHeading').on('click', function() {
8069 				glow('#furtherInfoContent').slideOpen();
8070 			});
8071 			
8072 		@example
8073 			// add content onto an element, and slide to reveal the new content
8074 			glow('<div>' + newContent + '</div>').appendTo('#content').height(0).slideOpen();
8075 			
8076 	*/
8077 	NodeListProto.slideOpen = animShortcut('slideOpen', 'slideShut', function(item) {		
8078 		var currentHeight = item.css('height'),
8079 			fullHeight;
8080 		
8081 		if ( item.css('overflow') === 'visible' ) {
8082 			item.css('overflow', 'hidden');
8083 		}
8084 		
8085 		item.css('height', 'auto');
8086 		fullHeight = item.height();
8087 		item.css('height', currentHeight);
8088 		return {height: fullHeight}
8089 	}, 'easeBoth', function() {
8090 		this.css('height', 'auto').scrollTop(0);
8091 	}, lockToBottom);
8092 	
8093 	/**
8094 		@name glow.NodeList#slideShut
8095 		@function
8096 		@description Slide elements shut
8097 			This animates an element's height from its current height to zero.
8098 			
8099 			If the element is currently sliding open, the slideOpen animation
8100 			will be automatically stopped.
8101 		
8102 		@param {number} [duration=1] Duration in seconds
8103 		@param {Object} [opts] Options object
8104 			@param {function|string} [opts.tween='easeBoth'] The motion of the animation.
8105 				Strings are treated as properties of {@link glow.tweens}, although
8106 				a tween function can be provided.
8107 			@param {boolean} [opts.lockToBottom=false] Lock the bottom of the content to the bottom of the element.
8108 				This means the top of the content is hidden first, rather than the bottom.
8109 			
8110 		@returns {glow.NodeList}
8111 		
8112 		@example
8113 			var menuContent = glow('#menu div.content');
8114 			
8115 			glow('#menu').on('mouseenter', function() {
8116 				menuContent.slideOpen();
8117 			}).on('mouseleave', function() {
8118 				menuContent.slideShut();
8119 			});
8120 	*/
8121 	NodeListProto.slideShut = animShortcut('slideShut', 'slideOpen', function(item) {
8122 		if ( item.css('overflow') === 'visible' ) {
8123 			item.css('overflow', 'hidden');
8124 		}
8125 		return {height: 0}
8126 	}, 'easeBoth', function() {}, lockToBottom);
8127 	
8128 	/**
8129 		@private
8130 		@function
8131 		@description Add frame listener to lock content to the bottom of an item.
8132 		@param {glow.anim.Anim} anim Anim to alter
8133 		@param {glow.NodeList} element Element being animated
8134 		@param {Object} opts Options from slide[Open|Shut|Toggle]
8135 	*/
8136 	function lockToBottom(anim, element, opts) {
8137 		var node = element[0],
8138 			scrollHeight = node.scrollHeight;
8139 		
8140 		if (opts.lockToBottom) {
8141 			anim.on('frame', function() {
8142 				element.scrollTop( scrollHeight - node.offsetHeight );
8143 			});
8144 		}
8145 	}
8146 	
8147 	/**
8148 		@name glow.NodeList#slideToggle
8149 		@function
8150 		@description Slide elements open/shut
8151 			If the element is currently sliding open/shut, the slideOpen/slideShut animation
8152 			will be automatically stopped.
8153 			
8154 			// Implementation note: (delete me later)
8155 			If the element has a height of 0, then slide open, otherwise slide shut.
8156 			UNLESS there's slideShut animation currently happening on this element,
8157 			then slide open.
8158 		
8159 		@param {number} [duration=1] Duration in seconds
8160 		@param {Object} [opts] Options object
8161 			@param {function|string} [opts.tween='easeBoth'] The motion of the animation.
8162 				Strings are treated as properties of {@link glow.tweens}, although
8163 				a tween function can be provided.
8164 			@param {boolean} [opts.lockToBottom=false] Lock the bottom of the content to the bottom of the element.
8165 				This means the top of the content is hidden first & shown last.
8166 			
8167 		@returns {glow.NodeList}
8168 		
8169 		@example
8170 			var menuContent = glow('#menuContent');
8171 			
8172 			glow('#toggleMenu').on('click', function() {
8173 				menuContent.slideToggle();
8174 			});
8175 	*/
8176 	NodeListProto.slideToggle = function(duration, opts) {
8177 		var i = this.length,
8178 			item,
8179 			slideShutAnim;
8180 		
8181 		while (i--) {
8182 			item = this.item(i);
8183 			if (item[0].nodeType === 1) {
8184 				// if the element has an height of 0, or is currently sliding shut
8185 				if ( item.height() === 0 || ((slideShutAnim = item.data('glow_slideShut')) && slideShutAnim.playing) ) {
8186 					item.slideOpen(duration, opts);
8187 				}
8188 				else {
8189 					item.slideShut(duration, opts);
8190 				}
8191 			}
8192 		}
8193 		
8194 		return this;
8195 	};
8196 });
8197 /**
8198 	@name glow.net
8199 	@namespace
8200 	@description Methods for getting data & resources from other locations. Sometimes referred to as AJAX.
8201 */
8202 Glow.provide(function(glow) {
8203 	var net = {},
8204 		undefined,
8205 		emptyFunc = function(){};
8206 	
8207 	/**
8208 		@private
8209 		@function
8210 		@description Create XhrRequest factory methods
8211 		
8212 		@param {string} method HTTP method
8213 		@returns {function} Factory method
8214 	*/
8215 	function createXhrFactory(method) {
8216 		return function(url, data, opts) {
8217 			// only put & post use the data param
8218 			if (method === 'POST' || method === 'PUT') {
8219 				opts = opts || {};
8220 				opts.data = data;
8221 			}
8222 			else {
8223 				opts = data;
8224 			}
8225 			
8226 			return new net.XhrRequest(method, url, opts);
8227 		}
8228 	}
8229 	
8230 	/**
8231 		@name glow.net.get
8232 		@function
8233 		@description Makes an HTTP GET request to a given url.
8234 			This is a shortcut to creating an instance of {@link glow.net.XhrRequest}.
8235 	 
8236 		@param {string} url Url to make the request to.
8237 			This can be a relative path. You cannot make requests for files on
8238 			other domains (including sub-domains). For cross-domain requests, see
8239 			{@link glow.dom.getJsonp} and {@link glow.dom.crossDomainGet}.
8240 		@param {Object} [opts] Options.
8241 			These options are the same as the constructor options for {@link glow.net.XhrRequest}.
8242 	 
8243 		@returns {glow.net.XhrRequest}
8244 	 
8245 		@example
8246 			glow.net.get('myFile.html').on('load', function(response){
8247 				alert( 'Got file:' + response.text() );
8248 			}).on('error', function(response){
8249 				alert( 'Something went wrong:' + response.text() );
8250 			});
8251 			
8252 	*/
8253 	net.get = createXhrFactory('GET');
8254 	
8255 	/**
8256 		@name glow.net.post
8257 		@function
8258 		@description Makes an HTTP POST request to a given url
8259 			This is a shortcut to creating an instance of {@link glow.net.XhrRequest}.
8260 		 
8261 		@param {string} url Url to make the request to.
8262 			This can be a relative path. You cannot make requests for files on
8263 			other domains (including sub-domains). For cross-domain requests, see
8264 			{@link glow.dom.getJsonp} and {@link glow.dom.crossDomainGet}.
8265 		@param {Object|String} data Data to send.
8266 			This can be either a JSON-style object or a urlEncoded string.
8267 		@param {Object} [opts] Options.
8268 			These options are the same as the constructor options for {@link glow.net.XhrRequest}.
8269 		
8270 		@returns {glow.net.XhrRequest}
8271 	 
8272 		@example
8273 			glow.net.post('myFile.html', {
8274 				key: 'value',
8275 				otherkey: ['value1', 'value2']
8276 			}).on('load', function(response) {
8277 				alert( 'Got file:' + response.text() );
8278 			});
8279 	*/
8280 	net.post = createXhrFactory('POST');
8281 	
8282 	/**
8283 		@name glow.net.put
8284 		@function
8285 		@description Makes an HTTP PUT request to a given url
8286 			This is a shortcut to creating an instance of {@link glow.net.XhrRequest}.
8287 		 
8288 		@param {string} url Url to make the request to.
8289 			This can be a relative path. You cannot make requests for files on
8290 			other domains (including sub-domains). For cross-domain requests, see
8291 			{@link glow.dom.getJsonp} and {@link glow.dom.crossDomainGet}.
8292 		@param {Object|String} data Data to send.
8293 			This can be either a JSON-style object or a urlEncoded string.
8294 		@param {Object} [opts] Options.
8295 			These options are the same as the constructor options for {@link glow.net.XhrRequest}.
8296  
8297 		@returns {glow.net.XhrRequest}
8298  
8299 		@example
8300 			glow.net.put('myFile.html', {
8301 				key: 'value',
8302 				otherkey: ['value1', 'value2']
8303 			}).on('load', function(response) {
8304 				// handle response
8305 			});
8306 	*/
8307 	net.put = createXhrFactory('PUT');
8308 	
8309 	/**
8310 		@name glow.net.del
8311 		@function
8312 		@description Makes an HTTP DELETE request to a given url.
8313 			This is a shortcut to creating an instance of {@link glow.net.XhrRequest}.
8314 		 
8315 		@param {string} url Url to make the request to.
8316 			This can be a relative path. You cannot make requests for files on
8317 			other domains (including sub-domains). For cross-domain requests, see
8318 			{@link glow.dom.getJsonp} and {@link glow.dom.crossDomainGet}.
8319 		@param {Object} [opts] Options.
8320 			These options are the same as the constructor options for {@link glow.net.XhrRequest}.
8321  
8322 		@returns {glow.net.XhrRequest}
8323  
8324 		@example
8325 			glow.net.del('myFile.html').on('load', function(response) {
8326 				// handle response
8327 			});
8328 	*/
8329 	
8330 	net.del = createXhrFactory('DELETE');		
8331 		
8332 	// export
8333 	glow.net = net;
8334 });
8335 Glow.provide(function(glow) {
8336 	var undefined,
8337 		XhrRequestProto,
8338 		events = glow.events,
8339 		removeAllListeners = events.removeAllListeners;
8340 	
8341 	/**
8342 		@private
8343 		@function
8344 		@description Creates an XMLHttpRequest transport
8345 		@returns XMLHttpRequest
8346 	*/
8347 	var xmlHTTPRequest = window.ActiveXObject ?
8348 		function() {
8349 			return new ActiveXObject('Microsoft.XMLHTTP');
8350 		} :
8351 		function() {
8352 			return new XMLHttpRequest();
8353 		};
8354 		
8355 	/**
8356 		@private
8357 		@function
8358 		@description Apply option object defaults.
8359 		
8360 		@param {object} opts Options object to apply defaults to.
8361 		@param {string} method HTTP method.
8362 		
8363 		@returns {object} New opts object with defaults applied.
8364 	*/
8365 	function applyOptsDefaults(opts, method) {
8366 		opts = glow.util.apply({
8367 			headers: {}
8368 		}, opts);
8369 		
8370 		var headers = opts.headers;
8371 		
8372 		// convert data to string
8373 		if (typeof opts.data === 'object') {
8374 			opts.data = glow.util.encodeUrl(opts.data);
8375 		}
8376 		
8377 		// add requested with header if one hasn't been added
8378 		if ( !headers['X-Requested-With'] ) {
8379 			headers['X-Requested-With'] = 'XMLHttpRequest';
8380 		}
8381 		
8382 		if (method !== 'GET' && !headers["Content-Type"]) {
8383 			headers["Content-Type"] = 'application/x-www-form-urlencoded;';
8384 		}
8385 		
8386 		return opts;
8387 	}
8388 	
8389 	/**
8390 		@name glow.net.XhrRequest
8391 		@class
8392 		@param {string} method The HTTP method to use for the request.
8393 			Methods are case sensitive in some browsers.
8394 		@param {string} url Url to make the request to.
8395 			This can be a relative path. You cannot make requests for files on
8396 			other domains (including sub-domains). For cross-domain requests, see
8397 			{@link glow.dom.getJsonp} and {@link glow.dom.crossDomainGet}.
8398 		@param {Object} [opts] Options object
8399 			@param {Object} [opts.headers] A hash of headers to send along with the request.
8400 				eg `{'Accept-Language': 'en-gb'}`
8401 			@param {boolean} [opts.cacheBust=false] Prevent the browser returning a cached response.
8402 				If true, a value is added to the query string to ensure a fresh version of the
8403 	 			file is being fetched.
8404 			@param {number} [opts.timeout] Time to allow for the request in seconds.
8405 				No timeout is set by default. Once the time is reached, the error
8406 				event will fire with a '408' status code.
8407 			@param {boolean} [opts.forceXml=false] Treat the response as XML.
8408 				This will allow you to use {@link glow.net.XhrResponse#xml response.xml()}
8409 				even if the response has a non-XML mime type.
8410 			@param {Object|string} [opts.data] Data to send.
8411 				This can be either a JSON-style object or a urlEncoded string.
8412 				
8413 		@description Create an XHR request.
8414 			Most common requests can be made using shortcuts methods in {@link glow.net},
8415 			such as {@link glow.net.get}.
8416 		
8417 		@example
8418 			new glow.net.XhrRequest('DELETE', 'whatever.php', {
8419 				timeout: 10
8420 			}).on('load', function(response) {
8421 				alert( response.text() );
8422 			});
8423 	*/
8424 	function XhrRequest(method, url, opts) {
8425 		this._opts = opts = applyOptsDefaults(opts, method);
8426 		
8427 		var request = this,
8428 			nativeRequest = request.nativeRequest = xmlHTTPRequest(), //request object
8429 			i;
8430 
8431 		// add the cacheBust to the url
8432 		if (opts.cacheBust) {
8433 			url = url + (url.indexOf('?') === -1 ? '?' : '&') + 'cachebuster=' + new Date().valueOf();
8434 		}
8435 
8436 		request.complete = false;
8437 		
8438 		//open needs to go first to maintain cross-browser support for readystates
8439 		nativeRequest.open(method, url, true);
8440  
8441 		//add custom headers
8442 		for (i in opts.headers) {
8443 			nativeRequest.setRequestHeader( i, opts.headers[i] );
8444 		}
8445 		
8446 		// force the reponse to be treated as xml
8447 		// IE doesn't support overrideMineType, we need to deal with that in {@link glow.net.XhrResponse#xml}
8448 		if (opts.forceXml && nativeRequest.overrideMimeType) {
8449 			nativeRequest.overrideMimeType('application/xml');
8450 		}
8451 		
8452 		//sort out the timeout if there is one
8453 		if (opts.timeout) {			 
8454 			request._timeout = setTimeout(function() {
8455 				var response = new glow.net.XhrResponse(request, true);
8456 				request.abort().fire('error', response);
8457 			}, opts.timeout * 1000);
8458 		}
8459 		
8460 		nativeRequest.onreadystatechange = function() {
8461 			if (nativeRequest.readyState === 4) {
8462 				var response = new glow.net.XhrResponse(request);
8463 				
8464 				//clear the timeout
8465 				clearTimeout(request._timeout);
8466 				
8467 				//set as completed
8468 				request.completed = true;
8469 				request.fire(response.successful ? 'load' : 'error', response);
8470 				
8471 				// prevent parent scopes leaking (cross-page) in IE
8472 				nativeRequest.onreadystatechange = new Function();
8473 				removeAllListeners(request);
8474 			}
8475 		};
8476 		
8477 		// make sure it doesn't complete before listeners are attached
8478 		setTimeout(function() {
8479 			nativeRequest.send(opts.data || null);
8480 		}, 0);
8481 	}
8482 	glow.util.extend(XhrRequest, events.Target);
8483 	XhrRequestProto = XhrRequest.prototype;
8484 	
8485 	/**
8486 		@name glow.net.XhrRequest#_timeout
8487 		@private
8488 		@description setTimeout ID
8489 		@type number
8490 	*/
8491 	
8492 	/**
8493 		@name glow.net.XhrRequest#complete
8494 		@description Boolean indicating whether the request has completed
8495 		@example
8496 			// request.complete with an asynchronous call
8497 			var request = glow.net.get(
8498 				"myFile.html").on('load', 
8499 				function(response){
8500 					alert(request.complete); // returns true
8501 				})
8502 				
8503 					
8504 		@type boolean
8505 	*/
8506 	
8507 	/**
8508 		@name glow.net.XhrRequest#nativeRequest
8509 		@description The request object from the browser.
8510 			This may not have the same properties and methods across user agents.
8511 			Also, this will be undefined if the request originated from getJsonp.
8512 			
8513 		@type Object
8514 	*/
8515 	
8516 	/**
8517 		@name glow.net.XhrRequest#abort
8518 		@function
8519 		@description Aborts a request
8520 			The load & error events will not fire.
8521 		@example
8522 			var request = glow.net.get('myFile.html').on('load', function(response) {
8523 				//handle response
8524 			}).on('abort', function() {
8525 				alert('Something bad happened. The request was aborted.');
8526 			});
8527 			
8528 			request.abort(); // alerts "Something bad happened.  The request was aborted"
8529 		@returns this
8530 	*/
8531 	XhrRequestProto.abort = function() {
8532 		if ( !this.completed && !this.fire('abort').defaultPrevented() ) {
8533 			clearTimeout(this._timeout);
8534 			this.nativeRequest.onreadystatechange = new Function();
8535 			removeAllListeners(this);
8536 		}
8537 		return this;
8538 	};
8539 		 
8540 	/**
8541 		@name glow.net.XhrRequest#event:load
8542 		@event
8543 		@param {glow.net.XhrResponse} response
8544 		@description Fired when the request is sucessful
8545 			This will be fired when request returns with an HTTP code of 2xx. 
8546 	*/
8547  
8548 	/**
8549 		@name glow.net.XhrRequest#event:abort
8550 		@event
8551 		@param {glow.events.Event} event Event Object
8552 		@description Fired when the request is aborted
8553 			If you cancel the default (eg, by returning false) the request
8554 			will continue.
8555 	*/
8556  
8557 	/**
8558 		@name glow.net.XhrRequest#event:error
8559 		@event
8560 		@param {glow.net.XhrResponse} response
8561 		@description Fired when the request is unsucessful
8562 			This will be fired when request returns with an HTTP code which
8563 			isn't 2xx or the request times out.
8564 	*/
8565 	
8566 	glow.net.XhrRequest = XhrRequest;
8567 });
8568 Glow.provide(function(glow) {
8569 	var XhrResponseProto,
8570 		util = glow.util;
8571 	
8572 	/**
8573 		@name glow.net.XhrResponse
8574 		@class
8575 		@extends glow.events.Event
8576 		@description The event object for {@link glow.net.XhrRequest}'s 'load' & 'error' events.
8577 		@glowPrivateConstructor There is no direct constructor.
8578 	*/
8579 	
8580 	/*
8581 		These params are hidden as we don't want users to try and create instances of this...
8582 	 
8583 		@param {glow.net.XhrRequest} [request] Original request object
8584 		@param {Boolean} [timedOut=false] Set to true if the response timed out
8585 		
8586 	*/
8587 	function XhrResponse(request, timedOut) {
8588 		var nativeResponse = this.nativeResponse = request.nativeRequest;
8589 		this._request = request;
8590 		
8591 		//IE reports status as 1223 rather than 204, for laffs
8592 		this.status = timedOut ? 408 :
8593 			nativeResponse.status == 1223 ? 204 : nativeResponse.status;
8594 
8595 		this.timedOut = !!timedOut;
8596 			
8597 		this.successful = (this.status >= 200 && this.status < 300) ||
8598 			//from cache
8599 			this.status == 304 ||
8600 			//watch our for requests from file://
8601 			(this.status == 0 && nativeResponse.responseText);
8602 	}
8603 
8604 	util.extend(XhrResponse, glow.events.Event);
8605 	XhrResponseProto = XhrResponse.prototype;
8606 	
8607 	/**
8608 		@name glow.net.XhrResponse#_request
8609 		@private
8610 		@description Original request object
8611 		@type glow.net.XhrRequest
8612 	*/
8613 	
8614 	/**
8615 		@name glow.net.XhrResponse#nativeResponse
8616 		@description The response object from the browser.
8617 			This may not have the same properties and methods across user agents.
8618 		@type XMLHttpRequest
8619 	*/
8620 	
8621 	/**
8622 		@name glow.net.XhrResponse#status
8623 		@description HTTP status code of the response
8624 		@type number
8625 	*/
8626 
8627 	/**
8628 		@name glow.net.XhrResponse#timedOut
8629 		@description Boolean indicating if the requests time out was reached.
8630 		@type boolean
8631 	*/
8632 	
8633 	/**
8634 		@name glow.net.XhrResponse#successful
8635 		@description  Boolean indicating if the request returned successfully.
8636 		@type boolean
8637 	*/
8638 	
8639 	/**
8640 		@name glow.net.XhrResponse#text
8641 		@function
8642 		@description Gets the body of the response as plain text
8643 		@returns {string}
8644 	*/
8645 
8646 	XhrResponseProto.text = function() {
8647 		return this.nativeResponse.responseText;
8648 	};
8649 	
8650 	/**
8651 		@name glow.net.XhrResponse#xml
8652 		@function
8653 		@description Gets the body of the response as xml
8654 		@returns {XML} 
8655 	*/
8656 	
8657 	XhrResponseProto.xml = function() {
8658 		var nativeResponse = this.nativeResponse,
8659 			contentType = this.header("Content-Type");
8660 		
8661 		if (
8662 			// IE 6 & 7 fail to recognise Content-Types ending +xml (eg application/rss+xml)
8663 			// Files from the filesystem don't have a content type, but could be xml files, parse them to be safe
8664 			glow.env.ie && (
8665 				contentType.slice(-4) === '+xml' ||
8666 				contentType === '' ||
8667 				this._request._opts.forceXml
8668 			)
8669 		) {
8670 			var doc = new ActiveXObject("Microsoft.XMLDOM");
8671 			doc.loadXML( nativeResponse.responseText );
8672 			return doc;
8673 		}
8674 		else {
8675 			return nativeResponse.responseXML;
8676 		}				
8677 	};
8678 	
8679 	/**
8680 		@name glow.net.XhrResponse#json
8681 		@function
8682 		@description Gets the body of the response as a JSON object.
8683 		
8684 		@param {boolean} [safeMode=false]
8685 			If true, the response will be parsed using a string parser which
8686 			will filter out non-JSON javascript, this will be slower but
8687 			recommended if you do not trust the data source.
8688 	
8689 		@returns {object}
8690 	*/
8691 	XhrResponseProto.json = function(safe) {
8692 		return util.decodeJson(this.text(), {safeMode:safe});
8693 	};
8694 	
8695 	/**
8696 		@name glow.net.XhrResponse#nodeList
8697 		@function
8698 		@description Gets the body of the response as a {@link glow.NodeList}.
8699 	
8700 		@returns {glow.NodeList}
8701 	*/
8702 	XhrResponseProto.nodeList = function(safe) {
8703 		return glow(
8704 			glow.NodeList._strToNodes( this.text() )
8705 		);
8706 	};
8707 
8708 	/**
8709 		@name glow.net.XhrResponse#header
8710 		@function
8711 		@description Gets a header from the response.
8712 	
8713 		@param {string} name Header name
8714 		@returns {string} Header value
8715 	
8716 		@example var contentType = myResponse.header("Content-Type");
8717 	*/
8718 	
8719 	XhrResponseProto.header = function(name) {
8720 		return this.nativeResponse.getResponseHeader(name);
8721 	};
8722 
8723 	/**
8724 		@name glow.net.XhrResponse#statusText
8725 		@function
8726 		@description Gets the meaning of {@link glow.net.XhrResponse#status status}.
8727 	
8728 		@returns {string}
8729 	*/
8730 	XhrResponseProto.statusText = function() {
8731 		return this.timedOut ? "Request Timeout" : this.nativeResponse.statusText;
8732 	};
8733 	
8734 	glow.net.XhrResponse = XhrResponse;
8735 });
8736 Glow.provide(function(glow) {
8737 	var undefined,
8738 		JsonpRequestProto,
8739 		net = glow.net,
8740 		emptyFunc = function(){},
8741 		events = glow.events,
8742 		// Script elements that have been added via {@link glow.net.jsonp jsonp}, keyed by callback name
8743 		scriptElements = {},
8744 		scriptElementsLen = 0,
8745 		callbackPrefix = 'c',
8746 		// Name of the global object used to store jsonp callbacks
8747 		globalObjectName = '_' + glow.UID + 'jsonp',
8748 		head = glow('head'),
8749 		// a reference to the global object holding the callbacks
8750 		globalObject;
8751 	
8752 	/**
8753 		@private
8754 		@function
8755 		@description Handle jsonp load.
8756 		@param {glow.net.JsonpRequest} request
8757 		@param {Object[]} args Arguments object passed to the callback from the jsonp source
8758 	*/
8759 	function jsonpLoad(request, args) {
8760 		// we have to call listeners manually as we don't provide a real event object. A bit of a hack.
8761 		var loadListeners = events._getListeners(request).load,
8762 			i;
8763 			
8764 		if (loadListeners) {
8765 			loadListeners = loadListeners.slice(0);
8766 			i = loadListeners.length;
8767 			while (i--) {
8768 				loadListeners[i][0].apply( loadListeners[i][1], args );
8769 			}
8770 		}
8771 		//set as completed
8772 		request.completed = true;
8773 
8774 		cleanUp(request);
8775 	}
8776 	
8777 	/**
8778 		@private
8779 		@function
8780 		@description Clean up to avoid memory leaks
8781 		@param {glow.net.JsonpRequest} request
8782 		@param {boolean} [leaveEmptyFunc] Replace global callback with blank function.
8783 			If false, the global callback will be set to undefined, which is better for memory,
8784 			but in some cases the callback may later be called (like a timed out request) so an
8785 			empty function needs to be used to avoid errors.
8786 	*/
8787 	function cleanUp(request, leaveEmptyFunc) {
8788 		var callbackName = request._callbackName;
8789 		
8790 		clearTimeout(request._timeout);
8791 		globalObject[callbackName] = leaveEmptyFunc ? emptyFunc : undefined;
8792 		glow( scriptElements[callbackName] ).destroy();
8793 		scriptElements[callbackName] = undefined;
8794 	}
8795 	
8796 	/**
8797 		@name glow.net.JsonpRequest
8798 		@class
8799 		@description A JSONP request.
8800 			Although instance of this can be created manually, using
8801 			{@link glow.net.jsonp} is preferred. 
8802 	*/
8803 	// the params for this are the same as {@link glow.net.jsonp}.
8804 	function JsonpRequest(url, opts) {
8805 		opts = opts || {};
8806 		
8807 		var newIndex = scriptElements.length,
8808 			//script element that gets inserted on the page
8809 			//generated name of the callback
8810 			callbackName = this._callbackName = callbackPrefix + (scriptElementsLen++),
8811 			// script element to add to the page
8812 			script = scriptElements[callbackName] = document.createElement('script'),
8813 			request = this,
8814 			timeout = opts.timeout,
8815 			charset = opts.charset;
8816 		
8817 		// add the callback name to the url
8818 		url = glow.util.interpolate(url, {
8819 			callback: globalObjectName + '.' + callbackName
8820 		});
8821 		
8822 		// create the global object if it doesn't exist already
8823 		globalObject || ( globalObject = window[globalObjectName] = {} );
8824 		
8825 		// create our callback
8826 		globalObject[callbackName] = function() {
8827 			jsonpLoad(request, arguments);
8828 		};
8829 		
8830 		// set charset
8831 		charset && (script.charset = charset);
8832 		
8833 		if (opts.timeout) {
8834 			request._timeout = setTimeout(function() {
8835 				request.abort().fire('error');
8836 			}, timeout * 1000);
8837 		}
8838 			
8839 		script.src = url;
8840 		
8841 		//add script to page
8842 		head.prepend(script);
8843 		
8844 		script = undefined;
8845 	}
8846 	
8847 	glow.util.extend(JsonpRequest, events.Target);
8848 	JsonpRequestProto = JsonpRequest.prototype;
8849 	
8850 	/**
8851 		@name glow.net.JsonpRequest#_callbackName
8852 		@private
8853 		@description The name of the callback, used as a property name in globalObject and scriptElements
8854 	*/
8855 	
8856 	/**
8857 		@name glow.net.JsonpRequest#_timeout
8858 		@private
8859 		@description timeout ID
8860 		@type number
8861 	*/
8862 	
8863 	/**
8864 		@name glow.net.JsonpRequest#complete
8865 		@description Boolean indicating whether the request has completed
8866 		@type boolean
8867 	*/
8868 	JsonpRequestProto.complete = false;
8869 	
8870 	/**
8871 		@name glow.net.JsonpRequest#abort
8872 		@function
8873 		@description Abort the request.
8874 			The script file may still load, but the 'load' event will not fire.
8875 		@returns this
8876 	*/
8877 	JsonpRequestProto.abort = function() {
8878 		this.fire('abort');
8879 		cleanUp(this, true);
8880 		return this;
8881 	};
8882 	
8883 	/**
8884 		@name glow.net.JsonpRequest#event:load
8885 		@event
8886 		@description Fired when the request is sucessful.
8887 			The parameters to this event are whatever the datasource provides.
8888 			
8889 		@example
8890 			glow.net.jsonp('http://twitter.com/statuses/user_timeline/15390783.json?callback={callback}')
8891 				.on('load', function(data) {
8892 					alert(data);
8893 				});
8894 	*/
8895  
8896 	/**
8897 		@name glow.net.JsonpRequest#event:abort
8898 		@event
8899 		@param {glow.events.Event} event Event Object
8900 		@description Fired when the request is aborted.
8901 	*/
8902  
8903 	/**
8904 		@name glow.net.JsonpRequest#event:error
8905 		@event
8906 		@param {glow.events.Event} event Event Object
8907 		@description Fired when the request times out.
8908 	*/
8909 	
8910 	/**
8911 		@name glow.net.jsonp
8912 		@function
8913 		@description Fetch JSON via JSONP.
8914 			This can be used cross domain, but should only be used with trusted
8915 			sources as any javascript included in the script will be executed.
8916 			
8917 			This method only works if the server allows you to specify a callback
8918 			name for JSON data. Not all JSON sources support this, check the API of the
8919 			data source to ensure you're using the correct querystring parameter
8920 			to set the callback name.
8921 	 
8922 		@param {string} url Url of the script.
8923 			Set the callback name via the querystring to `{callback}`, Glow will
8924 			replace this with another value and manage the callback internally.
8925 			
8926 			Check the API of your data source for the correct parameter name.
8927 			Eg, in Flickr it's `jsoncallback={callback}`, in Twitter it's
8928 			`callback={callback}`.
8929 		@param {object} [opts]
8930 			@param {number} [opts.timeout] Time to allow for the request in seconds.
8931 			@param {string} [opts.charset] Charset attribute value for the script.
8932 	 
8933 		@returns {glow.net.JsonpRequest}
8934 	 
8935 		@example
8936 			glow.net.jsonp('http://twitter.com/statuses/user_timeline/15390783.json?callback={callback}', {
8937 				timeout: 5
8938 			}).on('load', function(data) {
8939 				alert(data);
8940 			}).on('error', function() {
8941 				alert('Request timeout');
8942 			});
8943 	*/
8944 	net.jsonp = function(url, opts) {
8945 		return new glow.net.JsonpRequest(url, opts);
8946 	};
8947 	
8948 	glow.net.JsonpRequest = JsonpRequest;
8949 });
8950 Glow.provide(function(glow) {
8951 	var undefined,
8952 		ResourceRequestProto,
8953 		ResourceResponseProto,
8954 		net = glow.net;
8955 	
8956 	/**
8957 		@private
8958 		@function
8959 		@description Normalise urls param.
8960 			Normalise ResourceRequest's urls parameter to an object with 'css', 'js' and 'img' properties.
8961 	*/
8962 	function normaliseUrlsParam(urls) {
8963 		var r = {
8964 				js: [],
8965 				css: [],
8966 				img: []
8967 			},
8968 			url;
8969 		
8970 		if (typeof urls === 'object' && !urls.push) {
8971 			r = glow.util.apply(r, urls);
8972 		}
8973 		else {
8974 			// convert urls to an array if need be
8975 			typeof urls === 'string' && ( urls = [urls] );
8976 			
8977 			// forwards loop, maintain order
8978 			for (var i = 0, len = urls.length; i < len; i++) {
8979 				url = urls[i];
8980 				if ( url.slice(-4) === '.css' ) {
8981 					r.css[r.css.length] = url;
8982 				}
8983 				else if ( url.slice(-3) === '.js' ) {
8984 					r.js[r.js.length] = url;
8985 				}
8986 				else {
8987 					r.img[r.img.length] = url;
8988 				}
8989 			}
8990 		}
8991 		
8992 		return r;
8993 	}
8994 	
8995 	/**
8996 		@name glow.net.ResourceRequest
8997 		@class
8998 		@description Request made via {@link glow.net.getResources}
8999 		@glowPrivateConstructor There is no direct constructor.
9000 	*/
9001 	function ResourceRequest(urls) {
9002 		urls = normaliseUrlsParam(urls);
9003 		
9004 		var request = this,
9005 			js = urls.js,
9006 			css = urls.css,
9007 			img = urls.img,
9008 			jsLen = js.length,
9009 			cssLen = css.length,
9010 			imgLen = img.length,
9011 			i;
9012 		
9013 		request.totalResources = jsLen + cssLen + imgLen;
9014 		
9015 		// ensure events don't fire until they're added
9016 		setTimeout(function() {
9017 			// guess it makes sense to load CSS, js then images (the browser will queue the requests)
9018 			for (i = 0; i < cssLen; i++) {
9019 				loadCss( request, css[i] );
9020 			}
9021 			for (i = 0; i < jsLen; i++) {
9022 				loadJs( request, js[i] );
9023 			}
9024 			for (i = 0; i < imgLen; i++) {
9025 				loadImg( request, img[i] );
9026 			}
9027 		}, 0);
9028 		
9029 	}
9030 	
9031 	glow.util.extend(ResourceRequest, glow.events.Target);
9032 	ResourceRequestProto = ResourceRequest.prototype;
9033 	
9034 	/**
9035 		@name glow.net.ResourceRequest#totalResources
9036 		@type number
9037 		@description Total number of resources requested.
9038 	*/
9039 	ResourceRequestProto.totalResources = 0;
9040 	
9041 	/**
9042 		@name glow.net.ResourceRequest#totalLoaded
9043 		@type number
9044 		@description Total number of resources successfully loaded.
9045 	*/
9046 	ResourceRequestProto.totalLoaded = 0;
9047 	
9048 	/**
9049 		@private
9050 		@function
9051 		@description Update a request after a resource loads.
9052 		
9053 		@param {glow.net.ResourceRequest} request.
9054 		@param {string} url Url of the requested resource.
9055 		@param {glow.NodeList} resource The element used to load the resource.
9056 		@param {string} type 'js', 'css' or 'img'
9057 	*/
9058 	function progress(request, url, resource, type) {
9059 		
9060 		var totalLoaded = ++request.totalLoaded;
9061 		
9062 		request.fire('progress', {
9063 			resource: resource,
9064 			url: url,
9065 			type: type
9066 		});
9067 		
9068 		if (totalLoaded === request.totalResources) {
9069 			request.fire('load');
9070 		}
9071 	}
9072 	
9073 	/**
9074 		@private
9075 		@function
9076 		@description Start loading an image
9077 		
9078 		@param {glow.net.ResourceRequest} request
9079 		@param {string} imgUrl
9080 	*/
9081 	function loadImg(request, imgUrl) {
9082 		var img = new Image;
9083 		// keep the url in its original format
9084 		glow(img).data('srcUrl', imgUrl).on('load', imgLoaded, request);
9085 		img.src = imgUrl;
9086 	}
9087 	
9088 	/**
9089 		@private
9090 		@function
9091 		@description Process a loaded image.
9092 			'this' is the ResourceRequest
9093 	*/
9094 	function imgLoaded(event) {
9095 		var img = glow(event.attachedTo);
9096 		progress( this, img.data('srcUrl'), img, 'img' );
9097 	}
9098 	
9099 	/**
9100 		@private
9101 		@function
9102 		@description Start loading a script
9103 		
9104 		@param {glow.net.ResourceRequest} request
9105 		@param {string} scriptUrl
9106 	*/
9107 	function loadJs(request, scriptUrl){
9108 		var script = glow( document.createElement('script') )
9109 			.data('srcUrl', scriptUrl)
9110 			.prependTo('head');
9111 		
9112 		// two methods, one for IE (readystatechange) and the other for others
9113 		script.on('readystatechange', jsLoaded, request).on('load', jsLoaded, request);
9114 		
9115 		script[0].src = scriptUrl;
9116 	}
9117 	
9118 	/**
9119 		@private
9120 		@function
9121 		@description Process a loaded script.
9122 			'this' is the ResourceRequest
9123 	*/
9124 	function jsLoaded(event) {
9125 		var script = glow(event.attachedTo),
9126 			scriptElm = script[0],
9127 			readyState = scriptElm.readyState;
9128 		
9129 		if ( !readyState || readyState === 'loaded' || readyState === 'complete' ) {
9130 			// remove events to prevent double-firing
9131 			script.detach('readystatechange', jsLoaded).detach('load', jsLoaded);
9132 			progress( this, script.data('srcUrl'), script, 'js' );
9133 		}
9134 	}
9135 	
9136 	/**
9137 		@private
9138 		@function
9139 		@description Start loading a CSS file
9140 		
9141 		@param {glow.net.ResourceRequest} request
9142 		@param {string} cssUrl
9143 	*/
9144 	// This technique was found in http://code.google.com/p/ajaxsoft/source/browse/trunk/xLazyLoader
9145 	function loadCss(request, cssUrl){
9146 		var currentHostname,
9147 			urlHostname,
9148 			link = glow('<link rel="stylesheet" type="text/css" media="all" href="' + cssUrl + '" />').data('srcUrl', cssUrl);
9149 		
9150 		// we have to do something special for Gecko browsers when the css is from another domain
9151 		if ( glow.env.gecko && /^(?:https?\:|\/\/)/.test(cssUrl) ) {
9152 			currentHostname = location.hostname.replace('www.', '');
9153 			urlHostname = cssUrl.replace(/https?:\/\/|www\.|:.*/g, '').replace(/\/.*/g, '');
9154 			
9155 			if (currentHostname !== urlHostname) {
9156 				// ack, we have to cheat
9157 				setTimeout(function() {
9158 					cssLoaded.call(request, {
9159 						attachedTo: link
9160 					});
9161 				}, 500);
9162 			}
9163 		}
9164 		else {
9165 			// two methods, one for IE (readystatechange), and one for opera
9166 			link.on('readystatechange', cssLoaded, request).on('load', cssLoaded, request);
9167 			// ...and one more for Moz & webkit
9168 			(function pollCssRules() {
9169 				try {
9170 					link[0].sheet.cssRules;
9171 					// we'll error before the next line if CSS hasn't loaded
9172 					cssLoaded.call(request, {
9173 						attachedTo: link
9174 					});
9175 				}
9176 				catch (e) {
9177 					if ( !link.data('loaded') ) {
9178 						setTimeout(pollCssRules, 20);
9179 					}
9180 				};
9181 			})();
9182 		}
9183 		
9184 		//link[0].href = cssUrl;
9185 		link.prependTo('head');
9186 	}
9187 	
9188 	/**
9189 		@private
9190 		@function
9191 		@description Process a loaded stylesheet.
9192 			'this' is the ResourceRequest
9193 	*/
9194 	function cssLoaded(event) {
9195 		var link = glow(event.attachedTo),
9196 			linkElm = link[0],
9197 			readyState = linkElm.readyState;
9198 		
9199 		if ( !readyState || readyState === 'loaded' || readyState === 'complete' ) {
9200 			// just incase there's a timeout still waiting
9201 			if ( link.data('loaded') ) {
9202 				return;
9203 			}
9204 			link.data('loaded', true);
9205 			
9206 			// remove events to prevent double-firing
9207 			link.detach('readystatechange', cssLoaded).detach('load', cssLoaded);
9208 			progress( this, link.data('srcUrl'), link, 'css' );
9209 		}
9210 	}
9211 	
9212 	
9213 	/**
9214 		@name glow.net.ResourceRequest#event:load
9215 		@event
9216 		@param {glow.events.Event} event Event Object
9217 		@description Fired when all the requested items have completed.
9218 	*/
9219  
9220 	/**
9221 		@name glow.net.ResourceRequest#event:progress
9222 		@event
9223 		@description Fired when a single resource loads.
9224 		
9225 		@param {glow.events.Event} event Event Object
9226 			@param {string} event.url Url of the loaded resource.
9227 			@param {glow.NodeList} event.resource The element used to load the resource.
9228 				This will be a `<script>`, `<link>`, or `<img>` element.
9229 			@param {string} event.type 'js', 'css' or 'img'.
9230 	*/
9231 	
9232 	/**
9233 		@name glow.net.getResources
9234 		@function
9235 		@description Load scripts, images & CSS.
9236 			Files can be loaded from other domains.
9237 			
9238 			Note: Due to a cross-browser restriction, 'load' may fire before
9239 			CSS files from another domain are fully loaded in Gecko browsers.
9240 	 
9241 		@param {string[]|string|Object} url
9242 			Url(s) to load. Urls ending in ".css" are assumed to be CSS files,
9243 			Urls ending in ".js" are assumed to be JavaScript. All other files
9244 			will be treated as images.
9245 			
9246 			You can provide an object in the form `{js: [], css: [], img: []}` to
9247 			be explicit about how to treat each file.
9248 			
9249 		@returns {glow.net.ResourceRequest}
9250 	 
9251 		@example
9252 			// load a single CSS file with a callback specified
9253 			glow.net.getResources('/styles/custom.css').on('load', function() {
9254 				// CSS has now loaded
9255 			});
9256 			
9257 		@example
9258 			// load a single CSS file with a callback specified
9259 			glow.net.getResources([
9260 				'/imgs/whatever.png',
9261 				'/style/screen.css',
9262 			]).on('load', function() {
9263 				// CSS & image now loaded
9264 			});
9265 			
9266 		@example
9267 			// load multiple files by specifying and array
9268 			glow.net.getResources({
9269 				js: ['http://www.server.com/script', 'http://www.server.com/anotherScript'],
9270 				img: ['http://www.server.com/product4/thumb']
9271 			}).on('progress', function(event) {
9272 				// update a progress meter
9273 			}).on('load', function(response){
9274 				// files now loaded
9275 			});
9276 	*/
9277 	net.getResources = function(urls, opts) {
9278 		/*!debug*/
9279 			if (arguments.length < 1 && arguments.length > 2) {
9280 				glow.debug.warn('[wrong count] glow.net.getResources expects 1 or 2 arguments, not ' + arguments.length + '.');
9281 			}
9282 		/*gubed!*/
9283 		return new glow.net.ResourceRequest(urls, opts);
9284 	};
9285 	
9286 	glow.net.ResourceRequest = ResourceRequest;	
9287 });
9288 Glow.provide(function(glow) {
9289 	var undefined,
9290 		CrossDomainRequestProto,
9291 		CrossDomainResponseProto,
9292 		net = glow.net,
9293 		// We borrow some methods from XhrRequest later
9294 		XhrResponseProto = net.XhrRequest.prototype,
9295 		Target = glow.events.Target;
9296 
9297 	/**
9298 		@name glow.net.CrossDomainRequest
9299 		@class
9300 		@description Cross-domain request via window.name
9301 			A request made via a form submission in a hidden iframe, with the
9302 			result being communicated via the name attribute of the iframe's window.
9303 			
9304 			The URL that's requested should respond with a blank HTML page containing JavaScript
9305 			that assigns the result to window.name as a string:
9306  
9307 			`<script type="text/javascript">window.name = 'result string';</script>`
9308 			
9309 			Instances of this are returned by shortcut methods {@link glow.net.crossDomainGet}
9310 			and {@link glow.net.crossDomainPost}
9311 	
9312 		@param {string} method The HTTP method to use for the request.
9313 			Only 'POST' and 'GET' are considered cross-browser.
9314 		@param {string} url The URL to request.
9315 		@param {Object} [opts]
9316 			@param {Object|string} [opts.data] Data to send.
9317 				This can be either a JSON-style object or a urlEncoded string.
9318 			@param {number} [opts.timeout] Time to allow for the request in seconds.
9319 				No timeout is set by default.
9320 			@param {string} [opts.blankUrl='/favicon.ico']
9321 				The path of a page on same domain as the caller, ideally a page likely
9322 				to be in the user's cache.
9323 	*/
9324 	function CrossDomainRequest(method, url, opts) {
9325 		var request = this,
9326 			timeout;
9327 		
9328 		request._opts = opts = glow.util.apply({
9329 			data: {},
9330 			blankUrl: '/favicon.ico'
9331 		}, opts);
9332 		
9333 		// convert data to object
9334 		if (typeof opts.data === 'string') {
9335 			opts.data = glow.util.decodeUrl(opts.data);
9336 		}
9337 		
9338 		// set timeout for the request
9339 		timeout = opts.timeout;
9340 		if (timeout) {
9341 			request._timeout = setTimeout(function () {				
9342 				request.fire('error');					
9343 				cleanup(request);
9344 			}, timeout * 1000);
9345 		}
9346 		
9347 		addIframe(request);
9348 		buildAndSubmitForm(request, method, url);
9349 	}
9350 	glow.util.extend(CrossDomainRequest, Target);
9351 	CrossDomainRequestProto = CrossDomainRequest.prototype;
9352 	
9353 	/**
9354 		@name glow.net.CrossDomainRequest#_opts
9355 		@private
9356 		@type Object
9357 		@description Options object with defaults applied
9358 	*/
9359 	
9360 	/**
9361 		@name glow.net.CrossDomainRequest#_iframe
9362 		@private
9363 		@type glow.NodeList
9364 		@description Iframe used to send the data.
9365 	*/
9366 	
9367 	/**
9368 		@name glow.net.CrossDomainRequest#_timeout
9369 		@private
9370 		@type number
9371 		@description setTimeout id for request timeout
9372 	*/
9373 
9374 	/**
9375 		@private
9376 		@function
9377 		@description Add a hidden iframe for posting the request
9378 		@param {glow.net.CrossDomainRequest} request
9379 	*/
9380 	function addIframe(request) {
9381 		var iframe = request._iframe = glow(
9382 			'<iframe style="visibility: hidden; position: absolute; height: 0;"></iframe>'
9383 		).appendTo(document.body);
9384 	};
9385 		
9386 	/**
9387 		@private
9388 		@function
9389 		@description Add a form to the iframe & submit it
9390 		
9391 		@param {glow.net.CrossDomainRequest} request
9392 		@param {string} method The HTTP method to use for the request.
9393 			Only 'POST' and 'GET' are considered cross-browser.
9394 		@param {string} url The URL to request.
9395 	*/
9396 	function buildAndSubmitForm(request, method, url) {
9397 		var iframe = request._iframe,
9398 			win = iframe[0].contentWindow,
9399 			doc = win.document,
9400 			form,
9401 			data = request._opts.data;
9402 
9403 		// IE needs an empty document to be written to written to the iframe
9404 		if (glow.env.ie) {
9405 			doc.open();
9406 			doc.write('<html><body></body></html>');
9407 			doc.close();
9408 		}
9409 		
9410 		// create form
9411 		form = doc.createElement('form');
9412 		form.action = url;
9413 		form.method = method;
9414 
9415 		doc.body.appendChild(form);
9416 		
9417 		// build form elements
9418 		for (var i in data) {
9419 			if ( !data.hasOwnProperty(i) ) { continue; }
9420 			
9421 			if (data[i] instanceof Array) {
9422 				for (var j = 0, jLen = data[i].length; j < jLen; j++) {
9423 					addHiddenInput( form, i, this.data[i][j] );
9424 				}
9425 			}
9426 			else {
9427 				addHiddenInput( form, i, this.data[i] );
9428 			}
9429 		}
9430 		
9431 		// submit - the setTimeout makes the function run in the context of the form
9432 		win.setTimeout(function () {
9433 			form.submit();
9434 		}, 0);
9435 		
9436 		// listen for form submitting
9437 		iframe.on('load', handleResponse, request);
9438 	}
9439 
9440 	/**
9441 		@private
9442 		@function
9443 		@description Add a hidden input to a form for a piece of data.
9444 		
9445 		@param {HTMLFormElement} form
9446 		@param {string} name Input name
9447 		@param {string} value Input value
9448 	*/
9449 	function addHiddenInput(form, name, value) {
9450 		var input = form.ownerDocument.createElement('input');
9451 		input.type = 'hidden';
9452 		input.name = name;
9453 		input.value = value;
9454 		form.appendChild(input);
9455 	}
9456 
9457 	/**
9458 		@private
9459 		@function
9460 		@description Callback for load event in the hidden iframe.
9461 			`this` is the request.
9462 	*/
9463 	function handleResponse() {		
9464 		var err,
9465 			href,
9466 			win = this._iframe[0].contentWindow;
9467 		
9468 		try {
9469 			href = win.location.href;
9470 		}
9471 		catch (e) {
9472 			err = e;
9473 		}
9474 		
9475 		if (href !== 'about:blank' || err) {
9476 			clearTimeout(this._timeout);
9477 			this._iframe.detach('load', handleResponse).on('load', readHandler, this);
9478 			
9479 			win.location = window.location.protocol + '//' + window.location.host + this._opts.blankUrl;
9480 		}
9481 	}
9482 
9483 	/**
9484 		@private
9485 		@function
9486 		@description Callback for load event of blank page in same origin.
9487 			`this` is the request.
9488 	*/
9489 	function readHandler() {
9490 		this.fire( 'load', new CrossDomainResponse(this._iframe[0].contentWindow.name) );			
9491 		cleanup(this);			
9492 	}
9493 			
9494 	/**
9495 		@private
9496 		@function
9497 		@description Removes the iframe and any event listeners.
9498 		@param {glow.net.CrossDomainRequest} request
9499 	*/
9500 	function cleanup(request) {
9501 		request._iframe.destroy();
9502 		glow.events.removeAllListeners(request);
9503 	}
9504 	
9505 	/**
9506 		@name glow.net.CrossDomainRequest#event:load
9507 		@event
9508 		@param {glow.net.CrossDomainResponse} response
9509 		@description Fired when the request is sucessful.
9510 	*/
9511  
9512 	/**
9513 		@name glow.net.CrossDomainRequest#event:error
9514 		@event
9515 		@param {glow.events.Event} event Event Object
9516 		@description Fired when the request times out.
9517 	*/
9518 	
9519 	/**
9520 		@name glow.net.CrossDomainResponse
9521 		@class
9522 		@description Response object for cross-domain requests.
9523 			This is provided in {@link glow.net.CrossDomainRequest}'s 'load' event.
9524 		@glowPrivateConstructor There is no direct constructor.
9525 	*/
9526 	function CrossDomainResponse(textResponse) {
9527 		this._text = textResponse;
9528 	}
9529 	glow.util.extend(CrossDomainResponse, Target);
9530 	CrossDomainResponseProto = CrossDomainResponse.prototype;
9531 	
9532 	/**
9533 		@name glow.net.CrossDomainResponse#_text
9534 		@private
9535 		@type string
9536 		@description Text response from the server
9537 	*/
9538 	
9539 	/**
9540 		@name glow.net.CrossDomainResponse#text
9541 		@function
9542 		@description Gets the body of the response as plain text.
9543 		@returns {string}
9544 	*/
9545 	CrossDomainResponseProto.text = function() {
9546 		return this._text;
9547 	}
9548 	
9549 	/**
9550 		@name glow.net.CrossDomainResponse#json
9551 		@function
9552 		@description Gets the body of the response as a JSON object.
9553 		
9554 		@param {boolean} [safeMode=false]
9555 			If true, the response will be parsed using a string parser which
9556 			will filter out non-JSON javascript, this will be slower but
9557 			recommended if you do not trust the data source.
9558 	
9559 		@returns {object}
9560 	*/
9561 	CrossDomainResponseProto.json = XhrResponseProto.json;
9562 	
9563 	/**
9564 		@name glow.net.CrossDomainResponse#nodeList
9565 		@function
9566 		@description Gets the body of the response as a {@link glow.NodeList}.
9567 	
9568 		@returns {glow.NodeList}
9569 	*/
9570 	CrossDomainResponseProto.nodeList = XhrResponseProto.nodeList;
9571 	
9572 	// ...and now, the factory methods! Yey!
9573 	
9574 	/**
9575 		@name glow.net.crossDomainPost
9576 		@function
9577 		@description Cross-domain post via window.name
9578 			A request made via a form submission in a hidden iframe, with the
9579 			result being communicated via the name attribute of the iframe's window.
9580 			
9581 			The URL that's requested should respond with a blank HTML page containing JavaScript
9582 			that assigns the result to window.name as a string:
9583  
9584 			`<script type="text/javascript">window.name = 'result string';</script>`
9585 
9586 		@param {string} url The URL to request.
9587 		@param {Object|string} data Data to send.
9588 			This can be either a JSON-style object or a urlEncoded string.
9589 		@param {Object} [opts]
9590 			@param {number} [opts.timeout] Time to allow for the request in seconds.
9591 				No timeout is set by default.
9592 			@param {string} [opts.blankUrl='/favicon.ico']
9593 				The path of a page on same domain as the caller, ideally a page likely
9594 				to be in the user's cache.
9595 	*/
9596 	
9597 	net.crossDomainPost = function(url, data, opts) {
9598 		opts = opts || {};
9599 		opts.data = data;
9600 		return new CrossDomainRequest('POST', url, opts);
9601 	};
9602 	
9603 	
9604 	/**
9605 		@name glow.net.crossDomainGet
9606 		@function
9607 		@description Cross-domain get via window.name
9608 			A request made via a form submission in a hidden iframe, with the
9609 			result being communicated via the name attribute of the iframe's window.
9610 			
9611 			The URL that's requested should respond with a blank HTML page containing JavaScript
9612 			that assigns the result to window.name as a string:
9613  
9614 			`<script type="text/javascript">window.name = 'result string';</script>`
9615 	
9616 		@param {string} url The URL to request.
9617 		@param {Object} [opts]
9618 			@param {number} [opts.timeout] Time to allow for the request in seconds.
9619 				No timeout is set by default.
9620 			@param {string} [opts.blankUrl='/favicon.ico']
9621 				The path of a page on same domain as the caller, ideally a page likely
9622 				to be in the user's cache.
9623 	*/
9624 	
9625 	net.crossDomainGet = function(url, opts) {
9626 		return new CrossDomainRequest('GET', url, opts);
9627 	};
9628 
9629 	// export
9630 	glow.net.CrossDomainRequest  = CrossDomainRequest;
9631 	glow.net.CrossDomainResponse = CrossDomainResponse;
9632 });
9633 Glow.provide(function(glow) {
9634 	var tweens = glow.tweens = {};
9635 	/**
9636 	@name glow.tweens
9637 	@namespace
9638 	@description Functions for controlling the motion of an animation
9639 	*/
9640 	
9641 	/*
9642 	@name _reverse
9643 	@private
9644 	@description Takes a tween function and returns a function which does the reverse
9645 	*/
9646 	function _reverse(tween) {
9647 		return function(t) {
9648 			return 1 - tween(1 - t);
9649 		}
9650 	}
9651 	
9652 	/**
9653 	@name glow.tweens.linear
9654 	@function
9655 	@description Creates linear tween.	
9656 		Will transition values from start to finish with no
9657 		acceleration or deceleration.
9658 	
9659 	@returns {function}
9660 	*/
9661 	tweens.linear = function() {
9662 		return function(t) { return t; };
9663 	};
9664 	
9665 	/**
9666 	@name glow.tweens.easeIn
9667 	@function
9668 	@description Creates a tween which starts off slowly and accelerates.
9669 	
9670 	@param {number} [strength=2] How strong the easing will be.
9671 	
9672 		The higher the number the slower the animation starts and the quicker it ends.
9673 	
9674 	@returns {function}
9675 	*/
9676 	tweens.easeIn = function(strength) {
9677 		strength = strength || 2;
9678 		return function(t) {
9679 			return Math.pow(1, strength - 1) * Math.pow(t, strength);
9680 		}	
9681 	};
9682 	
9683 	
9684 	/**
9685 	@name glow.tweens.easeOut
9686 	@function
9687 	@description Creates a tween which starts off fast and decelerates.
9688 	
9689 	@param {number} [strength=2] How strong the easing will be.
9690 	
9691 		The higher the number the quicker the animation starts and the slower it ends.
9692 	
9693 	@returns {function}
9694 	*/
9695 	tweens.easeOut = function(strength) {
9696 		return _reverse(this.easeIn(strength));
9697 	};
9698 	
9699 	
9700 	/**
9701 	@name glow.tweens.easeBoth
9702 	@function
9703 	@description Creates a tween which starts off slowly, accelerates then decelerates after the half way point.
9704 	
9705 		This produces a smooth and natural looking transition.
9706 	
9707 	@param {number} [strength=2] How strong the easing is.
9708 	
9709 		A higher number produces a greater difference between
9710 		start/end speed and the mid speed.
9711 	
9712 	@returns {function}
9713 	*/
9714 	tweens.easeBoth = function(strength) {
9715 		return this.combine(this.easeIn(strength), this.easeOut(strength));
9716 	};
9717 	
9718 	
9719 	/**
9720 	@name glow.tweens.overshootIn
9721 	@function
9722 	@description Returns the reverse of {@link glow.tweens.overshootOut overshootOut}
9723 	
9724 	@param {number} [amount=1.70158] How much to overshoot.
9725 	
9726 		The default is 1.70158 which results in a 10% overshoot.
9727 	
9728 	@returns {function}
9729 	*/
9730 	tweens.overshootIn = function(amount) {
9731 		return _reverse(this.overshootOut(amount));
9732 	};
9733 	
9734 	
9735 	/**
9736 	@name glow.tweens.overshootOut
9737 	@function
9738 	@description Creates a tween which overshoots its end point then returns to its end point.
9739 	
9740 	@param {number} [amount=1.70158] How much to overshoot.
9741 	
9742 		The default is 1.70158 which results in a 10% overshoot.
9743 	
9744 	@returns {function}
9745 	*/
9746 	tweens.overshootOut = function(amount) {
9747 		amount = amount || 1.70158;
9748 		return function(t) {
9749 			if (t == 0 || t == 1) { return t; }
9750 				return ((t -= 1)* t * ((amount + 1) * t + amount) + 1);
9751 			}
9752 	};
9753 	
9754 	
9755 	/**
9756 	@name glow.tweens.overshootBoth
9757 	@function
9758 	@description Returns a combination of {@link glow.tweens.overshootIn overshootIn} and {@link glow.tweens.overshootOut overshootOut}
9759 	
9760 	@param {number} [amount=1.70158] How much to overshoot.
9761 	
9762 		The default is 1.70158 which results in a 10% overshoot.
9763 	
9764 	@returns {function}
9765 	*/
9766 	tweens.overshootBoth = function(amount) {
9767 		return this.combine(this.overshootIn(amount), this.overshootOut(amount));	
9768 	};
9769 	
9770 	
9771 	/**
9772 	@name glow.tweens.bounceIn
9773 	@function
9774 	@description Returns the reverse of {@link glow.tweens.bounceOut bounceOut}
9775 	
9776 	@returns {function}
9777 	*/
9778 	tweens.bounceIn = function() {
9779 		return _reverse(this.bounceOut());
9780 	};
9781 	
9782 	
9783 	/**
9784 	@name glow.tweens.bounceOut
9785 	@function
9786 	@description Returns a tween which bounces against the final value 3 times before stopping
9787 	
9788 	@returns {function}
9789 	*/
9790 	tweens.bounceOut = function() {
9791 		return function(t) {
9792 			if (t < (1 / 2.75)) {
9793 				return 7.5625 * t * t;
9794 			}
9795 			
9796 			else if (t < (2 / 2.75)) {
9797 				return (7.5625 * (t -= (1.5 / 2.75)) * t + .75);
9798 			}
9799 			
9800 			else if (t < (2.5 / 2.75)) {
9801 				return (7.5625 * (t -= (2.25 / 2.75)) * t + .9375);
9802 			}
9803 			
9804 			else {
9805 				return (7.5625 * (t -= (2.625 / 2.75)) * t + .984375);
9806 			}
9807 		};	
9808 	};
9809 		
9810 	
9811 	/**
9812 	@name glow.tweens.elasticIn
9813 	@function
9814 	@description Returns the reverse of {@link glow.tweens.elasticOut elasticOut}
9815 	
9816 	@param {number} [amplitude=1] How strong the elasticity will be.
9817 	
9818 	@param {number} [frequency=3.33] The frequency.
9819 	
9820 	@returns {function}
9821 	*/
9822 	tweens.elasticIn = function(amplitude, frequency) {
9823 		return _reverse(this.elasticOut(amplitude, frequency));
9824 	};
9825 	
9826 	
9827 	/**
9828 	@name glow.tweens.elasticOut
9829 	@function
9830 	@description Creates a tween which has an elastic movement.
9831 	
9832 		You can tweak the tween using the parameters but you'll
9833 		probably find the defaults sufficient.
9834 	
9835 	@param {number} [amplitude=1] How strong the elasticity is.
9836 	
9837 	@param {number} [frequency=3.33] The frequency.
9838 	
9839 	@returns {function}
9840 	*/
9841 	tweens.elasticOut = function(amplitude, frequency) {
9842 		var period = 1 / (frequency || 10 / 3);
9843 		amplitude = amplitude || 1;
9844 		return function (t) {
9845 			var s;
9846 			if (t == 0 || t == 1) {
9847 				return t;
9848 			}
9849 			if (amplitude < 1) {
9850 				s = period / 4;
9851 			}
9852 			else {
9853 				s = period / (2 * Math.PI) * Math.asin(1 / amplitude);
9854 			}
9855 			return amplitude * Math.pow(2, -10 * t) * Math.sin( (t-s) * (2 * Math.PI) / period) + 1;
9856 		}
9857 	};
9858 		
9859 	
9860 	/**
9861 	@name glow.tweens.combine
9862 	@function
9863 	@description Create a tween from two tweens.
9864 	
9865 		This can be useful to make custom tweens which, for example,
9866 		start with an easeIn and end with an overshootOut. To keep
9867 		the motion natural, you should configure your tweens so the
9868 		first ends and the same velocity that the second starts.
9869 	
9870 	@param {function} tweenIn Tween to use for the first half
9871 	
9872 	@param {function} tweenOut Tween to use for the second half
9873 	
9874 	@example
9875 		// 4.5 has been chosen for the easeIn strength so it
9876 		// ends at the same velocity as overshootOut starts.
9877 		var myTween = glow.tweens.combine(
9878 			glow.tweens.easeIn(4.5),
9879 			glow.tweens.overshootOut()
9880 		);
9881 	
9882 	@returns {function}
9883 	*/
9884 	tweens.combine = function(tweenIn, tweenOut) {
9885 		return function (t) {
9886 			if (t < 0.5) {
9887 				return tweenIn(t * 2) / 2;
9888 			}
9889 			else {
9890 				return tweenOut((t - 0.5) * 2) / 2 + 0.5;
9891 			}
9892 		}	
9893 	}
9894 	
9895 });
9896 
9897 /**
9898 	@name glow.anim
9899 	@namespace
9900 	@description Creating and synchronising animations
9901 */
9902 Glow.provide(function(glow) {
9903 	var undefined,
9904 		AnimProto,
9905 		activeAnims = [],
9906 		activeAnimsLen = 0,
9907 		animInterval;
9908 	
9909 	/**
9910 		@private
9911 		@function
9912 		@description This is called on each interval
9913 			This set the properties of each animation per frame.
9914 			
9915 			This is the drill sgt of the Anim world.
9916 	*/
9917 	function onInterval() {
9918 		var dateNum = new Date().valueOf(),
9919 			i = activeAnimsLen,
9920 			anim;
9921 		
9922 		while (i--) {
9923 			// ideally, this processing would be a function of Anim, but it's quicker this way
9924 			anim = activeAnims[i];
9925 			anim.position = (dateNum - anim._syncTime) / 1000;
9926 			
9927 			// see if this animation is ready to complete
9928 			if (anim.position >= anim.duration) {
9929 				anim.position = anim.duration;
9930 				anim.value = anim.tween(1);
9931 				// render final frame
9932 				anim.fire('frame');
9933 				// fire 'frame' and 'complete' and see if we're going to loop (preventing default)
9934 				if ( anim.fire('complete').defaultPrevented() || anim.loop ) {
9935 					// loop the animation
9936 					anim._syncTime = dateNum;
9937 				}
9938 				// else deactivave the anim
9939 				else {
9940 					// reset the stop position so further starts start from the beginning
9941 					anim._stopPos = 0;
9942 					deactivateAnim(anim);
9943 					// destroy the anim if needed
9944 					anim.destroyOnComplete && anim.destroy();
9945 				}
9946 			}
9947 			else {
9948 				// set up the value and render a frame
9949 				anim.value = anim.tween( anim.position / anim.duration );
9950 				anim.fire('frame');
9951 			}
9952 		}
9953 	}
9954 	
9955 	/**
9956 		@private
9957 		@function
9958 		@description Calls 'frame' on an animation on an interval	
9959 	*/
9960 	function activateAnim(anim) {
9961 		// if this is the first anim, start the timer
9962 		if (!activeAnimsLen) {
9963 			animInterval = setInterval(onInterval, 13);
9964 		}
9965 		activeAnims[ activeAnimsLen++ ] = anim;
9966 		anim.playing = true;
9967 	}
9968 	
9969 	/**
9970 		@private
9971 		@function
9972 		@description Stops calling 'frame' on an animation on an interval
9973 	*/
9974 	function deactivateAnim(anim) {
9975 		// decided to search forward, animations ending are more likely to be older & at the start of the array.
9976 		// This mutates activeAnims
9977 		for (var i = 0, leni = activeAnims.length; i < leni; i++) {
9978 			if (activeAnims[i] === anim) {
9979 				activeAnims.splice(i, 1);
9980 				activeAnimsLen--;
9981 				// if we're out of anims, stop the timer
9982 				if (!activeAnimsLen) {
9983 					clearInterval(animInterval);
9984 				}
9985 				anim.playing = false;
9986 				return;
9987 			}
9988 		}
9989 	}
9990 	
9991 	/**
9992 		@name glow.anim.Anim
9993 		@extends glow.events.Target
9994 		@class
9995 		@description Animate an object.
9996 			To animate CSS properties, see {@link glow.NodeList#anim}.
9997 			
9998 			Once you have an Anim instance, the {@link glow.anim.Anim#prop} method
9999 			can be used to easily animate object properties from one value to another.
10000 			If this isn't suitable, listen for the 'frame' event to change values
10001 			over time.
10002 			
10003 		@param {number} duration Length of the animation in seconds.
10004 		@param {Object} [opts] Object of options.
10005 		@param {function|string} [opts.tween='easeBoth'] The way the value changes over time.
10006 			Strings are treated as properties of {@link glow.tweens} (eg 'bounceOut'), although
10007 			a tween function can be provided.
10008 
10009 			The default is an {@link glow.tweens.easeBoth easeBoth} tween.
10010 			Looped animations will fire a 'complete' event on each loop.
10011 		@param {boolean} [opts.destroyOnComplete=true] Destroy the animation once it completes (unless it loops).
10012 			Shortcut for {@link glow.anim.Anim#destroyOnComplete}.
10013 		@param {boolean} [opts.loop=false] Loop the animation.
10014 			Shortcut for setting {@link glow.anim.Anim#loop}.
10015 			
10016 		@example
10017 			// Using glow.anim.Anim to animate an SVG blur over 5 seconds, with an easeOut tween
10018 			// feGaussianBlurElm is a reference to an <feGaussianBlur /> element.
10019 			new glow.anim.Anim(5, {
10020 				tween: 'easeOut'
10021 			}).target(feGaussianBlurElm).prop('stdDeviation', {
10022 				from: 0,
10023 				to: 8
10024 			}).start();
10025 			
10026 		@example
10027 			// Animate a CSS property we don't support in glow.NodeList#anim
10028 			// This rotates a Mozilla CSS gradient
10029 			var styleObject = glow('#nav').prop('style');
10030 			
10031 			new glow.anim.Anim(10).target(styleObject).prop('background', {
10032 				// the question-mark in the template is replaced with the animated value
10033 				template: '-moz-linear-gradient(?deg, red, blue)'
10034 				from: 0,
10035 				to: 360
10036 			}).start();
10037 			
10038 		@example
10039 			// Animate a CSS property we don't support in glow.NodeList#anim
10040 			// This changes the colour of a webkit drop shadow from yellow to blue
10041 			var styleObject = glow('#nav').prop('style');
10042 			
10043 			new glow.anim.Anim(3).target(styleObject).prop('WebkitBoxShadow', {
10044 				// the ? in the template are replaced with the animate values
10045 				template: 'rgb(?, ?, ?) 0px 4px 14px'
10046 				// provide a 'from' and 'to' value for each question-mark
10047 				from: [255, 255, 0],
10048 				to: [0, 0, 255],
10049 				// round the value, colours can't be fractional
10050 				round: true
10051 			}).start();
10052 			
10053 		@example
10054 			// Make an ASCII progress bar animate from:
10055 			// [--------------------] 0%
10056 			// to
10057 			// [++++++++++++++++++++] 100%
10058 			var progressBar = glow('#progressBar'),
10059 				// our progress bar is 20 chars
10060 				barSize = 20;
10061 				
10062 			new glow.anim.Anim(2).on('frame', function() {
10063 				var onChars = Math.floor(this.value * barSize),
10064 					offChars = barSize - onChars,
10065 					// add the + and - chars
10066 					barStr = new Array(onChars + 1).join('+') + new Array(offChars + 1).join('-');
10067 				
10068 				progressBar.text('[' + barStr + '] ' + Math.floor(this.value * 100) + '%');
10069 			}).start();
10070 
10071 		@see {@link glow.NodeList#anim} - shortcut for animating CSS values on an element.
10072 	*/
10073 	
10074 	function Anim(duration, opts) {
10075 		/*!debug*/
10076 			if (arguments.length < 1 || arguments.length > 2) {
10077 				glow.debug.warn('[wrong count] glow.anim.Anim expects 1 or 2 arguments, not ' + arguments.length + '.');
10078 			}
10079 			if ( isNaN(duration) ) {
10080 				glow.debug.warn('[wrong type] glow.anim.Anim expects number as "duration" argument, not ' + typeof duration + '.');
10081 			}
10082 			if (opts !== undefined && typeof opts !== 'object') {
10083 				glow.debug.warn('[wrong type] glow.anim.Anim expects object as "opts" argument, not ' + typeof opts + '.');
10084 			}
10085 			if ( opts && typeof opts.tween === 'string' && !glow.tweens[opts.tween] ) {
10086 				glow.debug.warn('[unexpected value] glow.anim.Anim - tween ' + opts.tween + ' does not exist');
10087 			}
10088 		/*gubed!*/
10089 		
10090 		opts = glow.util.apply({
10091 			destroyOnComplete: true
10092 			// other options have falsey defaults
10093 		}, opts || {});
10094 		
10095 		this.destroyOnComplete = opts.destroyOnComplete;
10096 		
10097 
10098 		if (typeof opts.tween === 'string') {
10099 			this.tween = glow.tweens[opts.tween]();
10100 		}
10101 		else if (opts.tween) {
10102 			this.tween = opts.tween;
10103 		}
10104 		
10105 		this.loop = !!opts.loop;
10106 		this.duration = +duration;
10107 		// defined & used in prop.js
10108 		this._targets = [];
10109 	};
10110 	
10111 	glow.util.extend(Anim, glow.events.Target);
10112 	AnimProto = Anim.prototype;
10113 	
10114 	/**
10115 		@name glow.anim.Anim#_syncTime
10116 		@private
10117 		@type number
10118 		@description Number used to work out where the animation should be against the current date
10119 			If an animation starts at 0, this number will be new Date().valueOf(), it'll be
10120 			lower for animations that start at a midpoint
10121 	*/
10122 	
10123 	/**
10124 		@name glow.anim.Anim#_stopPos
10125 		@private
10126 		@type number
10127 		@description The position the animation was stopped at
10128 			This is set on `.stop()` and used to resume from
10129 			the same place on `.start()`
10130 	*/
10131 	
10132 	/**
10133 		@name glow.anim.Anim#duration
10134 		@type number
10135 		@description Length of the animation in seconds.
10136 	*/
10137 	
10138 	/**
10139 		@name glow.anim.Anim#tween
10140 		@type function
10141 		@description The tween used by the animation.
10142 	*/
10143 	AnimProto.tween = glow.tweens.easeBoth();
10144 	
10145 	/**
10146 		@name glow.anim.Anim#position
10147 		@readOnly
10148 		@type number
10149 		@description Position of the animation in seconds.
10150 	*/
10151 	AnimProto.position = 0;
10152 	
10153 	/**
10154 		@name glow.anim.Anim#playing
10155 		@readOnly
10156 		@type boolean
10157 		@description `true` if the animation is playing.
10158 	*/
10159 	AnimProto.playing = false;
10160 	
10161 	/**
10162 		@name glow.anim.Anim#loop
10163 		@type boolean
10164 		@description Loop the animation?
10165 			This value can be changed while an animation is playing.
10166 			
10167 			Looped animations will fire a 'complete' event at the end of each loop.
10168 	*/
10169 	
10170 	/**
10171 		@name glow.anim.Anim#destroyOnComplete
10172 		@type boolean
10173 		@description Destroy the animation once it completes (unless it loops).
10174 			This will free any DOM references the animation may have created. Once
10175 			the animation is destroyed, it cannot be started again.
10176 	*/
10177 	
10178 	/**
10179 		@name glow.anim.Anim#value
10180 		@type number
10181 		@readOnly
10182 		@description Current tweened value of the animation, usually between 0 & 1.
10183 			This can be used in frame events to change values between their start
10184 			and end value.
10185 			
10186 			The value may be greater than 1 or less than 0 if the tween
10187 			overshoots the start or end position. {@link glow.tweens.elasticOut}
10188 			for instance will result in values higher than 1, but will still end at 1.
10189 		
10190 		@example
10191 			// Work out a value between startValue & endValue for the current point in the animation
10192 			var currentValue = (endValue - startValue / myAnim.value) + startValue;
10193 	*/
10194 	AnimProto.value = 0;
10195 	
10196 	/**
10197 		@name glow.anim.Anim#start
10198 		@function
10199 		@description Starts playing the animation
10200 			If the animation is already playing, this has no effect.
10201 		
10202 		@param {number} [position] Position to start the animation at, in seconds.
10203 			By default, this will be the last position of the animation (if it was stopped)
10204 			or 0.
10205 		
10206 		@returns this
10207 	*/
10208 	AnimProto.start = function(position) {
10209 		/*!debug*/
10210 			if (arguments.length > 1) {
10211 				glow.debug.warn('[wrong count] glow.anim.Anim#start expects 0 or 1 argument, not ' + arguments.length + '.');
10212 			}
10213 			if (position !== undefined && typeof position !== 'number') {
10214 				glow.debug.warn('[wrong type] glow.anim.Anim#start expects number as "position" argument, not ' + typeof position + '.');
10215 			}
10216 		/*gubed!*/
10217 		
10218 		if ( !this.playing && !this.fire('start').defaultPrevented() ) {
10219 			// we set 'playing' here so goTo knows
10220 			this.playing = true;
10221 			this.goTo(position === undefined ? (this._stopPos || 0) : position);
10222 			activateAnim(this);
10223 		}
10224 		return this;
10225 	};
10226 	
10227 	/**
10228 		@name glow.anim.Anim#stop
10229 		@function
10230 		@description Stops the animation playing.
10231 			Stopped animations can be resumed by calling {@link glow.anim.Anim#start start}.
10232 			
10233 			If the animation isn't playing, this has no effect.
10234 		@returns this
10235 	*/
10236 	AnimProto.stop = function() {
10237 		/*!debug*/
10238 			if (arguments.length !== 0) {
10239 				glow.debug.warn('[wrong count] glow.anim.Anim#stop expects 0 arguments, not ' + arguments.length + '.');
10240 			}
10241 		/*gubed!*/
10242 		if ( this.playing && !this.fire('stop').defaultPrevented() ) {
10243 			this._stopPos = this.position;
10244 			deactivateAnim(this);
10245 		}
10246 		return this;
10247 	};
10248 	
10249 	/**
10250 		@name glow.anim.Anim#destroy
10251 		@function
10252 		@description Destroys the animation & detaches references to objects
10253 			This frees memory & is called automatically when an animation
10254 			completes.
10255 		@returns undefined
10256 	*/
10257 	AnimProto.destroy = function() {
10258 		/*!debug*/
10259 			if (arguments.length !== 0) {
10260 				glow.debug.warn('[wrong count] glow.anim.Anim#destroy expects 0 arguments, not ' + arguments.length + '.');
10261 			}
10262 		/*gubed!*/
10263 		glow.events.removeAllListeners( [this] );
10264 		this._targets = undefined;
10265 	};
10266 	
10267 	/**
10268 		@name glow.anim.Anim#goTo
10269 		@function
10270 		@description Goes to a specific point in the animation.
10271 		@param {number} pos Position in the animation to go to, in seconds
10272 
10273 		@example
10274 			// move the animation to 2.5 seconds in
10275 			// If the animation is playing, it will continue to play from the new position.
10276 			// Otherwise, it will simply move to that position.
10277 			myAnim.goTo(2.5);
10278 			
10279 		@returns this
10280 	*/
10281 	AnimProto.goTo = function(position) {
10282 		/*!debug*/
10283 			if (arguments.length !== 1) {
10284 				glow.debug.warn('[wrong count] glow.anim.Anim#goTo expects 1 argument, not ' + arguments.length + '.');
10285 			}
10286 			if (typeof position !== 'number') {
10287 				glow.debug.warn('[wrong type] glow.anim.Anim#goTo expects number as "position" argument, not ' + typeof position + '.');
10288 			}
10289 		/*gubed!*/
10290 		if (position > this.duration) {
10291 			position = this.duration;
10292 		}
10293 		else if (position < 0) {
10294 			position = 0;
10295 		}
10296 		// set stopPos to this so the next call to start() starts from here
10297 		this._stopPos = this.position = position;
10298 		// move the syncTime for this position if we're playing
10299 		if (this.playing) {
10300 			this._syncTime = new Date - (position * 1000);
10301 		}
10302 		this.value = this.tween(position / this.duration);
10303 		this.fire('frame');
10304 		return this;
10305 	};
10306 	
10307 	/**
10308 		@name glow.anim.Anim#event:start
10309 		@event
10310 		@description Fires when an animation starts.
10311 			Preventing this event (by returning false or calling {@link glow.events.Event#preventDefault preventDefault})
10312 			prevents this animation from starting.
10313 		
10314 		@param {glow.events.Event} event Event Object
10315 	*/
10316 	
10317 	/**
10318 		@name glow.anim.Anim#event:frame
10319 		@event
10320 		@description Fires on each frame of the animation
10321 			Use a combination of this event and {@link glow.anim.Anim#value value}
10322 			to create custom animations.
10323 			
10324 			See the {@link glow.anim.Anim constructor} for usage examples.
10325 		
10326 		@param {glow.events.Event} event Event Object
10327 	*/
10328 	
10329 	/**
10330 		@name glow.anim.Anim#event:stop
10331 		@event
10332 		@description Fires when an animation is stopped before completing
10333 			Preventing this event (by returning false or calling {@link glow.events.Event#preventDefault preventDefault})
10334 			prevents this animation from stopping.
10335 		
10336 		@param {glow.events.Event} event Event Object
10337 	*/
10338 	
10339 	/**
10340 		@name glow.anim.Anim#event:complete
10341 		@event
10342 		@description Fires when an animation completes
10343 			Preventing this event (by returning false or calling {@link glow.events.Event#preventDefault preventDefault})
10344 			causes the animation to loop.
10345 		
10346 		@param {glow.events.Event} event Event Object
10347 		
10348 		@example
10349 			// Make an animation loop 5 times
10350 			var loopCount = 5;
10351 			myAnim.on('complete', function() {
10352 				return !loopCount--;
10353 			});
10354 	*/
10355 	
10356 	// export
10357 	glow.anim = {};
10358 	glow.anim.Anim = Anim;
10359 });
10360 Glow.provide(function(glow) {
10361 	/**
10362 		@name glow.anim.Anim#_evalFunc
10363 		@function
10364 		@private
10365 		@description  Evals a function to be used as a frame listener
10366 			This function is isolated from the others to reduce the impact of
10367 			eval() on compression and garbage collection
10368 			
10369 			'targets' is used by the compiled function
10370 	*/
10371 	glow.anim.Anim.prototype._evalFunc = function evalFunc(s, targets) {
10372 		eval('var f=function(){' + s + '}');
10373 		return f;
10374 	}
10375 });
10376 
10377 Glow.provide(function(glow) {
10378 	var undefined,
10379 		AnimProto = glow.anim.Anim.prototype;
10380 	
10381 	/**
10382 		@name glow.anim.Anim#_targets
10383 		@private
10384 		@type Object[]
10385 		@description An array of objects added via #target
10386 	*/
10387 	
10388 	/**
10389 		@name glow.anim.Anim#target
10390 		@function
10391 		@description Set the object for subsequent calls to {@link glow.anim.Anim#prop prop} to act on.
10392 		@param {Object} newTarget The target object
10393 			
10394 		@returns this
10395 		
10396 		@example
10397 			// animate objToAnimate.value from 0 to 10 over 3 seconds
10398 			// and anotherObjToAnimate.data from -100 to 20 over 3 seconds
10399 		
10400 			var objToAnimate = {},
10401 				anotherObjToAnimate = {};
10402 		
10403 			new glow.anim.Anim(3).target(objToAnimate).prop('value', {
10404 				from: 0,
10405 				to: 10
10406 			}).target(anotherObjToAnimate).prop('data', {
10407 				from: 100,
10408 				to: -20
10409 			})
10410 	*/
10411 	AnimProto.target = function(newTarget) {
10412 		/*!debug*/
10413 			if (arguments.length !== 1) {
10414 				glow.debug.warn('[wrong count] glow.anim.Anim#target expects 1 argument, not ' + arguments.length + '.');
10415 			}
10416 			if (typeof newTarget !== 'object') {
10417 				glow.debug.warn('[wrong type] glow.anim.Anim#target expects object as "newTarget" argument, not ' + typeof newTarget + '.');
10418 			}
10419 		/*gubed!*/
10420 		this._targets[ this._targets.length ] = newTarget;
10421 		return this;
10422 	};
10423 	
10424 	/**
10425 		@name glow.anim.Anim#_funcStr
10426 		@private
10427 		@type Object
10428 		@description The string for the function _propFunc
10429 			This is retained so it can be added to for further
10430 			calls to prop
10431 	*/
10432 	AnimProto._funcStr = '';
10433 	
10434 	/**
10435 		@private
10436 		@description Returns a string that calculates the current value for a property
10437 	*/
10438 	function buildValueCalculator(from, to, max, min, round) {
10439 		// start with (from + (from - to) * this.value)
10440 		var str = '(' + Number(from) + '+' + (to - from) + '*this.value)';
10441 		
10442 		// wrap in functions to keep values within range / round values if needed
10443 		if (min !== undefined) {
10444 			str = 'Math.max(' + str + ', ' + min + ')';
10445 		}
10446 		if (max !== undefined) {
10447 			str = 'Math.min(' + str + ', ' + max + ')';
10448 		}
10449 		if (round) {
10450 			str = 'Math.round(' + str + ')';
10451 		}
10452 		
10453 		return str;
10454 	}
10455 	
10456 	/**
10457 		@private
10458 		@description Turn a template into a script that outputs values in place of ?
10459 	*/
10460 	function compileTemplate(template, from, to, max, min, round) {
10461 		// no template? That's easy.
10462 		if (!template) {
10463 			return buildValueCalculator(from, to, max, min, round);
10464 		}
10465 		
10466 		var templateParts = template.split('?'),
10467 			templatePart,
10468 			str = '"' + templateParts[0].replace(/"/g, '\\"') + '"',
10469 			// discover which values are arrays
10470 			Array = window.Array,
10471 			fromIsArray = from.constructor === Array,
10472 			toIsArray = to.constructor === Array,
10473 			maxIsArray = max !== undefined && max.constructor === Array,
10474 			minIsArray = min !== undefined && min.constructor === Array,
10475 			roundIsArray = round.constructor === Array,
10476 			iMinusOne = 0;
10477 		
10478 		for (var i = 1, leni = templateParts.length; i < leni; i++, iMinusOne++) {
10479 			templatePart = templateParts[i];
10480 			
10481 			if ( templateParts[iMinusOne].slice(-1) === '\\' ) {
10482 				// the user wants a literal question mark, put it back
10483 				str += '+"?"';
10484 			}
10485 			else {
10486 				// remove trailing slash, it's being used to escape a ?
10487 				if ( templatePart.slice(-1) === '\\' ) {
10488 					templatePart = templatePart.slice(0, -1);
10489 				}
10490 				str += '+' +
10491 					buildValueCalculator(
10492 						fromIsArray ? from[iMinusOne] : from,
10493 						toIsArray ? to[iMinusOne] : to,
10494 						maxIsArray ? max[iMinusOne] : max,
10495 						minIsArray ? min[iMinusOne] : min,
10496 						roundIsArray ? round[iMinusOne] : round
10497 					) +
10498 					'+"' + templatePart.replace(/"/g, '\\"') + '"';
10499 			}
10500 		}
10501 		return str;
10502 	}
10503 	
10504 	/**
10505 		@private
10506 		@description Builds the function for an animation object's frame listener
10507 			This function animatate object properties as instructed by #prop
10508 	*/
10509 	function buildFunction(anim, targetIndex, propName, conf) {
10510 		var targets = anim._targets,
10511 			// this is going to be our listener for the frame event
10512 			functionStr = anim._funcStr,
10513 			func;
10514 		
10515 		functionStr += 'var target=targets[' + targetIndex + '];' +
10516 			'target["' + propName.replace(/"/g, '\\"') + '"]=' +
10517 			compileTemplate(conf.template, conf.from, conf.to, conf.max, conf.min, conf.round) +
10518 			';'; 
10519 		
10520 		// retain new function string
10521 		anim._funcStr = functionStr;
10522 		
10523 		// eval to create a single function to be called
10524 		func = anim._evalFunc(functionStr, targets);
10525 		
10526 		// remove old listener & add new one
10527 		anim.detach('frame', anim._propFunc).on('frame', func);
10528 		// retain new func so we can remove it later
10529 		anim._propFunc = func;
10530 		func = functionStr = undefined;
10531 	}
10532 	
10533 	/**
10534 		@private
10535 		@description Determines the value(s) to animate from
10536 	*/
10537 	function getFromVals(propValue, conf) {
10538 		var results,
10539 			template = conf.template,
10540 			templateRegexStr;
10541 			
10542 		// this is easy if from values are already specified
10543 		// or there isn't a template to follow
10544 		if (conf.from !== undefined || !template) {
10545 			return conf.from || propValue;
10546 		}
10547 		
10548 		// turn the template into a regular expression, turning the ? into regex for detecting numbers
10549 		templateRegexStr = glow.util.escapeRegex(template).replace(/([^\\]|^)\\\?/g, '$1(\\-?(?:\\d+)?(?:\\.\\d+)?)');
10550 		results = new RegExp(templateRegexStr).exec(propValue);
10551 		if (!results) {
10552 			throw new Error('glow.anim.Anim#prop: Could not detect start values using template: ' + template);
10553 		}
10554 		else {
10555 			return Array.prototype.slice.call(results, 1);
10556 		}
10557 	}
10558 	
10559 	/**
10560 		@name glow.anim.Anim#prop
10561 		@function
10562 		@description Animate a property of an object.
10563 			This shortcut adds a listener onto the animation's 'frame' event
10564 			and changes a specific property from one value to another.
10565 			
10566 			Values can be simple, such as '42', or more complex, such as 'rgba(255, 255, 0, 0.8)'
10567 			
10568 			Before calling this, set the target object via {@link glow.anim.Anim#target}.
10569 			
10570 		@param {string} propertyName Name of the property to animate.
10571 		@param {Object} conf Animation configuration object.
10572 			All configuration properties are optional with the exception of
10573 			'to', and 'from' in some cases (conditions below).
10574 		
10575 		@param {string} [conf.template] Template for complex values
10576 			Templates can be used for values which are strings rather than numbers.
10577 			
10578 			Question-marks are used within templates as placeholders for animated
10579 			values. For instance, in the template '?em' the question-mark would be
10580 			replaced with a number resulting in animated values like '1.5em'.
10581 			
10582 			Multiple Question-marks can be used for properties with more than
10583 			one animated value, eg 'rgba(?, ?, ?, ?)'. The values will be animated
10584 			independently.
10585 			
10586 			A literal question-mark can be placed in a template by preceeding it
10587 			with a backslash.
10588 			
10589 		@param {number|number[]} [conf.from] Value(s) to animate from.
10590 			This can be a single number, or an array of numbers; one for each
10591 			question-mark in the template.
10592 			
10593 			If omitted, the from value(s) will be taken from the object. This
10594 			will fail if the current value is undefined or is in a format
10595 			different to the template.
10596 			
10597 		@param {number|number[]} conf.to Value(s) to animate to.
10598 			This can be a single number, or an array of numbers; one for each
10599 			question-mark in the template.
10600 			
10601 		@param {boolean|boolean[]} [conf.round=false] Round values to the nearest whole number.
10602 			Use this to prevent the property being set to a fractional value.
10603 			
10604 			This can be a single boolean, or an array of booleans; one for each
10605 			question-mark in the template. This is useful for templates like 'rgba(?, ?, ?, ?)',
10606 			where the rgb values need to be whole numbers, but the alpha value is
10607 			between 0-1.
10608 		
10609 		@param {number|number[]} [conf.min] Minimum value(s)
10610 			Use this to stop values animating beneath certain values.
10611 			
10612 			Eg, some tweens go beyond their end position, but heights cannot
10613 			be negative.
10614 			
10615 			This can be a single number, or an array of numbers; one for each
10616 			question-mark in the template. 'undefined' means no restriction.
10617 			
10618 		@param {number|number[]} [conf.max] Maximum value(s)
10619 			Use this to stop values animating beyond certain values.
10620 			
10621 			Eg, some tweens go beyond their end position, but colour values cannot
10622 			be greater than 255.
10623 			
10624 			This can be a single number, or an array of numbers; one for each
10625 			question-mark in the template. 'undefined' means no restriction.
10626 			
10627 		@returns this
10628 		
10629 		@example
10630 			// Using glow.anim.Anim to animate an SVG blur over 5 seconds, with an easeOut tween
10631 			new glow.anim.Anim(5, {
10632 				tween: 'easeOut'
10633 			}).target(feGaussianBlurElm).prop('stdDeviation', {
10634 				from: 0,
10635 				to: 8
10636 			}).start();
10637 			
10638 		@example
10639 			// Animate a CSS property we don't support in glow.NodeList#anim
10640 			// This rotates a Mozilla CSS gradient
10641 			var styleObject = glow('#nav').prop('style');
10642 			
10643 			new glow.anim.Anim(10).target(styleObject).prop('background', {
10644 				// the question-mark in the template is replaced with the animate value
10645 				template: '-moz-linear-gradient(?deg, red, blue)'
10646 				from: 0,
10647 				to: 360
10648 			}).start();
10649 			
10650 		@example
10651 			// Animate a CSS property we don't support in glow.NodeList#anim
10652 			// This changes the colour of a webkit drop shadow from yellow to blue
10653 			var styleObject = glow('#nav').prop('style');
10654 			
10655 			new glow.anim.Anim(3).target(styleObject).prop('WebkitBoxShadow', {
10656 				// the ? in the template are replaced with the animate values
10657 				template: 'rgb(?, ?, ?) 0px 4px 14px'
10658 				// provide a 'from' and 'to' value for each question-mark
10659 				from: [255, 255, 0],
10660 				to: [0, 0, 255],
10661 				// round the value, colours can't be fractional
10662 				round: true
10663 			}).start();
10664 	*/
10665 	AnimProto.prop = function(propName, conf) {
10666 		/*!debug*/
10667 			if (arguments.length !== 2) {
10668 				glow.debug.warn('[wrong count] glow.anim.Anim#prop expects 2 arguments, not ' + arguments.length + '.');
10669 			}
10670 			if (typeof propName !== 'string') {
10671 				glow.debug.warn('[wrong type] glow.anim.Anim#prop expects string as "propName" argument, not ' + typeof propName + '.');
10672 			}
10673 			if (typeof conf !== 'object') {
10674 				glow.debug.warn('[wrong type] glow.anim.Anim#prop expects object as "conf" argument, not ' + typeof conf + '.');
10675 			}
10676 			if (conf.to === undefined || (!conf.to.push && typeof conf.to !== 'number') ) {
10677 				glow.debug.warn('[wrong type] glow.anim.Anim#prop expects number/array as "conf.to" argument, not ' + typeof conf.to + '.');
10678 			}
10679 			if (conf.from !== undefined && (!conf.from.push && typeof conf.from !== 'number') ) {
10680 				glow.debug.warn('[wrong type] glow.anim.Anim#prop expects number/array as "conf.from" argument, not ' + typeof conf.from + '.');
10681 			}
10682 			if (conf.template !== undefined && typeof conf.template !== 'string') {
10683 				glow.debug.warn('[wrong type] glow.anim.Anim#prop expects string as "conf.template" argument, not ' + typeof conf.template + '.');
10684 			}
10685 			if (this._targets.length === 0) {
10686 				glow.debug.warn('[unmet prerequisite] glow.anim.Anim#target must be called before glow.anim.Anim#prop');
10687 			}
10688 		/*gubed!*/
10689 		
10690 		var targetIndex = this._targets.length - 1,
10691 			target = this._targets[targetIndex];
10692 		
10693 		// default conf
10694 		conf = glow.util.apply({
10695 			from: getFromVals(target[propName], conf),
10696 			round: false
10697 		}, conf);
10698 		
10699 		buildFunction(this, targetIndex, propName, conf);
10700 		
10701 		return this;
10702 	};
10703 });
10704 Glow.provide(function(glow) {
10705 	var undefined,
10706 		AnimProto = glow.anim.Anim.prototype;
10707 	
10708 	/**
10709 		@private
10710 		@description Mirrors a tween
10711 	*/
10712 	function mirrorTween(tween) {
10713 		return function(t) {
10714 			return tween(1 - t);
10715 		}
10716 	}
10717 	
10718 	/**
10719 		@name glow.anim.Anim#_preReverseTween
10720 		@private
10721 		@type function
10722 		@description This is the tween before it was reversed
10723 			This means that anim.reverse().reverse() doesn't
10724 			wrap the tween function twice, it stores it here
10725 			so it can reinstate it.
10726 	*/
10727 	
10728 	/**
10729 		@name glow.anim.Anim#reversed
10730 		@private
10731 		@type boolean
10732 		@description Is the animation in a reversed state?
10733 			This starts off as false, and is true if {@link glow.anim.Anim#reverse reverse}
10734 			is called. If reverse is called again, this is false.
10735 			
10736 			This is useful in 'complete' listeners to determine where the animation
10737 			ended.
10738 	*/
10739 	AnimProto.reversed = false;
10740 	
10741 	/**
10742 		@name glow.anim.Anim#reverse
10743 		@function
10744 		@description Reverses this animation
10745 			Adjusts the tween of this animation so it plays in reverse. If
10746 			the animation is currently playing, it will continue to play.
10747 			
10748 			The current position of the animation is also reversed, so if a
10749 			3 second animation is currently 2 seconds in, it will be one
10750 			second in when reversed.
10751 			
10752 			This is handy for animations that do something on (for example)
10753 			mouseenter, then need to animate back on mouseleave
10754 		
10755 		@returns this
10756 		
10757 		@example
10758 			// change a nav item's background colour from white to yellow
10759 			// when the mouse is over it, and back again when the mouse
10760 			// exits.
10761 			//
10762 			// If the mouse leaves the item before the animation
10763 			// completes, it animates back from whatever position it
10764 			// ended on.
10765 			glow('#nav').delegate('mouseenter', 'li', function() {
10766 				var fadeAnim = glow(this).data('fadeAnim');
10767 				
10768 				if (fadeAnim) {
10769 					// we've already created the animation, just reverse it and go!
10770 					fadeAnim.reverse().start();
10771 				}
10772 				else {
10773 					// create our animation, this will only happen once per element
10774 					glow(this).data('fadeAnim',
10775 						glow(this).anim(0.5, {
10776 							'background-color': 'yellow'
10777 						}, {
10778 							// don't destroy, we want to reuse this animation
10779 							destroyOnComplete: false
10780 						});
10781 					);
10782 				}
10783 				
10784 			}).delegate('mouseleave', 'li', function() {
10785 				// Get our animation, reverse it and go!
10786 				glow(this).data('fadeAnim').reverse().start();
10787 			});
10788 	*/
10789 	AnimProto.reverse = function() {
10790 		/*!debug*/
10791 			if (arguments.length !== 0) {
10792 				glow.debug.warn('[wrong count] glow.anim.Anim#reverse expects 0 arguments, not ' + arguments.length + '.');
10793 			}
10794 		/*gubed!*/
10795 		var newPosition = this.position && (1 - this.position / this.duration) * this.duration,
10796 			oldTween = this.tween;
10797 		
10798 		// set reversed property
10799 		this.reversed = !this.reversed;
10800 		
10801 		// reverse the tween
10802 		this.tween = this._preReverseTween || mirrorTween(this.tween);
10803 		this._preReverseTween = oldTween;
10804 		return this.goTo(newPosition);
10805 	}
10806 	
10807 	/**
10808 		@name glow.anim.Anim#pingPong
10809 		@function
10810 		@description Alters the animation so it plays forward, then in reverse
10811 			The duration of the animation is doubled.
10812 		
10813 		@returns this
10814 		
10815 		@example
10816 			// Fades #myDiv to red then back to its original colour
10817 			// The whole animation takes 2 seconds
10818 			glow('#myDiv').anim(1, {
10819 				'background-color': 'red'
10820 			}).pingPong();
10821 	*/
10822 	AnimProto.pingPong = function() {
10823 		/*!debug*/
10824 			if (arguments.length !== 0) {
10825 				glow.debug.warn('[wrong count] glow.anim.Anim#pingPong expects 0 arguments, not ' + arguments.length + '.');
10826 			}
10827 		/*gubed!*/
10828 		var oldTween = this.tween,
10829 			oldTweenReversed = mirrorTween(oldTween);
10830 		// double the length of the animation
10831 		this.duration *= 2;
10832 		this.tween = function(t) {
10833 			return (t < 0.5) ? oldTween(t * 2) : oldTweenReversed( (t - 0.5) * 2 );
10834 		}
10835 		// invalidate the stored reversed tween
10836 		this._preReverseTween = undefined;
10837 		this.reversed = false;
10838 		
10839 		return this.goTo(this.position / 2);
10840 	}
10841 });
10842 Glow.provide(function(glow) {
10843 	var undefined,
10844 		TimelineProto,
10845 		Anim = glow.anim.Anim;
10846 	
10847 	/**
10848 		@private
10849 		@description Listener for the start event on the sync anim the timeline uses
10850 			'this' is the Timeline
10851 	*/
10852 	function animStart(e) {
10853 		this.fire('start', e);
10854 		this.playing = !e.defaultPrevented();
10855 	}
10856 	
10857 	/**
10858 		@private
10859 		@description Listener for the stop event on the sync anim the timeline uses
10860 			'this' is the Timeline
10861 	*/
10862 	function animStop(e) {
10863 		this.fire('stop', e);
10864 		this.playing = e.defaultPrevented();
10865 	}
10866 	
10867 	/**
10868 		@private
10869 		@description Listener for the frame event on the sync anim the timeline uses
10870 			'this' is the Timeline
10871 	*/
10872 	function animFrame(e) {
10873 		this.goTo(this._anim.position);
10874 		// if we're still playing, fire frame
10875 		if (this._anim.playing) {
10876 			this.fire('frame', e);
10877 		}
10878 	}
10879 	
10880 	/**
10881 		@private
10882 		@description Listener for the complete event on the sync anim the timeline uses
10883 			'this' is the Timeline
10884 	*/
10885 	function animComplete(e) {
10886 		// mirror .loop
10887 		this._anim.loop = this.loop;
10888 		// fire complete with same event object so it can be cancelled by user
10889 		this.fire('complete', e);
10890 		// find out if we're going to loop, set .playing
10891 		var loop = this.playing = ( this.loop || e.defaultPrevented() );
10892 		// if we're not looping, destroy
10893 		if (!loop && this.destroyOnComplete) {
10894 			this.destroy();
10895 		}
10896 	}
10897 	
10898 	/**
10899 		@name glow.anim.Timeline
10900 		@extends glow.events.Target
10901 		@class
10902 		@description Sequence and synchronise multiple animations
10903 			This can be used to easily chain animations together
10904 			and ensure that multiple animations stay in sync
10905 			with each other.
10906 			
10907 		@param {Object} [opts] Options object.
10908 		
10909 		@param {boolean} [opts.loop=true] Loop the animation.
10910 			Looped timelines will fire a 'complete' event on each loop.
10911 			
10912 		@param {boolean} [opts.destroyOnComplete=true] Destroy animations in the timeline once it completes (unless it loops).
10913 			This will free any DOM references the animations may have created. Once
10914 			the animations are destroyed, the timeline cannot be started again.
10915 			
10916 		@example
10917 			// play 3 animations one after another
10918 			new glow.anim.Timeline().track(anim1, anim2, anim3).start();
10919 			
10920 		@example
10921 			// play 2 animations at the same time
10922 			new glow.anim.Timeline()
10923 				.track(anim1)
10924 				.track(anim2)
10925 				.start();
10926 			
10927 		@example
10928 			// play 2 animations with a second pause in between
10929 			new glow.anim.Timeline().track(anim1, 1, anim2).start();
10930 			
10931 		@example
10932 			// Make a 'mexican wave'
10933 			// #waveContainer contains 100 divs absolutely positioned next to each other
10934 			
10935 			var animTimeline = new glow.anim.Timeline({
10936 				loop: true
10937 			});
10938 			
10939 			//create a wave up & wave down anim for each div
10940 			var wavingDivs = glow("#waveContainer div").each(function(i) {
10941 				var div = glow(this);
10942 			
10943 				animTimeline.track(
10944 					// add a pause to the start of the anim, this creates the wave effect
10945 					(i / 100),
10946 					// move up & down
10947 					div.anim(1, {
10948 						top: [70, 0]
10949 					}).pingPong()
10950 				);
10951 			});
10952 			
10953 			animTimeline.start();
10954 	*/
10955 	function Timeline(opts) {
10956 		/*!debug*/
10957 			if (arguments.length > 1) {
10958 				glow.debug.warn('[wrong count] glow.anim.Timeline expects 0 or 1 arguments, not ' + arguments.length + '.');
10959 			}
10960 			if (opts !== undefined && typeof opts !== 'object') {
10961 				glow.debug.warn('[wrong type] glow.anim.Iimeline expects object as "opts" argument, not ' + typeof opts + '.');
10962 			}
10963 		/*gubed!*/
10964 		
10965 		opts = opts || {};
10966 		this.destroyOnComplete = (opts.destroyOnComplete !== false);
10967 		this.loop = !!opts.loop;
10968 		this._tracks = [];
10969 		this._currentIndexes = [];
10970 		this._startPos = [];
10971 		
10972 		// create an animation to sync the timeline
10973 		this._anim = new Anim(0, {
10974 				destroyOnComplete: false,
10975 				tween: 'linear'
10976 			})
10977 			.on('start', animStart, this)
10978 			.on('stop', animStop, this)
10979 			.on('frame', animFrame, this)
10980 			.on('complete', animComplete, this);
10981 	}
10982 	glow.util.extend(Timeline, glow.events.Target);
10983 	TimelineProto = Timeline.prototype;
10984 	
10985 	/**
10986 		@name glow.anim.Timeline#duration
10987 		@type number
10988 		@description Length of the animation in seconds
10989 		
10990 		// implementation note: (delete this later)
10991 		This will need to be generated after each call to #track
10992 		Won't be too expensive, just work out the length of the new
10993 		track and Math.max(newTrack, this.duration)
10994 	*/
10995 	TimelineProto.duration = 0;
10996 	
10997 	/**
10998 		@name glow.anim.Timeline#position
10999 		@type number
11000 		@description Position of the animation in seconds
11001 	*/
11002 	TimelineProto.position = 0;
11003 	
11004 	/**
11005 		@name glow.anim.Timeline#playing
11006 		@description true if the animation is playing.
11007 		@returns {boolean}
11008 	*/
11009 	TimelineProto.playing = false;
11010 	
11011 	/**
11012 		@name glow.anim.Timeline#loop
11013 		@description Loop the animation?
11014 			This value can be changed while the animation is playing.
11015 			
11016 			Looped animations will fire a 'complete' event on each loop.
11017 			
11018 		@returns {boolean}
11019 	*/
11020 	
11021 	/**
11022 		@name glow.anim.Timeline#destroyOnComplete
11023 		@type boolean
11024 		@description Destroy the animation once it completes (unless it loops)?
11025 			This will free any DOM references the animation may have created. Once
11026 			the animation is destroyed, it cannot be started again.
11027 	*/
11028 	
11029 	/**
11030 		@name glow.anim.Timeline#_tracks
11031 		@private
11032 		@type Array[]
11033 		@description An array of arrays.
11034 			Each array represents a track, containing a combination of
11035 			animations and functions
11036 	*/
11037 	
11038 	/**
11039 		@name glow.anim.Timeline#_currentIndexes
11040 		@private
11041 		@type number[]
11042 		@description Array of the current indexes within _tracks
11043 			The indexes refer to which items that were last sent a .goTo90
11044 	*/
11045 	
11046 	/**
11047 		@name glow.anim.Timeline#_startPos
11048 		@private
11049 		@type Array[]
11050 		@description Mirrors _tracks
11051 			Contains the start positions of the items in _tracks
11052 	*/
11053 	
11054 	/**
11055 		@name glow.anim.Timeline#_anim
11056 		@private
11057 		@type glow.anim.Anim
11058 		@description The single animation used to fire frames for this animation
11059 	*/
11060 	
11061 	/**
11062 		@name glow.anim.Timeline#_lastPos
11063 		@private
11064 		@type number
11065 		@description Last position rendered
11066 	*/
11067 	TimelineProto._lastPos = 0;
11068 	
11069 	/**
11070 		@name glow.anim.Timeline#start
11071 		@function
11072 		@description Starts playing the animation
11073 		
11074 		@param {number} [start] Position to start the animation at, in seconds.
11075 			By default, this will be the last position of the animation (if it was stopped)
11076 			or 0.
11077 		
11078 		@returns this
11079 	*/
11080 	TimelineProto.start = function() {
11081 		this._anim.start();
11082 		return this;
11083 	};
11084 	
11085 	/**
11086 		@name glow.anim.Timeline#stop
11087 		@function
11088 		@description Stops the animation playing.
11089 			Stopped animations can be resumed by calling {@link glow.anim.Timeline#start start}.
11090 		@returns this
11091 	*/
11092 	TimelineProto.stop = function() {
11093 		/*!debug*/
11094 			if (arguments.length !== 0) {
11095 				glow.debug.warn('[wrong count] glow.anim.Timeline#stop expects 0 arguments, not ' + arguments.length + '.');
11096 			}
11097 		/*gubed!*/
11098 		
11099 		var i = this._tracks.length,
11100 			item;
11101 		
11102 		this._anim.stop();
11103 		// check in case the event has been cancelled
11104 		if (!this._anim.playing) {
11105 			while (i--) {
11106 				// get the current playing item for this track
11107 				item = this._tracks[i][ this._currentIndexes[i] ];
11108 				// check there is an item playing
11109 				if (item) {
11110 					item.fire('stop');
11111 					item.playing = false;
11112 				}
11113 			}
11114 		}
11115 		return this;
11116 	};
11117 	
11118 	/**
11119 		@name glow.anim.Timeline#destroy
11120 		@function
11121 		@description Destroys all animations in the timeline & detaches references to DOM nodes
11122 			This frees memory & is called automatically when the animation completes
11123 		@returns undefined
11124 	*/
11125 	TimelineProto.destroy = function() {
11126 		/*!debug*/
11127 			if (arguments.length !== 0) {
11128 				glow.debug.warn('[wrong count] glow.anim.Timeline#destroy expects 0 arguments, not ' + arguments.length + '.');
11129 			}
11130 		/*gubed!*/
11131 		
11132 		var i = this._tracks.length,
11133 			j,
11134 			item;
11135 		
11136 		// destroy animations in tracks	
11137 		while (i--) {
11138 			j = this._tracks[i].length;
11139 			while (j--) {
11140 				item = this._tracks[i][j];
11141 				item.destroy && item.destroy();
11142 			}
11143 		}
11144 		
11145 		// destroy syncing animation
11146 		this._anim.destroy();
11147 		// remove listeners
11148 		glow.events.removeAllListeners( [this] );
11149 		this._tracks = undefined;
11150 		
11151 	};
11152 
11153 	/**
11154 		@private
11155 		@function
11156 		@description Moves a timeline forward onto timeline.position
11157 			This deals with moving all the tracks forward from their
11158 			current position to the new position. This is done on
11159 			every frame, via timeline.goTo
11160 	*/
11161 	function moveForward(timeline) {
11162 		var i = timeline._tracks.length,
11163 			track,
11164 			item,
11165 			itemIndex,
11166 			itemStart,
11167 			timelinePosition = timeline.position;
11168 		
11169 		while (i--) {
11170 			track = timeline._tracks[i];
11171 			itemIndex = timeline._currentIndexes[i];
11172 
11173 			while ( item = track[itemIndex] ) {
11174 				itemStart = timeline._startPos[i][itemIndex];
11175 				// deal with functions in the timeline
11176 				if (typeof item === 'function') {
11177 					item();
11178 					itemIndex++;
11179 					break;
11180 				}
11181 				// deal with animations in the timeline
11182 				else if (timelinePosition - itemStart >= item.duration) {
11183 					// the animation we're currently playing has come to
11184 					// an end, play the last frame and move on to the next
11185 					item.goTo(item.duration).fire('complete');
11186 					item.playing = false;
11187 				}
11188 				else {
11189 					// the animation we're playing is somewhere in the middle
11190 					if (!item.playing) {
11191 						// ohh, we're just starting this animation
11192 						item.fire('start');
11193 						item.playing = true;
11194 					}
11195 					item.goTo(timelinePosition - itemStart);
11196 					// we're not done with this item, break
11197 					break;
11198 				}
11199 				itemIndex++;
11200 			}
11201 			timeline._currentIndexes[i] = itemIndex;
11202 		}
11203 	}
11204 	
11205 	/**
11206 		@private
11207 		@function
11208 		@description
11209 			This goes through all animations that start after the new position
11210 			& before the previous position and calls their first frames.
11211 	*/
11212 	function moveBackward(timeline) {
11213 		var i = timeline._tracks.length,
11214 			j,
11215 			track,
11216 			item,
11217 			itemStart,
11218 			timelinePosition = timeline.position;
11219 		
11220 		while (i--) {
11221 			track = timeline._tracks[i];
11222 			j = timeline._currentIndexes[i] + 1;
11223 			
11224 			while (j--) {
11225 				item = track[j];
11226 				
11227 				if (!item) {
11228 					continue;
11229 				}
11230 				// we don't need to reset items before the new position,
11231 				// their frames are rendered by 'moveForward'
11232 				if ( timeline._startPos[i][j] < timeline.position ) {
11233 					break;
11234 				}
11235 				// we only want to deal with animations
11236 				if (typeof item !== 'function') {
11237 					item.goTo(0);
11238 				}
11239 			}
11240 			
11241 			timeline._currentIndexes[i] = j;
11242 		}
11243 		
11244 		// as a shortcut, we use 'moveForward' to trigger the frame for the new position
11245 		// on the current items
11246 		moveForward(timeline);
11247 	}
11248 	
11249 	/**
11250 		@name glow.anim.Timeline#goTo
11251 		@function
11252 		@description Goes to a specific point in the animation.
11253 		@param {number} position Position in the animation to go to, in seconds
11254 		
11255 		@example
11256 			// move the animation to 2.5 seconds in
11257 			// If the animation is playing, it will continue to play from the new position.
11258 			// Otherwise, it will simply move to that position.
11259 			myTimeline.goTo(2.5);
11260 			
11261 		@returns {glow.anim.Timeline}
11262 	*/
11263 	TimelineProto.goTo = function(position) {
11264 		/*!debug*/
11265 			if (arguments.length !== 1) {
11266 				glow.debug.warn('[wrong count] glow.anim.Timeline#goTo expects 1 argument, not ' + arguments.length + '.');
11267 			}
11268 			if (typeof position !== 'number') {
11269 				glow.debug.warn('[wrong type] glow.anim.Timeline#goTo expects number as "position" argument, not ' + typeof position + '.');
11270 			}
11271 		/*gubed!*/
11272 		
11273 		var resetAll;
11274 		if (position > this.duration) {
11275 			position = this.duration;
11276 		}
11277 		else if (position < 0) {
11278 			position = 0;
11279 		}
11280 		
11281 		this.position = position;
11282 		
11283 		(position < this._lastPos) ? moveBackward(this) : moveForward(this);
11284 		
11285 		this._lastPos = position;
11286 		return this;
11287 	};
11288 	
11289 	/**
11290 		@private
11291 		@description This method is applied to animations / timeline when they're adopted
11292 	*/
11293 	function methodNotAllowed() {
11294 		throw new Error('Cannot call this method on items contained in a timeline');
11295 	}
11296 	
11297 	/**
11298 		@private
11299 		@description Overwrite methods on animations / timelines that no longer apply
11300 	*/
11301 	function adoptAnim(anim) {
11302 		anim.stop();
11303 		anim.start = anim.stop = anim.reverse = anim.pingPong = methodNotAllowed;
11304 	}
11305 	
11306 	/**
11307 		@name glow.anim.Timeline#track
11308 		@function
11309 		@description Add a track of animations to the timeline
11310 			Animations in a track will run one after another.
11311 			
11312 			Each track runs at the same time, always staying in sync.
11313 		
11314 		@param {number|function|glow.anim.Anim|glow.anim.Timeline} item+ Item to add to the timelines
11315 			Animation timelines can be placed within animation timelines
11316 			
11317 			Numbers will be treated as number of seconds to pause before the next item.
11318 			
11319 			Functions will be called. If the function takes 0.5 seconds to call, the next
11320 			animation will start 0.5 seconds in, keeping everything in sync.
11321 			
11322 		@returns this
11323 	*/
11324 	TimelineProto.track = function() {
11325 		/*!debug*/
11326 			if (arguments.length < 1) {
11327 				glow.debug.warn('[wrong count] glow.anim.Timeline#track expects at least 1 argument, not ' + arguments.length + '.');
11328 			}
11329 		/*gubed!*/
11330 		
11331 		var args = arguments,
11332 			tracksLen = this._tracks.length,
11333 			track = this._tracks[tracksLen] = [],
11334 			trackDuration = 0,
11335 			trackDurations = this._startPos[tracksLen] = [],
11336 			trackItem;
11337 		
11338 		// loop through the added tracks
11339 		for (var i = 0, leni = args.length; i < leni; i++) {
11340 			trackItem = track[i] = args[i];
11341 			
11342 			if (trackItem instanceof Anim || trackItem instanceof Timeline) {
11343 				adoptAnim(trackItem);
11344 			}
11345 			// convert numbers into empty animations
11346 			else if (typeof trackItem === 'number') {
11347 				trackItem = track[i] = new Anim(trackItem);
11348 			}
11349 			/*!debug*/
11350 				else if (typeof trackItem !== 'function') {
11351 					glow.debug.warn('[wrong type] glow.anim.Timeline#track all arguments must be number/glow.anim.Anim/glow.anim.Timeline/function, arg ' + i + ' is ' + typeof trackItem + '.');
11352 				}
11353 			/*gubed!*/
11354 			
11355 			// record the start time for this anim
11356 			trackDurations[i] = trackDuration;
11357 			trackDuration += trackItem.duration || 0;
11358 		}
11359 		
11360 		// update duration and anim duration
11361 		this._anim.duration = this.duration = Math.max(this.duration, trackDuration);
11362 		this._currentIndexes[tracksLen] = 0;
11363 		
11364 		return this;
11365 	};
11366 	
11367 	/**
11368 		@name glow.anim.Timeline#event:start
11369 		@event
11370 		@description Fires when an animation starts.
11371 			Preventing this event (by returning false or calling {@link glow.events.Event#preventDefault preventDefault})
11372 			prevents this animation from starting.
11373 		
11374 		@param {glow.events.Event} event Event Object
11375 	*/
11376 	
11377 	/**
11378 		@name glow.anim.Timeline#event:frame
11379 		@event
11380 		@description Fires on each frame of the animation
11381 		
11382 		@param {glow.events.Event} event Event Object
11383 	*/
11384 	
11385 	/**
11386 		@name glow.anim.Timeline#event:stop
11387 		@event
11388 		@description Fires when an animation is stopped before completing
11389 			Preventing this event (by returning false or calling {@link glow.events.Event#preventDefault preventDefault})
11390 			prevents this animation from stopping.
11391 		
11392 		@param {glow.events.Event} event Event Object
11393 	*/
11394 	
11395 	/**
11396 		@name glow.anim.Timeline#event:complete
11397 		@event
11398 		@description Fires when an animation completes
11399 			Preventing this event (by returning false or calling {@link glow.events.Event#preventDefault preventDefault})
11400 			causes the animation to loop.
11401 		
11402 		@param {glow.events.Event} event Event Object
11403 		
11404 		@example
11405 			// Make an animation loop 5 times
11406 			var loopCount = 5;
11407 			myTimeline.on('complete', function() {
11408 				return !!loopCount--;
11409 			});
11410 	*/
11411 	
11412 	// export
11413 	glow.anim.Timeline = Timeline;
11414 });
11415 Glow.complete('core', '2.0.0b1');
11416