docs/ 40755 0 0 0 11343467400 7042 5ustar 0 0 docs/symbols/ 40755 0 0 0 11343467400 10532 5ustar 0 0 docs/symbols/src/ 40755 0 0 0 11343467400 11321 5ustar 0 0 glow/ 40755 0 0 0 11343467400 7062 5ustar 0 0 glow/2.0.0-alpha1/ 40755 0 0 0 11343467400 10663 5ustar 0 0 CHANGES100644 0 0 254 11343467400 7163 0ustar 0 0 Glow Changelog 2.0.0 alpha 1 First release including the following modules: * Glow - the loader * glow - the library * glow.NodeList * glow.events * glow.env * glow.utilCONTRIBUTORS100644 0 0 503 11343467400 10045 0ustar 0 0 For more information regarding contributing to Glow, please see: http://www.bbc.co.uk/glow/community/contributors/ BBC Glow engineering team: Jake Archibald [http://github.com/jakearchibald] Frances Berriman [http://github.com/phae] Steve Elson [http://github.com/elson] Michael Mathews [http://github.com/micmath] LICENCE100644 0 0 23676 11343467400 7232 0ustar 0 0 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS README100644 0 0 12005 11343467400 7105 0ustar 0 0 Glow 2.0.0 alpha 1 ================== This is the first release of Glow 2. It is intended for testing and shouldn't be used in a live environment. Loading Glow ============ There are 2 options for loading Glow onto your page Asynchronous loading (recommended) ---------------------------------- This makes Glow load in the background, which doesn't block page rendering. This can also be used to load only the parts of Glow that are needed. Another benefit is glow doesn't hit the global scope, it only exists inside your ready callback. Although it isn't ideal in terms of bandwidth, multiple versions of glow can be loaded safely on the same page. For larger apps, this pattern may be more useful Later, when we add more features, Glow will be split into packages. If we had any widgets, you would load them like this: At the moment there's only one package, 'core'. This is loaded as soon as you call Glow(2). These methods of loading Glow replace Gloader, which was used to load Glow 1. The full docs for Glow can be found in glow.debug.js, search for "@name Glow" Synchronous loading ------------------- Using the loader is optional, you can load Glow synchronously into the global scope via: Interacting with the DOM ======================== You can use glow as a function to get elements by CSS selector or create them by HTML string. glow('#nav li:first-child').css('background', 'red'); glow('

New paragraph

').appendTo('body'); glow() is simply a shortcut. glow('h1') is the same as new glow.NodeList('h1') The full docs for glow.NodeList can be found in core.debug.js, search for "@name glow.NodeList" Differences from Glow 1 ----------------------- The NodeList API is very similar to Glow 1, most of the work has been on performance for this module. Glow 2 uses Sizzle as its CSS selector engine, greatly improving the range of selectors that can be used. CSS can also be used to search for, and filter elements. For instance: // get all the items in the NodeList that have class 'active' myNodeList.filter('.active'); // get the parent element that's a child list-item of #nav myNodeList.parent('#nav > li'); Events ====== The full docs for glow.events can be found in core.debug.js, search for "@name glow.events" Custom Events ------------- Now, any object can extend or be enhanced by glow.events.Target. This gives it instance methods like 'on', 'detach' and 'fire'. function Ball() {} glow.util.extend(Ball, glow.event.Target, { bounce: function() { // fire the bounce event this.fire('bounce'); } }); // and other code can listen for these events var myBall = new Ball(); myBall.on('bounce', function() { glow('body').append('

boing!

'); }); DOM Events ---------- Rather than the long-winded glow.events.addListener, NodeList now has an 'on' method. glow('#nav a').on('click', function() { alert('You just clicked a link in the nav'); }); The above will add a listener to each link in #nav. As a more efficient alternative, you could use event delegation... glow('#nav').delegate('click', 'a', function() { alert('You just clicked a link in the nav'); }); The above adds one listener to the nav, but only fires your callback if the user clicks on a link inside the nav. This means that the event will also fire for links created after the listener is added. Keyboard Events --------------- Keyboard events have been normalised to fire keydown and keyup once per key press, whereas the keypress event will repeat while the key is held down. Details of which keys have been pressed have been normalised across browsers. // adding key listeners for a sideways-scrolling shooter glow('#spaceshipGameScreen').on('keydown', function() { if (e.key === 'up') { spaceship.startMoveUp(); } else if (e.key === 'down') { spaceship.startMoveDown(); } }).on('keyup', function(e) { if (e.key === 'up') { spaceship.stopMoveUp(); } else if (e.key === 'down') { spaceship.stopMoveDown(); } }).on('keypress', function(e) { if (e.key === 'space') { spaceship.fireLasers(); } }) docs/files.html100644 0 0 10163 11343467400 11150 0ustar 0 0 JsDoc Reference - File Index
Class Index | File Index

Classes


File Index

./build/2.0.0-alpha1/core.js


./build/2.0.0-alpha1/glow.js


Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:24 GMT-0000 (GMT)
docs/index.html100644 0 0 11457 11343467400 11164 0ustar 0 0 JsDoc Reference - Index
Class Index | File Index

Classes


Class Index

_global_


glow


glow.env


glow.events


glow.events.DomEvent


glow.events.Event


glow.events.KeyboardEvent


glow.events.Target


glow.NodeList


glow.util


Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:24 GMT-0000 (GMT)
docs/symbols/_global_.html100644 0 0 16142 11343467400 13277 0ustar 0 0 JsDoc Reference - _global_
Class Index | File Index

Classes


Built-In Namespace _global_

Method Summary
Method Attributes Method Name and Description
 
Glow(version, opts)
Creates an instance of the Glow JavaScript Library.
Method Detail
Glow(version, opts)
Creates an instance of the Glow JavaScript Library.
Defined in: glow.js.
Parameters:
{string} version Optional
{object} opts Optional
{string} opts.base Optional
The path to the base folder, in which the Glow versions are kept.
{boolean} opts.debug Optional
Have all filenames modified to point to debug versions.

Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:21 GMT-0000 (GMT)
docs/symbols/glow.env.html100644 0 0 33224 11343467400 13300 0ustar 0 0 JsDoc Reference - glow.env
Class Index | File Index

Classes


Namespace glow.env


Defined in: core.js.

Namespace Summary
Constructor Attributes Constructor Name and Description
 
Information about the browser and characteristics
Field Summary
Field Attributes Field Name and Description
<static>  
glow.env.gecko
Gecko version number to one decimal place (eg 1.9) or NaN on non-gecko browsers.
<static>  
glow.env.ie
IE version number to one decimal place (eg 6.0) or NaN on non-IE browsers.
<static>  
glow.env.khtml
KHTML version number to one decimal place or NaN on non-KHTML browsers.
<static>  
glow.env.opera
Opera version number to one decimal place (eg 10.0) or NaN on non-Opera browsers.
<static>  
glow.env.standardsMode
True if the browser reports itself to be in 'standards mode' Otherwise, the browser is in 'quirks mode'
<static>  
glow.env.version
Version number of the browser in use as a string.
<static>  
glow.env.webkit
Webkit version number to one decimal place (eg 531.9) or NaN on non-Webkit browsers.
Namespace Detail
glow.env
Information about the browser and characteristics
Field Detail
<static> {number} glow.env.gecko
Gecko version number to one decimal place (eg 1.9) or NaN on non-gecko browsers. The most popular browser using the Gecko engine is Firefox.
			if (glow.env.gecko < 1.9) {
				// runs in Firefox 2 and other browsers that use Gecko earlier than 1.9
			}
See:
Versions of Gecko used by browsers

<static> {number} glow.env.ie
IE version number to one decimal place (eg 6.0) or NaN on non-IE browsers. This number will also be populated for browser based on IE's trident engine
			if (glow.env.ie < 9) {
				// runs in IE pre-9.0
				glow('#content').css('background', 'deadmoomin.png');
			}

<static> {number} glow.env.khtml
KHTML version number to one decimal place or NaN on non-KHTML browsers. Konqueror is the most popular browsers using KHTML.

<static> {number} glow.env.opera
Opera version number to one decimal place (eg 10.0) or NaN on non-Opera browsers.
			if (glow.env.opera < 10) {
				// runs in Opera pre-10.0
			}

<static> {boolean} glow.env.standardsMode
True if the browser reports itself to be in 'standards mode' Otherwise, the browser is in 'quirks mode'
See:
Quirks Mode vs Standards Mode

<static> {string} glow.env.version
Version number of the browser in use as a string. This caters for version numbers that aren't 'real' numbers, like "7b" or "1.9.1"

<static> {number} glow.env.webkit
Webkit version number to one decimal place (eg 531.9) or NaN on non-Webkit browsers. Safari and Google Chrome are the most popular browsers using Webkit.
			if (glow.env.webkit < 526) {
				// runs in Safari pre-4.0, and Chrome pre-1.0
			}
See:
Versions of Webkit used by Safari
Versions of Webkit used by Google Chrome

Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:22 GMT-0000 (GMT)
docs/symbols/glow.events.domevent.html100644 0 0 37036 11343467400 15641 0ustar 0 0 JsDoc Reference - glow.events.DomEvent
Class Index | File Index

Classes


Class glow.events.DomEvent


Extends glow.events.Event.

Defined in: core.js.

Class Summary
Constructor Attributes Constructor Name and Description
 
glow.events.DomEvent(nativeEvent, properties)
Describes a DOM event that occurred You don't need to create instances of this class if you're simply listening to events.
Field Summary
Field Attributes Field Name and Description
 
Was the alt key pressed during the event?
 
A number representing which button was pressed.
 
Was the ctrl key pressed during the event?
 
The horizontal position of the mouse pointer in the page in pixels.
 
The vertical position of the mouse pointer in the page in pixels.
 
The native event object provided by the browser.
 
A related HTMLElement For mouseover / mouseenter events, this will refer to the previous element the mouse was over.
 
Was the shift key pressed during the event?
 
The element that the event originated from.
 
The native type of the event, like 'click' or 'keydown'.
 
The number of clicks the mouse wheel moved.
Fields borrowed from class glow.events.Event:
attachedTo
Methods borrowed from class glow.events.Event:
defaultPrevented, preventDefault
Class Detail
glow.events.DomEvent(nativeEvent, properties)
Describes a DOM event that occurred You don't need to create instances of this class if you're simply listening to events. One will be provided as the first argument in your callback.
Parameters:
{Event|string} nativeEvent
A native browser event read properties from, or the name of a native event.
{Object} properties Optional
Properties to add to the Event instance. Each key-value pair in the object will be added to the Event as properties
Field Detail
altKey
Was the alt key pressed during the event?

button
A number representing which button was pressed. 0 for the left button, 1 for the middle button or 2 for the right button.

ctrlKey
Was the ctrl key pressed during the event?

mouseLeft
The horizontal position of the mouse pointer in the page in pixels.

mouseTop
The vertical position of the mouse pointer in the page in pixels.

nativeEvent
The native event object provided by the browser.

related
A related HTMLElement For mouseover / mouseenter events, this will refer to the previous element the mouse was over. For mouseout / mouseleave events, this will refer to the element the mouse is now over.

shiftKey
Was the shift key pressed during the event?

source
The element that the event originated from. For example, you could attach a listener to an
    element to listen for clicks. If the user clicked on an
  1. the source property would be the
  2. element, and {@link glow.DomEvent#attachedTo attachedTo} would be the
      .

type
The native type of the event, like 'click' or 'keydown'.

wheelData
The number of clicks the mouse wheel moved. Up values are positive, down values are negative.

Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:22 GMT-0000 (GMT)
docs/symbols/glow.events.event.html100644 0 0 31304 11343467400 15131 0ustar 0 0 JsDoc Reference - glow.events.Event
Class Index | File Index

Classes


Class glow.events.Event


Defined in: core.js.

Class Summary
Constructor Attributes Constructor Name and Description
 
glow.events.Event(properties)
Describes an event that occurred.
Field Summary
Field Attributes Field Name and Description
 
The object the listener was attached to.
 
The actual object/element that the event originated from.
Method Summary
Method Attributes Method Name and Description
 
Has the default been prevented for this event? This should be used by whatever fires the event to determine if it should carry out of the default action.
 
Prevent the default action of the event.
Class Detail
glow.events.Event(properties)
Describes an event that occurred. You don't need to create instances of this class if you're simply listening to events. One will be provided as the first argument in your callback.
		// creating a simple event object
		var event = new glow.events.Event({
			velocity: 50,
			direction: 180
		});
		       
		// 'velocity' and 'direction' are simple made-up properties
		// you may want to add to your event object
		// inheriting from glow.events.Event to make a more
		// specialised event object
		       
		function RocketEvent() {
			// ...
		}
		       
		// inherit from glow.events.Event
		glow.util.extend(RocketEvent, glow.events.Event, {
			getVector: function() {
				return // ...
			}
		});
		       
		// firing the event
		rocketInstance.fire( 'landingGearDown', new RocketEvent() );
		       
		// how a user would listen to the event
		rocketInstance.on('landingGearDown', function(rocketEvent) {
			var vector = rocketEvent.getVector();
		});
Parameters:
{Object} properties Optional
Properties to add to the Event instance. Each key-value pair in the object will be added to the Event as properties.
Field Detail
attachedTo
The object the listener was attached to. If null, this value will be populated by glow.events.Target#fire

{Element} source
The actual object/element that the event originated from. For example, you could attach a listener to an 'ol' element to listen for clicks. If the user clicked on an 'li' the source property would be the 'li' element, and 'attachedTo' would be the 'ol'.
Method Detail
{Boolean} defaultPrevented()
Has the default been prevented for this event? This should be used by whatever fires the event to determine if it should carry out of the default action.
		// fire the 'show' event
		// read if the default action has been prevented
		if ( overlayInstance.fire('show').defaultPrevented() == false ) {
		    // go ahead and show
		}
Returns:
{Boolean} Returns true if {@link glow.events.Event#preventDefault preventDefault} has been called for this event.

preventDefault()
Prevent the default action of the event. Eg, if the click event on a link is cancelled, the link is not followed. Returning false from an event listener has the same effect as calling this function. For custom events, it's down to whatever fired the event to decide what to do in this case. See {@link glow.events.Event#defaultPrevented defaultPrevented}
		myLinks.on('click', function(event) {
			event.preventDefault();
		});
		       
		// same as...
		       
		myLinks.on('click', function(event) {
			return false;
		});

Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:22 GMT-0000 (GMT)
docs/symbols/glow.events.html100644 0 0 31544 11343467400 14017 0ustar 0 0 JsDoc Reference - glow.events
Class Index | File Index

Classes


Namespace glow.events


Defined in: core.js.

Namespace Summary
Constructor Attributes Constructor Name and Description
 
Handling custom events
Method Summary
Method Attributes Method Name and Description
<static>  
glow.events.addListeners(attachTo, name, callback, thisVal)
Convenience method to add listeners to many objects at once.
<static>  
glow.events.fire(items, eventName, event)
Convenience method to fire events on multiple items at once.
<static>  
glow.events.removeAllListeners(items)
Removes all listeners attached to a given object.
<static>  
glow.events.removeListeners(items, eventName, callback)
Namespace Detail
glow.events
Handling custom events
Method Detail
<static> glow.events.addListeners(attachTo, name, callback, thisVal)
Convenience method to add listeners to many objects at once. If you want to add a listener to a single object, use its 'on' method.
Parameters:
{Object[]} attachTo
Array of objects to add listeners to.
{string} name
Name of the event to listen for. Event names are case sensitive.
{function} callback
Function to call when the event is fired. The callback will be passed a single event object. The type of this object depends on the event (see documentation for the event you're listening to).
{Object} thisVal Optional
Value of 'this' within the callback. By default, this is the object being listened to.
See:
glow.events.Target#fire

<static> glow.events.fire(items, eventName, event)
Convenience method to fire events on multiple items at once. If you want to fire events on a single object, use its 'fire' method.
Parameters:
{Object[]} items
Array of objects to add listeners to
{string} eventName
Name of the event to fire
{glow.events.Event|Object} event Optional
Event object to pass into listeners. You can provide a simple object of key-value pairs which will be added as properties on the glow.events.Event instance.

<static> glow.events.removeAllListeners(items)
Removes all listeners attached to a given object. This removes not only listeners you added, but listeners others added too. For this reason it should only be used as part of a cleanup operation on objects that are about to be destroyed.
Parameters:
{Object[]} items
Items to remove events from

<static> glow.events.removeListeners(items, eventName, callback)
Parameters:
{Object[]} items
Items to remove events from.
{string} eventName
Name of the event to remove.
{function} callback
A reference to the original callback used when the listener was added.

Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:22 GMT-0000 (GMT)
docs/symbols/glow.events.keyboardevent.html100644 0 0 27756 11343467400 16672 0ustar 0 0 JsDoc Reference - glow.events.KeyboardEvent
Class Index | File Index

Classes


Class glow.events.KeyboardEvent


Extends glow.events.DomEvent.

Defined in: core.js.

Class Summary
Constructor Attributes Constructor Name and Description
 
glow.events.KeyboardEvent(nativeEvent, properties)
Describes a keyboard event that occurred You don't need to create instances of this class if you're simply listening to events.
Field Summary
Field Attributes Field Name and Description
 
key
The key pressed This is a string representing the key pressed.
 
The character entered.
Fields borrowed from class glow.events.DomEvent:
altKey, button, ctrlKey, mouseLeft, mouseTop, nativeEvent, related, shiftKey, source, type, wheelData
Fields borrowed from class glow.events.Event:
attachedTo
Methods borrowed from class glow.events.Event:
defaultPrevented, preventDefault
Class Detail
glow.events.KeyboardEvent(nativeEvent, properties)
Describes a keyboard event that occurred You don't need to create instances of this class if you're simply listening to events. One will be provided as the first argument in your callback.
Parameters:
{Event} nativeEvent
A native browser event read properties from
{Object} properties Optional
Properties to add to the Event instance. Each key-value pair in the object will be added to the Event as properties
Field Detail
key
The key pressed This is a string representing the key pressed. Alphanumeric keys are represented by 0-9 and A-Z uppercase. Other safe cross-browser values are:
  • backspace
  • tab
  • return
  • shift
  • alt
  • escape
  • space
  • pageup
  • pagedown
  • end
  • home
  • left
  • up
  • right
  • down
  • insert
  • delete
  • ;
  • =
  • -
  • f1
  • f2
  • f3
  • f4
  • f5
  • f6
  • f7
  • f8
  • f9
  • f10
  • f11
  • f12
  • numlock
  • scrolllock
  • pause
  • ,
  • .
  • /
  • [
  • \
  • ]
  • Some keys may trigger actions in your browser and operating system, some are not cancelable.
    				glow(document).on('keypress', function(event) {
    					switch (event.key) {
    						case 'up':
    							// do stuff
    							break;
    						case 'down':
    							// do stuff
    							break;
    					}
    				});

    keyChar
    The character entered. This is only available during 'keypress' events. If the user presses shift and 1, event.key will be "1", but event.keyChar will be "!".
                    // only allow numbers to be entered into the ageInput field
    				glow('#ageInput').on('keypress', function(event) {
    					return !isNaN( Number(event.keyChar) );
    				});

    Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:22 GMT-0000 (GMT)
    docs/symbols/glow.events.target.html100644 0 0 34147 11343467400 15306 0ustar 0 0 JsDoc Reference - glow.events.Target
    Class Index | File Index

    Classes


    Class glow.events.Target


    Defined in: core.js.

    Class Summary
    Constructor Attributes Constructor Name and Description
     
    An object that can have event listeners and fire events.
    Method Summary
    Method Attributes Method Name and Description
     
    detach(eventName, callback, thisVal)
    Remove an event listener.
    <static>  
    glow.events.Target.extend(obj)
    Convenience method to add Target instance methods onto an object.
     
    fire(eventName, event)
    Fire an event.
     
    on(eventName, callback, thisVal)
    Listen for an event
    Class Detail
    glow.events.Target()
    An object that can have event listeners and fire events. Extend this class to make your own objects have 'on' and 'fire' methods.
    		// Ball is our constructor
    		function Ball() {
    			// ...
    		}
    		       
    		// make Ball inherit from Target
    		glow.util.extend(Ball, glow.events.Target, {
    			// additional methods for Ball here, eg:
    			bowl: function() {
    				// ...
    			}
    		});
    		       
    		// now instances of Ball can receive event listeners
    		var myBall = new Ball();
    		myBall.on('bounce', function() {
    			alert('BOING!');
    		});
    		       
    		// and events can be fired from Ball instances
    		myBall.fire('bounce');
    Method Detail
    detach(eventName, callback, thisVal)
    Remove an event listener.
    		function showListener() {
    		    // ...
    		}
    		       
    		// add listener
    		myObj.on('show', showListener);
    		       
    		// remove listener
    		myObj.detach('show', showListener);
    		// note the following WILL NOT WORK
    		       
    		// add listener
    		myObj.on('show', function() {
    		    alert('hi');
    		});
    		       
    		// remove listener
    		myObj.detach('show', function() {
    			alert('hi');
    		});
    		       
    		// this is because both callbacks are different function instances
    Parameters:
    {string} eventName
    Name of the event to remove.
    {function} callback
    Callback to detach.
    {Object} thisVal Optional
    Value of 'this' within the callback. By default, this is the object being listened to.
    Returns:
    this Target object

    <static> glow.events.Target.extend(obj)
    Convenience method to add Target instance methods onto an object. If you want to add Target methods to a class, extend glow.events.Target instead.
    		var myApplication = {};
    		       
    		glow.events.Target.extend(myApplication);
    		       
    		// now myApplication can fire events...
    		myApplication.fire('load');
    		       
    		// and other objects can listen for those events
    		myApplication.on('load', function(e) {
    			alert('App loaded');
    		});
    Parameters:
    {Object} obj
    Object to add Target instance methods to.

    fire(eventName, event)
    Fire an event.
    		myObj.fire('show');
    		// adding properties to the event object
    		myBall.fire('bounce', {
    		    velocity: 30
    		});
    		// BallBounceEvent extends glow.events.Event but has extra methods
    		myBall.fire( 'bounce', new BallBounceEvent(myBall) );
    Parameters:
    {string} eventName
    Name of the event to fire.
    {glow.events.Event|Object} event Optional
    Event object to pass into listeners. You can provide a simple object of key-value pairs which will be added as properties of a glow.events.Event instance.
    Returns:
    glow.events.Event

    on(eventName, callback, thisVal)
    Listen for an event
    		myObj.on('show', function() {
    		    // do stuff
    		});
    Parameters:
    {string} eventName
    Name of the event to listen for.
    {function} callback
    Function to call when the event fires. The callback is passed a single event object. The type of this object depends on the event (see documentation for the event you're listening to).
    {Object} thisVal Optional
    Value of 'this' within the callback. By default, this is the object being listened to.
    Returns:
    this

    Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:23 GMT-0000 (GMT)
    docs/symbols/glow.html100644 0 0 24125 11343467400 12511 0ustar 0 0 JsDoc Reference - glow
    Class Index | File Index

    Classes


    Namespace glow


    Version @VERSION@.

    Defined in: core.js.

    Namespace Summary
    Constructor Attributes Constructor Name and Description
     
    The glow library namespace The library can also be used as a function, which is a shortcut to glow.NodeList.
    Method Summary
    Method Attributes Method Name and Description
     
    load()
    Add a package to this instance of the Glow library.
     
    loaded(onLoadCallback)
    Do something when all the packages load.
     
    ready(onReadyCallback)
    Do something when all the packages load and the DOM is ready.
    Namespace Detail
    glow
    The glow library namespace The library can also be used as a function, which is a shortcut to glow.NodeList.
    		var links = glow('a');
    		// is the same as
    		var links = new glow.NodeList('a');
    Method Detail
    load()
    Add a package to this instance of the Glow library.
    Defined in: glow.js.
    Parameters:
    {string[]} ...
    The names of 1 or more packages to add.

    loaded(onLoadCallback)
    Do something when all the packages load.
    Defined in: glow.js.
    Parameters:
    {function} onLoadCallback
    Called when all the packages load.

    ready(onReadyCallback)
    Do something when all the packages load and the DOM is ready.
    Defined in: glow.js.
    Parameters:
    {function} onReadyCallback
    Called when all the packages load and the DOM is available.

    Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:22 GMT-0000 (GMT)
    docs/symbols/glow.nodelist.html100644 0 0 336070 11343467400 14356 0ustar 0 0 JsDoc Reference - glow.NodeList
    Class Index | File Index

    Classes


    Class glow.NodeList


    Defined in: core.js.

    Class Summary
    Constructor Attributes Constructor Name and Description
     
    glow.NodeList(contents)
    An array-like collection of DOM Nodes It is recommended to create a NodeList using the shortcut function glow.
    Field Summary
    Field Attributes Field Name and Description
     
    Number of nodes in the NodeList
    Method Summary
    Method Attributes Method Name and Description
     
    addClass(name)
    Adds a class to each node.
     
    after(nodes)
    Insert node(s) after each node in this NodeList.
     
    ancestors(filter)
    Gets the unique ancestor nodes of each node as a new NodeList.
     
    append(nodes)
    Appends node to each node in this NodeList.
     
    appendTo(node)
    Appends nodes in this NodeList to given node(s) If appending to more than one node, the NodeList is appended to the first node and clones are appended to the others.
     
    attr(name, value)
    Gets or sets attributes.
     
    before(nodes)
    Insert node(s) before each node in this NodeList.
     
    Gets the child elements of each node as a new NodeList.
     
    Clones each node in the NodeList, along with data & event listeners
     
    contains(Single)
    Find if this NodeList contains the given element
     
    copy()
    Copies each node in the NodeList, excluding data & event listeners
     
    css(property, value)
    Get / set a CSS property value
     
    data(key, val)
    Use this to safely attach arbitrary data to any DOM Element.
     
    delegate(eventName, selector, callback, thisVal)
    Listen for an event occurring on child elements matching a selector.
     
    Removes each element from the document The element, attached listeners & attached data will be destroyed to free up memory.
     
    detach(eventName, callback)
    detach a listener from elements This will detach the listener from each dom node in the NodeList.
     
    detachDelegate(eventName, selector, callback)
    detach a delegated listener from elements This will detach the listener from each dom node in the NodeList.
     
    each(callback)
    Calls a function for each node in the list.
     
    Removes the nodes' contents
     
    eq(nodeList)
    Compares this NodeList to another Returns true if both NodeLists contain the same items in the same order
     
    filter(test)
    Filter the NodeList
     
    fire(eventName, event)
    Fire an event on dom nodes within the NodeList Note, this will only trigger event listeners to be called, it won't for example, move the mouse or click a link on the page.
     
    get(selector)
    Gets decendents of nodes that match a CSS selector.
     
    hasAttr(name)
    Does the node have a particular attribute? The first node in this NodeList is tested.
     
    hasClass(name)
    Does the node have a particular class? The first node in this NodeList is tested.
     
    height(height)
    Gets / set element height Return value does not include the padding or border of the element in browsers supporting the correct box model.
     
    hide()
    Hides all items in the NodeList.
     
    html(htmlString)
    Gets / sets HTML content Either gets content of the first element, or sets the content for all elements in the list
     
    insertAfter(nodes)
    Insert this NodeList after the given nodes If inserting after more than one node, the NodeList is inserted after the first node and clones are inserted after the others.
     
    insertBefore(nodes)
    Insert this NodeList before the given nodes If inserting before more than one node, the NodeList is inserted before the first node and clones are inserted before the others.
     
    is(selector)
    Tests if the first element matches a CSS selector
     
    item(index)
    Get a single item from the list as an NodeList Negative numbers can be used to get items from the end of the list.
     
    next(search)
    Gets the next sibling element for each node in the ElementList.
     
    Gets the offset from the top left of the document.
     
    on(eventName, callback, thisVal)
    Listen for an event.
     
    parent(search)
    Gets the unique parent nodes of each node as a new NodeList.
     
    Get the top & left position of an element relative to its positioned parent This is useful if you want to make a position:static element position:absolute and retain the original position of the element
     
    prepend(nodes)
    Prepends nodes to each node in this NodeList.
     
    prependTo(node)
    Prepends nodes in this NodeList to given node(s) If prepending to more than one node, the NodeList is prepended to the first node and clones are prepended to the others.
     
    prev(search)
    Gets the previous sibling element for each node in the ElementList.
     
    prop(name, value)
    Gets or sets node properties.
     
    push(nodes)
    Adds nodes to the NodeList
     
    Removes each element from the document If you no longer need the elements, consider using {@link glow.NodeList#destroy destroy}
     
    removeAttr(name)
    Removes an attribute from each node.
     
    Removes a class from each node.
     
    Removes data previously added by glow.NodeList#data from each node in this NodeList.
     
    replaceWith(elements)
    Replace elements with another
     
    Gets/sets the number of pixels the element has scrolled horizontally To get/set the scroll position of the window, use this method on a nodelist containing the window object.
     
    scrollTop(val)
    Gets/sets the number of pixels the element has scrolled vertically To get/set the scroll position of the window, use this method on a nodelist containing the window object.
     
    show()
    Shows all hidden items in the NodeList.
     
    slice(start, end)
    Get a section of an NodeList Operates in the same way as an Array's slice method
     
    sort(func)
    Sort the elements in the list.
     
    text(text)
    Gets / set the text content Either gets content of the first element, or sets the content for all elements in the list
     
    Toggles a class on each node.
     
    Removes the parent of each item in the list
     
    val(value)
    Gets or sets form values for the first node.
     
    width(width)
    Gets / set element width Return value does not include the padding or border of the element in browsers supporting the correct box model.
     
    wrap(wrapper)
    Wraps the given NodeList with the specified element(s).
    Event Summary
    Event Attributes Event Name and Description
     
    keydown(event)
    Fires when the user presses a key Only fires if the element has focus, listen for this event on the document to catch all keydowns.
     
    keypress(event)
    Fires when a key's command executes.
     
    keyup(event)
    Fires when the user releases a key Only fires if the element has focus, listen for this event on the document to catch all keyups.
     
    mouseenter(event)
    Fires when the mouse enters the element specifically, does not bubble
     
    mouseleave(event)
    Fires when the mouse leaves the element specifically, does not bubble
    Class Detail
    glow.NodeList(contents)
    An array-like collection of DOM Nodes It is recommended to create a NodeList using the shortcut function glow.
    			// empty NodeList
    			var myNodeList = glow();
    			// using glow to return a NodeList then chaining methods
    			glow('p').addClass('eg').append('
    Hello!
    ');
    			// creating an element from a string
    			glow('
    Hello!
    ').appendTo('body');
    Parameters:
    {string | glow.NodeList | Node | Node[] | Window} contents
    Items to populate the NodeList with. This parameter will be passed to glow.NodeList#push. Strings will be treated as CSS selectors unless they start with '<', in which case they'll be treated as an HTML string.
    See:
    Supported CSS selectors
    Field Detail
    {Number} length
    Number of nodes in the NodeList
    			// get the number of paragraphs on the page
    			glow('p').length;
    Method Detail
    {glow.NodeList} addClass(name)
    Adds a class to each node.
    		glow("#login a").addClass("highlight");
    Parameters:
    {string} name
    The name of the class to add.
    Returns:
    {glow.NodeList}

    {glow.NodeList} after(nodes)
    Insert node(s) after each node in this NodeList. If there is more than one node in this NodeList, 'nodes' will be inserted after the first element and clones will be inserted after each subsequent element.
    			// adds a paragraph after each heading
    			glow('h1, h2, h3').after('

    That was a nice heading.

    ');
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes
    Node(s) to insert Strings will be treated as HTML strings.
    Returns:
    {glow.NodeList} Original NodeList

    {glow.dom.NodeList} ancestors(filter)
    Gets the unique ancestor nodes of each node as a new NodeList.
    		// get ancestor elements for anchor elements 
    		var ancestors = glow.dom.get("a").ancestors();
    Parameters:
    {Function|string} filter Optional
    Filter test If a string is provided, it is used in a call to {@link glow.ElementList#is ElementList#is}. If a function is provided it will be passed 2 arguments, the index of the current item, and the ElementList being itterated over. Inside the function 'this' refers to the HTMLElement. Return true to keep the node, or false to remove it.
    Returns:
    {glow.dom.NodeList} Returns NodeList

    {glow.NodeList} append(nodes)
    Appends node to each node in this NodeList. If there is more than one node in this NodeList, then the given nodes are appended to the first node and clones are appended to the other nodes.
    			// ends every paragraph with '...'
    			glow('p').append('...');
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes
    Nodes(s) to append Strings will be treated as HTML strings.
    Returns:
    {glow.NodeList} Original NodeList

    {glow.NodeList} appendTo(node)
    Appends nodes in this NodeList to given node(s) If appending to more than one node, the NodeList is appended to the first node and clones are appended to the others.
    			// appends '...' to every paragraph
    			glow('...').appendTo('p');
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} node
    Node(s) to append to. Strings will be treated as CSS selectors or HTML strings.
    Returns:
    {glow.NodeList} The appended nodes.

    {string | undefined | glow.NodeList} attr(name, value)
    Gets or sets attributes. When getting an attribute, it is retrieved from the first node in this NodeList. Setting attributes applies the change to each element in this NodeList. To set an attribute, pass in the name as the first parameter and the value as a second parameter. To set multiple attributes in one call, pass in an object of name/value pairs as a single parameter. For browsers that don't support manipulating attributes using the DOM, this method will try to do the right thing (i.e. don't expect the semantics of this method to be consistent across browsers as this is not possible with currently supported browsers).
    		var myNodeList = glow(".myImgClass");
    
    		// get an attribute
    		myNodeList.attr("class");
    
    		// set an attribute
    		myNodeList.attr("class", "anotherImgClass");
    
    		// set multiple attributes
    		myNodeList.attr({
    		  src: "a.png",
    		  alt: "Cat jumping through a field"
    		});
    Parameters:
    {string | Object} name
    The name of the attribute, or an object of name/value pairs
    {string} value Optional
    The value to set the attribute to.
    Returns:
    {string | undefined | glow.NodeList} When setting attributes this method returns its own NodeList, otherwise returns the attribute value. The attribute name is always treated as case-insensitive. When getting, the returned value will be of type string unless that particular attribute was never set and there is no default value, in which case the returned value will be an empty string.

    {glow.NodeList} before(nodes)
    Insert node(s) before each node in this NodeList. If there is more than one node in this NodeList, 'nodes' will be inserted before the first element and clones will be inserted before each subsequent element.
    			// adds a div before each paragraph
    			glow('p').before('
    Here comes a paragraph!
    ');
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes
    Node(s) to insert Strings will be treated as HTML strings.
    Returns:
    {glow.NodeList} Original NodeList

    {glow.dom.NodeList} children()
    Gets the child elements of each node as a new NodeList.
    		// get all list items
    		var items = glow.dom.get("ul, ol").children();
    Returns:
    {glow.dom.NodeList} Returns a new NodeList containing all the child nodes

    {glow.NodeList} clone()
    Clones each node in the NodeList, along with data & event listeners
    			// get a copy of all heading elements
    			var myClones = glow.get("h1, h2, h3, h4, h5, h6").clone();
    Returns:
    {glow.NodeList} Returns a new NodeList containing clones of all the nodes in the NodeList

    {boolean} contains(Single)
    Find if this NodeList contains the given element
    Parameters:
    {string | HTMLELement | NodeList} Single
    element to check for
    Returns:
    {boolean} myElementList.contains(elm) // Returns true if an element in myElementList contains elm, or IS elm.

    {glow.NodeList} copy()
    Copies each node in the NodeList, excluding data & event listeners
    			// get a copy of all heading elements
    			var myCopies = glow.get("h1, h2, h3, h4, h5, h6").copy();
    Returns:
    {glow.NodeList} Returns a new NodeList containing copies of all the nodes in the NodeList

    {glow.NodeList | string} css(property, value)
    Get / set a CSS property value
    		// get value from first node
    		glow("#subNav").css("display");
    		// set left padding to 10px on all nodes
    		glow("#subNav li").css("padding-left", "2em");
    		// where appropriate, px is assumed when no unit is passed
    		glow("#mainPromo").css("margin-top", 300);
    		// set multiple CSS values at once
    		// NOTE: Property names containing a hyphen such as font-weight must be quoted
    		glow("#myDiv").css({
    			'font-weight': 'bold',
    			'padding'	 : '10px',
    			'color'		 : '#00cc99'
    		});
    Parameters:
    {string | Object} property
    The CSS property name, or object of property-value pairs to set
    {string | number} value Optional
    The value to apply Number values will be treated as 'px' unless the CSS property accepts a unitless value. If value is omitted, the value for the given property will be returned
    Returns:
    {glow.NodeList | string} Returns the NodeList when setting value, or the CSS value when getting values. CSS values are strings. For instance, "height" will return "25px" for an element 25 pixels high. You can use parseInt to convert these values.

    {Object} data(key, val)
    Use this to safely attach arbitrary data to any DOM Element. This method is useful when you wish to avoid memory leaks that are possible when adding your own data directly to DOM Elements. When called with no arguments, will return glow's entire data store for the first node in this NodeList. Otherwise, when given a name, will return the associated value from the first node in this NodeList. When given both a name and a value, will store that data on every node in this NodeList. Optionally you can pass in a single object composed of multiple name, value pairs.
    	
    	glow("p").data("tea", "milky");
    	var colour = glow("p").data("tea"); // milky
    Parameters:
    {string|Object} key Optional
    The name of the value in glow's data store.
    {Object} val Optional
    The value you wish to associate with the given name.
    Returns:
    {Object} When setting a value this method can be chained, as in that case it will return itself.
    See:
    glow.NodeList#removeData
    glow.NodeList#removeData

    delegate(eventName, selector, callback, thisVal)
    Listen for an event occurring on child elements matching a selector. 'delegate' will catch events which occur on matching items created after the listener was added.
    			// Using 'on' to catch clicks on links in a list
    			glow.get('#nav a').on('click', function() {
    				// do stuff
    			});
    			
    			// The above adds a listener to each link, any links created later
    			// will not have this listener, so we won't hear about them.
    			
    			// Using 'delegate' to catch clicks on links in a list
    			glow.get('#nav').delegate('click', 'a', function() {
    				// do stuff
    			});
    			
    			// The above only adds one listener to #nav which tracks clicks
    			// to any links within. This includes elements created after 'delegate'
    			// was called.
    			// Using delegate to change class names on table rows so :hover
    			// behaviour can be emulated in IE6
    			glow.get('#contactData').delegate('mouseover', 'tr', function() {
    				glow.get(this).addClass('hover');
    			});
    			
    			glow.get('#contactData').delegate('mouseout', 'tr', function() {
    				glow.get(this).removeClass('hover');
    			});
    Parameters:
    {String} eventName
    Name of the event to listen for. This can be any regular DOM event ('click', 'mouseover' etc) or a special event of NodeList.
    {String} selector
    CSS selector of child elements to listen for events on For example, if you were wanting to hear events from links, this would be 'a'.
    {Function} callback
    Function to call when the event fires. The callback is passed a single event object. The type of this object is glow.DomEvent unless otherwise stated.
    {Object} thisVal Optional
    Value of 'this' within the callback. By default, this is the dom node matched by 'selector'.
    Returns:
    this

    {glow.NodeList} destroy()
    Removes each element from the document The element, attached listeners & attached data will be destroyed to free up memory. Detroyed elements may not be reused in some browsers.
    			// destroy all links in the document
    			glow("a").destroy();
    Returns:
    {glow.NodeList} An empty NodeList

    detach(eventName, callback)
    detach a listener from elements This will detach the listener from each dom node in the NodeList.
    			function clickListener(domEvent) {
    				// ...
    			}
    			
    			// adding listeners
    			glow.get('a').on('click', clickListener);
    			
    			// removing listeners
    			glow.get('a').detach('click', clickListener);
    Parameters:
    {String} eventName
    Name of the event to detach the listener from
    {Function} callback
    Listener callback to detach
    Returns:
    this

    detachDelegate(eventName, selector, callback)
    detach a delegated listener from elements This will detach the listener from each dom node in the NodeList.
    			function clickListener(domEvent) {
    				// ...
    			}
    			
    			// adding listeners
    			glow.get('#nav').delegate('click', 'a', clickListener);
    			
    			// removing listeners
    			glow.get('#nav').detachDelegate('click', 'a', clickListener);
    Parameters:
    {String} eventName
    Name of the event to detach the listener from
    {String} selector
    CSS selector of child elements the listener is listening to
    {Function} callback
    Listener callback to detach
    Returns:
    this

    {glow.NodeList} each(callback)
    Calls a function for each node in the list.
    			// add "link number: x" to each link, where x is the index of the link
    			glow("a").each(function(i, nodeList) {
    				glow(this).append(' link number: ' + i);
    			});
    			// breaking out of an each loop
    			glow("a").each(function(i, nodeList) {
    				// do stuff
    				if ( glow(this).hasClass('whatever') ) {
    					// we don't want to process any more links
    					return false;
    				}
    			});
    Parameters:
    {Function} callback
    The function to call for each node. The function will be passed 2 arguments, the index of the current item, and the NodeList being iterated over. Inside the function 'this' refers to the Node. Returning false from this function stops further iterations
    Returns:
    {glow.NodeList}

    {glow.NodeList} empty()
    Removes the nodes' contents
    			// remove the contents of all textareas
    			glow("textarea").empty();
    Returns:
    {glow.NodeList} Original nodes

    {boolean} eq(nodeList)
    Compares this NodeList to another Returns true if both NodeLists contain the same items in the same order
    			// the following returns true
    			glow('#blah').eq( document.getElementById('blah') );
    Parameters:
    {Node | Node[] | glow.NodeList} nodeList
    The NodeList to compare to.
    Returns:
    {boolean}
    See:
    for testing if a NodeList item matches a selector

    {glow.NodeList} filter(test)
    Filter the NodeList
    			// return images with a width greater than 320
    			glow("img").filter(function () {
    				return glow(this).width() > 320;
    			});
    			// Get items that don't have an alt attribute
    			myElementList.filter(':not([alt])');
    Parameters:
    {Function|string} test
    Filter test If a string is provided it's treated as a CSS selector. Elements which match the CSS selector are added to the new NodeList. If 'test' is a function, it will be called per node in the NodeList. The function is passed 2 arguments, the index of the current item, and the ElementList being itterated over. Inside the function 'this' refers to the node. Return true to add the element to the new NodeList.
    Returns:
    {glow.NodeList} A new NodeList containing the filtered nodes

    fire(eventName, event)
    Fire an event on dom nodes within the NodeList Note, this will only trigger event listeners to be called, it won't for example, move the mouse or click a link on the page.
    		   glow.get('#testLink').on('click', function() {
    			   alert('Link clicked!');
    		   });
    		   
    		   // The following causes 'Link clicked!' to be alerted, but doesn't
    		   // cause the browser to follow the link
    		   glow.get('#testLink').fire('click');
    Parameters:
    {String} eventName
    Name of the event to fire
    {glow.events.Event} event Optional
    Event object to pass into listeners. You can provide a simple object of key / value pairs which will be added as properties of a glow.events.Event instance.
    Returns:
    glow.events.Event

    {glow.NodeList} get(selector)
    Gets decendents of nodes that match a CSS selector.
    		// create a new NodeList
    		var myNodeList = glow.dom.create("
    Link
    "); // get 'a' tags that are decendants of the NodeList nodes myNewNodeList = myNodeList.get("a");
    Parameters:
    {String} selector
    CSS selector
    Returns:
    {glow.NodeList} Returns a new NodeList containing matched elements

    {boolean|undefined} hasAttr(name)
    Does the node have a particular attribute? The first node in this NodeList is tested.
    		if ( glow("#myImg").hasAttr("alt") ){
    			// ...
    		}
    Parameters:
    {string} name
    The name of the attribute to test for.
    Returns:
    {boolean|undefined} Returns undefined if the first node is not an element, or if the NodeList is empty, otherwise returns true/false to indicate if that attribute exists on the first element.

    {boolean} hasClass(name)
    Does the node have a particular class? The first node in this NodeList is tested.
    		if ( glow("#myInput").hasClass("errored") ){
    			// ...
    		}
    Parameters:
    {string} name
    The name of the class to test for.
    Returns:
    {boolean}

    {glow.NodeList | number} height(height)
    Gets / set element height Return value does not include the padding or border of the element in browsers supporting the correct box model. You can use this to easily get the height of the document or window, see example below.
    		// get the height of #myDiv
    		glow("#myDiv").height();
    		// set the height of list items in #myList to 200 pixels
    		glow("#myList > li").height(200);
    		// get the height of the document
    		glow(document).height();
    		// get the height of the window
    		glow(window).height();
    Parameters:
    {Number} height Optional
    New height in pixels for each element in the list If ommited, the height of the first element is returned
    Returns:
    {glow.NodeList | number} Height of first element, or original NodeList when setting heights.

    {glow.NodeList} hide()
    Hides all items in the NodeList.
    		// Hides all list items within #myList
    		glow("#myList li").hide();
    Returns:
    {glow.NodeList}

    {glow.NodeList | string} html(htmlString)
    Gets / sets HTML content Either gets content of the first element, or sets the content for all elements in the list
    			// get the html in #footer
    			var footerContents = glow("#footer").html();
    			// set a new footer
    			glow("#footer").html("Hello World!");
    Parameters:
    {String} htmlString Optional
    String to set as the HTML of elements If omitted, the html for the first element in the list is returned.
    Returns:
    {glow.NodeList | string} Returns the original NodeList when setting, or the HTML content when getting.

    {glow.NodeList} insertAfter(nodes)
    Insert this NodeList after the given nodes If inserting after more than one node, the NodeList is inserted after the first node and clones are inserted after the others.
    			// adds a paragraph after each heading
    			glow('

    HAI!

    ').insertAfter('h1, h2, h3');
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes
    Node(s) to insert after Strings will be treated as CSS selectors.
    Returns:
    {glow.NodeList} Inserted nodes.

    {glow.NodeList} insertBefore(nodes)
    Insert this NodeList before the given nodes If inserting before more than one node, the NodeList is inserted before the first node and clones are inserted before the others.
    			// adds a div before each paragraph
    			glow('
    Here comes a paragraph!
    ').insertBefore('p');
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes
    Node(s) to insert before Strings will be treated as CSS selectors.
    Returns:
    {glow.NodeList} Inserted nodes.

    {boolean} is(selector)
    Tests if the first element matches a CSS selector
    			if ( myNodeList.is(':visible') ) {
    				// ...
    			}
    Parameters:
    {string} selector
    CSS selector
    Returns:
    {boolean}

    {glow.NodeList} item(index)
    Get a single item from the list as an NodeList Negative numbers can be used to get items from the end of the list.
    			// get the html from the fourth element
    			myNodeList.item(3).html();
    			// add a class name to the last item
    			myNodeList.item(-1).addClass('last');
    Parameters:
    {number} index
    The numeric index of the node to return.
    Returns:
    {glow.NodeList} A new NodeList containing a single item

    {glow.ElementList} next(search)
    Gets the next sibling element for each node in the ElementList. If a filter is provided, the next item that matches the filter is returned, or none if no match is found.
    		// gets the element following #myLink (if there is one)
    		var next = glow.get("#myLink").next();
    		// get the next sibling link element after #skipLink
    		glow.get('#skipLink').next('a')
    Parameters:
    {string | HTMLElement | NodeList} search Optional
    Search value If provided, will seek the next sibling element until a match is found
    Returns:
    {glow.ElementList} A new ElementList containing the next sibling elements that match the (optional) filter.

    {Object} offset()
    Gets the offset from the top left of the document. If the NodeList contains multiple items, the offset of the first item is returned.
    		glow("#myDiv").offset().top
    Returns:
    {Object} Returns an object with "top" & "left" properties in pixels

    on(eventName, callback, thisVal)
    Listen for an event. This will listen for a particular event on each dom node in the NodeList. If you're listening to many children of a particular item, you may get better performance from glow.NodeList#delegate.
    		   glow.get('#testLink').on('click', function(domEvent) {
    			   // do stuff
    			   
    			   // if you want to cancel the default action (following the link)...
    			   return false;
    		   });
    Parameters:
    {String} eventName
    Name of the event to listen for. This can be any regular DOM event ('click', 'mouseover' etc) or a special event of NodeList.
    {Function} callback
    Function to call when the event fires. The callback is passed a single event object. The type of this object is glow.DomEvent unless otherwise stated.
    {Object} thisVal Optional
    Value of 'this' within the callback. By default, this is the dom node being listened to.
    Returns:
    this

    {glow.NodeList} parent(search)
    Gets the unique parent nodes of each node as a new NodeList.
    		// elements which contain links
    		var parents = glow.dom.get("a").parent();
    Parameters:
    {string | HTMLElement | NodeList} search Optional
    Search value If provided, will seek the next parent element until a match is found
    Returns:
    {glow.NodeList} Returns a new NodeList containing the parent nodes, with duplicates removed

    {Object} position()
    Get the top & left position of an element relative to its positioned parent This is useful if you want to make a position:static element position:absolute and retain the original position of the element
    		// get the top distance from the positioned parent
    		glow("#elm").position().top
    Returns:
    {Object} An object with 'top' and 'left' number properties

    {glow.NodeList} prepend(nodes)
    Prepends nodes to each node in this NodeList. If there is more than one node in this NodeList, then the given nodes are prepended to the first node and clones are prepended to the other nodes.
    			// prepends every paragraph with 'Paragraph: '
    			glow('p').prepend('Paragraph: ');
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes
    Nodes(s) to prepend Strings will be treated as HTML strings.
    Returns:
    {glow.NodeList} Original NodeList

    {glow.NodeList} prependTo(node)
    Prepends nodes in this NodeList to given node(s) If prepending to more than one node, the NodeList is prepended to the first node and clones are prepended to the others.
    			// prepends 'Paragraph: ' to every paragraph
    			glow('Paragraph: ').prependTo('p');
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} node
    Node(s) to prepend to Strings will be treated as CSS selectors or HTML strings.
    Returns:
    {glow.NodeList} The prepended nodes.

    {glow.ElementList} prev(search)
    Gets the previous sibling element for each node in the ElementList. If a filter is provided, the previous item that matches the filter is returned, or none if no match is found.
    		// gets the element before #myLink (if there is one)
    		var next = glow.get("#myLink").prev();
    		// get the previous sibling link element before #skipLink
    		glow.get('#skipLink').prev('a')
    Parameters:
    {string | HTMLElement | NodeList} search Optional
    Search value If provided, will seek the previous sibling element until a match is found
    Returns:
    {glow.ElementList} A new ElementList containing the previous sibling elements that match the (optional) filter.

    {string | glow.NodeList} prop(name, value)
    Gets or sets node properties. This function gets / sets node properties, to get attributes, see {@link glow.NodeList#attr NodeList#attr}. When getting a property, it is retrieved from the first node in this NodeList. Setting properties to each element in this NodeList. To set multiple properties in one call, pass in an object of name/value pairs.
    		var myNodeList = glow("#formElement");
    
    		// get the node name
    		myNodeList.prop("nodeName");
    
    		// set a property
    		myNodeList.prop("_secretValue", 10);
    
    		// set multiple properties
    		myNodeList.prop({
    			checked: true,
    			_secretValue: 10
    		});
    Parameters:
    {string | Object} name
    The name of the property, or an object of name/value pairs
    {string} value Optional
    The value to set the property to.
    Returns:
    {string | glow.NodeList} When setting properties it returns the NodeList, otherwise returns the property value.

    {glow.NodeList} push(nodes)
    Adds nodes to the NodeList
    			myNodeList.push('
    Foo
    ').push('h1');
    Parameters:
    {string | Node | Node[] | glow.NodeList} nodes
    Node(s) to add to the NodeList Strings will be treated as CSS selectors or HTML strings.
    Returns:
    {glow.NodeList}

    {glow.NodeList} remove()
    Removes each element from the document If you no longer need the elements, consider using {@link glow.NodeList#destroy destroy}
    			// take all the links out of a document
    			glow("a").remove();
    Returns:
    {glow.NodeList} The removed elements

    {glow.NodeList} removeAttr(name)
    Removes an attribute from each node.
    		glow("a").removeAttr("target");
    Parameters:
    {string} name
    The name of the attribute to remove.
    Returns:
    {glow.NodeList}

    {glow.NodeList} removeClass(name)
    Removes a class from each node.
    		glow("#footer #login a").removeClass("highlight");
    Parameters:
    {string} name
    The name of the class to remove.
    Returns:
    {glow.NodeList}

    removeData(key)
    Removes data previously added by glow.NodeList#data from each node in this NodeList. When called with no arguments, will delete glow's entire data store for each node in this NodeList. Otherwise, when given a name, will delete the associated value from each node in this NodeList.
    Parameters:
    {string} key Optional
    The name of the value in glow's data store.
    See:
    glow.NodeList#data

    {glow.NodeList} replaceWith(elements)
    Replace elements with another
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} elements
    Element(s) to insert into the document If there is more than one element in the NodeList, then the given elements replace the first element, clones are appended to the other elements.
    Returns:
    {glow.NodeList} The replaced elements Call {@link glow.NodeList#destroy destroy} on these if you no longer need them.

    {glow.NodeList | number} scrollLeft(val)
    Gets/sets the number of pixels the element has scrolled horizontally To get/set the scroll position of the window, use this method on a nodelist containing the window object.
    		// get the scroll left value of #myDiv
    		var scrollPos = glow("#myDiv").scrollLeft();
    		// scrollPos is a number, eg: 45
    		// set the scroll left value of #myDiv to 20
    		glow("#myDiv").scrollLeft(20);
    		// get the scrollLeft of the window
    		glow(window).scrollLeft();
    		// scrollPos is a number, eg: 45
    Parameters:
    {Number} val Optional
    New left scroll position Omit this to get the current scroll position
    Returns:
    {glow.NodeList | number} Current scrollLeft value, or NodeList when setting scroll position.

    {glow.NodeList | number} scrollTop(val)
    Gets/sets the number of pixels the element has scrolled vertically To get/set the scroll position of the window, use this method on a nodelist containing the window object.
    		// get the scroll top value of #myDiv
    		var scrollPos = glow("#myDiv").scrollTop();
    		// scrollPos is a number, eg: 45
    		// set the scroll top value of #myDiv to 20
    		glow("#myDiv").scrollTop(20);
    		// get the scrollTop of the window
    		glow(window).scrollTop();
    		// scrollPos is a number, eg: 45
    Parameters:
    {Number} val Optional
    New top scroll position Omit this to get the current scroll position
    Returns:
    {glow.NodeList | number} Current scrollTop value, or NodeList when setting scroll position.

    {glow.NodeList} show()
    Shows all hidden items in the NodeList.
    		// Show element with ID myDiv
    		glow("#myDiv").show();
    		// Show all list items within #myList
    		glow("#myList li").show();
    Returns:
    {glow.NodeList}

    {glow.NodeList} slice(start, end)
    Get a section of an NodeList Operates in the same way as an Array's slice method
    		var myNodeList = glow("

    "); myNodeList.slice(1, 2); // selects the paragraph myNodeList.slice(-1); // same thing, selects the paragraph
    Parameters:
    {number} start
    Start index If negative, it specifies a position measured from the end of the list
    {number} end Optional
    End index By default, this is the end of the list. A negative end specifies a position measured from the end of the list.
    Returns:
    {glow.NodeList} A new sliced NodeList

    {glow.NodeList} sort(func)
    Sort the elements in the list. Items will already be in document order if a CSS selector was used to fetch them.
    			//get links in alphabetical (well, lexicographical) order
    			var links = glow("a").sort(function(elementA, elementB) {
    				return glow(elementA).text() < glow(elementB).text() ? -1 : 1;
    			})
    Parameters:
    {Function} func Optional
    Function to determine sort order This function will be passed 2 elements (elementA, elementB). The function should return a number less than 0 to sort elementA lower than elementB and greater than 0 to sort elementA higher than elementB. If no function is provided, elements will be sorted in document order.
    Returns:
    {glow.NodeList} A new sorted NodeList

    {glow.NodeList | String} text(text)
    Gets / set the text content Either gets content of the first element, or sets the content for all elements in the list
    			// set text
    			var div = glow("
    ").text("Fun & games!"); //
    Func & games!
    			// get text
    			var mainHeading = glow('#mainHeading').text();
    Parameters:
    {String} text Optional
    String to set as the text of elements If omitted, the test for the first element in the list is returned.
    Returns:
    {glow.NodeList | String} Returns the original NodeList when setting, or the text content when getting.

    {glow.NodeList} toggleClass(name)
    Toggles a class on each node.
    		glow(".onOffSwitch").toggleClass("on");
    Parameters:
    {string} name
    The name of the class to toggle.
    Returns:
    {glow.NodeList}

    {glow.NodeList} unwrap()
    Removes the parent of each item in the list
    			// Before: 

    Hello

    // unwrap the given element glow("#mySpan").unwrap(); // After:
    Hello
    Returns:
    {glow.NodeList} The now unwrapped elements

    {glow.dom.NodeList | String | Object} val(value)
    Gets or sets form values for the first node.

    This method is not applicable to XML NodeLists.

    Getting values from form elements

    The returned value depends on the type of element, see below:
    Radio button or checkbox
    If checked, then the contents of the value attribute, otherwise an empty string.
    Select
    The contents of value attribute of the selected option
    Select (multiple)
    An array of selected option values.
    Other form element
    The value of the input.

    Getting values from a form

    If the first element in the NodeList is a form, then an object is returned containing the form data. Each item property of the object is a value as above, apart from when multiple elements of the same name exist, in which case the it will contain an array of values.

    Setting values for form elements

    If a value is passed and the first element of the NodeList is a form element, then the form element is given that value. For select elements, this means that the first option that matches the value will be selected. For selects that allow multiple selection, the options which have a value that exists in the array of values/match the value will be selected and others will be deselected. Currently checkboxes and radio buttons are not checked or unchecked, just their value is changed. This does mean that this does not do exactly the reverse of getting the value from the element (see above) and as such may be subject to change

    Setting values for forms

    If the first element in the NodeList is a form and the value is an object, then each element of the form has its value set to the corresponding property of the object, using the method described above.
    		// get a value
    		var username = glow.dom.get("input#username").val();
    		
    		/ get values from a form
    		var userDetails = glow.dom.get("form").val();
    		// set a value
    		glow.dom.get("input#username").val("example username");
    		// set values in a form
    		glow.dom.get("form").val({
    			username : "another",
    			name     : "A N Other"
    		});
    Parameters:
    {String | Object} value Optional
    The value to set the form element/elements to.
    Returns:
    {glow.dom.NodeList | String | Object} When used to set a value it returns the NodeList, otherwise returns the value as described above.

    {glow.NodeList | number} width(width)
    Gets / set element width Return value does not include the padding or border of the element in browsers supporting the correct box model. You can use this to easily get the width of the document or window, see example below.
    		// get the width of #myDiv
    		glow("#myDiv").width();
    		// set the width of list items in #myList to 200 pixels
    		glow("#myList > li").width(200);
    		// get the width of the document
    		glow(document).width();
    		// get the width of the window
    		glow(window).width();
    Parameters:
    {Number} width Optional
    New width in pixels for each element in the list If ommited, the width of the first element is returned
    Returns:
    {glow.NodeList | number} width of first element, or original NodeList when setting widths.

    {glow.NodeList} wrap(wrapper)
    Wraps the given NodeList with the specified element(s). The given NodeList items will always be placed in the first child element that contains no further elements. Each item in a given NodeList will be wrapped individually.
    			// Hello
    			glow("#mySpan").wrap("

    "); // Makes: //
    //

    // Hello //

    //
    Parameters:
    {string | HTMLElement | HTMLElement[] | glow.NodeList} wrapper
    Element to use as a wrapper Strings will be treated as HTML strings if they begin with <, else they'll be treated as a CSS selector.
    Returns:
    {glow.NodeList} The NodeList with new wrapper parents
    Event Detail
    keydown(event)
    Fires when the user presses a key Only fires if the element has focus, listen for this event on the document to catch all keydowns. This event related to the user pressing a key on the keyboard, if you're more concerned about the character entered, see the {@link glow.NodeList#event:keypress keypress} event. keydown will only fire once, when the user presses the key. The order of events is keydown, keypress*, keyup. keypress may fire many times if the user holds the key down.
    Parameters:
    {glow.events.KeyboardEvent} event
    Event Object

    keypress(event)
    Fires when a key's command executes. For instance, if you hold down a key, it's action will occur many times. This event will fire on each action. This event is useful when you want to react to keyboard repeating, or to detect when a character is entered into a field. The order of events is keydown, keypress*, keyup. keypress may fire many times if the user holds the key down.
    Parameters:
    {glow.events.KeyboardEvent} event
    Event Object

    keyup(event)
    Fires when the user releases a key Only fires if the element has focus, listen for this event on the document to catch all keyups. This event related to the user pressing a key on the keyboard, if you're more concerned about the character entered, see the {@link glow.NodeList#event:keypress keypress} event. The order of events is keydown, keypress*, keyup. keypress may fire many times if the user holds the key down.
    Parameters:
    {glow.events.KeyboardEvent} event
    Event Object

    mouseenter(event)
    Fires when the mouse enters the element specifically, does not bubble
    Parameters:
    {glow.events.DomEvent} event
    Event Object

    mouseleave(event)
    Fires when the mouse leaves the element specifically, does not bubble
    Parameters:
    {glow.events.DomEvent} event
    Event Object

    Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:24 GMT-0000 (GMT)
    docs/symbols/glow.util.html100644 0 0 25036 11343467400 13467 0ustar 0 0 JsDoc Reference - glow.util
    Class Index | File Index

    Classes


    Namespace glow.util


    Defined in: core.js.

    Namespace Summary
    Constructor Attributes Constructor Name and Description
     
    Core JavaScript helpers
    Method Summary
    Method Attributes Method Name and Description
    <static>  
    glow.util.apply(destination, source)
    Copies properties from one object to another All properties from 'source' will be copied onto 'destination', potentially overwriting existing properties on 'destination'.
    <static>  
    glow.util.extend(sub, base, additionalProperties)
    Copies the prototype of one object to another The 'subclass' can also access the 'base class' via subclass.base
    Namespace Detail
    glow.util
    Core JavaScript helpers
    Method Detail
    <static> {Object} glow.util.apply(destination, source)
    Copies properties from one object to another All properties from 'source' will be copied onto 'destination', potentially overwriting existing properties on 'destination'. Properties from 'source's prototype chain will not be copied.
    			var obj = glow.util.apply({foo: "hello", bar: "world"}, {bar: "everyone"});
    			//results in {foo: "hello", bar: "everyone"}
    Parameters:
    {Object} destination
    Destination object
    {Object} source
    Properties of this object will be copied onto the destination
    Returns:
    {Object} The destination object.

    <static> glow.util.extend(sub, base, additionalProperties)
    Copies the prototype of one object to another The 'subclass' can also access the 'base class' via subclass.base
    			function MyClass(arg) {
    				this.prop = arg;
    			}
    			MyClass.prototype.showProp = function() {
    				alert(this.prop);
    			};
    			function MyOtherClass(arg) {
    				//call the base class's constructor
    				arguments.callee.base.apply(this, arguments);
    			}
    			glow.util.extend(MyOtherClass, MyClass, {
    				setProp: function(newProp) {
    					this.prop = newProp;
    				}
    			});
    
    			var test = new MyOtherClass("hello");
    			test.showProp(); // alerts "hello"
    			test.setProp("world");
    			test.showProp(); // alerts "world"
    Parameters:
    {Function} sub
    Class which inherits properties.
    {Function} base
    Class to inherit from.
    {Object} additionalProperties
    An object of properties and methods to add to the subclass.

    Documentation generated by JsDoc Toolkit 2.3.2 on Wed Mar 03 2010 14:15:24 GMT-0000 (GMT)
    docs/symbols/src/build_2.0.0-alpha1_core.js.html100644 0 0 5113163 11343467400 17001 0ustar 0 0
      1 /*!
      2 	Copyright 2010 British Broadcasting Corporation
      3 
      4 	Licensed under the Apache License, Version 2.0 (the "License");
      5 	you may not use this file except in compliance with the License.
      6 	You may obtain a copy of the License at
      7 
      8 	   http://www.apache.org/licenses/LICENSE-2.0
      9 
     10 	Unless required by applicable law or agreed to in writing, software
     11 	distributed under the License is distributed on an "AS IS" BASIS,
     12 	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 	See the License for the specific language governing permissions and
     14 	limitations under the License.
     15 */
     16 /**
     17 	@name glow
     18 	@namespace
     19 	@version @VERSION@
     20 	@description The glow library namespace
     21 		The library can also be used as a function, which is a shortcut to
     22 		{@link glow.NodeList}.
     23 		
     24 	@example
     25 		var links = glow('a');
     26 		// is the same as
     27 		var links = new glow.NodeList('a');
     28 */
     29 if (!window.Glow) { // loading packages via user SCRIPT tags?
     30 	window.Glow = {
     31 		provide: function(f) {
     32 			f(glow);
     33 		},
     34 		complete: function(n, version) {
     35 			glow.version = version;
     36 		}
     37 	};
     38 	
     39 	window.glow = function(nodeListContents) {
     40 		return new glow.NodeList(nodeListContents);
     41 	};
     42 	
     43 	glow.load = function() {
     44 		throw new Error('Method load() is not available without glow.js');
     45 	}
     46 }
     47 
     48 
     49 Glow.provide(function(glow) {
     50 	/*!debug*/
     51 	var glowbug = {
     52 	errors: []
     53 	,
     54 	log: function(message, fileName, lineNumber) {
     55 		this._add('Log', message, fileName, lineNumber);
     56 	}
     57 	,
     58 	warn: function(message, fileName, lineNumber) {
     59 		this._add('Warn', message, fileName, lineNumber);
     60 	}
     61 	,
     62 	error: function(message, fileName, lineNumber) {
     63 		this._add('Error', message, fileName, lineNumber);
     64 	}
     65 	,
     66 	_add: function(level, message, fileName, lineNumber) {
     67 		var e = new Error(message, fileName, lineNumber);
     68 		
     69 		e.message = message;
     70 		e.name = 'Glow'+level;
     71 		e.level = level.toLowerCase();
     72 		
     73 		var match = /\[([^\]]+)\]/.exec(message);
     74 		if (match) e.type = match[1]; // may be undefined
     75 		else e.type = 'message';
     76 		
     77 		this.errors.push(e);
     78 		
     79 		this.out(e);
     80 	}
     81 	,
     82 	out: function(e) {
     83 		var message = '['+e.level+'] '+e.message;
     84 		
     85 		if (window.console) {
     86 			if (e.level === 'warn' && window.console.warn) {
     87 				console.warn(message);
     88 			}
     89 			else if (e.level === 'error' && window.console.error) {
     90 				console.error(message);
     91 			}
     92 			else if (window.console.log) {
     93 				console.log(message);
     94 			}
     95 		}
     96 		else if (window.opera && opera.postError) {
     97 			opera.postError(message);
     98 		}
     99 		else { // use our own console
    100 			glowbug.console.log(e.level, message);
    101 		}
    102 	}
    103 };
    104 
    105 glowbug.console = {
    106 	messages: [],
    107 	log: function(level, message) {
    108 		if (!this._w) {
    109 			try {
    110 				this._w = window.open('', 'report', 'width=350,height=250,menubar=0,toolbar=0,location=no,status=0,scrollbars=1,resizable=1');
    111 				this._w.document.write(
    112 					'<html><head><title>Console<\/title><style>body{background-color: #ddd;} .message{background-color:#FFF;padding:4px;margin:0px;border-bottom:1px solid #ccc;} .warn {background-color: #E5E6B6;} .error{background-color: #D39C9E;}<\/style><\/head>'
    113 					+ '<body style="font: 11px monaco"><code id="messages"><\/code><\/body><\/html>'
    114 				)
    115 				this._w.document.close();
    116 			}
    117 			catch(ignored) {
    118 				this._w = null;
    119 			}
    120 		}
    121 		
    122 		if (this._w) {
    123 			var p = this._w.document.createElement('P');
    124 			p.className = 'message ' + level;
    125 			p.innerHTML = message;
    126 			this._w.document.getElementById('messages').appendChild(p);
    127 			
    128 			var dh = this._w.document.body.scrollHeight
    129 			var ch = this._w.document.body.clientHeight
    130 			if (dh > ch) { this._w.scrollTo(0, dh-ch); }
    131 		}
    132 	}
    133 }
    134 	if (typeof glowbug != 'undefined') { glow.debug = glowbug; }
    135 	/*gubed!*/
    136 });
    137 Glow.provide(function(glow) {
    138 	/**
    139 		@name glow.env
    140 		@namespace
    141 		@description Information about the browser and characteristics
    142 	*/
    143 	
    144 	// parse the useragent string, setting NaN if match isn't found
    145 	var ua = navigator.userAgent.toLowerCase(),
    146 		nanArray = [0, NaN],
    147 		opera  = (/opera[\s\/]([\w\.]+)/.exec(ua) || nanArray)[1],
    148 		ie     = opera ? NaN : (/msie ([\w\.]+)/.exec(ua) || nanArray)[1],
    149 		gecko  = (/rv:([\w\.]+).*gecko\//.exec(ua) || nanArray)[1],
    150 		webkit = (/applewebkit\/([\w\.]+)/.exec(ua) || nanArray)[1],
    151 		khtml  = (/khtml\/([\w\.]+)/.exec(ua) || nanArray)[1],
    152 		toNumber = parseFloat,
    153 		env = {};
    154 	
    155 	/**
    156 		@name glow.env.gecko
    157 		@type number
    158 		@description Gecko version number to one decimal place (eg 1.9) or NaN on non-gecko browsers.
    159 			The most popular browser using the Gecko engine is Firefox.
    160 		
    161 		@see <a href="http://en.wikipedia.org/wiki/Gecko_(layout_engine)#Usage">Versions of Gecko used by browsers</a>
    162 		
    163 		@example
    164 			if (glow.env.gecko < 1.9) {
    165 				// runs in Firefox 2 and other browsers that use Gecko earlier than 1.9
    166 			}
    167 	*/
    168 	env.gecko = toNumber(gecko);
    169 	
    170 	/**
    171 		@name glow.env.ie
    172 		@type number
    173 		
    174 		@description IE version number to one decimal place (eg 6.0) or NaN on non-IE browsers.
    175 			This number will also be populated for browser based on IE's trident engine
    176 			
    177 		@example
    178 			if (glow.env.ie < 9) {
    179 				// runs in IE pre-9.0
    180 				glow('#content').css('background', 'deadmoomin.png');
    181 			}
    182 	*/
    183 	env.ie = toNumber(ie);
    184 	
    185 	/**
    186 		@name glow.env.opera
    187 		@type number
    188 		
    189 		@description Opera version number to one decimal place (eg 10.0) or NaN on non-Opera browsers.
    190 		
    191 		@example
    192 			if (glow.env.opera < 10) {
    193 				// runs in Opera pre-10.0
    194 			}
    195 	*/
    196 	env.opera = toNumber(opera);
    197 	
    198 	/**
    199 		@name glow.env.webkit
    200 		@type number
    201 		
    202 		@description Webkit version number to one decimal place (eg 531.9) or NaN on non-Webkit browsers.
    203 			Safari and Google Chrome are the most popular browsers using Webkit.
    204 			
    205 		@see <a href="http://en.wikipedia.org/wiki/Safari_version_history#Release_history">Versions of Webkit used by Safari</a>
    206 		@see <a href="http://en.wikipedia.org/wiki/Google_Chrome#Release_history">Versions of Webkit used by Google Chrome</a>
    207 			
    208 		@example
    209 			if (glow.env.webkit < 526) {
    210 				// runs in Safari pre-4.0, and Chrome pre-1.0
    211 			}
    212 	*/
    213 	env.webkit = toNumber(webkit);
    214 
    215 	/**
    216 		@name glow.env.khtml
    217 		@type number
    218 		
    219 		@description KHTML version number to one decimal place or NaN on non-KHTML browsers.
    220 			Konqueror is the most popular browsers using KHTML.
    221 	*/
    222 	env.khtml = toNumber(khtml);
    223 	
    224 	/**
    225 		@name glow.env.standardsMode
    226 		@type boolean
    227 		@description True if the browser reports itself to be in 'standards mode'
    228 			Otherwise, the browser is in 'quirks mode'
    229 			
    230 		@see <a href="http://en.wikipedia.org/wiki/Quirks_mode">Quirks Mode vs Standards Mode</a>
    231 	*/
    232 	env.standardsMode = document.compatMode != "BackCompat" && (!env.ie || env.ie >= 6);
    233 	
    234 	/**
    235 		@name glow.env.version
    236 		@type string
    237 		@description Version number of the browser in use as a string.
    238 			This caters for version numbers that aren't 'real' numbers, like "7b" or "1.9.1"
    239 	*/
    240 	env.version = ie || gecko || webkit || opera || khtml || '';
    241 	
    242 	// export
    243 	glow.env = env;
    244 });
    245 // start-source: core/ready.js
    246 /*debug*///log.info('executing core/ready.js');
    247 Glow.provide(
    248 	function(glow) {
    249 		var readyQueue = [],
    250 			domReadyQueue = [],
    251 			blockersActive = 0,
    252 			processingReadyQueue = false;
    253 			
    254 		glow._readyBlockers = {};
    255 		
    256  		/*debug*///log.info('overwriting Glow ready with glow.ready');	
    257 		glow.ready = function (f) { /*debug*///log.info('glow.ready()');
    258 			if (this.isReady) {
    259 				f();
    260 			}
    261 			else {
    262 				readyQueue.push(f);
    263 			}
    264 			return glow;
    265 		};
    266 		
    267 		glow.onDomReady = function(f) {
    268 			//just run function if already ready
    269 			if (glow.isDomReady) {
    270 				f();
    271 			}
    272 			else {
    273 				domReadyQueue.push(f);
    274 			}
    275 		};
    276 		
    277 		glow._addReadyBlock = function(name) { /*debug*///log.info('_addReadyBlock('+name+')');
    278 			if (typeof glow._readyBlockers[name] === 'undefined') { glow._readyBlockers[name] = 0; }
    279 			glow._readyBlockers[name]++;
    280 			glow.isReady = false;
    281 			blockersActive++; /*debug*///log.info('  » blockersActive '+blockersActive+'.');
    282 			return glow;
    283 		}
    284 			
    285 		glow._removeReadyBlock = function(name) { /*debug*///log.info('_removeReadyBlock('+name+')');
    286 			if (glow._readyBlockers[name]) {
    287 				glow._readyBlockers[name]--;
    288 				blockersActive--;  /*debug*///log.info('  » blockersActive '+blockersActive+'.');
    289 				// if we're out of blockers
    290 				if (!blockersActive) {
    291 					// call our queue
    292 					glow.isReady = true;
    293 					runReadyQueue();
    294 				}
    295 			}
    296 			return glow;
    297 		}
    298 		
    299 		// add blockers for any packages that started loading before core (this package) was built
    300 		if (glow._build) { // only defined when using big Glow
    301 			for (var i = 0, len = glow._build.loading.length; i < len; i++) {
    302 				glow._addReadyBlock('glow_loading_'+glow._build.loading[i]);
    303 			}
    304 			
    305 			for (var j = 0, lenj = glow._build.callbacks.length; j < lenj; j++) {
    306 				if (glow._addReadyBlock) { glow._addReadyBlock('glow_loading_loadedcallback'); }
    307 			}
    308 		}
    309 		
    310 		function runDomReadyQueue() { /*debug*///log.info('runDomReadyQueue()');
    311 			glow.isDomReady = true;
    312 			// run all functions in the array
    313 			for (var i = 0, len = domReadyQueue.length; i < len; i++) {
    314 				domReadyQueue[i]();
    315 			}
    316 		}
    317 	
    318 		function runReadyQueue() { /*debug*///log.info('runReadyQueue()');
    319 			// if we're already processing the queue, just exit, the other instance will take care of it
    320 			if (processingReadyQueue) { return; }
    321 			
    322 			/*debug*///log.info('readyQueue: '+readyQueue.length);
    323 			processingReadyQueue = true;
    324 			while (readyQueue.length) {
    325 				var callback = readyQueue.shift();
    326 				/*debug*///log.info('callback: '+callback);
    327 				callback(glow);
    328 				
    329 				// check if the previous function has created a blocker
    330 				if (blockersActive) { break; }
    331 			}
    332 			processingReadyQueue = false;
    333 		}
    334 		
    335 		/**
    336 			@private
    337 			@function
    338 			@name bindReady
    339 			@description Add listener to document to detect when page is ready.
    340 		 */
    341 		function bindReady() { /*debug*///log.info('bindReady()');
    342 			//don't do this stuff if the dom is already ready
    343 			if (glow.isDomReady) { return; }
    344 			glow._addReadyBlock('glow_domReady'); // wait for dom to be ready
    345 			
    346 			function onReady() { /*debug*///log.info('onReady()');
    347 				runReadyQueue();
    348 				glow._removeReadyBlock('glow_domReady');
    349 			}
    350 					
    351 			if (document.readyState == 'complete') { // already here!
    352 				 /*debug*///log.info('already complete');
    353 				onReady();
    354 			}
    355 			else if (glow.env.ie && document.attachEvent) { /*debug*///log.info('bindready() - document.attachEvent');
    356 				// like IE
    357 				
    358 				// not an iframe...
    359 				if (document.documentElement.doScroll && window == top) {
    360 					(function() {  /*debug*///log.info('doScroll');
    361 						try {
    362 							document.documentElement.doScroll('left');
    363 						}
    364 						catch(error) {
    365 							setTimeout(arguments.callee, 0);
    366 							return;
    367 						}
    368 				
    369 						// and execute any waiting functions
    370 						onReady();
    371 					})();
    372 				}
    373 				else {
    374 					// an iframe...
    375 					document.attachEvent(
    376 						'onreadystatechange',
    377 						function() { /*debug*///log.info('onreadystatechange');
    378 							if (document.readyState == 'complete') {
    379 								document.detachEvent('onreadystatechange', arguments.callee);
    380 								onReady();
    381 							}
    382 						}
    383 					);
    384 				}
    385 			}
    386 			else if (document.readyState) { /*debug*///log.info('bindready() - document.readyState');
    387 				// like pre Safari
    388 				(function() { /*debug*///log.info('loaded|complete');
    389 					if ( /loaded|complete/.test(document.readyState) ) {
    390 						onReady();
    391 					}
    392 					else {
    393 						setTimeout(arguments.callee, 0);
    394 					}
    395 				})();
    396 			}
    397 			else if (document.addEventListener) {/*debug*///log.info('bindready() - document.addEventListener');
    398 				// like Mozilla, Opera and recent webkit
    399 				document.addEventListener( 
    400 					'DOMContentLoaded',
    401 					function(){ /*debug*///log.info('glow DOMContentLoaded');
    402 						document.removeEventListener('DOMContentLoaded', arguments.callee, false);
    403 						onReady();
    404 					},
    405 					false
    406 				);
    407 			}
    408 			else {
    409 				throw new Error('Unable to bind glow ready listener to document.');
    410 			}
    411 		}
    412 	
    413 		glow.notSupported = ( // here are the browsers we don't support
    414 			glow.env.ie < 6 ||
    415 			(glow.env.gecko < 1.9 && !/^1\.8\.1/.test(env.version)) ||
    416 			glow.env.opera < 9 ||
    417 			glow.env.webkit < 412
    418 		);
    419 		// deprecated
    420 		glow.isSupported = !glow.notSupported;
    421 		
    422 		// block 'ready' if browser isn't supported
    423 		if (glow.notSupported) {
    424 			glow._addReadyBlock('glow_browserSupport');
    425 		}
    426 		
    427 		bindReady();
    428 	}
    429 );
    430 // end-source: core/ready.js
    431 /**
    432 	@name glow.util
    433 	@namespace
    434 	@description Core JavaScript helpers
    435 */
    436 Glow.provide(function(glow) {
    437 	var util = {},
    438 		undefined;
    439 	
    440 	/**
    441 		@name glow.util.apply
    442 		@function
    443 		@description Copies properties from one object to another
    444 			All properties from 'source' will be copied onto
    445 			'destination', potentially overwriting existing properties
    446 			on 'destination'.
    447 			
    448 			Properties from 'source's prototype chain will not be copied.
    449 		
    450 		@param {Object} destination Destination object
    451 		
    452 		@param {Object} source Properties of this object will be copied onto the destination
    453 		
    454 		@returns {Object} The destination object.
    455 		
    456 		@example
    457 			var obj = glow.util.apply({foo: "hello", bar: "world"}, {bar: "everyone"});
    458 			//results in {foo: "hello", bar: "everyone"}
    459 	*/
    460 	util.apply = function(destination, source) {
    461 		/*!debug*/
    462 			if (arguments.length != 2) {
    463 				glow.debug.warn('[wrong count] glow.util.apply expects 2 arguments, not '+arguments.length+'.');
    464 			}
    465 			if (typeof destination != 'object') {
    466 				glow.debug.warn('[wrong type] glow.util.apply expects argument "destination" to be of type object, not ' + typeof destination + '.');
    467 			}
    468 			if (typeof source != 'object') {
    469 				glow.debug.warn('[wrong type] glow.util.apply expects argument "source" to be of type object, not ' + typeof source + '.');
    470 			}
    471 		/*gubed!*/
    472 		for (var i in source) {
    473 			if ( source.hasOwnProperty(i) ) {
    474 				destination[i] = source[i];
    475 			}
    476 		}
    477 		return destination;
    478 	};
    479 	
    480 	/**
    481 		@name glow.util.extend
    482 		@function
    483 		@description Copies the prototype of one object to another
    484 			The 'subclass' can also access the 'base class' via subclass.base
    485 
    486 		@param {Function} sub Class which inherits properties.
    487 		@param {Function} base Class to inherit from.
    488 		@param {Object} additionalProperties An object of properties and methods to add to the subclass.
    489 
    490 		@example
    491 			function MyClass(arg) {
    492 				this.prop = arg;
    493 			}
    494 			MyClass.prototype.showProp = function() {
    495 				alert(this.prop);
    496 			};
    497 			function MyOtherClass(arg) {
    498 				//call the base class's constructor
    499 				arguments.callee.base.apply(this, arguments);
    500 			}
    501 			glow.util.extend(MyOtherClass, MyClass, {
    502 				setProp: function(newProp) {
    503 					this.prop = newProp;
    504 				}
    505 			});
    506 
    507 			var test = new MyOtherClass("hello");
    508 			test.showProp(); // alerts "hello"
    509 			test.setProp("world");
    510 			test.showProp(); // alerts "world"
    511 	*/
    512 	util.extend = function(sub, base, additionalProperties) {
    513 		/*!debug*/
    514 			if (arguments.length < 2) {
    515 				glow.debug.warn('[wrong count] glow.util.extend expects at least 2 arguments, not '+arguments.length+'.');
    516 			}
    517 			if (typeof sub != 'function') {
    518 				glow.debug.error('[wrong type] glow.util.extend expects argument "sub" to be of type function, not ' + typeof sub + '.');
    519 			}
    520 			if (typeof base != 'function') {
    521 				glow.debug.error('[wrong type] glow.util.extend expects argument "base" to be of type function, not ' + typeof base + '.');
    522 			}
    523 		/*gubed!*/
    524 		var f = function () {}, p;
    525 		f.prototype = base.prototype;
    526 		p = new f();
    527 		sub.prototype = p;
    528 		p.constructor = sub;
    529 		sub.base = base;
    530 		if (additionalProperties) {
    531 			util.apply(sub.prototype, additionalProperties);
    532 		}
    533 	};
    534 	
    535 	// export
    536 	glow.util = util;
    537 });
    538 Glow.provide(function(glow) {
    539 	/**
    540 	@name glow.events
    541 	@namespace
    542 	@description Handling custom events
    543 	*/
    544 	var events = {};
    545 		
    546 	/* storage variables */
    547 	
    548 	var eventListeners = {}, 
    549 		eventId = 1, /* TODO: camelCase */
    550 		objIdCounter = 1, 
    551 		eventKey = '__eventId' + glow.UID; 
    552 		
    553 	
    554 	/**
    555 	@name glow.events.addListeners
    556 	@function
    557 	@param {Object[]} attachTo Array of objects to add listeners to.
    558 	@param {string} name Name of the event to listen for.
    559 		Event names are case sensitive.
    560 	@param {function} callback Function to call when the event is fired.
    561 		The callback will be passed a single event object. The type of this
    562 		object depends on the event (see documentation for the event
    563 		you're listening to).
    564 	@param {Object} [thisVal] Value of 'this' within the callback.
    565 		By default, this is the object being listened to.
    566 	@see glow.events.Target#fire
    567 	@description Convenience method to add listeners to many objects at once.
    568 		If you want to add a listener to a single object, use its
    569 		'on' method.
    570 	*/
    571 	events.addListeners = function (attachTo, name, callback, thisVal) {
    572 		var listenerIds = [],
    573 			objIdent,
    574 			listener,
    575 			eventsOnObject,
    576 			currentListeners;
    577 	
    578 		//attach the event for each element, return an array of listener ids
    579 		var i = attachTo.length;
    580 		while (i--) {
    581 			objIdent = attachTo[i][eventKey];
    582 			if (!objIdent){
    583 				objIdent = attachTo[i][eventKey] = objIdCounter++;
    584 			}
    585 					
    586 			listener = [ callback, thisVal ];
    587 			eventsOnObject = eventListeners[objIdent];
    588 			if(!eventsOnObject){
    589 				eventsOnObject = eventListeners[objIdent] = {};
    590 			}
    591 					
    592 			currentListeners = eventsOnObject[name];
    593 			if(!currentListeners){
    594 				eventsOnObject[name] = [listener];
    595 			}
    596 			else{
    597 				currentListeners[currentListeners.length] = listener;
    598 			}							
    599 		}
    600 		return events;
    601 	};
    602 	
    603 	events._getPrivateEventKey = function(node) {
    604 		if (!node[eventKey]) {
    605 			node[eventKey] = objid++;
    606 		}
    607 		
    608 		return node[eventKey];
    609 	}
    610 	
    611 	/**
    612 	@name glow.events.fire
    613 	@function
    614 	@param {Object[]} items      Array of objects to add listeners to
    615 	@param {string}   eventName  Name of the event to fire
    616 	@param {glow.events.Event|Object} [event] Event object to pass into listeners.
    617        You can provide a simple object of key-value pairs which will
    618        be added as properties on the glow.events.Event instance.
    619 		
    620 	@description Convenience method to fire events on multiple items at once.
    621 		If you want to fire events on a single object, use its
    622 		'fire' method.
    623 	*/
    624 		
    625 	events.fire = function (items, eventName, event) {
    626 		if (! event) {
    627 			event = new events.Event();
    628 		}
    629 		else if ( event.constructor === Object ) {
    630 			event = new events.Event( event )
    631 		}
    632 		
    633 		// for loop, because order matters!
    634 		for(var i = 0, len = items.length; i < len; i++) { 
    635 			callListeners(items[i], eventName, event);
    636 		}
    637 			
    638 		return event;
    639 	};
    640 
    641 	
    642 	/**
    643 	 @name glow.events-callListeners
    644 	 @private
    645 	*/
    646 	function callListeners(item, eventName, event, thisVal) {
    647 		var objIdent = item[eventKey],
    648 			listenersForEvent,
    649 			returnedVal;			
    650 
    651 		if (!objIdent || !eventListeners[objIdent]) {
    652 			return event;
    653 		}
    654 				
    655 		listenersForEvent = eventListeners[objIdent][eventName];
    656 			
    657 		if (!listenersForEvent) {
    658 			return event;
    659 		}
    660 		// Slice to make sure we get a unique copy.
    661 		listenersForEvent = listenersForEvent.slice(0);
    662 		for (var i = 0, len = listenersForEvent.length; i < len; i++){
    663 			returnVal = listenersForEvent[i][0].call((listenersForEvent[i][1] || thisVal || item), event);
    664 			if (returnVal === false){
    665 				event.preventDefault();
    666 			}
    667 		}
    668 			
    669 		return event;
    670 	}
    671 	events._callListeners = callListeners;
    672 		
    673 		
    674 	/**
    675 	@name glow.events.removeAllListeners
    676 	@function
    677 	@param {Object[]} items Items to remove events from		    
    678 	@description Removes all listeners attached to a given object.
    679 		This removes not only listeners you added, but listeners others
    680 		added too. For this reason it should only be used as part of a cleanup
    681 		operation on objects that are about to be destroyed.
    682 	*/
    683 	
    684 	events.removeAllListeners = function (items) {
    685 		var objIdent,
    686 		i = items.length;		
    687 		
    688 		while(i--){
    689 			
    690 			objIdent = items[i][eventKey];
    691 			
    692 			if (!objIdent) {
    693 				return false;
    694 			}
    695 			else {
    696 				delete eventListeners[objIdent];
    697 			}
    698 		}
    699 
    700 		return true;
    701 	};
    702 
    703 
    704 	/**
    705 	@name glow.events.removeListeners
    706 	@function
    707 	@param {Object[]} items Items to remove events from.
    708 	@param {string} eventName Name of the event to remove.
    709 	@param {function} callback A reference to the original callback used when the listener was added.
    710 	@decription Removes listeners for an event.
    711 	*/
    712 	events.removeListeners = function (item, eventName, callback) { /* TODO: items! */
    713 		var objIdent,
    714 			listenersForEvent,
    715 			i = item.length;
    716 		
    717 	
    718 		while(i--){
    719 			
    720 			objIdent = item[i][eventKey];
    721 				
    722 			if(!objIdent || !eventListeners[objIdent]){
    723 				return events;
    724 			}
    725 			
    726 		
    727 			listenersForEvent = eventListeners[objIdent][eventName];
    728 			if(!listenersForEvent){
    729 				return events;
    730 			}
    731 			
    732 			// for loop, because order matters
    733 			for(var j = 0, lenj = listenersForEvent.length; j < lenj; j++){						
    734 				if (listenersForEvent[j][0] === callback){
    735 					listenersForEvent.splice(j, 1);
    736 					break;
    737 				}
    738 		
    739 			}
    740 		}
    741 		
    742 		return events;			
    743 	};
    744 	
    745 	/**
    746 		Copies the events from one nodelist to another
    747 		@private
    748 		@name glow.events._copyEvent
    749 		@see glow.NodeList#clone
    750 		@function
    751 	*/
    752 	events._copyEvent = function(from, to){
    753 		var listenersToCopy,
    754 		i = [from].length,
    755 		listenersForEvent,
    756 		name,
    757 		callback,
    758 		thisVal;
    759 		
    760 		while(i--){
    761 			
    762 			var objIdent = [from][i][eventKey];
    763 			
    764 			listenersForEvent = eventListeners[objIdent];
    765 			
    766 				
    767 			if(!objIdent){
    768 					
    769 				return false;
    770 			}
    771 			else{
    772 				for ( var eventName in eventListeners[objIdent] ) {
    773 					name = eventName;
    774 					callback = eventListeners[objIdent][eventName][0][0];
    775 					thisVal = eventListeners[objIdent][eventName][0][1];
    776 				}				
    777 				events._addDomEventListener([to], name, callback, thisVal);
    778 		}
    779 	
    780 		return;
    781 		}
    782 		
    783 	}
    784 	///**
    785 	//@name glow.events.getListeners
    786 	//@function
    787 	//@param {Object[]} item Item to find events for
    788 	//@decription Returns a list of listeners attached for the given item.
    789 	//
    790 	//*/	
    791 	//glow.events.getListeners = function(item){
    792 	//	var objIdent; 
    793 	//	for (var i = 0, len = item.length; i < len; i++) {
    794 	//		
    795 	//		objIdent = item[i][eventKey];
    796 	//		
    797 	//		if (!objIdent) {
    798 	//			return false;
    799 	//		}
    800 	//		else {
    801 	//			// todo: need to return listeners in a sensible format
    802 	//			return eventListeners[objIdent];
    803 	//		}
    804 	//	}
    805 	//
    806 	//
    807 	//	return false;
    808 	//};
    809 	//
    810 	///**
    811 	//@name glow.events.hasListener
    812 	//@function
    813 	//@param {Object[]} item  Item to find events for
    814 	//@param {String}   eventName  Name of the event to match
    815 	//@decription Returns true if an event is found for the item supplied
    816 	//
    817 	//*/
    818 	//
    819 	//glow.events.hasListener = function (item, eventName) {
    820 	//	var objIdent,
    821 	//		listenersForEvent;
    822 	//		
    823 	//	for (var i = 0, len = item.length; i < len; i++) {	
    824 	//		objIdent = item[i][eventKey];
    825 	//			
    826 	//		if (!objIdent || !eventListeners[objIdent]) {
    827 	//			return false;
    828 	//		}
    829 	//				
    830 	//		listenersForEvent = eventListeners[objIdent][eventName];
    831 	//		if (!listenersForEvent) {
    832 	//			return false;
    833 	//		}
    834 	//		else {
    835 	//			return true;							
    836 	//		}					
    837 	//	}
    838 	//	
    839 	//	return false;			
    840 	//};
    841 	
    842 	/**
    843 	@name glow.events.Target
    844 	@class
    845 	@description An object that can have event listeners and fire events.
    846 		Extend this class to make your own objects have 'on' and 'fire'
    847 		methods.
    848 		
    849 	@example
    850 		// Ball is our constructor
    851 		function Ball() {
    852 			// ...
    853 		}
    854 		       
    855 		// make Ball inherit from Target
    856 		glow.util.extend(Ball, glow.events.Target, {
    857 			// additional methods for Ball here, eg:
    858 			bowl: function() {
    859 				// ...
    860 			}
    861 		});
    862 		       
    863 		// now instances of Ball can receive event listeners
    864 		var myBall = new Ball();
    865 		myBall.on('bounce', function() {
    866 			alert('BOING!');
    867 		});
    868 		       
    869 		// and events can be fired from Ball instances
    870 		myBall.fire('bounce');
    871 	*/
    872 	
    873 	events.Target = function () {
    874 			
    875 	};
    876 	var targetProto = events.Target.prototype;
    877 		
    878 	/**
    879 	@name glow.events.Target.extend
    880 	@function
    881 	@param {Object} obj Object to add Target instance methods to.
    882 		
    883 	@description Convenience method to add Target instance methods onto an object.
    884 		If you want to add Target methods to a class, extend glow.events.Target instead.
    885 		       
    886 	@example
    887 		var myApplication = {};
    888 		       
    889 		glow.events.Target.extend(myApplication);
    890 		       
    891 		// now myApplication can fire events...
    892 		myApplication.fire('load');
    893 		       
    894 		// and other objects can listen for those events
    895 		myApplication.on('load', function(e) {
    896 			alert('App loaded');
    897 		});
    898 	*/
    899 	
    900 	events.Target.extend = function (obj) {
    901 		glow.util.apply( obj, glow.events.Target.prototype );
    902 	};
    903 		
    904 	/**
    905 	@name glow.events.Target#on
    906 	@function
    907 	@param {string} eventName Name of the event to listen for.
    908 	@param {function} callback Function to call when the event fires.
    909 		The callback is passed a single event object. The type of this
    910 		object depends on the event (see documentation for the event
    911 		you're listening to).
    912 	@param {Object} [thisVal] Value of 'this' within the callback.
    913 		By default, this is the object being listened to.
    914 		
    915 	@description Listen for an event
    916 		
    917 	@returns this
    918 		
    919 	@example
    920 		myObj.on('show', function() {
    921 		    // do stuff
    922 		});
    923 	*/
    924 	
    925 	targetProto.on = function(eventName, callback, thisVal) {
    926 		glow.events.addListeners([this], eventName, callback, thisVal);
    927 		return this;
    928 	}
    929 		
    930 	/**
    931 	@name glow.events.Target#detach
    932 	@function
    933 	@param {string} eventName Name of the event to remove.
    934 	@param {function} callback Callback to detach.
    935 	@param {Object} [thisVal] Value of 'this' within the callback.
    936 		By default, this is the object being listened to.
    937 	@description Remove an event listener.
    938 		
    939 	@returns this Target object
    940 		
    941 	@example
    942 		function showListener() {
    943 		    // ...
    944 		}
    945 		       
    946 		// add listener
    947 		myObj.on('show', showListener);
    948 		       
    949 		// remove listener
    950 		myObj.detach('show', showListener);
    951 		       
    952 	@example
    953 		// note the following WILL NOT WORK
    954 		       
    955 		// add listener
    956 		myObj.on('show', function() {
    957 		    alert('hi');
    958 		});
    959 		       
    960 		// remove listener
    961 		myObj.detach('show', function() {
    962 			alert('hi');
    963 		});
    964 		       
    965 		// this is because both callbacks are different function instances
    966 	
    967 	*/
    968 		
    969 	targetProto.detach = function(eventName, callback) {
    970 		glow.events.removeListeners(this, eventName, callback);
    971 		return this;
    972 	}
    973 		
    974 	/**
    975 	@name glow.events.Target#fire
    976 	@function
    977 	@param {string} eventName Name of the event to fire.
    978 	@param {glow.events.Event|Object} [event] Event object to pass into listeners.
    979 		    You can provide a simple object of key-value pairs which will
    980 		    be added as properties of a glow.events.Event instance.
    981 		
    982 	@description Fire an event.
    983 		
    984 	@returns glow.events.Event
    985 		
    986 	@example
    987 		myObj.fire('show');
    988 		       
    989 	@example
    990 		// adding properties to the event object
    991 		myBall.fire('bounce', {
    992 		    velocity: 30
    993 		});
    994 	       
    995 	@example
    996 		// BallBounceEvent extends glow.events.Event but has extra methods
    997 		myBall.fire( 'bounce', new BallBounceEvent(myBall) );
    998 	*/
    999 	
    1000 	targetProto.fire = function(eventName, event) {			
    1001 		return callListeners(this, eventName, event);
    1002 	}
    1003 		
    1004 	/**
    1005 	@name glow.events.Event
    1006 	@class
    1007 	@param {Object} [properties] Properties to add to the Event instance.
    1008 		Each key-value pair in the object will be added to the Event as
    1009 		properties.
    1010 	       
    1011 	@description Describes an event that occurred.
    1012 		You don't need to create instances of this class if you're simply
    1013 		listening to events. One will be provided as the first argument
    1014 		in your callback.
    1015 	       
    1016 	@example
    1017 		// creating a simple event object
    1018 		var event = new glow.events.Event({
    1019 			velocity: 50,
    1020 			direction: 180
    1021 		});
    1022 		       
    1023 		// 'velocity' and 'direction' are simple made-up properties
    1024 		// you may want to add to your event object
    1025 		       
    1026 	@example
    1027 		// inheriting from glow.events.Event to make a more
    1028 		// specialised event object
    1029 		       
    1030 		function RocketEvent() {
    1031 			// ...
    1032 		}
    1033 		       
    1034 		// inherit from glow.events.Event
    1035 		glow.util.extend(RocketEvent, glow.events.Event, {
    1036 			getVector: function() {
    1037 				return // ...
    1038 			}
    1039 		});
    1040 		       
    1041 		// firing the event
    1042 		rocketInstance.fire( 'landingGearDown', new RocketEvent() );
    1043 		       
    1044 		// how a user would listen to the event
    1045 		rocketInstance.on('landingGearDown', function(rocketEvent) {
    1046 			var vector = rocketEvent.getVector();
    1047 		});
    1048 	*/
    1049 		
    1050 	events.Event = function(obj) {			
    1051 		if (obj) {
    1052 			glow.util.apply(this, obj);
    1053 		}
    1054 	};
    1055 	var eventProto = events.Event.prototype;
    1056 	/**
    1057 	@name glow.events.Event#attachedTo
    1058 	@type {Object}
    1059 	@description The object the listener was attached to.
    1060 		If null, this value will be populated by {@link glow.events.Target#fire}
    1061 	*/
    1062 		
    1063 	/**
    1064 	@name glow.events.Event#source
    1065 	@type Element
    1066 	@description The actual object/element that the event originated from.
    1067 			
    1068 		For example, you could attach a listener to an 'ol' element to 
    1069 		listen for clicks. If the user clicked on an 'li' the source property 
    1070 		would be the 'li' element, and 'attachedTo' would be the 'ol'.
    1071 	*/
    1072 		
    1073 
    1074 		
    1075 	/**
    1076 	@name glow.events.Event#preventDefault
    1077 	@function
    1078 	@description Prevent the default action of the event.
    1079 		Eg, if the click event on a link is cancelled, the link
    1080 		is not followed.
    1081 		       
    1082 		Returning false from an event listener has the same effect
    1083 		as calling this function.
    1084 		       
    1085 		For custom events, it's down to whatever fired the event
    1086 		to decide what to do in this case. See {@link glow.events.Event#defaultPrevented defaultPrevented}
    1087 		       
    1088 	@example
    1089 		myLinks.on('click', function(event) {
    1090 			event.preventDefault();
    1091 		});
    1092 		       
    1093 		// same as...
    1094 		       
    1095 		myLinks.on('click', function(event) {
    1096 			return false;
    1097 		});
    1098 	*/
    1099 	
    1100 	eventProto.preventDefault = function () {	
    1101 		this._defaultPrevented = true;		
    1102 	};
    1103 
    1104 		
    1105 	/**
    1106 	@name glow.events.Event#defaultPrevented
    1107 	@function
    1108 	@description Has the default been prevented for this event?
    1109 		This should be used by whatever fires the event to determine if it should
    1110 		carry out of the default action.
    1111 		
    1112 	@returns {Boolean} Returns true if {@link glow.events.Event#preventDefault preventDefault} has been called for this event.
    1113 		
    1114 	@example
    1115 		// fire the 'show' event
    1116 		// read if the default action has been prevented
    1117 		if ( overlayInstance.fire('show').defaultPrevented() == false ) {
    1118 		    // go ahead and show
    1119 		}
    1120 	*/
    1121 	
    1122 	eventProto.defaultPrevented = function () {
    1123 		return this._defaultPrevented;
    1124 	};
    1125 
    1126 	
    1127 	/* Export */
    1128 	glow.events = events;
    1129 });
    1130 Glow.provide(function(glow) {
    1131 	var document = window.document,
    1132 		undef = undefined,
    1133 		domEventHandlers = []; // like: domEventHandlers[uniqueId][eventName].count, domEventHandlers[uniqueId][eventName].callback
    1134 	
    1135 	/** 
    1136 		@name glow.events.DomEvent
    1137 		@constructor
    1138 		@extends glow.events.Event
    1139 		
    1140 		@param {Event|string} nativeEvent A native browser event read properties from, or the name of a native event.
    1141 		
    1142 		@param {Object} [properties] Properties to add to the Event instance.
    1143 		   Each key-value pair in the object will be added to the Event as
    1144 		   properties
    1145 		
    1146 		@description Describes a DOM event that occurred
    1147 		   You don't need to create instances of this class if you're simply
    1148 		   listening to events. One will be provided as the first argument
    1149 		   in your callback.
    1150 	*/
    1151 	function DomEvent(e, properties) {
    1152 		/** 
    1153 			@name glow.events.DomEvent#nativeEvent
    1154 			@type {Event | MouseEvent | UIEvent}
    1155 			@description The native event object provided by the browser.
    1156 		 */
    1157 		 this.nativeEvent = e;
    1158 		
    1159 		/** 
    1160 			@name glow.events.DomEvent#type
    1161 			@type {string}
    1162 			@description The native type of the event, like 'click' or 'keydown'.
    1163 		 */
    1164 		this.type = e.type;
    1165 		
    1166 		/** 
    1167 			@name glow.events.DomEvent#source
    1168 			@type {HTMLElement}
    1169 			@description The element that the event originated from.
    1170 				For example, you could attach a listener to an <ol> element to listen for
    1171 				clicks. If the user clicked on an <li> the source property would be the
    1172 				<li> element, and {@link glow.DomEvent#attachedTo attachedTo} would be
    1173 				the <ol>.
    1174 		*/
    1175 		if (e.target) { this.source = e.target; } // like FF
    1176 		else if (e.srcElement) { this.source = e.srcElement; } // like IE
    1177 		if (this.source && this.source.nodeType !== 1) { // like a textNode
    1178 			this.source = this.source.parentNode;
    1179 		}
    1180 		
    1181 		/** 
    1182 			@name glow.events.DomEvent#related
    1183 			@type {HTMLElement}
    1184 			@description A related HTMLElement
    1185 				For mouseover / mouseenter events, this will refer to the previous element
    1186 				the mouse was over.
    1187 				
    1188 				For mouseout / mouseleave events, this will refer to the element the mouse
    1189 				is now over.
    1190 		*/
    1191 		this.related = e.relatedTarget || (this.type == 'mouseover' ? e.fromElement : e.toElement);
    1192 		
    1193 		/** 
    1194 			@name glow.events.DomEvent#shiftKey
    1195 			@type {boolean | undefined}
    1196 			@description Was the shift key pressed during the event?
    1197 		*/
    1198 		this.shiftKey = (e.shiftKey === undef)? undef : !!e.shiftKey;
    1199 		
    1200 		/** 
    1201 			@name glow.events.DomEvent#altKey
    1202 			@type {boolean | undefined}
    1203 			@description Was the alt key pressed during the event?
    1204 		*/
    1205 		this.altKey = (e.altKey === undef)? undef : !!e.altKey;
    1206 		
    1207 		/** 
    1208 			@name glow.events.DomEvent#ctrlKey
    1209 			@type {boolean | undefined}
    1210 			@description Was the ctrl key pressed during the event?
    1211 		*/
    1212 		this.ctrlKey = (e.ctrlKey === undef)? undef : !!e.ctrlKey;
    1213 		
    1214 		/**
    1215 			@name glow.events.DomEvent#button
    1216 			@type {number | undefined}
    1217 			@description A number representing which button was pressed.
    1218 				0 for the left button, 1 for the middle button or 2 for the right button.
    1219 		*/
    1220 		this.button = glow.env.ie ? (e.button & 1 ? 0 : e.button & 2 ? 2 : 1) : e.button;
    1221 		
    1222 		/** 
    1223 			@name glow.events.DomEvent#mouseTop
    1224 			@type {number}
    1225 			@description The vertical position of the mouse pointer in the page in pixels.
    1226 		*/
    1227 		/** 
    1228 			@name glow.events.DomEvent#mouseLeft
    1229 			@type {number}
    1230 			@description The horizontal position of the mouse pointer in the page in pixels.
    1231 		*/
    1232 		if (e.pageX !== undef || e.pageY !== undef) {
    1233 			this.mouseTop = e.pageY;
    1234 			this.mouseLeft = e.pageX;
    1235 		}
    1236 		else if (e.clientX !== undef || e.clientY !== undef) {
    1237 			this.mouseTop = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    1238 			this.mouseLeft = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
    1239 		}
    1240 		
    1241 		/** 
    1242 			@name glow.events.DomEvent#wheelData
    1243 			@type {number}
    1244 			@description The number of clicks the mouse wheel moved.
    1245 				Up values are positive, down values are negative.
    1246 		*/
    1247 		if (this.type == 'mousewheel') {
    1248 			// this works in latest opera, but have read that it needs to be switched in direction
    1249 			// if there was an opera bug, I can't find which version it was fixed in
    1250 			this.wheelDelta =
    1251 				e.wheelDelta ? e.wheelDelta / 120 :
    1252 				e.detail ? - e.detail / 3 :
    1253 				0;
    1254 		}
    1255 		
    1256 		for (var key in properties) {
    1257 			this[key] = properties[key];
    1258 		}
    1259 	}
    1260 	
    1261 	glow.util.extend(DomEvent, glow.events.Event); // DomEvent extends Event
    1262 
    1263 	
    1264 	/**
    1265 		Add listener for an event fired by the browser.
    1266 		@private
    1267 		@name glow.events._addDomEventListener
    1268 		@see glow.NodeList#on
    1269 		@function
    1270 	*/
    1271 	glow.events._addDomEventListener = function(nodeList, eventName, callback, thisVal, selector) {
    1272 		var i = nodeList.length, // TODO: should we check that this nodeList is deduped?
    1273 			attachTo,
    1274 			id,
    1275 			eId = eventName + (selector? '/'+selector : '');
    1276 	
    1277 		while (i-- && nodeList[i]) {
    1278 			attachTo = nodeList[i];
    1279 
    1280 			// will add a unique id to this node, if there is not one already
    1281 			glow.events.addListeners([attachTo], eventName, callback, thisVal);
    1282 			id = glow.events._getPrivateEventKey(attachTo);
    1283 
    1284 			// check if there is already a handler for this kind of event attached
    1285 			// to this node (which will run all associated callbacks in Glow)
    1286 			if (!domEventHandlers[id]) { domEventHandlers[id] = {}; }
    1287 
    1288 			if (domEventHandlers[id][eId] && domEventHandlers[id][eId].count > 0) { // already have handler in place
    1289 				domEventHandlers[id][eId].count++;
    1290 				continue;
    1291 			}
    1292 
    1293 			// no bridge in place yet
    1294 			domEventHandlers[id][eId] = { callback: null, count:1 };
    1295 			
    1296 			// attach a handler to tell Glow to run all the associated callbacks
    1297 			(function(attachTo) {
    1298 				var handler = domHandle(attachTo, eventName, selector);
    1299 				
    1300 				if (attachTo.addEventListener) { // like DOM2 browsers	
    1301 					attachTo.addEventListener(handler.domName, handler, (eventName === 'focus' || eventName === 'blur')); // run in bubbling phase except for focus and blur, see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
    1302 				}
    1303 				else if (attachTo.attachEvent) { // like IE
    1304 					if (eventName === 'focus')  attachTo.attachEvent('onfocusin', handler); // see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
    1305 					else if (eventName === 'blur') attachTo.attachEvent('onfocusout', handler); // cause that's how IE rolls...
    1306 					attachTo.attachEvent('on' + handler.domName, handler);
    1307 				}
    1308 				// older browsers?
    1309 				
    1310 				domEventHandlers[id][eId].callback = handler;
    1311 			})(attachTo);
    1312 		}
    1313 	}
    1314 	
    1315 	function domHandle(attachTo, eventName, selector) {
    1316 		var handler;
    1317 		
    1318 		if (eventName === 'mouseenter') {
    1319 			handler = function(nativeEvent, node) {
    1320 				var e = new glow.events.DomEvent(nativeEvent),
    1321 					container = node || attachTo;
    1322 				
    1323 				if (!new glow.NodeList(container).contains(e.related)) {
    1324 					var result = glow.events._callListeners(attachTo, eventName, e, node); // fire() returns result of callback
    1325 					
    1326 					if (typeof result === 'boolean') { return result; }
    1327 					else { return !e.defaultPrevented(); }
    1328 				}
    1329 			};
    1330 			
    1331 			if (selector) {
    1332 				handler = delegate(attachTo, eventName, selector, handler);
    1333 			}
    1334 			
    1335 			handler.domName = 'mouseover';
    1336 		}
    1337 		else if (eventName === 'mouseleave') {
    1338 			handler = function(nativeEvent, node) {
    1339 				var e = new glow.events.DomEvent(nativeEvent),
    1340 					container = node || attachTo;
    1341 					
    1342 				if (!new glow.NodeList(container).contains(e.related)) {
    1343 					var result = glow.events._callListeners(attachTo, eventName, e, node); // fire() returns result of callback
    1344 					
    1345 					if (typeof result === 'boolean') { return result; }
    1346 					else { return !e.defaultPrevented(); }
    1347 				}
    1348 			};
    1349 			
    1350 			if (selector) {
    1351 				handler = delegate(attachTo, eventName, selector, handler);
    1352 			}
    1353 			
    1354 			handler.domName = 'mouseout';
    1355 		}
    1356 		else {
    1357 			handler = function(nativeEvent, node) {
    1358 				var domEvent = new glow.events.DomEvent(nativeEvent);
    1359 				var result = glow.events._callListeners(attachTo, eventName, domEvent, node); // fire() returns result of callback
    1360 				
    1361 				if (typeof result === 'boolean') { return result; }
    1362 				else { return !domEvent.defaultPrevented(); }
    1363 			};
    1364 			
    1365 			if (selector) {
    1366 				handler = delegate(attachTo, eventName, selector, handler);
    1367 			}
    1368 			
    1369 			handler.domName = eventName;
    1370 		}
    1371 		
    1372 		return handler;
    1373 	}
    1374 	
    1375 	// wraps a handler in code to detect delegation
    1376 	function delegate(attachTo, eventName, selector, handler) {
    1377 	
    1378 		return function(nativeEvent) { //console.log('dispatched, selector is: '+selector);
    1379 			var e = new glow.events.DomEvent(nativeEvent);
    1380 				node = e.source;
    1381 			
    1382 			// if the source matches the selector
    1383 			while (node) {
    1384 				if (!!glow._sizzle.matches(selector, [node]).length) {
    1385 					// the wrapped handler is called here, pass in the node that matched so it can be used as `this`
    1386 					return handler(nativeEvent, node);
    1387 					//
    1388 				}
    1389 				
    1390 				if (node === attachTo) { break; } // don't check parents above the attachTo
    1391 				
    1392 				node = node.parentNode;
    1393 			}
    1394 		};
    1395 	}
    1396 	
    1397 	/**
    1398 		Remove listener for an event fired by the browser.
    1399 		@private
    1400 		@name glow.events._removeDomEventListener
    1401 		@see glow.NodeList#detach
    1402 		@function
    1403 	*/
    1404 	glow.events._removeDomEventListener = function(nodeList, eventName, callback, selector) {
    1405 		var i = nodeList.length, // TODO: should we check that this nodeList is deduped?
    1406 			attachTo,
    1407 			id,
    1408 			eId = eventName + (selector? '/'+selector : ''),
    1409 			bridge,
    1410 			handler;
    1411 			
    1412 		while (i-- && nodeList[i]) {
    1413 			attachTo = nodeList[i];
    1414 			
    1415 			// skip if there is no bridge for this kind of event attached
    1416 			id = glow.events._getPrivateEventKey(attachTo);
    1417 			if (!domEventHandlers[id] && !domEventHandlers[id][eId]) { continue; }
    1418 			
    1419 			glow.events.removeListeners([attachTo], eventName, callback);
    1420 
    1421 			bridge = domEventHandlers[id][eId]
    1422 			
    1423 			if (bridge.count > 0) {
    1424 				bridge.count--; // one less listener associated with this event
    1425 		
    1426 				if (bridge.count === 0) {  // no more listeners associated with this event
    1427 					// detach bridge handler to tell Glow to run all the associated callbacks
    1428 					
    1429 					handler = bridge.callback;
    1430 														
    1431 					if (attachTo.removeEventListener) { // like DOM2 browsers	
    1432 						attachTo.removeEventListener(handler.domName, handler, (eventName === 'focus' || eventName === 'blur')); // run in bubbling phase except for focus and blur, see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
    1433 					}
    1434 					else if (attachTo.detachEvent) { // like IE
    1435 						if (eventName === 'focus')  attachTo.detachEvent('onfocusin', handler); // see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html
    1436 						else if (eventName === 'blur') attachTo.detachEvent('onfocusout', handler); // cause that's how IE rolls...
    1437 						attachTo.detachEvent('on' + handler.domName, handler);
    1438 					}
    1439 				}
    1440 			}
    1441 		}
    1442 	}
    1443 
    1444 // see: http://developer.yahoo.com/yui/3/event/#eventsimulation
    1445 // see: http://developer.yahoo.com/yui/docs/YAHOO.util.UserAction.html
    1446 // 	function simulateDomEvent(nodeList, domEvent) {
    1447 // 		var i = nodeList.length,
    1448 // 			eventName = domEvent.type,
    1449 // 			nativeEvent,
    1450 // 			node,
    1451 // 			fire;
    1452 // 		
    1453 // 		if (document.createEvent) {
    1454 // 			var nativeEvent = document.createEvent('MouseEvent'); // see: 
    1455 // 			nativeEvent.initEvent(eventName, true, true);
    1456 // 			
    1457 // 			fire = function(el) {
    1458 // 				return !el.dispatchEvent(nativeEvent);
    1459 // 			}
    1460 // 		}
    1461 // 		else {
    1462 // 			fire = function(el) {
    1463 // 				var nativeEvent = document.createEventObject(); 
    1464 // 				return el.fireEvent('on'+eventName, nativeEvent);
    1465 // 			}
    1466 // 		}
    1467 // 		
    1468 // 		while (i--) {
    1469 // 			node = nodeList[i];
    1470 // 			if (node.nodeType !== 1) { continue; }
    1471 // 			fire(node);
    1472 // 		}
    1473 // 	}
    1474 	
    1475 	// export
    1476 	glow.events.DomEvent = DomEvent;
    1477 });
    1478 Glow.provide(function(glow) {
    1479 	var document = window.document,
    1480 		undefined,
    1481         keyboardEventProto,
    1482 		$env = glow.env,
    1483 		// the keyCode for the last keydown (returned to undefined on keyup)
    1484 		activeKey,
    1485 		// the charCode for the last keypress (returned to undefined on keyup & keydown)
    1486 		activeChar,
    1487 		DomEvent = glow.events.DomEvent,
    1488 		_callListeners = glow.events._callListeners,
    1489 		_getPrivateEventKey = glow.events._getPrivateEventKey,
    1490 		// object of event names & listeners, eg:
    1491 		// {
    1492 		//    eventId: [
    1493 		//        2, // the number of glow listeners added for this node
    1494 		//        keydownListener,
    1495 		//        keypressListener,
    1496 		//        keyupListener
    1497 		//    ]
    1498 		// }
    1499 		// This lets us remove these DOM listeners from the node when the glow listeners reaches zero
    1500 		eventKeysRegistered = {}; 
    1501 	
    1502 	/** 
    1503 		@name glow.events.KeyboardEvent
    1504 		@constructor
    1505 		@extends glow.events.DomEvent
    1506 		
    1507 		@param {Event} nativeEvent A native browser event read properties from
    1508 		
    1509 		@param {Object} [properties] Properties to add to the Event instance.
    1510 		   Each key-value pair in the object will be added to the Event as
    1511 		   properties
    1512 		
    1513 		@description Describes a keyboard event that occurred
    1514 		   You don't need to create instances of this class if you're simply
    1515 		   listening to events. One will be provided as the first argument
    1516 		   in your callback.
    1517 	*/
    1518 	function KeyboardEvent(nativeEvent) {
    1519 		if (activeKey) {
    1520 			this.key = keyCodeToId(activeKey);
    1521 		}
    1522 		if (activeChar) {
    1523 			this.keyChar = String.fromCharCode(activeChar);
    1524 		}
    1525 		DomEvent.call(this, nativeEvent);
    1526 	}
    1527     
    1528     glow.util.extend(KeyboardEvent, DomEvent, {
    1529         /** 
    1530             @name glow.events.KeyboardEvent#key
    1531             @type {string}
    1532             @description The key pressed
    1533 				This is a string representing the key pressed.
    1534 				
    1535 				Alphanumeric keys are represented by 0-9 and A-Z uppercase. Other safe cross-browser values are:
    1536 				
    1537 				<dl>
    1538 					<li>backspace</li>
    1539 					<li>tab</li>
    1540 					<li>return</li>
    1541 					<li>shift</li>
    1542 					<li>alt</li>
    1543 					<li>escape</li>
    1544 					<li>space</li>
    1545 					<li>pageup</li>
    1546 					<li>pagedown</li>
    1547 					<li>end</li>
    1548 					<li>home</li>
    1549 					<li>left</li>
    1550 					<li>up</li>
    1551 					<li>right</li>
    1552 					<li>down</li>
    1553 					<li>insert</li>
    1554 					<li>delete</li>
    1555 					<li>;</li>
    1556 					<li>=</li>
    1557 					<li>-</li>
    1558 					<li>f1</li>
    1559 					<li>f2</li>
    1560 					<li>f3</li>
    1561 					<li>f4</li>
    1562 					<li>f5</li>
    1563 					<li>f6</li>
    1564 					<li>f7</li>
    1565 					<li>f8</li>
    1566 					<li>f9</li>
    1567 					<li>f10</li>
    1568 					<li>f11</li>
    1569 					<li>f12</li>
    1570 					<li>numlock</li>
    1571 					<li>scrolllock</li>
    1572 					<li>pause</li>
    1573 					<li>,</li>
    1574 					<li>.</li>
    1575 					<li>/</li>
    1576 					<li>[</li>
    1577 					<li>\</li>
    1578 					<li>]</li>
    1579 				</dl>
    1580 				
    1581 				Some keys may trigger actions in your browser and operating system, some
    1582 				are not cancelable.
    1583                 
    1584             @example
    1585 				glow(document).on('keypress', function(event) {
    1586 					switch (event.key) {
    1587 						case 'up':
    1588 							// do stuff
    1589 							break;
    1590 						case 'down':
    1591 							// do stuff
    1592 							break;
    1593 					}
    1594 				});
    1595         */
    1596         key: '',
    1597         /** 
    1598             @name glow.events.KeyboardEvent#keyChar
    1599             @type {string}
    1600             @description The character entered.
    1601                 This is only available during 'keypress' events.
    1602                 
    1603                 If the user presses shift and 1, event.key will be "1", but event.keyChar
    1604                 will be "!".
    1605                 
    1606             @example
    1607                 // only allow numbers to be entered into the ageInput field
    1608 				glow('#ageInput').on('keypress', function(event) {
    1609 					return !isNaN( Number(event.keyChar) );
    1610 				});
    1611         */
    1612         keyChar: ''
    1613     });
    1614 	
    1615 	// add a dom listener
    1616 	function addListener(elm, name, callback) {
    1617 		if (elm.addEventListener) { // like DOM2 browsers	
    1618 			elm.addEventListener(name, callback, false);
    1619 		}
    1620 		else if (elm.attachEvent) { // like IE
    1621 			elm.attachEvent('on' + name, callback);
    1622 		}
    1623 	}
    1624 	
    1625 	// remove a dom listener
    1626 	function removeListener(elm, name, callback) {
    1627 		if (elm.removeEventListener) { // like DOM2 browsers	
    1628 			elm.removeEventListener(name, callback, false);
    1629 		}
    1630 		else if (elm.detachEvent) { // like IE
    1631 			elm.detachEvent('on' + name, callback);
    1632 		}
    1633 	}
    1634 	
    1635 	// takes a keyCode from a keydown listener and returns true if the browser will also fire a keypress
    1636 	function expectKeypress(keyCode, defaultPrevented) {
    1637 		var keyName;
    1638 		
    1639 		// for browsers that fire keypress for the majority of keys
    1640 		if ($env.gecko || $env.opera) {
    1641 			return !noKeyPress[keyCode];
    1642 		}
    1643 		
    1644 		// for browsers that only fire keypress for printable chars
    1645 		keyName = keyCodeToId(keyCode);
    1646 		
    1647 		// is this a printable char?
    1648 		if (keyName.length === 1 && !noKeyPress[keyCode]) {
    1649 			// webkit doesn't fire keypress if the keydown has been prevented
    1650 			return !($env.webkit && defaultPrevented);
    1651 		}
    1652 		return false;
    1653 	}
    1654 	
    1655 	// Add the key listeners for firing glow's normalised key events.
    1656 	// returns an entry for eventKeysRegistered
    1657 	function addDomKeyListeners(attachTo) {
    1658 		var keydownHandler, keypressHandler, keyupHandler,
    1659 			// Even though the user may only be interested in one key event, we need all 3 listeners to normalise any of them
    1660 			// hash of which keys are down, keyed by keyCode
    1661 			keysDown = {};
    1662 		
    1663 		keydownHandler = function(nativeEvent) {
    1664 			var keyCode = nativeEvent.keyCode,
    1665 				preventDefault,
    1666 				preventDefaultKeyPress;
    1667 			
    1668 			// some browsers repeat this event while a key is held down, we don't want to do that
    1669 			if ( !keysDown[keyCode] ) {
    1670 				activeKey = keyCode;
    1671 				activeChar = undefined;
    1672 				preventDefault = _callListeners( attachTo, 'keydown', new KeyboardEvent(nativeEvent) ).defaultPrevented();
    1673 				keysDown[keyCode] = true;
    1674 			}
    1675 			// we want to fire a keyPress event here if the browser isn't going to fire one itself
    1676 			if ( !expectKeypress(keyCode, preventDefault) ) {
    1677 				preventDefaultKeyPress = _callListeners( attachTo, 'keypress', new KeyboardEvent(nativeEvent) ).defaultPrevented();
    1678 			}
    1679 			// return false if either the keydown or fake keypress event was cancelled
    1680 			return !(preventDefault || preventDefaultKeyPress);
    1681 		};
    1682 		
    1683 		keypressHandler = function(nativeEvent) {
    1684 			// some browsers store the charCode in .charCode, some in .keyCode
    1685 			activeChar = nativeEvent.charCode || nativeEvent.keyCode;
    1686 			// some browsers fire this event for non-printable chars, look at the previous keydown and see if we're expecting a printable char
    1687 			if ( keyCodeToId(activeKey).length > 1 ) {
    1688 				// non-printable chars have an ID length greater than 1
    1689 				activeChar = undefined;
    1690 			}
    1691 			var preventDefault = _callListeners( attachTo, 'keypress', new KeyboardEvent(nativeEvent) ).defaultPrevented();
    1692 			return !preventDefault;
    1693 		};
    1694 		
    1695 		keyupHandler = function(nativeEvent) {
    1696 			var keyCode = nativeEvent.keyCode,
    1697 				preventDefault;
    1698 				
    1699 			activeKey = keyCode;
    1700 			activeChar = undefined;
    1701 			preventDefault = _callListeners( attachTo, 'keyup', new KeyboardEvent(nativeEvent) ).defaultPrevented();
    1702 			keysDown[keyCode] = false;
    1703 			activeKey = undefined;
    1704 			return !preventDefault;
    1705 		};
    1706 		
    1707 		// add listeners to the dom
    1708 		addListener(attachTo, 'keydown',  keydownHandler);
    1709 		addListener(attachTo, 'keypress', keypressHandler);
    1710 		addListener(attachTo, 'keyup',    keyupHandler);
    1711 		
    1712 		return [1, keydownHandler, keypressHandler, keyupHandler];
    1713 	}
    1714 	
    1715 	/**
    1716 		@name glow.events._addKeyListener
    1717 		@private
    1718 		@function
    1719 		@description Add listener for a key event fired by the browser.
    1720 		@see glow.NodeList#on
    1721 	*/
    1722 	glow.events._addKeyListener = function(nodeList, name, callback, thisVal) {
    1723 		var i = nodeList.length,
    1724 			attachTo,
    1725 			eventKey;
    1726 		
    1727 		// will add a unique id to this node, if there is not one already
    1728 		glow.events.addListeners(nodeList, name, callback, thisVal);
    1729 	
    1730 		while (i--) {
    1731 			attachTo = nodeList[i];
    1732 
    1733 			// get the ID for this event
    1734 			eventKey = _getPrivateEventKey(attachTo);
    1735 			
    1736 			// if we've already attached DOM listeners for this, don't add them again
    1737 			if ( eventKeysRegistered[eventKey] ) {
    1738 				eventKeysRegistered[eventKey][0]++;
    1739 				continue;
    1740 			}
    1741 			else {
    1742 				eventKeysRegistered[eventKey] = addDomKeyListeners(attachTo);
    1743 			}
    1744 		}
    1745 	}
    1746 	
    1747 	/**
    1748 		Remove listener for an event fired by the browser.
    1749 		@private
    1750 		@name glow.events._removeKeyListener
    1751 		@see glow.NodeList#detach
    1752 		@function
    1753 	*/
    1754 	glow.events._removeKeyListener = function(nodeList, name, callback) {
    1755 		var i = nodeList.length,
    1756 			attachTo,
    1757 			eventKey,
    1758 			eventRegistry;
    1759 		
    1760 		// remove the glow events
    1761 		glow.events.removeListeners(nodeList, name, callback);
    1762 		
    1763 		while (i--) {
    1764 			attachTo = nodeList[i];
    1765 			
    1766 			// get the ID for this event
    1767 			eventKey = _getPrivateEventKey(attachTo);
    1768 			eventRegistry = eventKeysRegistered[eventKey];
    1769 			// exist if there are no key events registered for this node
    1770 			if ( !eventRegistry ) {
    1771 				return;
    1772 			}
    1773 			if ( --eventRegistry[0] === 0 ) {
    1774 				// our glow listener count is zero, we have no need for the dom listeners anymore
    1775 				removeListener( attachTo, 'keydown',   eventRegistry[1] );
    1776 				removeListener( attachTo, 'keypress',  eventRegistry[2] );
    1777 				removeListener( attachTo, 'keyup',     eventRegistry[3] );
    1778 				eventKeysRegistered[eventKey] = undefined;
    1779 			}
    1780 		}
    1781 	}
    1782 	
    1783 	// convert a keyCode to a string name for that key
    1784 	function keyCodeToId(keyCode) {
    1785 		// key codes for 0-9 A-Z are the same as their char codes
    1786 		if ( (keyCode >= keyCodeA && keyCode <= keyCodeZ) || (keyCode >= keyCode0 && keyCode <= keyCode9) ) {
    1787 			return String.fromCharCode(keyCode).toLowerCase();
    1788 		}
    1789 		return keyIds[keyCode] || 'unknown' + keyCode;
    1790 	}
    1791 	
    1792 	// keyCode to key name translation
    1793 	var keyCodeA = 'A'.charCodeAt(0),
    1794 		keyCodeZ = 'Z'.charCodeAt(0),
    1795 		keyCode0 = '0'.charCodeAt(0),
    1796 		keyCode9 = '9'.charCodeAt(0),
    1797 		// key codes for non-alphanumeric keys
    1798 		keyIds = {
    1799 			8: 'backspace',
    1800 			9: 'tab',
    1801 			13: 'return',
    1802 			16: 'shift',
    1803 			17: 'control',
    1804 			18: 'alt',
    1805 			19: 'pause',
    1806 			27: 'escape',
    1807 			32: 'space',
    1808 			33: 'pageup',
    1809 			34: 'pagedown',
    1810 			35: 'end',
    1811 			36: 'home',
    1812 			37: 'left',
    1813 			38: 'up',
    1814 			39: 'right',
    1815 			40: 'down',
    1816 			44: 'printscreen', // Only fires keyup in firefox, IE. Doesn't fire in webkit, opera.
    1817 			45: 'insert',
    1818 			46: 'delete',
    1819 			59: ';',
    1820 			61: '=',
    1821 			91: 'meta',
    1822 			93: 'menu', // no keycode in opera, doesn't fire in Chrome
    1823 			
    1824 			// these are number pad numbers, but Opera doesn't distinguish them from normal number keys so we normalise on that
    1825 				96: '0', 
    1826 				97: '1',
    1827 				98: '2',
    1828 				99: '3',
    1829 				100: '4',
    1830 				101: '5',
    1831 				102: '6',
    1832 				103: '7',
    1833 				104: '8',
    1834 				105: '9',
    1835 				106: '*', // opera fires 2 keypress events
    1836 				107: '+', // opera fires 2 keypress events
    1837 				109: '-', // opera sees - as insert
    1838 				110: '.', // opera sees this as n
    1839 				111: '/',
    1840 			// end of numpad
    1841 			
    1842 			112: 'f1',
    1843 			113: 'f2',
    1844 			114: 'f3',
    1845 			115: 'f4',
    1846 			116: 'f5',
    1847 			117: 'f6',
    1848 			118: 'f7',
    1849 			119: 'f8',
    1850 			120: 'f9',
    1851 			121: 'f10',
    1852 			122: 'f11',
    1853 			123: 'f12',
    1854 			144: 'numlock',
    1855 			145: 'scrolllock',
    1856 			188: ',',
    1857 			189: '-',
    1858 			190: '.',
    1859 			191: '/',
    1860 			192: "'",
    1861 			219: '[',
    1862 			220: '\\',
    1863 			221: ']',
    1864 			222: '#', // opera sees # key as 3. Pah.
    1865 			223: '`',
    1866 			224: 'meta', // same as [ in opera
    1867 			226: '\\' // this key appears on a US layout in webkit windows
    1868 		},
    1869 		noKeyPress = {};
    1870 	
    1871 	// corrections for particular browsers :(
    1872 	if ($env.gecko) {
    1873 		keyIds[107] = '=';
    1874 		
    1875 		noKeyPress = {
    1876 			16: 1,  // shift
    1877 			17: 1,  // control
    1878 			18: 1,  // alt
    1879 			144: 1, // numlock
    1880 			145: 1  // scrolllock
    1881 		};
    1882 	}
    1883 	else if ($env.opera) {
    1884 		keyIds[42] = '*';
    1885 		keyIds[43] = '+';
    1886 		keyIds[47] = '/';
    1887 		keyIds[222] = "'";
    1888 		keyIds[192] = '`';
    1889 		
    1890 		noKeyPress = {
    1891 			16: 1,  // shift
    1892 			17: 1,  // control
    1893 			18: 1   // alt
    1894 		};
    1895 	}
    1896 	else if ($env.webkit || $env.ie) {
    1897 		keyIds[186] = ';';
    1898 		keyIds[187] = '=';
    1899 	}
    1900 	
    1901 	// export
    1902 	glow.events.KeyboardEvent = KeyboardEvent;
    1903 });
    1904 Glow.provide(function(glow) {
    1905 	var NodeListProto, undefined,
    1906 		// shortcuts to aid compression
    1907 		document = window.document,
    1908 		arraySlice = Array.prototype.slice,
    1909 		arrayPush = Array.prototype.push;
    1910 	
    1911 	/**
    1912 		@name glow.NodeList
    1913 		@constructor
    1914 		@description An array-like collection of DOM Nodes
    1915 			It is recommended to create a NodeList using the shortcut function {@link glow}.
    1916 			
    1917 		@param {string | glow.NodeList | Node | Node[] | Window} contents Items to populate the NodeList with.
    1918 			This parameter will be passed to {@link glow.NodeList#push}.
    1919 			
    1920 			Strings will be treated as CSS selectors unless they start with '<', in which
    1921 			case they'll be treated as an HTML string.
    1922 			
    1923 		@example
    1924 			// empty NodeList
    1925 			var myNodeList = glow();
    1926 
    1927 		@example
    1928 			// using glow to return a NodeList then chaining methods
    1929 			glow('p').addClass('eg').append('<div>Hello!</div>');
    1930 			
    1931 		@example
    1932 			// creating an element from a string
    1933 			glow('<div>Hello!</div>').appendTo('body');
    1934 		
    1935 		@see <a href="http://wiki.github.com/jeresig/sizzle/">Supported CSS selectors</a>
    1936 	*/
    1937 	function NodeList(contents) {
    1938 		// call push if we've been given stuff to add
    1939 		contents && this.push(contents);
    1940 	}
    1941 	NodeListProto = NodeList.prototype;
    1942 	
    1943 	/**
    1944 		@name glow.NodeList#length
    1945 		@type Number
    1946 		@description Number of nodes in the NodeList
    1947 		@example
    1948 			// get the number of paragraphs on the page
    1949 			glow('p').length;
    1950 	*/
    1951 	NodeListProto.length = 0;
    1952 	
    1953 	/**
    1954 		@name glow.NodeList._strToNodes
    1955 		@private
    1956 		@function
    1957 		@description Converts a string to an array of nodes
    1958 		
    1959 		@param {string} str HTML string
    1960 		
    1961 		@returns {Node[]} Array of nodes (including text / comment nodes)
    1962 	*/
    1963 	NodeList._strToNodes = (function() {
    1964 		var	tmpDiv = document.createElement('div'),
    1965 			// these wraps are in the format [depth to children, opening html, closing html]
    1966 			tableWrap = [1, '<table>', '</table>'],
    1967 			emptyWrap = [0, '', ''],
    1968 			// webkit won't accept <link> elms to be the only child of an element,
    1969 			// it steals them and hides them in the head for some reason. Using
    1970 			// broken html fixes it for some reason
    1971 			paddingWrap = [1, 'b<div>', '</div>'],
    1972 			trWrap = [3, '<table><tbody><tr>', '</tr></tbody></table>'],
    1973 			wraps = {
    1974 				caption: tableWrap,
    1975 				thead: tableWrap,
    1976 				th: trWrap,
    1977 				colgroup: tableWrap,
    1978 				tbody: tableWrap,
    1979 				tr: [2, '<table><tbody>', '</tbody></table>'],
    1980 				td: trWrap,
    1981 				tfoot: tableWrap,
    1982 				option: [1, '<select multiple="multiple">', '</select>'],
    1983 				legend: [1, '<fieldset>', '</fieldset>'],
    1984 				link: paddingWrap,
    1985 				script: paddingWrap,
    1986 				style: paddingWrap,
    1987 				'!': paddingWrap
    1988 			};
    1989 		
    1990 		function strToNodes(str) {
    1991 			var r = [],
    1992 				tagName = ( /^<([\w!]+)/.exec(str) || [] )[1],
    1993 				// This matches str content with potential elements that cannot
    1994 				// be a child of <div>.  elmFilter declared at top of page.
    1995 				wrap = wraps[tagName] || emptyWrap, 
    1996 				nodeDepth = wrap[0],
    1997 				childElm = tmpDiv,
    1998 				exceptTbody,
    1999 				rLen = 0,
    2000 				firstChild;
    2001 			
    2002 			// Create the new element using the node tree contents available in filteredElm.
    2003 			childElm.innerHTML = (wrap[1] + str + wrap[2]);
    2004 			
    2005 			// Strip newElement down to just the required elements' parent
    2006 			while(nodeDepth--) {
    2007 				childElm = childElm.lastChild;
    2008 			}
    2009 			
    2010 			// pull nodes out of child
    2011 			if (wrap === tableWrap && str.indexOf('<tbody') === -1) {
    2012 				// IE7 (and earlier) sometimes gives us a <tbody> even though we didn't ask for one
    2013 				while (firstChild = childElm.firstChild) {
    2014 					if (firstChild.nodeName != 'TBODY') {
    2015 						r[rLen++] = firstChild;
    2016 					}
    2017 					childElm.removeChild(firstChild);
    2018 				}
    2019 			}
    2020 			else {
    2021 				while (firstChild = childElm.firstChild) {
    2022 					r[rLen++] = childElm.removeChild(firstChild);
    2023 				}
    2024 			}
    2025 			
    2026 			return r;
    2027 		}
    2028 		
    2029 		return strToNodes;
    2030 	})();
    2031 	
    2032 	// takes a collection and returns an array
    2033 	var collectionToArray = function(collection) {
    2034 		return arraySlice.call(collection, 0);
    2035 	};
    2036 	
    2037 	try {
    2038 		// look out for an IE bug
    2039 		arraySlice.call( document.documentElement.childNodes, 0 );
    2040 	}
    2041 	catch(e) {
    2042 		collectionToArray = function(collection) {
    2043 			// We can't use this trick on IE collections that are com-based, like HTMLCollections
    2044 			// Thankfully they don't have a constructor, so that's how we detect those
    2045 			if (collection instanceof Object) {
    2046 				return arraySlice.call(collection, 0);
    2047 			}
    2048 			var i   = collection.length,
    2049 				arr = [];
    2050 				
    2051 			while (i--) {
    2052 				arr[i] = collection[i];
    2053 			}
    2054 			return arr;
    2055 		}
    2056 	}
    2057 	
    2058 	/**
    2059 		@name glow.NodeList#push
    2060 		@function
    2061 		@description Adds nodes to the NodeList
    2062 		
    2063 		@param {string | Node | Node[] | glow.NodeList} nodes Node(s) to add to the NodeList
    2064 			Strings will be treated as CSS selectors or HTML strings.
    2065 		
    2066 		@returns {glow.NodeList}
    2067 		
    2068 		@example
    2069 			myNodeList.push('<div>Foo</div>').push('h1');
    2070 	*/
    2071 	NodeListProto.push = function(nodes) {
    2072 		/*!debug*/
    2073 			if (arguments.length !== 1) {
    2074 				glow.debug.warn('[wrong count] glow.NodeList#push expects 1 argument, not '+arguments.length+'.');
    2075 			}
    2076 		/*gubed!*/
    2077 		
    2078 		if (nodes) {
    2079 			if (typeof nodes === 'string') {
    2080 				// if the string begins <, treat it as html, otherwise it's a selector
    2081 				if (nodes.charAt(0) === '<') {
    2082 					nodes = NodeList._strToNodes(nodes);
    2083 				}
    2084 				else {
    2085 					nodes = glow._sizzle(nodes)
    2086 				}
    2087 				arrayPush.apply(this, nodes);
    2088 			}
    2089 			
    2090 			else if ( nodes.nodeType || nodes.window == nodes ) {
    2091 				if (this.length) {
    2092 					arrayPush.call(this, nodes);
    2093 				}
    2094 				else {
    2095 					this[0] = nodes;
    2096 					this.length = 1;
    2097 				}
    2098 			}
    2099 			else if (nodes.length !== undefined) {
    2100 				if (nodes.constructor != Array) {
    2101 					// convert array-like objects into an array
    2102 					nodes = collectionToArray(nodes);
    2103 				}
    2104 				arrayPush.apply(this, nodes);
    2105 			}
    2106 			/*!debug*/
    2107 			else {
    2108 				glow.debug.warn('[wrong type] glow.NodeList#push: Ignoring unexpected argument type, failing silently');
    2109 			}
    2110 			/*gubed!*/
    2111 		}
    2112 		/*!debug*/
    2113 		else {
    2114 			glow.debug.warn('[wrong type] glow.NodeList#push: Ignoring false argument type, failing silently');
    2115 		}
    2116 		/*gubed!*/
    2117 		return this;
    2118 	};
    2119 	
    2120 	/**
    2121 		@name glow.NodeList#eq
    2122 		@function
    2123 		@description Compares this NodeList to another
    2124 			Returns true if both NodeLists contain the same items in the same order
    2125 		
    2126 		@param {Node | Node[] | glow.NodeList} nodeList The NodeList to compare to.
    2127 		
    2128 		@returns {boolean}
    2129 		
    2130 		@see {@link glow.NodeList#is} for testing if a NodeList item matches a selector
    2131 		
    2132 		@example
    2133 			// the following returns true
    2134 			glow('#blah').eq( document.getElementById('blah') );
    2135 	*/
    2136 	NodeListProto.eq = function(nodeList) {
    2137 		/*!debug*/
    2138 			if (arguments.length !== 1) {
    2139 				glow.debug.warn('[wrong count] glow.NodeList#eq expects 1 argument, not ' + arguments.length + '.');
    2140 			}
    2141 			if (typeof nodeList !== 'object') {
    2142 				glow.debug.warn('[wrong type] glow.NodeList#eq expects object argument, not ' + typeof nodeList + '.');
    2143 			}
    2144 		/*gubed!*/
    2145 		
    2146 		var len = this.length,
    2147 			i = len;
    2148 		
    2149 		// normalise param to NodeList
    2150 		if ( !(nodeList instanceof NodeList) ) {
    2151 			nodeList = new NodeList(nodeList);
    2152 		}
    2153 		
    2154 		// quickly return false if lengths are different
    2155 		if (len != nodeList.length) {
    2156 			return false;
    2157 		}
    2158 		
    2159 		// loop through and return false on inequality
    2160 		while (i--) {
    2161 			if (this[i] !== nodeList[i]) {
    2162 				return false;
    2163 			}
    2164 		}
    2165 		
    2166 		return true;
    2167 	};
    2168 	
    2169 	/**
    2170 		@name glow.NodeList#slice
    2171 		@function
    2172 		@description Get a section of an NodeList
    2173 			Operates in the same way as an Array's slice method
    2174 		
    2175 		@param {number} start Start index
    2176 			If negative, it specifies a position measured from the end of the list
    2177 		
    2178 		@param {number} [end] End index
    2179 			By default, this is the end of the list. A negative end specifies
    2180 			a position measured from the end of the list.
    2181 		
    2182 		@returns {glow.NodeList} A new sliced NodeList
    2183 		
    2184 		@example
    2185 		var myNodeList = glow("<div></div><p></p>");
    2186 		myNodeList.slice(1, 2); // selects the paragraph
    2187 		myNodeList.slice(-1); // same thing, selects the paragraph
    2188 	*/
    2189 	NodeListProto.slice = function(/*start, end*/) {
    2190 		return new NodeList( arraySlice.apply(this, arguments) );
    2191 	};
    2192 	
    2193 	/**
    2194 		@name glow.NodeList#sort
    2195 		@function
    2196 		@description Sort the elements in the list.
    2197 			Items will already be in document order if a CSS selector
    2198 			was used to fetch them.
    2199 		
    2200 		@param {Function} [func] Function to determine sort order
    2201 			This function will be passed 2 elements (elementA, elementB). The function
    2202 			should return a number less than 0 to sort elementA lower than elementB
    2203 			and greater than 0 to sort elementA higher than elementB.
    2204 			
    2205 			If no function is provided, elements will be sorted in document order.
    2206 		
    2207 		@returns {glow.NodeList} A new sorted NodeList
    2208 		
    2209 		@example
    2210 			//get links in alphabetical (well, lexicographical) order
    2211 			var links = glow("a").sort(function(elementA, elementB) {
    2212 				return glow(elementA).text() < glow(elementB).text() ? -1 : 1;
    2213 			})
    2214 	*/
    2215 	NodeListProto.sort = function(func) {
    2216 		var items = collectionToArray(this),
    2217 			sortedElms = func ? items.sort(func) : glow._sizzle.uniqueSort(items);
    2218 		
    2219 		return new NodeList(sortedElms);
    2220 	};
    2221 	
    2222 	/**
    2223 		@name glow.NodeList#item
    2224 		@function
    2225 		@description Get a single item from the list as an NodeList
    2226 			Negative numbers can be used to get items from the end of the
    2227 			list.
    2228 		
    2229 		@param {number} index The numeric index of the node to return.
    2230 		
    2231 		@returns {glow.NodeList} A new NodeList containing a single item
    2232 		
    2233 		@example
    2234 			// get the html from the fourth element
    2235 			myNodeList.item(3).html();
    2236 			
    2237 		@example
    2238 			// add a class name to the last item
    2239 			myNodeList.item(-1).addClass('last');
    2240 	*/
    2241 	NodeListProto.item = function(index) {
    2242 		/*!debug*/
    2243 			if ( arguments.length !== 1 ) {
    2244 				glow.debug.warn('[wrong count] glow.NodeList#item expects 1 argument, got ' + arguments.length);
    2245 			}
    2246 		/*gubed!*/
    2247 		// TODO: test which of these methods is faster (use the current one unless significantly slower)
    2248 		return this.slice(index, (index + 1) || this.length);
    2249 		// return new NodeList( index < 0 ? this[this.length + index] : this[index] );
    2250 	};
    2251 	
    2252 	/**
    2253 		@name glow.NodeList#each
    2254 		@function
    2255 		@description Calls a function for each node in the list.
    2256 		
    2257 		@param {Function} callback The function to call for each node.
    2258 			The function will be passed 2 arguments, the index of the current item,
    2259 			and the NodeList being iterated over.
    2260 			
    2261 			Inside the function 'this' refers to the Node.
    2262 			
    2263 			Returning false from this function stops further iterations
    2264 		
    2265 		@returns {glow.NodeList}
    2266 		
    2267 		@example
    2268 			// add "link number: x" to each link, where x is the index of the link
    2269 			glow("a").each(function(i, nodeList) {
    2270 				glow(this).append(' link number: ' + i);
    2271 			});
    2272 		@example
    2273 			// breaking out of an each loop
    2274 			glow("a").each(function(i, nodeList) {
    2275 				// do stuff
    2276 				if ( glow(this).hasClass('whatever') ) {
    2277 					// we don't want to process any more links
    2278 					return false;
    2279 				}
    2280 			});
    2281 	*/
    2282 	NodeListProto.each = function(callback) {
    2283 		/*!debug*/
    2284 			if ( arguments.length !== 1 ) {
    2285 				glow.debug.warn('[wrong count] glow.NodeList#each expects 1 argument, got ' + arguments.length);
    2286 			}
    2287 			if (typeof callback != 'function') {
    2288 				glow.debug.warn('[wrong type] glow.NodeList#each expects "function", got ' + typeof callback);
    2289 			}
    2290 		/*gubed!*/
    2291 		for (var i = 0, len = this.length; i<len; i++) {
    2292 			if ( callback.call(this[i], i, this) === false ) {
    2293 				break;
    2294 			}
    2295 		}
    2296 		return this;
    2297 	};
    2298 	
    2299 	/**
    2300 		@name glow.NodeList#filter
    2301 		@function
    2302 		@description Filter the NodeList
    2303 		 
    2304 		@param {Function|string} test Filter test
    2305 			If a string is provided it's treated as a CSS selector. Elements
    2306 			which match the CSS selector are added to the new NodeList.
    2307 			
    2308 			If 'test' is a function, it will be called per node in the NodeList.
    2309 			
    2310 			The function is passed 2 arguments, the index of the current item,
    2311 			and the ElementList being itterated over.
    2312 			
    2313 			Inside the function 'this' refers to the node.
    2314 			Return true to add the element to the new NodeList.
    2315 		 
    2316 		@returns {glow.NodeList} A new NodeList containing the filtered nodes
    2317 		 
    2318 		@example
    2319 			// return images with a width greater than 320
    2320 			glow("img").filter(function () {
    2321 				return glow(this).width() > 320;
    2322 			});
    2323 		
    2324 		@example
    2325 			// Get items that don't have an alt attribute
    2326 			myElementList.filter(':not([alt])');
    2327 	*/
    2328 	NodeListProto.filter = function(test) {
    2329 		/*!debug*/
    2330 			if ( arguments.length !== 1 ) {
    2331 				glow.debug.warn('[wrong count] glow.NodeList#filter expects 1 argument, got ' + arguments.length);
    2332 			}
    2333 			if ( !/^(function|string)$/.test(typeof test) ) {
    2334 				glow.debug.warn('[wrong type] glow.NodeList#each expects function/string, got ' + typeof test);
    2335 			}
    2336 		/*gubed!*/
    2337 		var r = [],
    2338 			ri = 0;
    2339 		
    2340 		if (typeof test === 'string') {
    2341 			r = glow._sizzle.matches(test, this);
    2342 		}
    2343 		else {	
    2344 			for (var i = 0, len = this.length; i<len; i++) {
    2345 				if ( test.call(this[i], i, this) ) {
    2346 					r[ri++] = this[i];
    2347 				}
    2348 			}
    2349 		}
    2350 		
    2351 		return new NodeList(r);
    2352 	};
    2353 
    2354 	
    2355 	/**
    2356 		@name glow.NodeList#is
    2357 		@function
    2358 		@description Tests if the first element matches a CSS selector
    2359 
    2360 		@param {string} selector CSS selector
    2361 		
    2362 		@returns {boolean}
    2363 		
    2364 		@example
    2365 			if ( myNodeList.is(':visible') ) {
    2366 				// ...
    2367 			}
    2368 	*/
    2369 	NodeListProto.is = function(selector) {
    2370 		/*!debug*/
    2371 			if ( arguments.length !== 1 ) {
    2372 				glow.debug.warn('[wrong count] glow.NodeList#is expects 1 argument, got ' + arguments.length);
    2373 			}
    2374 			if ( typeof selector !== 'string' ) {
    2375 				glow.debug.warn('[wrong type] glow.NodeList#is expects string, got ' + typeof selector);
    2376 			}
    2377 		/*gubed!*/
    2378 		if ( !this[0] ) {
    2379 			return false;
    2380 		}
    2381 		return !!glow._sizzle.matches( selector, [ this[0] ] ).length;
    2382 	};
    2383 	
    2384 	// export
    2385 	glow.NodeList = NodeList;
    2386 });
    2387 Glow.provide(function(glow) {
    2388 	var undef
    2389 		, NodeListProto = glow.NodeList.prototype
    2390 	
    2391 		/**
    2392 			@private
    2393 			@name glow.NodeList-dom0PropertyMapping
    2394 			@description Mapping of HTML attribute names to DOM0 property names.
    2395 		*/
    2396 		, dom0PropertyMapping = { // keys must be lowercase
    2397 			'class'     : 'className',
    2398 			'for'       : 'htmlFor',
    2399 			'maxlength' : 'maxLength'
    2400 		}
    2401 		
    2402 		/**
    2403 			@private
    2404 			@name glow.NodeList-dataPropName
    2405 			@type String
    2406 			@description The property name added to the DomElement by the NodeList#data method.
    2407 		*/
    2408 		, dataPropName = '_uniqueData' + glow.UID
    2409 		
    2410 		/**
    2411 			@private
    2412 			@name glow.NodeList-dataIndex
    2413 			@type String
    2414 			@description The value of the dataPropName added by the NodeList#data method.
    2415 		*/
    2416 		, dataIndex = 1 // must be a truthy value
    2417 			
    2418 		/**
    2419 			@private
    2420 			@name glow.NodeList-dataCache
    2421 			@type Object
    2422 			@description Holds the data used by the NodeList#data method.
    2423 			
    2424 			The structure is like:
    2425 			[
    2426 				{
    2427 					myKey: "my data"
    2428 				}
    2429 			]
    2430 		*/
    2431 		, dataCache = [];
    2432 			
    2433 	/**
    2434 	@name glow.NodeList#addClass
    2435 	@function
    2436 	@description Adds a class to each node.
    2437 
    2438 	@param {string} name The name of the class to add.
    2439 
    2440 	@returns {glow.NodeList}
    2441 
    2442 	@example
    2443 		glow("#login a").addClass("highlight");
    2444 	*/
    2445 	NodeListProto.addClass = function(name) {
    2446 		var i = this.length;
    2447 		
    2448 		/*!debug*/
    2449 			if (arguments.length !== 1) {
    2450 				glow.debug.warn('[wrong count] glow.NodeList#addClass expects 1 argument, not '+arguments.length+'.');
    2451 			}
    2452 			else if (typeof arguments[0] !== 'string') {
    2453 				glow.debug.warn('[wrong type] glow.NodeList#addClass expects argument 1 to be of type string, not '+typeof arguments[0]+'.');
    2454 			}
    2455 		/*gubed!*/
    2456 		
    2457 		while (i--) {
    2458 			if (this[i].nodeType === 1) {
    2459 				_addClass(this[i], name);
    2460 			}
    2461 		}
    2462 		
    2463 		return this;
    2464 	};
    2465 	
    2466 	function _addClass(node, name) { // TODO: handle classnames separated by non-space characters?
    2467 		if ( (' ' + node.className + ' ').indexOf(' ' + name + ' ') === -1 ) {
    2468 			node.className += (node.className? ' ' : '') + name;
    2469 		}
    2470 	}
    2471 	
    2472 	/**
    2473 	@name glow.NodeList#attr
    2474 	@function
    2475 	@description Gets or sets attributes.
    2476 
    2477 		When getting an attribute, it is retrieved from the first
    2478 		node in this NodeList. Setting attributes applies the change
    2479 		to each element in this NodeList.
    2480 
    2481 		To set an attribute, pass in the name as the first
    2482 		parameter and the value as a second parameter.
    2483 
    2484 		To set multiple attributes in one call, pass in an object of
    2485 		name/value pairs as a single parameter.
    2486 
    2487 		For browsers that don't support manipulating attributes
    2488 		using the DOM, this method will try to do the right thing
    2489 		(i.e. don't expect the semantics of this method to be
    2490 		consistent across browsers as this is not possible with
    2491 		currently supported browsers).
    2492 
    2493 	@param {string | Object} name The name of the attribute, or an object of name/value pairs
    2494 	@param {string} [value] The value to set the attribute to.
    2495 
    2496 	@returns {string | undefined | glow.NodeList}
    2497 
    2498 		When setting attributes this method returns its own NodeList, otherwise
    2499 		returns the attribute value. The attribute name is always treated as
    2500 		case-insensitive. When getting, the returned value will be of type string unless
    2501 		that particular attribute was never set and there is no default value, in which
    2502 		case the returned value will be an empty string.
    2503 
    2504 	@example
    2505 		var myNodeList = glow(".myImgClass");
    2506 
    2507 		// get an attribute
    2508 		myNodeList.attr("class");
    2509 
    2510 		// set an attribute
    2511 		myNodeList.attr("class", "anotherImgClass");
    2512 
    2513 		// set multiple attributes
    2514 		myNodeList.attr({
    2515 		  src: "a.png",
    2516 		  alt: "Cat jumping through a field"
    2517 		});
    2518 	 */
    2519 	 // see: http://tobielangel.com/2007/1/11/attribute-nightmare-in-ie/
    2520 	NodeListProto.attr = function(/*arguments*/) {
    2521 		var args = arguments,
    2522 			argsLen = args.length,
    2523 			thisLen = this.length,
    2524 			name = keyvals = args[0], // using this API: attr(name) or attr({key: val}) ?
    2525 			dom0Property = '',
    2526 			node,
    2527 			attrNode;
    2528 		
    2529 		/*!debug*/
    2530 			if (arguments.length === 2 && typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#attr expects name to be of type string, not '+typeof arguments[0]+'.'); }
    2531 			else if (arguments.length === 1 && (typeof arguments[0] !== 'string' && arguments[0].constructor !== Object)) {glow.debug.warn('[wrong type] glow.NodeList#attr expects argument 1 to be of type string or an instance of Object.'); }
    2532 			else if (arguments.length === 0 ||  arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#attr expects 1 or 2 arguments, not '+arguments.length+'.'); }
    2533 		/*gubed!*/
    2534 		
    2535 		if (this.length === 0) { // is this an empty nodelist?
    2536 			return (argsLen > 1)? this : undef;
    2537 		}
    2538 		
    2539 		if (typeof keyvals === 'object') { // SETting value from {name: value} object
    2540 			for (name in keyvals) {
    2541 				if (!keyvals.hasOwnProperty(name)) { continue; }
    2542 				
    2543 				// in IE6 and IE7 the attribute name needs to be translated into dom property name
    2544 				if (glow.env.ie < 8) {
    2545 					dom0Property = dom0PropertyMapping[name.toLowerCase()];
    2546 				}
    2547 				
    2548 				var i = thisLen;
    2549 				while (i--) {
    2550 					node = this[i];
    2551 					
    2552 					if (node.nodeType !== 1) { continue; }
    2553 					
    2554 					if (dom0Property) {
    2555 						node[dom0Property] = keyvals[name];
    2556 					}
    2557 					else {
    2558 						node.setAttribute(name, keyvals[name], 0); // IE flags, 0: case-insensitive
    2559 					}
    2560 				}
    2561 			}
    2562 			
    2563 			return this;
    2564 		}
    2565 		else {
    2566 			node = this[0];
    2567 				
    2568 			if (node.nodeType !== 1) {
    2569 				return (argsLen > 1)? this : undef;
    2570 			}
    2571 
    2572 			if (argsLen === 1) { // GETting value from name
    2573 				if (node.attributes[name]) { // in IE node.getAttributeNode sometimes returns unspecified default values so we look for specified attributes if we can
    2574 					return (!node.attributes[name].specified)? '' : node.attributes[name].value;
    2575 				}
    2576 				else if (node.getAttributeNode) { // in IE getAttribute() does not always work so we use getAttributeNode if we can
    2577 					attrNode = node.getAttributeNode(name, 0);
    2578 					return (attrNode === null)? '' : attrNode.value;
    2579 				}
    2580 				else {
    2581 					value = node.getAttribute(name, 0, 2); // IE flags, 0: case-insensitive, 2: as string
    2582 					return (value === null)? '' : value;
    2583 				}	
    2584 			}
    2585 			else { // SETting a single value like attr(name, value), normalize to an keyval object
    2586 				if (glow.env.ie < 8) {
    2587 					dom0Property = dom0PropertyMapping[name.toLowerCase()];
    2588 				}
    2589 				
    2590 				if (dom0Property) {
    2591 					node[dom0Property] = args[1];
    2592 				}
    2593 				else {
    2594 					node.setAttribute(name, args[1], 0); // IE flags, 0: case-insensitive
    2595 				}
    2596 				return this;
    2597 			}
    2598 		}
    2599 	};
    2600 	/**
    2601 		Copies the data from one nodelist to another
    2602 		@private
    2603 		@name glow.NodeList._copyData
    2604 		@see glow.NodeList#clone
    2605 		@function
    2606 	*/
    2607 	glow.NodeList._copyData = function(from, to){
    2608 		if ( !from[dataPropName] ){
    2609 			return;
    2610 		}
    2611 		else{			
    2612 			to = new glow.NodeList(to);
    2613 			to.data( dataCache[from[dataPropName]] );			
    2614 			return;
    2615 		}
    2616 		
    2617 	}
    2618 	/**
    2619 		Used to remove the data when a node is destroyed
    2620 		@private
    2621 		@name glow.NodeList._copyData
    2622 		@see glow.NodeList#destroy
    2623 		@function
    2624 	*/
    2625 	glow.NodeList._destroyData = function(removeFrom){
    2626 		if ( !removeFrom && !removeFrom[0][dataPropName] ){
    2627 			return;
    2628 		}
    2629 		else{
    2630 			removeFromNode = new glow.NodeList(removeFrom);
    2631 			removeFromNode.removeData();			
    2632 			return;
    2633 		}
    2634 		
    2635 	}
    2636 	/**
    2637 	@name glow.NodeList#data
    2638 	@function
    2639 	@description Use this to safely attach arbitrary data to any DOM Element.
    2640 	
    2641 	This method is useful when you wish to avoid memory leaks that are possible when adding your own data directly to DOM Elements.
    2642 	
    2643 	When called with no arguments, will return glow's entire data store for the first node in this NodeList.
    2644 	
    2645 	Otherwise, when given a name, will return the associated value from the first node in this NodeList.
    2646 	
    2647 	When given both a name and a value, will store that data on every node in this NodeList.
    2648 	
    2649 	Optionally you can pass in a single object composed of multiple name, value pairs.
    2650 	
    2651 	@param {string|Object} [key] The name of the value in glow's data store.
    2652 	@param {Object} [val] The value you wish to associate with the given name.
    2653 	@see glow.NodeList#removeData
    2654 	@example
    2655 	
    2656 	glow("p").data("tea", "milky");
    2657 	var colour = glow("p").data("tea"); // milky
    2658 	@returns {Object} When setting a value this method can be chained, as in that case it will return itself.
    2659 	@see glow.NodeList#removeData
    2660 	*/
    2661 	NodeListProto.data = function (key, val) { /*debug*///console.log("data("+key+", "+val+")");
    2662 		var args = arguments,
    2663 			argsLen = args.length,
    2664 			keyvals = key, // like: data({key: val}) or data(key, val)
    2665 			index,
    2666 			node;
    2667 		
    2668 		/*!debug*/
    2669 			if (arguments.length === 2 && typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#data expects name argument to be of type string.'); }
    2670 			else if (arguments.length === 1 && (typeof arguments[0] !== 'string' && arguments[0].constructor !== Object)) {glow.debug.warn('[wrong type] glow.NodeList#data expects argument 1 to be of type string or an instance of Object.'); }
    2671 			else if (arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#data expects 0, 1 or 2 arguments.'); }
    2672 		/*gubed!*/
    2673 		
    2674 		if (argsLen > 1) { // SET key, val on every node
    2675 			var i = this.length;
    2676 			while (i--) {
    2677 				node = this[i];
    2678 				if (node.nodeType !== 1) { continue; }
    2679 				
    2680 				index = node[dataPropName];
    2681 				if (!index) { // assumes index is always > 0
    2682 					index = dataIndex++;
    2683 					
    2684 					node[dataPropName] = index;
    2685 					dataCache[index] = {};
    2686 				}
    2687 				dataCache[index][key] = val;
    2688 			}
    2689 			
    2690 			return this; // chainable with (key, val) signature
    2691 		}
    2692 		else if (typeof keyvals === 'object') { // SET keyvals on every node
    2693 			var i = this.length;
    2694 			while (i--) {
    2695 				node = this[i];
    2696 				if (node.nodeType !== 1) { continue; }
    2697 				
    2698 				index = node[dataPropName];
    2699 				if (!index) { // assumes index is always > 0
    2700 					index = dataIndex++;
    2701 					
    2702 					node[dataPropName] = index;
    2703 					dataCache[index] = {};
    2704 				}
    2705 				for (key in keyvals) {
    2706 					dataCache[index][key] = keyvals[key];
    2707 				}
    2708 			}
    2709 			
    2710 			return this; // chainable with ({key, val}) signature
    2711 		}
    2712 		else { // GET from first node
    2713 			node = this[0];
    2714 			if (node === undef || node.nodeType !== 1) { return undef; }
    2715 				
    2716 			if ( !(index = node[dataPropName]) ) {
    2717 				return undef;
    2718 			}
    2719 			
    2720 			if (key) {
    2721 				return dataCache[index][key];
    2722 			}
    2723 			
    2724 			// get the entire data cache object for this node
    2725 			return dataCache[index];
    2726 		}
    2727 	};
    2728 	
    2729 	/**
    2730 	@name glow.NodeList#hasAttr
    2731 	@function
    2732 	@description Does the node have a particular attribute?
    2733 		
    2734 		The first node in this NodeList is tested.
    2735 		
    2736 	@param {string} name The name of the attribute to test for.
    2737 
    2738 	@returns {boolean|undefined} Returns undefined if the first node is not an element,
    2739 	or if the NodeList is empty, otherwise returns true/false to indicate if that attribute exists
    2740 	on the first element.
    2741 
    2742 	@example
    2743 		if ( glow("#myImg").hasAttr("alt") ){
    2744 			// ...
    2745 		}
    2746 	*/
    2747 	NodeListProto.hasAttr = function(name) {
    2748 		var node;
    2749 		
    2750 		/*!debug*/
    2751 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#hasAttr expects 1 argument.'); }
    2752 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#hasAttr expects argument 1 to be of type string.'); }
    2753 		/*gubed!*/
    2754 		
    2755 		node = this[0];
    2756 		
    2757 		if (this.length && node.nodeType === 1) {
    2758 			if (node.attributes[name]) { // is an object in  IE, or else: undefined in IE < 8, null in IE 8
    2759 				return !!node.attributes[name].specified;
    2760 			}
    2761 			
    2762 			if (node.hasAttribute) { return node.hasAttribute(name); } // like FF, Safari, etc
    2763 			else { return node.attributes[name] !== undef; } // like IE7
    2764 		}
    2765 	};
    2766 	
    2767 	/**
    2768 	@name glow.NodeList#hasClass
    2769 	@function
    2770 	@description Does the node have a particular class?
    2771 
    2772 		The first node in this NodeList is tested.
    2773 
    2774 	@param {string} name The name of the class to test for.
    2775 
    2776 	@returns {boolean}
    2777 
    2778 	@example
    2779 		if ( glow("#myInput").hasClass("errored") ){
    2780 			// ...
    2781 		}
    2782 	*/
    2783 	NodeListProto.hasClass = function (name) {
    2784 		/*!debug*/
    2785 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#hasClass expects 1 argument.'); }
    2786 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#hasClass expects argument 1 to be of type string.'); }
    2787 		/*gubed!*/
    2788 		
    2789 		if (this.length && this[0].nodeType === 1) {
    2790 			return ( (' ' + this[0].className + ' ').indexOf(' ' + name + ' ') > -1 );
    2791 		}
    2792 	};
    2793 	
    2794 	/**
    2795 	@name glow.NodeList#prop
    2796 	@function
    2797 	@description Gets or sets node properties.
    2798 	
    2799 		This function gets / sets node properties, to get attributes,
    2800 		see {@link glow.NodeList#attr NodeList#attr}.
    2801 		
    2802 		When getting a property, it is retrieved from the first
    2803 		node in this NodeList. Setting properties to each element in
    2804 		this NodeList.
    2805 		
    2806 		To set multiple properties in one call, pass in an object of
    2807 		name/value pairs.
    2808 		
    2809 	@param {string | Object} name The name of the property, or an object of name/value pairs
    2810 	@param {string} [value] The value to set the property to.
    2811 
    2812 	@returns {string | glow.NodeList}
    2813 
    2814 		When setting properties it returns the NodeList, otherwise
    2815 		returns the property value.
    2816 
    2817 	@example
    2818 		var myNodeList = glow("#formElement");
    2819 
    2820 		// get the node name
    2821 		myNodeList.prop("nodeName");
    2822 
    2823 		// set a property
    2824 		myNodeList.prop("_secretValue", 10);
    2825 
    2826 		// set multiple properties
    2827 		myNodeList.prop({
    2828 			checked: true,
    2829 			_secretValue: 10
    2830 		});
    2831 	*/
    2832 	NodeListProto.prop = function(name, val) {
    2833 		var hash = name,
    2834 			argsLen = arguments.length;
    2835 		
    2836 		/*!debug*/
    2837 			if (arguments.length === 1 && (typeof name !== 'string' && name.constructor !== Object)) {glow.debug.warn('[wrong type] glow.NodeList#prop expects argument 1 to be of type string or Object.'); }
    2838 			else if (arguments.length === 2 && typeof name !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#prop expects name to be of type string.'); }
    2839 			else if (arguments.length === 0 || arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#prop expects 1 or 2 arguments.'); }
    2840 		/*gubed!*/
    2841 		
    2842 		if (this.length === 0) return;
    2843 		
    2844 		if (argsLen === 2 && typeof name === 'string') {
    2845 			for (var i = 0, ilen = this.length; i < ilen; i++) {
    2846 				if (this[i].nodeType === 1) { this[i][name] = val; }
    2847 			}
    2848 			return this;
    2849 		}
    2850 		else if (argsLen === 1 && hash.constructor === Object) {
    2851 			for (var key in hash) {
    2852 				for (var i = 0, ilen = this.length; i < ilen; i++) {
    2853 					if (this[i].nodeType === 1) { this[i][key] = hash[key]; }
    2854 				}
    2855 			}
    2856 			return this;
    2857 		}
    2858 		else if (argsLen === 1 && typeof name === 'string') {
    2859 			if (this[0].nodeType === 1) { return this[0][name]; }
    2860 		}
    2861 		else {
    2862 			throw new Error('Invalid parameters.');
    2863 		}
    2864 	};
    2865 	
    2866 	/**
    2867 	@name glow.NodeList#removeAttr
    2868 	@function
    2869 	@description Removes an attribute from each node.
    2870 
    2871 	@param {string} name The name of the attribute to remove.
    2872 
    2873 	@returns {glow.NodeList}
    2874 
    2875 	@example
    2876 		glow("a").removeAttr("target");
    2877 	*/
    2878 	NodeListProto.removeAttr = function (name) {
    2879 		var dom0Property;
    2880 		
    2881 		/*!debug*/
    2882 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#removeAttr expects 1 argument.'); }
    2883 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeAttr expects argument 1 to be of type string.'); }
    2884 		/*gubed!*/
    2885 	
    2886 		for (var i = 0, leni = this.length; i < leni; i++) {
    2887 			if (this[i].nodeType === 1) {
    2888 				if (glow.env.ie < 8) {
    2889 					if ( (dom0Property = dom0PropertyMapping[name.toLowerCase()]) ) {
    2890 						this[i][dom0Property] = '';
    2891 					}
    2892 				}
    2893 				
    2894 				if (this[i].removeAttribute) this[i].removeAttribute(name);
    2895 			}
    2896 		}
    2897 		return this;
    2898 	};
    2899 	
    2900 	/**
    2901 	@name glow.NodeList#removeClass
    2902 	@function
    2903 	@description Removes a class from each node.
    2904 
    2905 	@param {string} name The name of the class to remove.
    2906 
    2907 	@returns {glow.NodeList}
    2908 
    2909 	@example
    2910 		glow("#footer #login a").removeClass("highlight");
    2911 	*/
    2912 	NodeListProto.removeClass = function(name) {
    2913 		var node;
    2914 					
    2915 		/*!debug*/
    2916 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#removeClass() expects 1 argument.'); }
    2917 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeClass() expects argument 1 to be of type string.'); }
    2918 		/*gubed!*/
    2919 		
    2920 		var i = this.length;
    2921 		while (i--) {
    2922 			node = this[i];
    2923 			if (node.className) {
    2924 				_removeClass(node, name);
    2925 			}
    2926 		}
    2927 		return this;
    2928 	};
    2929 	
    2930 	function _removeClass(node, name) {
    2931 		var oldClasses = node.className.split(' '),
    2932 			newClasses = [];
    2933 			
    2934 		oldClasses = node.className.split(' ');
    2935 		newClasses = [];
    2936 		
    2937 		var i = oldClasses.length;
    2938 		while (i--) {
    2939 			if (oldClasses[i] !== name) {
    2940 				oldClasses[i] && newClasses.unshift(oldClasses[i]); // unshift to maintain original order
    2941 			}
    2942 		}
    2943 		node.className = (newClasses.length)? newClasses.join(' ') : '';
    2944 	}
    2945 	
    2946 	/**
    2947 	@name glow.NodeList#removeData
    2948 	@function
    2949 	@description Removes data previously added by {@link glow.NodeList#data} from each node in this NodeList.
    2950 	
    2951 	When called with no arguments, will delete glow's entire data store for each node in this NodeList.
    2952 	
    2953 	Otherwise, when given a name, will delete the associated value from each node in this NodeList.
    2954 	
    2955 	@param {string} [key] The name of the value in glow's data store.
    2956 	@see glow.NodeList#data
    2957 	*/
    2958 	NodeListProto.removeData = function(key) {
    2959 		var elm,
    2960 			i = this.length,
    2961 			index;
    2962 			// uses private scoped variables: dataCache, dataPropName
    2963 		
    2964 		/*!debug*/
    2965 			if (arguments.length > 1) { glow.debug.warn('[wrong count] glow.NodeList#removeData expects 0 or 1 arguments.'); }
    2966 			else if (arguments.length === 1 && typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeData expects argument 1 to be of type string.'); }
    2967 		/*gubed!*/
    2968 		
    2969 		while (i--) {
    2970 			elm = this[i];
    2971 			index = elm[dataPropName];
    2972 			
    2973 			if (index !== undef) {
    2974 				switch (arguments.length) {
    2975 					case 0:
    2976 						dataCache[index] = undef;
    2977 						elm[dataPropName] = undef;
    2978 						try {
    2979 							delete elm[dataPropName]; // IE 6 goes wobbly here
    2980 						}
    2981 						catch(e) { // remove expando from IE 6
    2982 							elm.removeAttribute && elm.removeAttribute(dataPropName);
    2983 						}
    2984 						break;
    2985 					case 1:
    2986 						dataCache[index][key] = undef;
    2987 						delete dataCache[index][key];
    2988 						break;
    2989 				}
    2990 			}
    2991 		}
    2992 		
    2993 		return this; // chainable
    2994 	};
    2995 	
    2996 	/**
    2997 	@name glow.NodeList#toggleClass
    2998 	@function
    2999 	@description Toggles a class on each node.
    3000 
    3001 	@param {string} name The name of the class to toggle.
    3002 
    3003 	@returns {glow.NodeList}
    3004 
    3005 	@example
    3006 		glow(".onOffSwitch").toggleClass("on");
    3007 	 */
    3008 	NodeListProto.toggleClass = function(name) {
    3009 		var node;
    3010 		
    3011 		/*!debug*/
    3012 			if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#toggleClass() expects 1 argument.'); }
    3013 			else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#toggleClass() expects argument 1 to be of type string.'); }
    3014 		/*gubed!*/
    3015 		
    3016 		for (var i = 0, leni = this.length; i < leni; i++) {
    3017 			node = this[i];
    3018 			if (node.className) {
    3019 				if ( (' ' + node.className + ' ').indexOf(' ' + name + ' ') > -1 ) {
    3020 					_removeClass(node, name);
    3021 				}
    3022 				else {
    3023 					_addClass(node, name);
    3024 				}
    3025 			}
    3026 		}
    3027 		
    3028 		return this;
    3029 	};
    3030 	
    3031 	/**
    3032 	@name glow.NodeList#val
    3033 	@function
    3034 	@description Gets or sets form values for the first node.
    3035 
    3036 		<p><em>This method is not applicable to XML NodeLists.</em></p>
    3037 
    3038 		<p><em>Getting values from form elements</em></p>
    3039 
    3040 		The returned value depends on the type of element, see below:
    3041 
    3042 		<dl>
    3043 		<dt>Radio button or checkbox</dt>
    3044 		<dd>If checked, then the contents of the value attribute, otherwise an empty string.</dd>
    3045 		<dt>Select</dt>
    3046 		<dd>The contents of value attribute of the selected option</dd>
    3047 		<dt>Select (multiple)</dt>
    3048 		<dd>An array of selected option values.</dd>
    3049 		<dt>Other form element</dt>
    3050 		<dd>The value of the input.</dd>
    3051 		</dl>
    3052 
    3053 		<p><em>Getting values from a form</em></p>
    3054 
    3055 		If the first element in the NodeList is a form, then an
    3056 		object is returned containing the form data. Each item
    3057 		property of the object is a value as above, apart from when
    3058 		multiple elements of the same name exist, in which case the
    3059 		it will contain an array of values.
    3060 
    3061 		<p><em>Setting values for form elements</em></p>
    3062 
    3063 		If a value is passed and the first element of the NodeList
    3064 		is a form element, then the form element is given that value.
    3065 		For select elements, this means that the first option that
    3066 		matches the value will be selected. For selects that allow
    3067 		multiple selection, the options which have a value that
    3068 		exists in the array of values/match the value will be
    3069 		selected and others will be deselected.
    3070 
    3071 		Currently checkboxes and radio buttons are not checked or
    3072 		unchecked, just their value is changed. This does mean that
    3073 		this does not do exactly the reverse of getting the value
    3074 		from the element (see above) and as such may be subject to
    3075 		change
    3076 
    3077 		<p><em>Setting values for forms</em></p>
    3078 
    3079 		If the first element in the NodeList is a form and the
    3080 		value is an object, then each element of the form has its
    3081 		value set to the corresponding property of the object, using
    3082 		the method described above.
    3083 
    3084 	@param {String | Object} [value] The value to set the form element/elements to.
    3085 
    3086 	@returns {glow.dom.NodeList | String | Object}
    3087 
    3088 		When used to set a value it returns the NodeList, otherwise
    3089 		returns the value as described above.
    3090 
    3091 	@example
    3092 		// get a value
    3093 		var username = glow.dom.get("input#username").val();
    3094 
    3095 	@example			
    3096 		/ get values from a form
    3097 		var userDetails = glow.dom.get("form").val();
    3098 
    3099 	@example
    3100 		// set a value
    3101 		glow.dom.get("input#username").val("example username");
    3102 
    3103 	@example
    3104 		// set values in a form
    3105 		glow.dom.get("form").val({
    3106 			username : "another",
    3107 			name     : "A N Other"
    3108 		});
    3109 	*/
    3110 	NodeListProto.val = function(){
    3111 		/*
    3112 			PrivateFunction: elementValue
    3113 			Get a value for a form element.
    3114 		*/
    3115 
    3116 		function elementValue (el) {
    3117 			var elType = el.type,
    3118 				elChecked = el.checked,
    3119 				elValue = el.value,
    3120 				vals = [],
    3121 				i = 0;
    3122 
    3123 			if (elType == "radio") {
    3124 				return elChecked ? elValue : "";
    3125 			} else if (elType == "checkbox") {
    3126 				return elChecked ? elValue : "";
    3127 			} else if (elType == "select-one") {
    3128 				return el.selectedIndex > -1 ?
    3129 					el.options[el.selectedIndex].value : "";
    3130 				} else if (elType == "select-multiple") {
    3131 				for (var length = el.options.length; i < length; i++) {
    3132 					if (el.options[i].selected) {
    3133 						vals[vals.length] = el.options[i].value;
    3134 					}
    3135 				}
    3136 				return vals;
    3137 			} else {
    3138 				return elValue;
    3139 			}
    3140 		}
    3141 
    3142 		/*
    3143 		PrivateMethod: formValues
    3144 			Get an object containing form data.
    3145 		*/
    3146 		function formValues (form) {
    3147 			var vals = {},
    3148 				radios = {},
    3149 				formElements = form.elements,
    3150 				i = 0,
    3151 				length = formElements.length,
    3152 				name,
    3153 				formElement,
    3154 				j,
    3155 				radio,
    3156 				nodeName;
    3157 
    3158 			for (; i < length; i++) {
    3159 				formElement = formElements[i];
    3160 				nodeName = formElement.nodeName.toLowerCase();
    3161 				name = formElement.name;
    3162 				
    3163 				// fieldsets & objects come back as form elements, but we don't care about these
    3164 				// we don't bother with fields that don't have a name
    3165 				// switch to whitelist?
    3166 				if (
    3167 					nodeName == "fieldset" ||
    3168 					nodeName == "object" ||
    3169 					!name
    3170 				) { continue; }
    3171 				if (formElement.type == "checkbox" && ! formElement.checked) {
    3172 					if (! name in vals) {
    3173 						vals[name] = undefined;
    3174 					}
    3175 				} else if (formElement.type == "radio") {
    3176 					if (radios[name]) {
    3177 						radios[name][radios[name].length] = formElement;
    3178 					} else {
    3179 						radios[name] = [formElement];
    3180 					}
    3181 				} else {
    3182 					var value = elementValue(formElement);
    3183 					if (name in vals) {
    3184 						if (vals[name].push) {
    3185 							vals[name][vals[name].length] = value;
    3186 						} else {
    3187 							vals[name] = [vals[name], value];
    3188 						}
    3189 					} else {
    3190 						vals[name] = value;
    3191 					}
    3192 				}
    3193 			}
    3194 			for (i in radios) {
    3195 				j = 0;
    3196 				for (length = radios[i].length; j < length; j++) {
    3197 					radio = radios[i][j];
    3198 					name = radio.name;
    3199 					if (radio.checked) {
    3200 						vals[radio.name] = radio.value;
    3201 						break;
    3202 					}
    3203 				}
    3204 				if (! name in vals) { vals[name] = undefined; }
    3205 			}
    3206 			return vals;
    3207 		}
    3208 
    3209 		/*
    3210 		PrivateFunction: setFormValues
    3211 			Set values of a form to those in passed in object.
    3212 		*/
    3213 		function setFormValues (form, vals) {
    3214 			var prop, currentField,
    3215 				fields = {},
    3216 				storeType, i = 0, n, len, foundOne, currentFieldType;
    3217 
    3218 			for (prop in vals) {
    3219 				currentField = form[prop];
    3220 				if (currentField && currentField[0] && !currentField.options) { // is array of fields
    3221 					//normalise values to array of vals
    3222 					vals[prop] = vals[prop] && vals[prop].push ? vals[prop] : [vals[prop]];
    3223 					//order the fields by types that matter
    3224 					fields.radios = [];
    3225 					fields.checkboxesSelects = [];
    3226 					fields.multiSelects = [];
    3227 					fields.other = [];
    3228 
    3229 					for (i = 0; currentField[i]; i++) {
    3230 						currentFieldType = currentField[i].type;
    3231 						if (currentFieldType == "radio") {
    3232 							storeType = "radios";
    3233 					} else if (currentFieldType == "select-one" || currentFieldType == "checkbox") {
    3234 							storeType = "checkboxesSelects";
    3235 						} else if (currentFieldType == "select-multiple") {
    3236 							storeType = "multiSelects";
    3237 						} else {
    3238 							storeType = "other";
    3239 						}
    3240 						//add it to the correct array
    3241 						fields[storeType][fields[storeType].length] = currentField[i];
    3242 					}
    3243 
    3244 					for (i = 0; fields.multiSelects[i]; i++) {
    3245 						vals[prop] = setValue(fields.multiSelects[i], vals[prop]);
    3246 					}
    3247 					for (i = 0; fields.checkboxesSelects[i]; i++) {
    3248 						setValue(fields.checkboxesSelects[i], "");
    3249 						for (n = 0, len = vals[prop].length; n < len; n++) {
    3250 							if (setValue(fields.checkboxesSelects[i], vals[prop][n])) {
    3251 								vals[prop].slice(n, 1);
    3252 								break;
    3253 							}
    3254 						}
    3255 					}
    3256 					for (i = 0; fields.radios[i]; i++) {
    3257 						fields.radios[i].checked = false;
    3258 						foundOne = false;
    3259 						for (n = 0, len = vals[prop].length; n < len; n++) {
    3260 							if (setValue(fields.radios[i], vals[prop][n])) {
    3261 								vals[prop].slice(n, 1);
    3262 								foundOne = true;
    3263 								break;
    3264 							}
    3265 							if (foundOne) { break; }
    3266 						}
    3267 					}
    3268 					for (i = 0; fields.other[i] && vals[prop][i] !== undefined; i++) {
    3269 						setValue(fields.other[i], vals[prop][i]);
    3270 					}
    3271 				} else if (currentField && currentField.nodeName) { // is single field, easy
    3272 					setValue(currentField, vals[prop]);
    3273 				}
    3274 			}
    3275 		}
    3276 
    3277 		/*
    3278 		PrivateFunction: setValue
    3279 			Set the value of a form element.
    3280 			Returns:
    3281 			values that weren't able to set if array of vals passed (for multi select). Otherwise true if val set, false if not
    3282 		*/
    3283 		function setValue (el, val) {
    3284 			var i = 0,
    3285 				length,
    3286 				n = 0,
    3287 				nlen,
    3288 				elOption,
    3289 				optionVal;
    3290 
    3291 				if (el.type == "select-one") {
    3292 				for (length = el.options.length; i < length; i++) {
    3293 					if (el.options[i].value == val) {
    3294 						el.selectedIndex = i;
    3295 						return true;
    3296 					}
    3297 				}
    3298 				return false;
    3299 			} else if (el.type == "select-multiple") {
    3300 				var isArray = !!val.push;
    3301 				for (i = 0, length = el.options.length; i < length; i++) {
    3302 					elOption = el.options[i];
    3303 					optionVal = elOption.value;
    3304 					if (isArray) {
    3305 						elOption.selected = false;
    3306 						for (nlen = val.length; n < nlen; n++) {
    3307 							if (optionVal == val[n]) {
    3308 								elOption.selected = true;
    3309 								val.splice(n, 1);
    3310 								break;
    3311 							}
    3312 						}
    3313 					} else {
    3314 						return elOption.selected = val == optionVal;
    3315 					}
    3316 				}
    3317 				return false;
    3318 			} else if (el.type == "radio" || el.type == "checkbox") {
    3319 				el.checked = val == el.value;
    3320 				return val == el.value;
    3321 			} else {
    3322 				el.value = val;
    3323 				return true;
    3324 			}
    3325 		}
    3326 
    3327 		// toplevel implementation
    3328 	
    3329 		var args = arguments,
    3330 			val = args[0],
    3331 			that = this,
    3332 			i = 0,
    3333 			length = that.length;
    3334 
    3335 		if (args.length === 0) {
    3336 			return that[0].nodeName == 'FORM' ?
    3337 				formValues(that[0]) :
    3338 				elementValue(that[0]);
    3339 		}
    3340 		if (that[0].nodeName == 'FORM') {
    3341 			if (! typeof val == 'object') {
    3342 				throw 'value for FORM must be object';
    3343 			}
    3344 			setFormValues(that[0], val);
    3345 		} else {
    3346 			for (; i < length; i++) {
    3347 				setValue(that[i], val);
    3348 			}
    3349 		}
    3350 		return that;		
    3351 	};
    3352 });
    3353 /*!
    3354  * Sizzle CSS Selector Engine - v1.0
    3355  *  Copyright 2009, The Dojo Foundation
    3356  *  Released under the MIT, BSD, and GPL Licenses.
    3357  *  More information: http://sizzlejs.com/
    3358  */
    3359 (function(){
    3360 
    3361 var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
    3362 	done = 0,
    3363 	toString = Object.prototype.toString,
    3364 	hasDuplicate = false,
    3365 	baseHasDuplicate = true;
    3366 
    3367 // Here we check if the JavaScript engine is using some sort of
    3368 // optimization where it does not always call our comparision
    3369 // function. If that is the case, discard the hasDuplicate value.
    3370 //   Thus far that includes Google Chrome.
    3371 [0, 0].sort(function(){
    3372 	baseHasDuplicate = false;
    3373 	return 0;
    3374 });
    3375 
    3376 var Sizzle = function(selector, context, results, seed) {
    3377 	results = results || [];
    3378 	var origContext = context = context || document;
    3379 
    3380 	if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
    3381 		return [];
    3382 	}
    3383 	
    3384 	if ( !selector || typeof selector !== "string" ) {
    3385 		return results;
    3386 	}
    3387 
    3388 	var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
    3389 		soFar = selector;
    3390 	
    3391 	// Reset the position of the chunker regexp (start from head)
    3392 	while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
    3393 		soFar = m[3];
    3394 		
    3395 		parts.push( m[1] );
    3396 		
    3397 		if ( m[2] ) {
    3398 			extra = m[3];
    3399 			break;
    3400 		}
    3401 	}
    3402 
    3403 	if ( parts.length > 1 && origPOS.exec( selector ) ) {
    3404 		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
    3405 			set = posProcess( parts[0] + parts[1], context );
    3406 		} else {
    3407 			set = Expr.relative[ parts[0] ] ?
    3408 				[ context ] :
    3409 				Sizzle( parts.shift(), context );
    3410 
    3411 			while ( parts.length ) {
    3412 				selector = parts.shift();
    3413 
    3414 				if ( Expr.relative[ selector ] ) {
    3415 					selector += parts.shift();
    3416 				}
    3417 				
    3418 				set = posProcess( selector, set );
    3419 			}
    3420 		}
    3421 	} else {
    3422 		// Take a shortcut and set the context if the root selector is an ID
    3423 		// (but not if it'll be faster if the inner selector is an ID)
    3424 		if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
    3425 				Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
    3426 			var ret = Sizzle.find( parts.shift(), context, contextXML );
    3427 			context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
    3428 		}
    3429 
    3430 		if ( context ) {
    3431 			var ret = seed ?
    3432 				{ expr: parts.pop(), set: makeArray(seed) } :
    3433 				Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
    3434 			set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
    3435 
    3436 			if ( parts.length > 0 ) {
    3437 				checkSet = makeArray(set);
    3438 			} else {
    3439 				prune = false;
    3440 			}
    3441 
    3442 			while ( parts.length ) {
    3443 				var cur = parts.pop(), pop = cur;
    3444 
    3445 				if ( !Expr.relative[ cur ] ) {
    3446 					cur = "";
    3447 				} else {
    3448 					pop = parts.pop();
    3449 				}
    3450 
    3451 				if ( pop == null ) {
    3452 					pop = context;
    3453 				}
    3454 
    3455 				Expr.relative[ cur ]( checkSet, pop, contextXML );
    3456 			}
    3457 		} else {
    3458 			checkSet = parts = [];
    3459 		}
    3460 	}
    3461 
    3462 	if ( !checkSet ) {
    3463 		checkSet = set;
    3464 	}
    3465 
    3466 	if ( !checkSet ) {
    3467 		throw "Syntax error, unrecognized expression: " + (cur || selector);
    3468 	}
    3469 
    3470 	if ( toString.call(checkSet) === "[object Array]" ) {
    3471 		if ( !prune ) {
    3472 			results.push.apply( results, checkSet );
    3473 		} else if ( context && context.nodeType === 1 ) {
    3474 			for ( var i = 0; checkSet[i] != null; i++ ) {
    3475 				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
    3476 					results.push( set[i] );
    3477 				}
    3478 			}
    3479 		} else {
    3480 			for ( var i = 0; checkSet[i] != null; i++ ) {
    3481 				if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
    3482 					results.push( set[i] );
    3483 				}
    3484 			}
    3485 		}
    3486 	} else {
    3487 		makeArray( checkSet, results );
    3488 	}
    3489 
    3490 	if ( extra ) {
    3491 		Sizzle( extra, origContext, results, seed );
    3492 		Sizzle.uniqueSort( results );
    3493 	}
    3494 
    3495 	return results;
    3496 };
    3497 
    3498 Sizzle.uniqueSort = function(results){
    3499 	if ( sortOrder ) {
    3500 		hasDuplicate = baseHasDuplicate;
    3501 		results.sort(sortOrder);
    3502 
    3503 		if ( hasDuplicate ) {
    3504 			for ( var i = 1; i < results.length; i++ ) {
    3505 				if ( results[i] === results[i-1] ) {
    3506 					results.splice(i--, 1);
    3507 				}
    3508 			}
    3509 		}
    3510 	}
    3511 
    3512 	return results;
    3513 };
    3514 
    3515 Sizzle.matches = function(expr, set){
    3516 	return Sizzle(expr, null, null, set);
    3517 };
    3518 
    3519 Sizzle.find = function(expr, context, isXML){
    3520 	var set, match;
    3521 
    3522 	if ( !expr ) {
    3523 		return [];
    3524 	}
    3525 
    3526 	for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
    3527 		var type = Expr.order[i], match;
    3528 		
    3529 		if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
    3530 			var left = match[1];
    3531 			match.splice(1,1);
    3532 
    3533 			if ( left.substr( left.length - 1 ) !== "\\" ) {
    3534 				match[1] = (match[1] || "").replace(/\\/g, "");
    3535 				set = Expr.find[ type ]( match, context, isXML );
    3536 				if ( set != null ) {
    3537 					expr = expr.replace( Expr.match[ type ], "" );
    3538 					break;
    3539 				}
    3540 			}
    3541 		}
    3542 	}
    3543 
    3544 	if ( !set ) {
    3545 		set = context.getElementsByTagName("*");
    3546 	}
    3547 
    3548 	return {set: set, expr: expr};
    3549 };
    3550 
    3551 Sizzle.filter = function(expr, set, inplace, not){
    3552 	var old = expr, result = [], curLoop = set, match, anyFound,
    3553 		isXMLFilter = set && set[0] && isXML(set[0]);
    3554 
    3555 	while ( expr && set.length ) {
    3556 		for ( var type in Expr.filter ) {
    3557 			if ( (match = Expr.match[ type ].exec( expr )) != null ) {
    3558 				var filter = Expr.filter[ type ], found, item;
    3559 				anyFound = false;
    3560 
    3561 				if ( curLoop === result ) {
    3562 					result = [];
    3563 				}
    3564 
    3565 				if ( Expr.preFilter[ type ] ) {
    3566 					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
    3567 
    3568 					if ( !match ) {
    3569 						anyFound = found = true;
    3570 					} else if ( match === true ) {
    3571 						continue;
    3572 					}
    3573 				}
    3574 
    3575 				if ( match ) {
    3576 					for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
    3577 						if ( item ) {
    3578 							found = filter( item, match, i, curLoop );
    3579 							var pass = not ^ !!found;
    3580 
    3581 							if ( inplace && found != null ) {
    3582 								if ( pass ) {
    3583 									anyFound = true;
    3584 								} else {
    3585 									curLoop[i] = false;
    3586 								}
    3587 							} else if ( pass ) {
    3588 								result.push( item );
    3589 								anyFound = true;
    3590 							}
    3591 						}
    3592 					}
    3593 				}
    3594 
    3595 				if ( found !== undefined ) {
    3596 					if ( !inplace ) {
    3597 						curLoop = result;
    3598 					}
    3599 
    3600 					expr = expr.replace( Expr.match[ type ], "" );
    3601 
    3602 					if ( !anyFound ) {
    3603 						return [];
    3604 					}
    3605 
    3606 					break;
    3607 				}
    3608 			}
    3609 		}
    3610 
    3611 		// Improper expression
    3612 		if ( expr === old ) {
    3613 			if ( anyFound == null ) {
    3614 				throw "Syntax error, unrecognized expression: " + expr;
    3615 			} else {
    3616 				break;
    3617 			}
    3618 		}
    3619 
    3620 		old = expr;
    3621 	}
    3622 
    3623 	return curLoop;
    3624 };
    3625 
    3626 var Expr = Sizzle.selectors = {
    3627 	order: [ "ID", "NAME", "TAG" ],
    3628 	match: {
    3629 		ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
    3630 		CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
    3631 		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
    3632 		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
    3633 		TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
    3634 		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
    3635 		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
    3636 		PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/
    3637 	},
    3638 	leftMatch: {},
    3639 	attrMap: {
    3640 		"class": "className",
    3641 		"for": "htmlFor"
    3642 	},
    3643 	attrHandle: {
    3644 		href: function(elem){
    3645 			return elem.getAttribute("href");
    3646 		}
    3647 	},
    3648 	relative: {
    3649 		"+": function(checkSet, part){
    3650 			var isPartStr = typeof part === "string",
    3651 				isTag = isPartStr && !/\W/.test(part),
    3652 				isPartStrNotTag = isPartStr && !isTag;
    3653 
    3654 			if ( isTag ) {
    3655 				part = part.toLowerCase();
    3656 			}
    3657 
    3658 			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
    3659 				if ( (elem = checkSet[i]) ) {
    3660 					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
    3661 
    3662 					checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
    3663 						elem || false :
    3664 						elem === part;
    3665 				}
    3666 			}
    3667 
    3668 			if ( isPartStrNotTag ) {
    3669 				Sizzle.filter( part, checkSet, true );
    3670 			}
    3671 		},
    3672 		">": function(checkSet, part){
    3673 			var isPartStr = typeof part === "string";
    3674 
    3675 			if ( isPartStr && !/\W/.test(part) ) {
    3676 				part = part.toLowerCase();
    3677 
    3678 				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
    3679 					var elem = checkSet[i];
    3680 					if ( elem ) {
    3681 						var parent = elem.parentNode;
    3682 						checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
    3683 					}
    3684 				}
    3685 			} else {
    3686 				for ( var i = 0, l = checkSet.length; i < l; i++ ) {
    3687 					var elem = checkSet[i];
    3688 					if ( elem ) {
    3689 						checkSet[i] = isPartStr ?
    3690 							elem.parentNode :
    3691 							elem.parentNode === part;
    3692 					}
    3693 				}
    3694 
    3695 				if ( isPartStr ) {
    3696 					Sizzle.filter( part, checkSet, true );
    3697 				}
    3698 			}
    3699 		},
    3700 		"": function(checkSet, part, isXML){
    3701 			var doneName = done++, checkFn = dirCheck;
    3702 
    3703 			if ( typeof part === "string" && !/\W/.test(part) ) {
    3704 				var nodeCheck = part = part.toLowerCase();
    3705 				checkFn = dirNodeCheck;
    3706 			}
    3707 
    3708 			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
    3709 		},
    3710 		"~": function(checkSet, part, isXML){
    3711 			var doneName = done++, checkFn = dirCheck;
    3712 
    3713 			if ( typeof part === "string" && !/\W/.test(part) ) {
    3714 				var nodeCheck = part = part.toLowerCase();
    3715 				checkFn = dirNodeCheck;
    3716 			}
    3717 
    3718 			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
    3719 		}
    3720 	},
    3721 	find: {
    3722 		ID: function(match, context, isXML){
    3723 			if ( typeof context.getElementById !== "undefined" && !isXML ) {
    3724 				var m = context.getElementById(match[1]);
    3725 				return m ? [m] : [];
    3726 			}
    3727 		},
    3728 		NAME: function(match, context){
    3729 			if ( typeof context.getElementsByName !== "undefined" ) {
    3730 				var ret = [], results = context.getElementsByName(match[1]);
    3731 
    3732 				for ( var i = 0, l = results.length; i < l; i++ ) {
    3733 					if ( results[i].getAttribute("name") === match[1] ) {
    3734 						ret.push( results[i] );
    3735 					}
    3736 				}
    3737 
    3738 				return ret.length === 0 ? null : ret;
    3739 			}
    3740 		},
    3741 		TAG: function(match, context){
    3742 			return context.getElementsByTagName(match[1]);
    3743 		}
    3744 	},
    3745 	preFilter: {
    3746 		CLASS: function(match, curLoop, inplace, result, not, isXML){
    3747 			match = " " + match[1].replace(/\\/g, "") + " ";
    3748 
    3749 			if ( isXML ) {
    3750 				return match;
    3751 			}
    3752 
    3753 			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
    3754 				if ( elem ) {
    3755 					if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
    3756 						if ( !inplace ) {
    3757 							result.push( elem );
    3758 						}
    3759 					} else if ( inplace ) {
    3760 						curLoop[i] = false;
    3761 					}
    3762 				}
    3763 			}
    3764 
    3765 			return false;
    3766 		},
    3767 		ID: function(match){
    3768 			return match[1].replace(/\\/g, "");
    3769 		},
    3770 		TAG: function(match, curLoop){
    3771 			return match[1].toLowerCase();
    3772 		},
    3773 		CHILD: function(match){
    3774 			if ( match[1] === "nth" ) {
    3775 				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
    3776 				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
    3777 					match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
    3778 					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
    3779 
    3780 				// calculate the numbers (first)n+(last) including if they are negative
    3781 				match[2] = (test[1] + (test[2] || 1)) - 0;
    3782 				match[3] = test[3] - 0;
    3783 			}
    3784 
    3785 			// TODO: Move to normal caching system
    3786 			match[0] = done++;
    3787 
    3788 			return match;
    3789 		},
    3790 		ATTR: function(match, curLoop, inplace, result, not, isXML){
    3791 			var name = match[1].replace(/\\/g, "");
    3792 			
    3793 			if ( !isXML && Expr.attrMap[name] ) {
    3794 				match[1] = Expr.attrMap[name];
    3795 			}
    3796 
    3797 			if ( match[2] === "~=" ) {
    3798 				match[4] = " " + match[4] + " ";
    3799 			}
    3800 
    3801 			return match;
    3802 		},
    3803 		PSEUDO: function(match, curLoop, inplace, result, not){
    3804 			if ( match[1] === "not" ) {
    3805 				// If we're dealing with a complex expression, or a simple one
    3806 				if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
    3807 					match[3] = Sizzle(match[3], null, null, curLoop);
    3808 				} else {
    3809 					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
    3810 					if ( !inplace ) {
    3811 						result.push.apply( result, ret );
    3812 					}
    3813 					return false;
    3814 				}
    3815 			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
    3816 				return true;
    3817 			}
    3818 			
    3819 			return match;
    3820 		},
    3821 		POS: function(match){
    3822 			match.unshift( true );
    3823 			return match;
    3824 		}
    3825 	},
    3826 	filters: {
    3827 		enabled: function(elem){
    3828 			return elem.disabled === false && elem.type !== "hidden";
    3829 		},
    3830 		disabled: function(elem){
    3831 			return elem.disabled === true;
    3832 		},
    3833 		checked: function(elem){
    3834 			return elem.checked === true;
    3835 		},
    3836 		selected: function(elem){
    3837 			// Accessing this property makes selected-by-default
    3838 			// options in Safari work properly
    3839 			elem.parentNode.selectedIndex;
    3840 			return elem.selected === true;
    3841 		},
    3842 		parent: function(elem){
    3843 			return !!elem.firstChild;
    3844 		},
    3845 		empty: function(elem){
    3846 			return !elem.firstChild;
    3847 		},
    3848 		has: function(elem, i, match){
    3849 			return !!Sizzle( match[3], elem ).length;
    3850 		},
    3851 		header: function(elem){
    3852 			return /h\d/i.test( elem.nodeName );
    3853 		},
    3854 		text: function(elem){
    3855 			return "text" === elem.type;
    3856 		},
    3857 		radio: function(elem){
    3858 			return "radio" === elem.type;
    3859 		},
    3860 		checkbox: function(elem){
    3861 			return "checkbox" === elem.type;
    3862 		},
    3863 		file: function(elem){
    3864 			return "file" === elem.type;
    3865 		},
    3866 		password: function(elem){
    3867 			return "password" === elem.type;
    3868 		},
    3869 		submit: function(elem){
    3870 			return "submit" === elem.type;
    3871 		},
    3872 		image: function(elem){
    3873 			return "image" === elem.type;
    3874 		},
    3875 		reset: function(elem){
    3876 			return "reset" === elem.type;
    3877 		},
    3878 		button: function(elem){
    3879 			return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
    3880 		},
    3881 		input: function(elem){
    3882 			return /input|select|textarea|button/i.test(elem.nodeName);
    3883 		}
    3884 	},
    3885 	setFilters: {
    3886 		first: function(elem, i){
    3887 			return i === 0;
    3888 		},
    3889 		last: function(elem, i, match, array){
    3890 			return i === array.length - 1;
    3891 		},
    3892 		even: function(elem, i){
    3893 			return i % 2 === 0;
    3894 		},
    3895 		odd: function(elem, i){
    3896 			return i % 2 === 1;
    3897 		},
    3898 		lt: function(elem, i, match){
    3899 			return i < match[3] - 0;
    3900 		},
    3901 		gt: function(elem, i, match){
    3902 			return i > match[3] - 0;
    3903 		},
    3904 		nth: function(elem, i, match){
    3905 			return match[3] - 0 === i;
    3906 		},
    3907 		eq: function(elem, i, match){
    3908 			return match[3] - 0 === i;
    3909 		}
    3910 	},
    3911 	filter: {
    3912 		PSEUDO: function(elem, match, i, array){
    3913 			var name = match[1], filter = Expr.filters[ name ];
    3914 
    3915 			if ( filter ) {
    3916 				return filter( elem, i, match, array );
    3917 			} else if ( name === "contains" ) {
    3918 				return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
    3919 			} else if ( name === "not" ) {
    3920 				var not = match[3];
    3921 
    3922 				for ( var i = 0, l = not.length; i < l; i++ ) {
    3923 					if ( not[i] === elem ) {
    3924 						return false;
    3925 					}
    3926 				}
    3927 
    3928 				return true;
    3929 			} else {
    3930 				throw "Syntax error, unrecognized expression: " + name;
    3931 			}
    3932 		},
    3933 		CHILD: function(elem, match){
    3934 			var type = match[1], node = elem;
    3935 			switch (type) {
    3936 				case 'only':
    3937 				case 'first':
    3938 					while ( (node = node.previousSibling) )	 {
    3939 						if ( node.nodeType === 1 ) { 
    3940 							return false; 
    3941 						}
    3942 					}
    3943 					if ( type === "first" ) { 
    3944 						return true; 
    3945 					}
    3946 					node = elem;
    3947 				case 'last':
    3948 					while ( (node = node.nextSibling) )	 {
    3949 						if ( node.nodeType === 1 ) { 
    3950 							return false; 
    3951 						}
    3952 					}
    3953 					return true;
    3954 				case 'nth':
    3955 					var first = match[2], last = match[3];
    3956 
    3957 					if ( first === 1 && last === 0 ) {
    3958 						return true;
    3959 					}
    3960 					
    3961 					var doneName = match[0],
    3962 						parent = elem.parentNode;
    3963 	
    3964 					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
    3965 						var count = 0;
    3966 						for ( node = parent.firstChild; node; node = node.nextSibling ) {
    3967 							if ( node.nodeType === 1 ) {
    3968 								node.nodeIndex = ++count;
    3969 							}
    3970 						} 
    3971 						parent.sizcache = doneName;
    3972 					}
    3973 					
    3974 					var diff = elem.nodeIndex - last;
    3975 					if ( first === 0 ) {
    3976 						return diff === 0;
    3977 					} else {
    3978 						return ( diff % first === 0 && diff / first >= 0 );
    3979 					}
    3980 			}
    3981 		},
    3982 		ID: function(elem, match){
    3983 			return elem.nodeType === 1 && elem.getAttribute("id") === match;
    3984 		},
    3985 		TAG: function(elem, match){
    3986 			return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
    3987 		},
    3988 		CLASS: function(elem, match){
    3989 			return (" " + (elem.className || elem.getAttribute("class")) + " ")
    3990 				.indexOf( match ) > -1;
    3991 		},
    3992 		ATTR: function(elem, match){
    3993 			var name = match[1],
    3994 				result = Expr.attrHandle[ name ] ?
    3995 					Expr.attrHandle[ name ]( elem ) :
    3996 					elem[ name ] != null ?
    3997 						elem[ name ] :
    3998 						elem.getAttribute( name ),
    3999 				value = result + "",
    4000 				type = match[2],
    4001 				check = match[4];
    4002 
    4003 			return result == null ?
    4004 				type === "!=" :
    4005 				type === "=" ?
    4006 				value === check :
    4007 				type === "*=" ?
    4008 				value.indexOf(check) >= 0 :
    4009 				type === "~=" ?
    4010 				(" " + value + " ").indexOf(check) >= 0 :
    4011 				!check ?
    4012 				value && result !== false :
    4013 				type === "!=" ?
    4014 				value !== check :
    4015 				type === "^=" ?
    4016 				value.indexOf(check) === 0 :
    4017 				type === "$=" ?
    4018 				value.substr(value.length - check.length) === check :
    4019 				type === "|=" ?
    4020 				value === check || value.substr(0, check.length + 1) === check + "-" :
    4021 				false;
    4022 		},
    4023 		POS: function(elem, match, i, array){
    4024 			var name = match[2], filter = Expr.setFilters[ name ];
    4025 
    4026 			if ( filter ) {
    4027 				return filter( elem, i, match, array );
    4028 			}
    4029 		}
    4030 	}
    4031 };
    4032 
    4033 var origPOS = Expr.match.POS;
    4034 
    4035 for ( var type in Expr.match ) {
    4036 	Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
    4037 	Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source );
    4038 }
    4039 
    4040 var makeArray = function(array, results) {
    4041 	array = Array.prototype.slice.call( array, 0 );
    4042 
    4043 	if ( results ) {
    4044 		results.push.apply( results, array );
    4045 		return results;
    4046 	}
    4047 	
    4048 	return array;
    4049 };
    4050 
    4051 // Perform a simple check to determine if the browser is capable of
    4052 // converting a NodeList to an array using builtin methods.
    4053 try {
    4054 	Array.prototype.slice.call( document.documentElement.childNodes, 0 );
    4055 
    4056 // Provide a fallback method if it does not work
    4057 } catch(e){
    4058 	makeArray = function(array, results) {
    4059 		var ret = results || [];
    4060 
    4061 		if ( toString.call(array) === "[object Array]" ) {
    4062 			Array.prototype.push.apply( ret, array );
    4063 		} else {
    4064 			if ( typeof array.length === "number" ) {
    4065 				for ( var i = 0, l = array.length; i < l; i++ ) {
    4066 					ret.push( array[i] );
    4067 				}
    4068 			} else {
    4069 				for ( var i = 0; array[i]; i++ ) {
    4070 					ret.push( array[i] );
    4071 				}
    4072 			}
    4073 		}
    4074 
    4075 		return ret;
    4076 	};
    4077 }
    4078 
    4079 var sortOrder;
    4080 
    4081 if ( document.documentElement.compareDocumentPosition ) {
    4082 	sortOrder = function( a, b ) {
    4083 		if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
    4084 			if ( a == b ) {
    4085 				hasDuplicate = true;
    4086 			}
    4087 			return a.compareDocumentPosition ? -1 : 1;
    4088 		}
    4089 
    4090 		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
    4091 		if ( ret === 0 ) {
    4092 			hasDuplicate = true;
    4093 		}
    4094 		return ret;
    4095 	};
    4096 } else if ( "sourceIndex" in document.documentElement ) {
    4097 	sortOrder = function( a, b ) {
    4098 		if ( !a.sourceIndex || !b.sourceIndex ) {
    4099 			if ( a == b ) {
    4100 				hasDuplicate = true;
    4101 			}
    4102 			return a.sourceIndex ? -1 : 1;
    4103 		}
    4104 
    4105 		var ret = a.sourceIndex - b.sourceIndex;
    4106 		if ( ret === 0 ) {
    4107 			hasDuplicate = true;
    4108 		}
    4109 		return ret;
    4110 	};
    4111 } else if ( document.createRange ) {
    4112 	sortOrder = function( a, b ) {
    4113 		if ( !a.ownerDocument || !b.ownerDocument ) {
    4114 			if ( a == b ) {
    4115 				hasDuplicate = true;
    4116 			}
    4117 			return a.ownerDocument ? -1 : 1;
    4118 		}
    4119 
    4120 		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
    4121 		aRange.setStart(a, 0);
    4122 		aRange.setEnd(a, 0);
    4123 		bRange.setStart(b, 0);
    4124 		bRange.setEnd(b, 0);
    4125 		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
    4126 		if ( ret === 0 ) {
    4127 			hasDuplicate = true;
    4128 		}
    4129 		return ret;
    4130 	};
    4131 }
    4132 
    4133 // Utility function for retreiving the text value of an array of DOM nodes
    4134 function getText( elems ) {
    4135 	var ret = "", elem;
    4136 
    4137 	for ( var i = 0; elems[i]; i++ ) {
    4138 		elem = elems[i];
    4139 
    4140 		// Get the text from text nodes and CDATA nodes
    4141 		if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
    4142 			ret += elem.nodeValue;
    4143 
    4144 		// Traverse everything else, except comment nodes
    4145 		} else if ( elem.nodeType !== 8 ) {
    4146 			ret += getText( elem.childNodes );
    4147 		}
    4148 	}
    4149 
    4150 	return ret;
    4151 }
    4152 
    4153 // Check to see if the browser returns elements by name when
    4154 // querying by getElementById (and provide a workaround)
    4155 (function(){
    4156 	// We're going to inject a fake input element with a specified name
    4157 	var form = document.createElement("div"),
    4158 		id = "script" + (new Date).getTime();
    4159 	form.innerHTML = "<a name='" + id + "'/>";
    4160 
    4161 	// Inject it into the root element, check its status, and remove it quickly
    4162 	var root = document.documentElement;
    4163 	root.insertBefore( form, root.firstChild );
    4164 
    4165 	// The workaround has to do additional checks after a getElementById
    4166 	// Which slows things down for other browsers (hence the branching)
    4167 	if ( document.getElementById( id ) ) {
    4168 		Expr.find.ID = function(match, context, isXML){
    4169 			if ( typeof context.getElementById !== "undefined" && !isXML ) {
    4170 				var m = context.getElementById(match[1]);
    4171 				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
    4172 			}
    4173 		};
    4174 
    4175 		Expr.filter.ID = function(elem, match){
    4176 			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
    4177 			return elem.nodeType === 1 && node && node.nodeValue === match;
    4178 		};
    4179 	}
    4180 
    4181 	root.removeChild( form );
    4182 	root = form = null; // release memory in IE
    4183 })();
    4184 
    4185 (function(){
    4186 	// Check to see if the browser returns only elements
    4187 	// when doing getElementsByTagName("*")
    4188 
    4189 	// Create a fake element
    4190 	var div = document.createElement("div");
    4191 	div.appendChild( document.createComment("") );
    4192 
    4193 	// Make sure no comments are found
    4194 	if ( div.getElementsByTagName("*").length > 0 ) {
    4195 		Expr.find.TAG = function(match, context){
    4196 			var results = context.getElementsByTagName(match[1]);
    4197 
    4198 			// Filter out possible comments
    4199 			if ( match[1] === "*" ) {
    4200 				var tmp = [];
    4201 
    4202 				for ( var i = 0; results[i]; i++ ) {
    4203 					if ( results[i].nodeType === 1 ) {
    4204 						tmp.push( results[i] );
    4205 					}
    4206 				}
    4207 
    4208 				results = tmp;
    4209 			}
    4210 
    4211 			return results;
    4212 		};
    4213 	}
    4214 
    4215 	// Check to see if an attribute returns normalized href attributes
    4216 	div.innerHTML = "<a href='#'></a>";
    4217 	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
    4218 			div.firstChild.getAttribute("href") !== "#" ) {
    4219 		Expr.attrHandle.href = function(elem){
    4220 			return elem.getAttribute("href", 2);
    4221 		};
    4222 	}
    4223 
    4224 	div = null; // release memory in IE
    4225 })();
    4226 
    4227 if ( document.querySelectorAll ) {
    4228 	(function(){
    4229 		var oldSizzle = Sizzle, div = document.createElement("div");
    4230 		div.innerHTML = "<p class='TEST'></p>";
    4231 
    4232 		// Safari can't handle uppercase or unicode characters when
    4233 		// in quirks mode.
    4234 		if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
    4235 			return;
    4236 		}
    4237 	
    4238 		Sizzle = function(query, context, extra, seed){
    4239 			context = context || document;
    4240 
    4241 			// Only use querySelectorAll on non-XML documents
    4242 			// (ID selectors don't work in non-HTML documents)
    4243 			if ( !seed && context.nodeType === 9 && !isXML(context) ) {
    4244 				try {
    4245 					return makeArray( context.querySelectorAll(query), extra );
    4246 				} catch(e){}
    4247 			}
    4248 		
    4249 			return oldSizzle(query, context, extra, seed);
    4250 		};
    4251 
    4252 		for ( var prop in oldSizzle ) {
    4253 			Sizzle[ prop ] = oldSizzle[ prop ];
    4254 		}
    4255 
    4256 		div = null; // release memory in IE
    4257 	})();
    4258 }
    4259 
    4260 (function(){
    4261 	var div = document.createElement("div");
    4262 
    4263 	div.innerHTML = "<div class='test e'></div><div class='test'></div>";
    4264 
    4265 	// Opera can't find a second classname (in 9.6)
    4266 	// Also, make sure that getElementsByClassName actually exists
    4267 	if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
    4268 		return;
    4269 	}
    4270 
    4271 	// Safari caches class attributes, doesn't catch changes (in 3.2)
    4272 	div.lastChild.className = "e";
    4273 
    4274 	if ( div.getElementsByClassName("e").length === 1 ) {
    4275 		return;
    4276 	}
    4277 	
    4278 	Expr.order.splice(1, 0, "CLASS");
    4279 	Expr.find.CLASS = function(match, context, isXML) {
    4280 		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
    4281 			return context.getElementsByClassName(match[1]);
    4282 		}
    4283 	};
    4284 
    4285 	div = null; // release memory in IE
    4286 })();
    4287 
    4288 function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
    4289 	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
    4290 		var elem = checkSet[i];
    4291 		if ( elem ) {
    4292 			elem = elem[dir];
    4293 			var match = false;
    4294 
    4295 			while ( elem ) {
    4296 				if ( elem.sizcache === doneName ) {
    4297 					match = checkSet[elem.sizset];
    4298 					break;
    4299 				}
    4300 
    4301 				if ( elem.nodeType === 1 && !isXML ){
    4302 					elem.sizcache = doneName;
    4303 					elem.sizset = i;
    4304 				}
    4305 
    4306 				if ( elem.nodeName.toLowerCase() === cur ) {
    4307 					match = elem;
    4308 					break;
    4309 				}
    4310 
    4311 				elem = elem[dir];
    4312 			}
    4313 
    4314 			checkSet[i] = match;
    4315 		}
    4316 	}
    4317 }
    4318 
    4319 function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
    4320 	for ( var i = 0, l = checkSet.length; i < l; i++ ) {
    4321 		var elem = checkSet[i];
    4322 		if ( elem ) {
    4323 			elem = elem[dir];
    4324 			var match = false;
    4325 
    4326 			while ( elem ) {
    4327 				if ( elem.sizcache === doneName ) {
    4328 					match = checkSet[elem.sizset];
    4329 					break;
    4330 				}
    4331 
    4332 				if ( elem.nodeType === 1 ) {
    4333 					if ( !isXML ) {
    4334 						elem.sizcache = doneName;
    4335 						elem.sizset = i;
    4336 					}
    4337 					if ( typeof cur !== "string" ) {
    4338 						if ( elem === cur ) {
    4339 							match = true;
    4340 							break;
    4341 						}
    4342 
    4343 					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
    4344 						match = elem;
    4345 						break;
    4346 					}
    4347 				}
    4348 
    4349 				elem = elem[dir];
    4350 			}
    4351 
    4352 			checkSet[i] = match;
    4353 		}
    4354 	}
    4355 }
    4356 
    4357 var contains = document.compareDocumentPosition ? function(a, b){
    4358 	return a.compareDocumentPosition(b) & 16;
    4359 } : function(a, b){
    4360 	return a !== b && (a.contains ? a.contains(b) : true);
    4361 };
    4362 
    4363 var isXML = function(elem){
    4364 	// documentElement is verified for cases where it doesn't yet exist
    4365 	// (such as loading iframes in IE - #4833) 
    4366 	var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
    4367 	return documentElement ? documentElement.nodeName !== "HTML" : false;
    4368 };
    4369 
    4370 var posProcess = function(selector, context){
    4371 	var tmpSet = [], later = "", match,
    4372 		root = context.nodeType ? [context] : context;
    4373 
    4374 	// Position selectors must be done after the filter
    4375 	// And so must :not(positional) so we move all PSEUDOs to the end
    4376 	while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
    4377 		later += match[0];
    4378 		selector = selector.replace( Expr.match.PSEUDO, "" );
    4379 	}
    4380 
    4381 	selector = Expr.relative[selector] ? selector + "*" : selector;
    4382 
    4383 	for ( var i = 0, l = root.length; i < l; i++ ) {
    4384 		Sizzle( selector, root[i], tmpSet );
    4385 	}
    4386 
    4387 	return Sizzle.filter( later, tmpSet );
    4388 };
    4389 
    4390 // Add Sizzle to Glow
    4391 // This file is injected into sizzle.js by the ant "deps" target
    4392 Glow.provide(function(glow) {
    4393 	glow._sizzle = Sizzle;
    4394 });
    4395 
    4396 return;
    4397 
    4398 
    4399 window.Sizzle = Sizzle;
    4400 
    4401 })();
    4402 Glow.provide(function(glow) {
    4403 	var NodeListProto = glow.NodeList.prototype
    4404 	/*
    4405 		PrivateVar: ucheck
    4406 			Used by unique(), increased by 1 on each use
    4407 		*/
    4408 		,	ucheck = 1
    4409 	/*
    4410 		PrivateVar: ucheckPropName
    4411 			This is the property name used by unique checks
    4412 		*/
    4413 
    4414 	, ucheckPropName = "_unique" + glow.UID;
    4415 	/*
    4416 		PrivateMethod: unique
    4417 			Get an array of nodes without duplicate nodes from an array of nodes.
    4418 
    4419 		Arguments:
    4420 			aNodes - (Array|<NodeList>)
    4421 
    4422 		Returns:
    4423 			An array of nodes without duplicates.
    4424 		*/
    4425 		//worth checking if it's an XML document?
    4426 		if (glow.env.ie) {
    4427 			var unique = function(aNodes) {
    4428 				if (aNodes.length == 1) { return aNodes; }
    4429 
    4430 				//remove duplicates
    4431 				var r = [],
    4432 					ri = 0,
    4433 					i = 0;
    4434 
    4435 				for (; aNodes[i]; i++) {
    4436 					if (aNodes[i].getAttribute(ucheckPropName) != ucheck && aNodes[i].nodeType == 1) {
    4437 						r[ri++] = aNodes[i];
    4438 					}
    4439 					aNodes[i].setAttribute(ucheckPropName, ucheck);
    4440 				}
    4441 				for (i=0; aNodes[i]; i++) {
    4442 					aNodes[i].removeAttribute(ucheckPropName);
    4443 				}
    4444 				ucheck++;
    4445 				return r;
    4446 			}
    4447 		} else {
    4448 			var unique = function(aNodes) {
    4449 				if (aNodes.length == 1) { return aNodes; }
    4450 
    4451 				//remove duplicates
    4452 				var r = [],
    4453 					ri = 0,
    4454 					i = 0;
    4455 
    4456 				for (; aNodes[i]; i++) {
    4457 					if (aNodes[i][ucheckPropName] != ucheck && aNodes[i].nodeType == 1) {
    4458 						r[ri++] = aNodes[i];
    4459 					}
    4460 					aNodes[i][ucheckPropName] = ucheck;
    4461 				}
    4462 				ucheck++;
    4463 				return r;
    4464 			}
    4465 		};
    4466 	/**
    4467 	@name glow.NodeList#parent
    4468 	@function
    4469 	@description Gets the unique parent nodes of each node as a new NodeList.
    4470 	@param {string | HTMLElement | NodeList} [search] Search value
    4471 		If provided, will seek the next parent element until a match is found
    4472 	@returns {glow.NodeList}
    4473 
    4474 		Returns a new NodeList containing the parent nodes, with
    4475 		duplicates removed
    4476 
    4477 	@example
    4478 		// elements which contain links
    4479 		var parents = glow.dom.get("a").parent();
    4480 	*/
    4481 	NodeListProto.parent = function(search) {
    4482 		var ret = [],
    4483 			ri = 0,
    4484 			i = this.length,
    4485 			node;
    4486 			
    4487 		while (i--) {				
    4488 			node = this[i];
    4489 			if (node.nodeType == 1) {
    4490 				if(search){						
    4491 					while(node = node.parentNode){											
    4492 						if (glow._sizzle.filter(search, [node]).length) {
    4493 							ret[ri++] = node;							
    4494 							break;
    4495 						}							
    4496 					}
    4497 				}
    4498 			
    4499 				else if(node = node.parentNode){
    4500 						ret[ri++] = node;						
    4501 				}
    4502 
    4503 			}
    4504 
    4505 		}
    4506 				
    4507 		return new glow.NodeList(unique(ret));			
    4508 	};
    4509 	
    4510 	/* Private method for prev() and next() */
    4511 	function getNextOrPrev(nodelist, dir, search) {
    4512 		var ret = [],
    4513 			ri = 0,
    4514 			node,
    4515 			i = 0,
    4516 			length = nodelist.length;
    4517 
    4518 		while (i < length) {			
    4519 			node = nodelist[i];			
    4520 			if(search){
    4521 				while (node = node[dir + 'Sibling']) {					
    4522 					if (node.nodeType == 1 && node.nodeName != '!') {						
    4523 						if (glow._sizzle.filter(search, [node]).length) {
    4524 							ret[ri++] = node;							
    4525 							break;
    4526 						}					
    4527 					}					
    4528 				}
    4529 			}
    4530 			else{
    4531 				while (node = node[dir + 'Sibling']) {					
    4532 					if (node.nodeType == 1 && node.nodeName != '!') {
    4533 							ret[ri++] = node;							
    4534 							 break;					
    4535 					}					
    4536 				}	
    4537 			}
    4538 		i++;
    4539 		}
    4540 		return new glow.NodeList(ret);
    4541 	}
    4542 	
    4543 	/**
    4544 	@name glow.NodeList#prev
    4545 	@function
    4546 	@description Gets the previous sibling element for each node in the ElementList.
    4547 		If a filter is provided, the previous item that matches the filter is returned, or
    4548 		none if no match is found.
    4549 	@param {string | HTMLElement | NodeList} [search] Search value
    4550 		If provided, will seek the previous sibling element until a match is found
    4551 	@returns {glow.ElementList}
    4552 		A new ElementList containing the previous sibling elements that match the (optional)
    4553 		filter.
    4554 	@example
    4555 		// gets the element before #myLink (if there is one)
    4556 		var next = glow.get("#myLink").prev();
    4557 	@example
    4558 		// get the previous sibling link element before #skipLink
    4559 		glow.get('#skipLink').prev('a')
    4560 	*/
    4561 	NodeListProto.prev = function(search) {
    4562 		return getNextOrPrev(this, 'previous', search);
    4563 	};
    4564 	
    4565 	/**
    4566 	@name glow.NodeList#next
    4567 	@function
    4568 	@description Gets the next sibling element for each node in the ElementList.
    4569 		If a filter is provided, the next item that matches the filter is returned, or
    4570 		none if no match is found.
    4571 	@param {string | HTMLElement | NodeList} [search] Search value
    4572 		If provided, will seek the next sibling element until a match is found
    4573 	@returns {glow.ElementList}
    4574 		A new ElementList containing the next sibling elements that match the (optional)
    4575 		filter.
    4576 	@example
    4577 		// gets the element following #myLink (if there is one)
    4578 		var next = glow.get("#myLink").next();
    4579 	@example
    4580 		// get the next sibling link element after #skipLink
    4581 		glow.get('#skipLink').next('a')
    4582 	*/
    4583 	NodeListProto.next = function(search) {
    4584 		return getNextOrPrev(this, 'next', search);	
    4585 	};
    4586 	
    4587 	
    4588 	/**
    4589 	@name glow.NodeList#get
    4590 	@function
    4591 	@description Gets decendents of nodes that match a CSS selector.
    4592 
    4593 	@param {String} selector CSS selector
    4594 
    4595 	@returns {glow.NodeList}
    4596 		Returns a new NodeList containing matched elements
    4597 
    4598 	@example
    4599 		// create a new NodeList
    4600 		var myNodeList = glow.dom.create("<div><a href='s.html'>Link</a></div>");
    4601 
    4602 		// get 'a' tags that are decendants of the NodeList nodes
    4603 		myNewNodeList = myNodeList.get("a");
    4604 	*/
    4605 	NodeListProto.get = function(selector) {
    4606 		var ret = [],
    4607 			i = this.length;
    4608 
    4609 		while (i--) {			
    4610 			glow._sizzle(selector, this[i], ret);
    4611 			
    4612 		}
    4613 		// need to remove uniqueSorts because they're slow. Replace with own method for unique.
    4614 		return new glow.NodeList(unique(ret));
    4615 	};
    4616 	
    4617 	
    4618 	
    4619 	/**
    4620 	@name glow.NodeList#ancestors
    4621 	@function
    4622 	@description Gets the unique ancestor nodes of each node as a new NodeList.
    4623 	@param {Function|string} [filter] Filter test
    4624 		If a string is provided, it is used in a call to {@link glow.ElementList#is ElementList#is}.
    4625 		If a function is provided it will be passed 2 arguments, the index of the current item,
    4626 		and the ElementList being itterated over.
    4627 		Inside the function 'this' refers to the HTMLElement.
    4628 		Return true to keep the node, or false to remove it.
    4629 	@returns {glow.dom.NodeList}
    4630 		Returns NodeList
    4631 
    4632 		@example
    4633 		// get ancestor elements for anchor elements 
    4634 		var ancestors = glow.dom.get("a").ancestors();
    4635 	*/
    4636 	NodeListProto.ancestors = function(filter) {
    4637 		var ret = [],
    4638 			ri = 0,
    4639 			i = 0,
    4640 			length = this.length,
    4641 			node;
    4642 					
    4643 		while (i < length) {
    4644 			node = this[i].parentNode;
    4645 					
    4646 			while (node && node.nodeType == 1) {							
    4647 				ret[ri++] = node;
    4648 				node = node.parentNode;
    4649 			}								
    4650 		i++;
    4651 		}
    4652 		if(filter){
    4653             ret = new glow.NodeList(ret);
    4654 			ret = ret.filter(filter);
    4655 		}
    4656 		return new glow.NodeList(unique(ret));
    4657 	};
    4658 	
    4659 	/*
    4660 		Private method to get the child elements for an html node (used by children())
    4661 	*/
    4662 		function getChildElms(node) {
    4663 			var r = [],
    4664 				childNodes = node.childNodes,
    4665 				i = 0,
    4666 				ri = 0;
    4667 			
    4668 			for (; childNodes[i]; i++) {
    4669 				if (childNodes[i].nodeType == 1 && childNodes[i].nodeName != '!') {
    4670 					r[ri++] = childNodes[i];
    4671 				}
    4672 			}
    4673 			return r;
    4674 		}
    4675 	
    4676 	/**
    4677 	@name glow.NodeList#children
    4678 	@function
    4679 	@description Gets the child elements of each node as a new NodeList.
    4680 
    4681 	@returns {glow.dom.NodeList}
    4682 
    4683 		Returns a new NodeList containing all the child nodes
    4684 				
    4685 	@example
    4686 		// get all list items
    4687 		var items = glow.dom.get("ul, ol").children();
    4688 	*/
    4689 	NodeListProto.children = function() {
    4690 		var ret = [],
    4691 			i = this.length;
    4692 				
    4693 		while(i--) {
    4694 			ret = ret.concat( getChildElms(this[i]) );
    4695 		}
    4696 		return new glow.NodeList(ret);	
    4697 	};
    4698 	
    4699 	/**
    4700 	@name glow.NodeList#contains
    4701 	@function
    4702 	@description Find if this NodeList contains the given element
    4703 		
    4704 	@param {string | HTMLELement | NodeList} Single element to check for
    4705 
    4706 	@returns {boolean}
    4707 		myElementList.contains(elm)
    4708 		// Returns true if an element in myElementList contains elm, or IS elm.
    4709 	*/
    4710 	NodeListProto.contains = function(elm) {
    4711 		var i = 0,
    4712 			node = new glow.NodeList(elm)[0],
    4713 			length = this.length,
    4714 			newNodes,
    4715 			toTest;
    4716 
    4717 		// missing some nodes? Return false
    4718 		if ( !node || !this.length ) {
    4719 			return false;
    4720 		}
    4721 	
    4722 		if (this[0].compareDocumentPosition) { //w3 method
    4723 			while (i < length) {
    4724 				//break out if the two are teh same
    4725 				if(this[i] == node){
    4726 					break;
    4727 				}
    4728 				//check against bitwise to see if node is contained in this
    4729 				else if (!(this[i].compareDocumentPosition(node) & 16)) {								
    4730 					return false;
    4731 				}
    4732 			i++;
    4733 			}
    4734 		}
    4735 		else if(node.contains){					
    4736 			for (; i < length; i++) {
    4737 				if ( !( this[i].contains( node  ) ) ) {
    4738 					return false;
    4739 				}
    4740 			}
    4741 		}				
    4742 		else { //manual method for last chance corale
    4743 			while (i < length) {
    4744 				toTest = node;
    4745 				while (toTest = toTest.parentNode) {
    4746 					if (this[i] == toTest) { break; }
    4747 				}
    4748 				if (!toTest) {
    4749 					return false;
    4750 				}
    4751 			i++;
    4752 			}
    4753 		}
    4754 			
    4755 		return true;
    4756 	};
    4757 });
    4758 Glow.provide(function(glow) {
    4759 	var NodeListProto = glow.NodeList.prototype,
    4760 		document = window.document,
    4761 		undefined;
    4762 	
    4763 	// create a fragment and insert a set of nodes into it
    4764 	function createFragment(nodes) {
    4765 		var fragment = document.createDocumentFragment(),
    4766 			i = 0,
    4767 			node;
    4768 		
    4769 		while ( node = nodes[i++] ) {
    4770 			fragment.appendChild(node);
    4771 		}
    4772 		
    4773 		return fragment;
    4774 	}
    4775 	
    4776 	// generate the #before and #after methods
    4777 	// after: 1 for #(insert)after, 0 for #(insert)before
    4778 	// insert: 1 for #insert(After|Before), 0 for #(after|before)
    4779 	function afterAndBefore(after, insert) {
    4780 		return function(elements) {
    4781 			var toAddList,
    4782 				toAddToList,
    4783 				fragmentToAdd,
    4784 				nextFragmentToAdd,
    4785 				item,
    4786 				itemParent;
    4787 			
    4788 			if (!this.length) { return this; }
    4789 			
    4790 			// normalise 'elements'
    4791 			// if we're dealing with append/prepend then strings are always treated as HTML strings
    4792 			if (!insert && typeof elements === 'string') {
    4793 				elements = new glow.NodeList( glow.NodeList._strToNodes(elements) );
    4794 			}
    4795 			else {
    4796 				elements = new glow.NodeList(elements);
    4797 			}
    4798 			
    4799 			// set the element we're going to add to, and the elements we're going to add
    4800 			if (insert) {
    4801 				toAddToList = elements;
    4802 				toAddList = new glow.NodeList(this);
    4803 			}
    4804 			else {
    4805 				toAddToList = this;
    4806 				toAddList = elements;
    4807 			}
    4808 			
    4809 			nextFragmentToAdd = createFragment(toAddList);
    4810 			
    4811 			for (var i = 0, leni = toAddToList.length, lasti = leni - 1; i < leni; i++) {
    4812 				item = toAddToList[i];
    4813 				fragmentToAdd = nextFragmentToAdd;
    4814 				
    4815 				// we can only append after if the element has a parent right?
    4816 				if (itemParent = item.parentNode) {
    4817 					if (i !== lasti) { // if not the last item
    4818 						nextFragmentToAdd = fragmentToAdd.cloneNode(true);
    4819 						insert && toAddList.push(nextFragmentToAdd.childNodes);
    4820 					}
    4821 					itemParent.insertBefore(fragmentToAdd, after ? item.nextSibling : item);
    4822 				}
    4823 			}
    4824 			
    4825 			return insert ? toAddList : toAddToList;
    4826 		}
    4827 	}
    4828 	
    4829 	// generate the #append, #appendTo, #prepend and #prependTo methods
    4830 	// append: 1 for #append(To), 0 for #prepend(To)
    4831 	// to: 1 for #(append|prepend)To, 0 for #(append|prepend)
    4832 	function appendAndPrepend(append, to) {
    4833 		return function(elements) {
    4834 			var toAddList,
    4835 				toAddToList,
    4836 				fragmentToAdd,
    4837 				nextFragmentToAdd,
    4838 				item;
    4839 			
    4840 			if (!this.length) { return this; }
    4841 			
    4842 			// normalise 'elements'
    4843 			// if we're dealing with append/prepend then strings are always treated as HTML strings
    4844 			if (!to && typeof elements === 'string') {
    4845 				elements = new glow.NodeList( glow.NodeList._strToNodes(elements) );
    4846 			}
    4847 			else {
    4848 				elements = new glow.NodeList(elements);
    4849 			}
    4850 				
    4851 			// set the element we're going to add to, and the elements we're going to add
    4852 			if (to) {
    4853 				toAddToList = elements;
    4854 				toAddList = new glow.NodeList(this);
    4855 			}
    4856 			else {
    4857 				toAddToList = this;
    4858 				toAddList = elements;
    4859 			}
    4860 			
    4861 			nextFragmentToAdd = createFragment(toAddList);
    4862 			
    4863 			for (var i = 0, leni = toAddToList.length, lasti = leni - 1; i < leni; i++) {
    4864 				item = toAddToList[i];
    4865 				fragmentToAdd = nextFragmentToAdd;
    4866 				
    4867 				// avoid trying to append to non-elements
    4868 				if (item.nodeType === 1) {
    4869 					if (i !== lasti) { // if not the last item
    4870 						nextFragmentToAdd = fragmentToAdd.cloneNode(true);
    4871 						// add the clones to the return element for appendTo / prependTo
    4872 						to && toAddList.push(nextFragmentToAdd.childNodes);
    4873 					}
    4874 					item.insertBefore(fragmentToAdd, append ? null : item.firstChild);
    4875 				}
    4876 			}
    4877 			
    4878 			return to ? toAddList : toAddToList;
    4879 		}
    4880 	}
    4881 	
    4882 	/**
    4883 		@name glow.NodeList#after
    4884 		@function
    4885 		@description Insert node(s) after each node in this NodeList.
    4886 			If there is more than one node in this NodeList, 'nodes'
    4887 			will be inserted after the first element and clones will be
    4888 			inserted after each subsequent element.
    4889 			
    4890 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert
    4891 			Strings will be treated as HTML strings.
    4892 		
    4893 		@returns {glow.NodeList} Original NodeList
    4894 		
    4895 		@example
    4896 			// adds a paragraph after each heading
    4897 			glow('h1, h2, h3').after('<p>That was a nice heading.</p>');
    4898 	*/
    4899 	NodeListProto.after = afterAndBefore(1);
    4900 	
    4901 	/**
    4902 		@name glow.NodeList#before
    4903 		@function
    4904 		@description Insert node(s) before each node in this NodeList.
    4905 			If there is more than one node in this NodeList, 'nodes'
    4906 			will be inserted before the first element and clones will be
    4907 			inserted before each subsequent element.
    4908 			
    4909 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert
    4910 			Strings will be treated as HTML strings.
    4911 		
    4912 		@returns {glow.NodeList} Original NodeList
    4913 		
    4914 		@example
    4915 			// adds a div before each paragraph
    4916 			glow('p').before('<div>Here comes a paragraph!</div>');
    4917 	*/
    4918 	NodeListProto.before = afterAndBefore(0);
    4919 	
    4920 	/**
    4921 		@name glow.NodeList#append
    4922 		@function
    4923 		@description Appends node to each node in this NodeList.
    4924 			If there is more than one node in this NodeList, then the given nodes
    4925 			are appended to the first node and clones are appended to the other
    4926 			nodes.
    4927 			
    4928 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Nodes(s) to append
    4929 			Strings will be treated as HTML strings.
    4930 		
    4931 		@returns {glow.NodeList} Original NodeList
    4932 		
    4933 		@example
    4934 			// ends every paragraph with '...'
    4935 			glow('p').append('<span>...</span>');
    4936 	*/
    4937 	NodeListProto.append = appendAndPrepend(1);
    4938 	
    4939 	/**
    4940 		@name glow.NodeList#prepend
    4941 		@function
    4942 		@description Prepends nodes to each node in this NodeList.
    4943 			If there is more than one node in this NodeList, then the given nodes
    4944 			are prepended to the first node and clones are prepended to the other
    4945 			nodes.
    4946 			
    4947 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Nodes(s) to prepend
    4948 			Strings will be treated as HTML strings.
    4949 		
    4950 		@returns {glow.NodeList} Original NodeList
    4951 		
    4952 		@example
    4953 			// prepends every paragraph with 'Paragraph: '
    4954 			glow('p').prepend('<span>Paragraph: </span>');
    4955 	*/
    4956 	NodeListProto.prepend = appendAndPrepend(0);
    4957 	
    4958 	/**
    4959 		@name glow.NodeList#appendTo
    4960 		@function
    4961 		@description Appends nodes in this NodeList to given node(s)
    4962 			If appending to more than one node, the NodeList is appended
    4963 			to the first node and clones are appended to the others.
    4964 			
    4965 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} node Node(s) to append to.
    4966 			Strings will be treated as CSS selectors or HTML strings.
    4967 		
    4968 		@returns {glow.NodeList} The appended nodes.
    4969 		
    4970 		@example
    4971 			// appends '...' to every paragraph
    4972 			glow('<span>...</span>').appendTo('p');
    4973 	*/
    4974 	NodeListProto.appendTo = appendAndPrepend(1, 1);
    4975 
    4976 	/**
    4977 		@name glow.NodeList#prependTo
    4978 		@function
    4979 		@description Prepends nodes in this NodeList to given node(s)
    4980 			If prepending to more than one node, the NodeList is prepended
    4981 			to the first node and clones are prepended to the others.
    4982 			
    4983 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} node Node(s) to prepend to
    4984 			Strings will be treated as CSS selectors or HTML strings.
    4985 		
    4986 		@returns {glow.NodeList} The prepended nodes.
    4987 		
    4988 		@example
    4989 			// prepends 'Paragraph: ' to every paragraph
    4990 			glow('<span>Paragraph: </span>').prependTo('p');
    4991 	*/
    4992 	NodeListProto.prependTo = appendAndPrepend(0, 1);
    4993 	
    4994 	/**
    4995 		@name glow.NodeList#insertAfter
    4996 		@function
    4997 		@description Insert this NodeList after the given nodes
    4998 			If inserting after more than one node, the NodeList is inserted
    4999 			after the first node and clones are inserted after the others.
    5000 			
    5001 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert after
    5002 			Strings will be treated as CSS selectors.
    5003 			
    5004 		@returns {glow.NodeList} Inserted nodes.
    5005 		
    5006 		@example
    5007 			// adds a paragraph after each heading
    5008 			glow('<p>HAI!</p>').insertAfter('h1, h2, h3');
    5009 	*/
    5010 	NodeListProto.insertAfter = afterAndBefore(1, 1);
    5011 	
    5012 	/**
    5013 		@name glow.NodeList#insertBefore
    5014 		@function
    5015 		@description Insert this NodeList before the given nodes
    5016 			If inserting before more than one node, the NodeList is inserted
    5017 			before the first node and clones are inserted before the others.
    5018 			
    5019 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert before
    5020 			Strings will be treated as CSS selectors.
    5021 			
    5022 		@returns {glow.NodeList} Inserted nodes.
    5023 		
    5024 		@example
    5025 			// adds a div before each paragraph
    5026 			glow('<div>Here comes a paragraph!</div>').insertBefore('p');
    5027 	*/
    5028 	NodeListProto.insertBefore = afterAndBefore(0, 1);
    5029 	
    5030 	/**
    5031 		@name glow.NodeList#destroy
    5032 		@function
    5033 		@description Removes each element from the document
    5034 			The element, attached listeners & attached data will be
    5035 			destroyed to free up memory.
    5036 			
    5037 			Detroyed elements may not be reused in some browsers.
    5038 			
    5039 		@returns {glow.NodeList} An empty NodeList
    5040 		
    5041 		@example
    5042 			// destroy all links in the document
    5043 			glow("a").destroy();
    5044 	*/
    5045 	var tmpDiv = document.createElement('div');
    5046 	
    5047 	NodeListProto.destroy = function() {
    5048 		glow.NodeList._destroyData(this);
    5049 		this.appendTo(tmpDiv);
    5050 		tmpDiv.innerHTML = '';
    5051 		return new glow.NodeList();
    5052 	};
    5053 	
    5054 	/**
    5055 		@name glow.NodeList#remove
    5056 		@function
    5057 		@description Removes each element from the document
    5058 			If you no longer need the elements, consider using
    5059 			{@link glow.NodeList#destroy destroy}
    5060 			
    5061 		@returns {glow.NodeList} The removed elements
    5062 
    5063 		@example
    5064 			// take all the links out of a document
    5065 			glow("a").remove();
    5066 	*/
    5067 	NodeListProto.remove = function() {
    5068 		var parent,
    5069 			node,
    5070 			i = this.length;
    5071 		
    5072 		while (i--) {
    5073 			node = this[i];
    5074 			if (parent = node.parentNode) {
    5075 				parent.removeChild(node);
    5076 			}
    5077 		}
    5078 		
    5079 		return this;
    5080 	};
    5081 	
    5082 	/**
    5083 		@name glow.NodeList#empty
    5084 		@function
    5085 		@description Removes the nodes' contents
    5086 
    5087 		@returns {glow.NodeList} Original nodes
    5088 
    5089 		@example
    5090 			// remove the contents of all textareas
    5091 			glow("textarea").empty();
    5092 	*/
    5093 	// TODO: is this shortcut worth doing?
    5094 	NodeListProto.empty = glow.env.ie ?
    5095 		// When you clean an element out using innerHTML it destroys its inner text nodes in IE8 and below
    5096 		// Here's an alternative method for IE:
    5097 		function() {
    5098 			var i = this.length, node, child;
    5099 			
    5100 			while (i--) {
    5101 				node = this[i];
    5102 				while (child = node.firstChild) {
    5103 					node.removeChild(child);
    5104 				}
    5105 			}
    5106 			
    5107 			return this;
    5108 		} :
    5109 		// method for most browsers
    5110 		function() {
    5111 			var i = this.length;
    5112 			
    5113 			while (i--) {
    5114 				this[i].innerHTML = '';
    5115 			}
    5116 			
    5117 			return this;
    5118 		}
    5119 
    5120 	/**
    5121 		@name glow.NodeList#replaceWith
    5122 		@function
    5123 		@description Replace elements with another
    5124 		
    5125 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} elements Element(s) to insert into the document
    5126 			If there is more than one element in the NodeList, then the given elements
    5127 			replace the first element, clones are appended to the other	elements.
    5128 			
    5129 		@returns {glow.NodeList} The replaced elements
    5130 			Call {@link glow.NodeList#destroy destroy} on these if you
    5131 			no longer need them.
    5132 	*/
    5133 	NodeListProto.replaceWith = function(elements) {
    5134 		return this.after(elements).remove();
    5135 	};
    5136 	
    5137 	/**
    5138 		@name glow.NodeList#wrap
    5139 		@function
    5140 		@description Wraps the given NodeList with the specified element(s).
    5141 			The given NodeList items will always be placed in the first
    5142 			child element that contains no further elements.
    5143 			
    5144 			Each item in a given NodeList will be wrapped individually.
    5145 		
    5146 		@param {string | HTMLElement | HTMLElement[] | glow.NodeList} wrapper Element to use as a wrapper
    5147 			Strings will be treated as HTML strings if they begin with <, else
    5148 			they'll be treated as a CSS selector.
    5149 		
    5150 		@returns {glow.NodeList} The NodeList with new wrapper parents
    5151 			
    5152 		@example
    5153 			// <span id="mySpan">Hello</span>
    5154 			glow("#mySpan").wrap("<div><p></p></div>");
    5155 			// Makes:
    5156 			// <div>
    5157 			//     <p>
    5158 			//         <span id="mySpan">Hello</span>
    5159 			//     </p>
    5160 			// </div>
    5161 			
    5162 	*/
    5163 	// get first child element node of an element, otherwise undefined
    5164 	function getFirstChildElm(parent) {					
    5165 		for (var child = parent.firstChild; child; child = child.nextSibling) {
    5166 			if (child.nodeType == 1) {
    5167 				return child;
    5168 			}			
    5169 		}			
    5170 		return undefined;			
    5171 	}
    5172 	
    5173 	NodeListProto.wrap = function(wrapper) {
    5174 		// normalise input
    5175 		wrapper = new glow.NodeList(wrapper);
    5176 		
    5177 		// escape if the wraper is non-existant or not an element
    5178 		if (!wrapper[0] || wrapper[0].nodeType != 1) {
    5179 			return this;
    5180 		}
    5181 		
    5182 		var toWrap,
    5183 			toWrapTarget,
    5184 			firstChildElm;
    5185 		
    5186 		for (var i = 0, leni = this.length; i<leni; i++) {
    5187 			toWrap = this[i];
    5188 			// get target element to insert toWrap in
    5189 			toWrapTarget = wrapper[0];
    5190 			
    5191 			while (toWrapTarget) {
    5192 				firstChildElm = getFirstChildElm(toWrapTarget);
    5193 					
    5194 				if (!firstChildElm) {
    5195 					break;
    5196 				}
    5197 				toWrapTarget = firstChildElm;
    5198 			}
    5199 			
    5200 			if (toWrap.parentNode) {						
    5201 				wrapper.insertBefore(toWrap);													
    5202 			}
    5203 			
    5204 			// If wrapping multiple nodes, we need to take a clean copy of the wrapping nodes
    5205 			if (i != leni-1) {
    5206 				wrapper = wrapper.clone();
    5207 			}
    5208 			
    5209 			toWrapTarget.appendChild(toWrap);
    5210 		}
    5211 		
    5212 		return this;
    5213 	};
    5214 	
    5215 	/**
    5216 		@name glow.NodeList#unwrap
    5217 		@function
    5218 		@description Removes the parent of each item in the list
    5219 		
    5220 		@returns {glow.NodeList} The now unwrapped elements
    5221 		
    5222 		@example
    5223 			// Before: <div><p><span id="mySpan">Hello</span></p></div>
    5224 			// unwrap the given element
    5225 			glow("#mySpan").unwrap();
    5226 			// After: <div><span id="mySpan">Hello</span></div>
    5227 	*/
    5228 	NodeListProto.unwrap = function() {
    5229 		var parentToRemove,
    5230 			childNodes,
    5231 			// get unique parents
    5232 			parentsToRemove = this.parent();
    5233 		
    5234 		for (var i = 0, leni = parentsToRemove.length; i < leni; i++) {				
    5235 			parentToRemove = parentsToRemove.slice(i, i+1);
    5236 			// make sure we get all children, including text nodes
    5237 			childNodes = new glow.NodeList( parentToRemove[0].childNodes );
    5238 			
    5239 			// if the item we're removing has no new parent (i.e. is not in document), then we just remove the child and destroy the old parent
    5240 			if (!parentToRemove[0].parentNode){
    5241 				childNodes.remove();
    5242 				parentToRemove.destroy();
    5243 			}
    5244 			else {
    5245 				childNodes.insertBefore(parentToRemove);
    5246 				parentToRemove.destroy();							
    5247 			}
    5248 		}
    5249 		return this;
    5250 	};
    5251 	
    5252 	/**
    5253 		@name glow.NodeList#clone
    5254 		@function
    5255 		@description Clones each node in the NodeList, along with data & event listeners
    5256 		
    5257 		@returns {glow.NodeList}
    5258 			Returns a new NodeList containing clones of all the nodes in
    5259 			the NodeList
    5260 		
    5261 		@example
    5262 			// get a copy of all heading elements
    5263 			var myClones = glow.get("h1, h2, h3, h4, h5, h6").clone();
    5264 	*/
    5265 	NodeListProto.clone = function() {
    5266 		var nodes = [],
    5267 			eventIdProp = '__eventId' + glow.UID,
    5268 			i = this.length;
    5269 		
    5270 		
    5271 		while (i--) {
    5272 			nodes[i] = this[i].cloneNode(true);
    5273 			// some browsers (ie) also clone node properties as attributes
    5274 			// we need to get rid of the eventId.
    5275 			allCloneElms = new glow.NodeList( nodes ).get("*").push( nodes );
    5276 			j = allCloneElms.length;
    5277 				while(j--) {
    5278 					nodes[i][eventIdProp] = null;
    5279 					
    5280 					// now copy over the data and events
    5281 					glow.events._copyEvent(this[i], nodes[i]);
    5282 					glow.NodeList._copyData(this[i], nodes[i]);
    5283 				}
    5284 			
    5285 		}
    5286 		// some browsers (ie) also clone node properties as attributes
    5287 		// we need to get rid of the eventId.
    5288 		//allCloneElms = new glow.NodeList( nodes ).get("*").push( nodes );
    5289 		//j = allCloneElms.length;
    5290 		
    5291 		//while(j--) {
    5292 			//allCloneElms[j][eventIdProp] = null;
    5293 		//}
    5294 		
    5295 		
    5296 				
    5297 		// copy data from base elements to clone elements
    5298 		/*allBaseElms = this.get("*").push( this );
    5299 		i = allCloneElms.length;
    5300 		while (i--) {
    5301 			allCloneElms[i].removeAttribute(dataPropName);
    5302 			glow.dom.get(allCloneElms[i]).data(
    5303 			glow.dom.get(allBaseElms[i]).data()
    5304 			);
    5305 		}*/
    5306 		
    5307 		return new glow.NodeList(nodes);
    5308 	};
    5309 	
    5310 	
    5311 	/**
    5312 		@name glow.NodeList#copy
    5313 		@function
    5314 		@description Copies each node in the NodeList, excluding data & event listeners
    5315 		
    5316 		@returns {glow.NodeList}
    5317 			Returns a new NodeList containing copies of all the nodes in
    5318 			the NodeList
    5319 		
    5320 		@example
    5321 			// get a copy of all heading elements
    5322 			var myCopies = glow.get("h1, h2, h3, h4, h5, h6").copy();
    5323 	*/
    5324 	NodeListProto.copy = function() {
    5325 		var nodes = [],
    5326 			i = this.length;
    5327 		
    5328 		while (i--) {
    5329 			nodes[i] = this[i].cloneNode(true);
    5330 		}
    5331 		
    5332 		return new glow.NodeList(nodes);
    5333 	};
    5334 	
    5335 	/**
    5336 		@name glow.NodeList#html
    5337 		@function
    5338 		@description Gets / sets HTML content
    5339 			Either gets content of the first element, or sets the content
    5340 			for all elements in the list
    5341 			
    5342 		@param {String} [htmlString] String to set as the HTML of elements
    5343 			If omitted, the html for the first element in the list is
    5344 			returned.
    5345 		
    5346 		@returns {glow.NodeList | string}
    5347 			Returns the original NodeList when setting,
    5348 			or the HTML content when getting.
    5349 			
    5350 		@example
    5351 			// get the html in #footer
    5352 			var footerContents = glow("#footer").html();
    5353 			
    5354 		@example
    5355 			// set a new footer
    5356 			glow("#footer").html("<strong>Hello World!</strong>");
    5357 	*/
    5358 	NodeListProto.html = function(htmlString) {
    5359 		// getting
    5360 		if (!arguments.length) {
    5361 			return this[0] ? this[0].innerHTML : '';
    5362 		}
    5363 		
    5364 		// setting
    5365 		var i = this.length,
    5366 			node;
    5367 		
    5368 		// normalise the string
    5369 		htmlString = htmlString ? String(htmlString): '';
    5370 		
    5371 		while (i--) {
    5372 			node = this[i];
    5373 			if (node.nodeType == 1) {
    5374 				try {
    5375 					// this has a habit of failing in IE for some elements
    5376 					node.innerHTML = htmlString;
    5377 				}
    5378 				catch (e) {
    5379 					new glow.NodeList(node).empty().append(htmlString);
    5380 				}
    5381 			}
    5382 		}
    5383 		
    5384 		return this;
    5385 	};
    5386 	
    5387 	/**
    5388 		@name glow.NodeList#text
    5389 		@function
    5390 		@description Gets / set the text content
    5391 			Either gets content of the first element, or sets the content
    5392 			for all elements in the list
    5393 		
    5394 		@param {String} [text] String to set as the text of elements
    5395 			If omitted, the test for the first element in the list is
    5396 			returned.
    5397 		
    5398 		@returns {glow.NodeList | String}
    5399 			Returns the original NodeList when setting,
    5400 			or the text content when getting.
    5401 
    5402 		@example
    5403 			// set text
    5404 			var div = glow("<div></div>").text("Fun & games!");
    5405 			// <div>Func & games!</div>
    5406 			
    5407 		@example
    5408 			// get text
    5409 			var mainHeading = glow('#mainHeading').text();
    5410 	*/
    5411 	NodeListProto.text = function(textString) {
    5412 		var firstNode = this[0],
    5413 			i = this.length,
    5414 			node;
    5415 		
    5416 		// getting
    5417 		if (!arguments.length) {
    5418 			// get the text by checking a load of properties in priority order
    5419 			return firstNode ?
    5420 				firstNode.textContent ||
    5421 				firstNode.innerText ||
    5422 				firstNode.nodeValue || '' // nodeValue for comment & text nodes
    5423 				: '';
    5424 		}
    5425 		
    5426 		// setting
    5427 		// normalise the string
    5428 		textString = textString ? String(textString): '';
    5429 		
    5430 		this.empty();
    5431 		while (i--) {
    5432 			node = this[i];
    5433 			if (node.nodeType == 1) {
    5434 				node.appendChild( document.createTextNode(textString) );
    5435 			}
    5436 			else {
    5437 				node.nodeValue = textString;
    5438 			}
    5439 		}
    5440 		
    5441 		return this;
    5442 	};
    5443 });
    5444 Glow.provide(function(glow) {
    5445 	var NodeListProto = glow.NodeList.prototype,
    5446 		doc = document,	
    5447 		win = window;
    5448 			
    5449 	/********************************PRIVATE METHODS*****************************************/
    5450 		
    5451 	/*
    5452 	PrivateMethod: toStyleProp
    5453 		Converts a css property name into its javascript name, such as "background-color" to "backgroundColor".
    5454 
    5455 	Arguments: prop - (String) CSS Property name
    5456 
    5457 	Returns: String, javascript style property name
    5458 	*/
    5459 	
    5460 	function toStyleProp(prop) {
    5461 		if (prop == 'float') {
    5462 			return glow.env.ie ? 'styleFloat' : 'cssFloat';
    5463 		}
    5464 		return prop.replace(/-(\w)/g, function(match, p1) {
    5465 			return p1.toUpperCase();
    5466 		});
    5467 	}
    5468 	/*
    5469 	PrivateMethod: getCssValue
    5470 		Get a computed css property
    5471 		
    5472 	Arguments:
    5473 		elm - element
    5474 		prop - css property or array of properties to add together
    5475 
    5476 	Returns:	String, value
    5477 	*/
    5478 	function getCssValue(elm, prop) {
    5479 		var r, //return value
    5480 			total = 0,
    5481 			i = 0,
    5482 			/*regex for detecting which css properties need to be calculated relative to the y axis*/
    5483 			usesYAxis = /height|top/,
    5484 			propLen = prop.length,
    5485 			cssPropRegex = /^(?:(width|height)|(border-(top|bottom|left|right)-width))$/,
    5486 			compStyle = doc.defaultView && (doc.defaultView.getComputedStyle(elm, null) || doc.defaultView.getComputedStyle),
    5487 			elmCurrentStyle = elm.currentStyle,
    5488 			oldDisplay,
    5489 			match,
    5490 			propTest = prop.push || cssPropRegex.exec(prop) || [];
    5491 
    5492 
    5493 		if (prop.push) { //multiple properties, add them up
    5494 			for (; i < propLen; i++) {
    5495 				total += parseInt( getCssValue(elm, prop[i]), 10 ) || 0;
    5496 			}
    5497 			return total + 'px';
    5498 		}
    5499 			
    5500 		if (propTest[1]) { // is width / height
    5501 			if (!isVisible(elm)) { //element may be display: none
    5502 				return tempBlock(elm, function() {
    5503 					return getElmDimension(elm, propTest[1]) + 'px';
    5504 				});
    5505 			}
    5506 			return getElmDimension(elm, propTest[1]) + 'px';
    5507 		}
    5508 		else if (propTest[2] //is border-*-width
    5509 			&& glow.env.ie
    5510 			&& getCssValue(elm, 'border-' + propTest[3] + '-style') == 'none'
    5511 		) {
    5512 			return '0';
    5513 		}
    5514 		else if (compStyle) { //W3 Method
    5515 			//this returns computed values
    5516 			if (typeof compStyle == 'function') {
    5517 			//safari returns null for compStyle when element is display:non
    5518 			oldDisplay = elm.style.display;
    5519 			r = tempBlock(elm, function() {
    5520 			if (prop == 'display') { //get true value for display, since we've just fudged it
    5521 				elm.style.display = oldDisplay;
    5522 				if (!doc.defaultView.getComputedStyle(elm, null)) {
    5523 					return 'none';
    5524 				}
    5525 				elm.style.display = 'block';
    5526 			}
    5527 			return getCssValue(elm, prop);
    5528 			});
    5529 			} else {
    5530 				// assume equal horizontal margins in safari 3
    5531 				// http://bugs.webkit.org/show_bug.cgi?id=13343
    5532 				// The above bug doesn't appear to be closed, but it works fine in Safari 4
    5533 				if (glow.env.webkit > 500 && glow.env.webkit < 526 && prop == 'margin-right' && compStyle.getPropertyValue('position') != 'absolute') {
    5534 					prop = 'margin-left';
    5535 				}
    5536 				r = compStyle.getPropertyValue(prop);
    5537 			}
    5538 		} else if (elmCurrentStyle) { //IE method
    5539 				if (prop == 'opacity') {
    5540 					match = /alpha\(opacity=([^\)]+)\)/.exec(elmCurrentStyle.filter);
    5541 					return match ? String(parseInt(match[1], 10) / 100) : '1';
    5542 				}
    5543 				//this returns cascaded values so needs fixing
    5544 				r = String(elmCurrentStyle[toStyleProp(prop)]);
    5545 				if (/^-?[\d\.]+(?!px)[%a-z]+$/i.test(r) && prop != 'font-size') {
    5546 					r = getPixelValue(elm, r, usesYAxis.test(prop)) + 'px';
    5547 				}
    5548 			}
    5549 			//some results need post processing
    5550 			if (prop.indexOf('color') != -1) { //deal with colour values
    5551 				r = normaliseCssColor(r).toString();
    5552 			} else if (r.indexOf('url') == 0) { //some browsers put quotes around the url, get rid
    5553 				r = r.replace(/\"/g,'');
    5554 			}
    5555 			return r;
    5556 		}
    5557 	/*
    5558 	PrivateMethod: isVisible
    5559 		Is the element visible?
    5560 	*/
    5561 	function isVisible(elm) {
    5562 		//this is a bit of a guess, if there's a better way to do this I'm interested!
    5563 		return elm.offsetWidth ||
    5564 			elm.offsetHeight;
    5565 	}
    5566 	/*
    5567 	PrivateMethod: normaliseCssColor
    5568 		Converts a CSS colour into "rgb(255, 255, 255)" or "transparent" format
    5569 	*/
    5570 
    5571 	function normaliseCssColor(val) {
    5572 		if (/^(transparent|rgba\(0, ?0, ?0, ?0\))$/.test(val)) { return 'transparent'; }
    5573 			var match, //tmp regex match holder
    5574 				r, g, b, //final colour vals
    5575 				hex, //tmp hex holder
    5576 				mathRound = Math.round,
    5577 				parseIntFunc = parseInt,
    5578 				parseFloatFunc = parseFloat,
    5579 					htmlColorNames = {
    5580 					black: 0,
    5581 					silver: 0xc0c0c0,
    5582 					gray: 0x808080,
    5583 					white: 0xffffff,
    5584 					maroon: 0x800000,
    5585 					red: 0xff0000,
    5586 					purple: 0x800080,
    5587 					fuchsia: 0xff00ff,
    5588 					green: 0x8000,
    5589 					lime: 0xff00,
    5590 					olive: 0x808000,
    5591 					yellow: 0xffff00,
    5592 					navy: 128,
    5593 					blue: 255,
    5594 					teal: 0x8080,
    5595 					aqua: 0xffff,
    5596 					orange: 0xffa500
    5597 				},
    5598 				colorRegex = /^rgb\(([\d\.]+)(%?),\s*([\d\.]+)(%?),\s*([\d\.]+)(%?)/i;
    5599 
    5600 			if (match = colorRegex.exec(val)) { //rgb() format, cater for percentages
    5601 				r = match[2] ? mathRound(((parseFloatFunc(match[1]) / 100) * 255)) : parseIntFunc(match[1]);
    5602 				g = match[4] ? mathRound(((parseFloatFunc(match[3]) / 100) * 255)) : parseIntFunc(match[3]);
    5603 				b = match[6] ? mathRound(((parseFloatFunc(match[5]) / 100) * 255)) : parseIntFunc(match[5]);
    5604 			} else {
    5605 				if (typeof val == 'number') {
    5606 					hex = val;
    5607 				} else if (val.charAt(0) == '#') {
    5608 					if (val.length == '4') { //deal with #fff shortcut
    5609 						val = '#' + val.charAt(1) + val.charAt(1) + val.charAt(2) + val.charAt(2) + val.charAt(3) + val.charAt(3);
    5610 					}
    5611 					hex = parseIntFunc(val.slice(1), 16);
    5612 				} else {
    5613 					hex = htmlColorNames[val];
    5614 				}
    5615 
    5616 				r = (hex) >> 16;
    5617 				g = (hex & 0x00ff00) >> 8;
    5618 				b = (hex & 0x0000ff);
    5619 			}
    5620 
    5621 			val = new String('rgb(' + r + ', ' + g + ', ' + b + ')');
    5622 			val.r = r;
    5623 			val.g = g;
    5624 			val.b = b;
    5625 			return val;
    5626 		}
    5627 	/*
    5628 	PrivateMethod: getElmDimension
    5629 		Gets the size of an element as an integer, not including padding or border
    5630 	*/
    5631 	var horizontalBorderPadding = [
    5632 				'border-left-width',
    5633 				'border-right-width',
    5634 				'padding-left',
    5635 				'padding-right'
    5636 			],
    5637 			verticalBorderPadding = [
    5638 				'border-top-width',
    5639 				'border-bottom-width',
    5640 				'padding-top',
    5641 				'padding-bottom'
    5642 			];
    5643 		function getElmDimension(elm, cssProp /* (width|height) */) {
    5644 			var r,
    5645 			doc = document,
    5646 			docElm = doc.documentElement,
    5647 			docBody = document.body,
    5648 			docElmOrBody = glow.env.standardsMode ? docElm : docBody,
    5649 			isWidth = (cssProp == 'width'),
    5650 			cssPropCaps = isWidth ? 'Width' : 'Height',
    5651 			cssBorderPadding;
    5652 
    5653 			if (elm.window) { // is window
    5654 				r = glow.env.webkit < 522.11 ? (isWidth ? elm.innerWidth				: elm.innerHeight) :
    5655 					glow.env.webkit			? (isWidth ? docBody.clientWidth		: elm.innerHeight) :
    5656 					glow.env.opera < 9.5		? (isWidth ? docBody.clientWidth		: docBody.clientHeight) :
    5657 					/* else */			  (isWidth ? docElmOrBody.clientWidth	: docElmOrBody.clientHeight);
    5658 
    5659 			}
    5660 			else if (elm.getElementById) { // is document
    5661 				// we previously checked offsetWidth & clientWidth here
    5662 				// but they returned values too large in IE6 scrollWidth seems enough
    5663 				r = Math.max(
    5664 					docBody['scroll' + cssPropCaps],
    5665 					docElm['scroll' + cssPropCaps]
    5666 				)
    5667 			}
    5668 			else {
    5669 				// get an array of css borders & padding
    5670 				cssBorderPadding = isWidth ? horizontalBorderPadding : verticalBorderPadding;
    5671 				r = elm['offset' + cssPropCaps] - parseInt( getCssValue(elm, cssBorderPadding) );
    5672 			}
    5673 			return r;
    5674 		}
    5675 		
    5676 	/*
    5677 	PrivateMethod: setElmsSize
    5678 		Set element's size
    5679 
    5680 	Arguments:
    5681 		elms - (<NodeList>) Elements
    5682 		val - (Mixed) Set element height / width. In px unless stated
    5683 		type - (String) "height" or "width"
    5684 
    5685 	Returns:
    5686 		Nowt.
    5687 	*/
    5688 	function setElmsSize(elms, val, type) {
    5689 		if (typeof val == 'number' || /\d$/.test(val)) {
    5690 			val += 'px';
    5691 		}
    5692 		for (var i = 0, len = elms.length; i < len; i++) {
    5693 			elms[i].style[type] = val;
    5694 		}
    5695 	}
    5696 	
    5697 	/*
    5698 	PrivateMethod: tempBlock
    5699 		Gives an element display:block (but keeps it hidden) and runs a function, then sets the element back how it was
    5700 
    5701 	Arguments:
    5702 		elm - element
    5703 		func - function to run
    5704 
    5705 	Returns:
    5706 		Return value of the function
    5707 	*/
    5708 	function tempBlock(elm, func) {
    5709 		//TODO: rather than recording individual style properties, just cache cssText? This was faster for getting the element size
    5710 		var r,
    5711 			elmStyle = elm.style,
    5712 			oldDisp = elmStyle.display,
    5713 			oldVis = elmStyle.visibility,
    5714 			oldPos = elmStyle.position;
    5715 
    5716 			elmStyle.visibility = 'hidden';
    5717 			elmStyle.position = 'absolute';
    5718 			elmStyle.display = 'block';
    5719 		if (!isVisible(elm)) {
    5720 			elmStyle.position = oldPos;
    5721 			r = tempBlock(elm.parentNode, func);
    5722 			elmStyle.display = oldDisp;
    5723 			elmStyle.visibility = oldVis;
    5724 		} else {
    5725 			r = func();
    5726 			elmStyle.display = oldDisp;
    5727 			elmStyle.position = oldPos;
    5728 			elmStyle.visibility = oldVis;
    5729 		}
    5730 		return r;
    5731 	}
    5732 	
    5733 	/*
    5734 	PrivateMethod: getPixelValue
    5735 		Converts a relative value into an absolute pixel value. Only works in IE with Dimension value (not stuff like relative font-size).
    5736 		Based on some Dean Edwards' code
    5737 
    5738 	Arguments:
    5739 		element - element used to calculate relative values
    5740 		value - (string) relative value
    5741 		useYAxis - (string) calulate relative values to the y axis rather than x
    5742 
    5743 	Returns:
    5744 		Number
    5745 	*/
    5746 	function getPixelValue(element, value, useYAxis) {
    5747 		// Remember the original values
    5748 		var axisPos = useYAxis ? 'top' : 'left',
    5749 			axisPosUpper = useYAxis ? 'Top' : 'Left',
    5750 			elmStyle = element.style,
    5751 			positionVal = elmStyle[axisPos],
    5752 			runtimePositionVal = element.runtimeStyle[axisPos],
    5753 			r;
    5754 			
    5755 		// copy to the runtime type to prevent changes to the display
    5756 		element.runtimeStyle[axisPos] = element.currentStyle[axisPos];
    5757 			// set value to left / top
    5758 		elmStyle[axisPos] = value;
    5759 		// get the pixel value
    5760 		r = elmStyle['pixel' + axisPosUpper];
    5761 			
    5762 		// revert values
    5763 		elmStyle[axisPos] = positionVal;
    5764 		element.runtimeStyle[axisPos] = runtimePositionVal;
    5765 			
    5766 		return r;
    5767 	}
    5768 	
    5769 	/*************************************** API METHODS ******************************************/
    5770 	/**
    5771 	@name glow.NodeList#css
    5772 	@function
    5773 	@description Get / set a CSS property value
    5774 		
    5775 	@param {string | Object} property The CSS property name, or object of property-value pairs to set
    5776 		
    5777 	@param {string | number} [value] The value to apply
    5778 		Number values will be treated as 'px' unless the CSS property
    5779 		accepts a unitless value.
    5780 		
    5781 		If value is omitted, the value for the given property will be returned
    5782 			
    5783 	@returns {glow.NodeList | string} Returns the NodeList when setting value, or the CSS value when getting values.
    5784 		CSS values are strings. For instance, "height" will return
    5785 		"25px" for an element 25 pixels high. You can use
    5786 		parseInt to convert these values.
    5787 		
    5788 	@example
    5789 		// get value from first node
    5790 		glow("#subNav").css("display");
    5791 		
    5792 	@example
    5793 		// set left padding to 10px on all nodes
    5794 		glow("#subNav li").css("padding-left", "2em");
    5795 		
    5796 	@example
    5797 		// where appropriate, px is assumed when no unit is passed
    5798 		glow("#mainPromo").css("margin-top", 300);
    5799 		
    5800 	@example
    5801 		// set multiple CSS values at once
    5802 		// NOTE: Property names containing a hyphen such as font-weight must be quoted
    5803 		glow("#myDiv").css({
    5804 			'font-weight': 'bold',
    5805 			'padding'	 : '10px',
    5806 			'color'		 : '#00cc99'
    5807 		});
    5808 	*/
    5809 	
    5810 	
    5811 	NodeListProto.css = function(prop, val) {
    5812 		var thisStyle,
    5813 			i = this.length,
    5814 			len = this.length,
    5815 			originalProp = prop,
    5816 			hasUnits = /width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/,
    5817 			style;
    5818 
    5819 			if (prop.constructor === Object) { // set multiple values
    5820 				for (style in prop) {
    5821 					this.css(style, prop[style]);
    5822 				}
    5823 				return this;
    5824 			}
    5825 			else if (val != undefined) { //set one CSS value
    5826 				prop = toStyleProp(prop);
    5827 				while (i--) {
    5828 					thisStyle = this[i].style;
    5829 						
    5830 					if (typeof val == 'number' && hasUnits.test(originalProp)) {
    5831 						val = val.toString() + 'px';
    5832 					}
    5833 					if (prop == 'opacity' && glow.env.ie) {
    5834 						//in IE the element needs hasLayout for opacity to work
    5835 						thisStyle.zoom = '1';
    5836 						if (val === '') {
    5837 							thisStyle.filter = '';
    5838 						} else {
    5839 							thisStyle.filter = 'alpha(opacity=' + Math.round(Number(val, 10) * 100) + ')';
    5840 						}
    5841 					} else {
    5842 						thisStyle[prop] = val;
    5843 					}
    5844 				}
    5845 					return this;
    5846 			} else { //getting stuff
    5847 				if (!len) { return; }
    5848 				return getCssValue(this[0], prop);
    5849 			}	
    5850 	};
    5851 	
    5852 	/**
    5853 	@name glow.NodeList#height
    5854 	@function
    5855 	@description Gets / set element height
    5856 		Return value does not include the padding or border of the element in
    5857 		browsers supporting the correct box model.
    5858 			
    5859 		You can use this to easily get the height of the document or
    5860 		window, see example below.
    5861 		
    5862 	@param {Number} [height] New height in pixels for each element in the list
    5863 		If ommited, the height of the first element is returned
    5864 		
    5865 	@returns {glow.NodeList | number}
    5866 		Height of first element, or original NodeList when setting heights.
    5867 		
    5868 	@example
    5869 		// get the height of #myDiv
    5870 		glow("#myDiv").height();
    5871 		
    5872 	@example
    5873 		// set the height of list items in #myList to 200 pixels
    5874 		glow("#myList > li").height(200);
    5875 		
    5876 	@example
    5877 		// get the height of the document
    5878 		glow(document).height();
    5879 		
    5880 	@example
    5881 		// get the height of the window
    5882 		glow(window).height();
    5883 	*/
    5884 	NodeListProto.height = function(height) {
    5885 		if (height == undefined) {
    5886 			return getElmDimension(this[0], 'height');
    5887 		}
    5888 		setElmsSize(this, height, 'height');
    5889 		return this;	
    5890 	};
    5891 	
    5892 	/**
    5893 	@name glow.NodeList#width
    5894 	@function
    5895 	@description Gets / set element width
    5896 		Return value does not include the padding or border of the element in
    5897 		browsers supporting the correct box model.
    5898 			
    5899 		You can use this to easily get the width of the document or
    5900 		window, see example below.
    5901 		
    5902 	@param {Number} [width] New width in pixels for each element in the list
    5903 		If ommited, the width of the first element is returned
    5904 		
    5905 	@returns {glow.NodeList | number}
    5906 		width of first element, or original NodeList when setting widths.
    5907 		
    5908 	@example
    5909 		// get the width of #myDiv
    5910 		glow("#myDiv").width();
    5911 		
    5912 	@example
    5913 		// set the width of list items in #myList to 200 pixels
    5914 		glow("#myList > li").width(200);
    5915 		
    5916 	@example
    5917 		// get the width of the document
    5918 		glow(document).width();
    5919 		
    5920 	@example
    5921 		// get the width of the window
    5922 		glow(window).width();
    5923 	*/
    5924 	NodeListProto.width = function(width) {
    5925 		if (width == undefined) {
    5926 			return getElmDimension(this[0], 'width');
    5927 		}
    5928 		setElmsSize(this, width, 'width');
    5929 		return this;
    5930 	};
    5931 	
    5932 	/**
    5933 	@name glow.NodeList#scrollLeft
    5934 	@function
    5935 	@description Gets/sets the number of pixels the element has scrolled horizontally
    5936 		To get/set the scroll position of the window, use this method on
    5937 		a nodelist containing the window object.
    5938 			
    5939 	@param {Number} [val] New left scroll position
    5940 		Omit this to get the current scroll position
    5941 			
    5942 	@returns {glow.NodeList | number}
    5943 		Current scrollLeft value, or NodeList when setting scroll position.
    5944 			
    5945 	@example
    5946 		// get the scroll left value of #myDiv
    5947 		var scrollPos = glow("#myDiv").scrollLeft();
    5948 		// scrollPos is a number, eg: 45
    5949 
    5950 	@example
    5951 		// set the scroll left value of #myDiv to 20
    5952 		glow("#myDiv").scrollLeft(20);
    5953 
    5954 	@example
    5955 		// get the scrollLeft of the window
    5956 		glow(window).scrollLeft();
    5957 		// scrollPos is a number, eg: 45
    5958 	*/
    5959 	NodeListProto.scrollLeft = function(val) {
    5960 		return scrollOffset(this, true, val);	
    5961 	};
    5962 	
    5963 	/**
    5964 	@name glow.NodeList#scrollTop
    5965 	@function
    5966 	@description Gets/sets the number of pixels the element has scrolled vertically
    5967 		To get/set the scroll position of the window, use this method on
    5968 		a nodelist containing the window object.
    5969 		
    5970 	@param {Number} [val] New top scroll position
    5971 		Omit this to get the current scroll position
    5972 			
    5973 	@returns {glow.NodeList | number}
    5974 		Current scrollTop value, or NodeList when setting scroll position.
    5975 
    5976 	@example
    5977 		// get the scroll top value of #myDiv
    5978 		var scrollPos = glow("#myDiv").scrollTop();
    5979 		// scrollPos is a number, eg: 45
    5980 
    5981 	@example
    5982 		// set the scroll top value of #myDiv to 20
    5983 		glow("#myDiv").scrollTop(20);
    5984 
    5985 	@example
    5986 		// get the scrollTop of the window
    5987 		glow(window).scrollTop();
    5988 		// scrollPos is a number, eg: 45
    5989 	*/
    5990 	NodeListProto.scrollTop = function(val) {
    5991 		return scrollOffset(this, false, val);	
    5992 	};
    5993 	/**
    5994 	@name glow.dom-getScrollOffset
    5995 	@private
    5996 	@description Get the scrollTop / scrollLeft of a particular element
    5997 	@param {Element} elm Element (or window object) to get the scroll position of
    5998 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
    5999 	*/
    6000 	function getScrollOffset(elm, isLeft) {
    6001 		var r,			
    6002 			scrollProp = 'scroll' + (isLeft ? 'Left' : 'Top');
    6003 			
    6004 		// are we dealing with the window object or the document object?
    6005 		if (elm.window) {
    6006 			// get the scroll of the documentElement or the pageX/Yoffset
    6007 			// - some browsers use one but not the other
    6008 			r = elm.document.documentElement[scrollProp]
    6009 				|| (isLeft ? elm.pageXOffset : elm.pageYOffset)
    6010 				|| 0;
    6011 		} else {
    6012 			r = elm[scrollProp];
    6013 		}
    6014 		return r;
    6015 	}
    6016 		
    6017 	/**
    6018 	@name glow.dom-setScrollOffset
    6019 	@private
    6020 	@description Set the scrollTop / scrollLeft of a particular element
    6021 	@param {Element} elm Element (or window object) to get the scroll position of
    6022 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
    6023 	@param {Number} newVal New scroll value
    6024 	*/
    6025 	function setScrollOffset(elm, isLeft, newVal) {
    6026 	// are we dealing with the window object or the document object?
    6027 		if (elm.window) {
    6028 			// we need to get whichever value we're not setting
    6029 			elm.scrollTo(
    6030 				isLeft  ? newVal : getScrollOffset(elm, true),
    6031 				!isLeft ? newVal : getScrollOffset(elm, false)
    6032 			);
    6033 		} else {
    6034 			elm['scroll' + (isLeft ? 'Left' : 'Top')] = newVal;
    6035 		}
    6036 	}
    6037 	
    6038 	/**
    6039 	@name glow.dom-scrollOffset
    6040 	@private
    6041 	@description Set/get the scrollTop / scrollLeft of a NodeList
    6042 	@param {glow.dom.NodeList} nodeList Elements to get / set the position of
    6043 	@param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top
    6044 	@param {Number} [val] Val to set (if not provided, we'll get the value)
    6045 	@returns NodeList for sets, Number for gets
    6046 	*/
    6047 	function scrollOffset(nodeList, isLeft, val) {
    6048 		var i = nodeList.length;
    6049 			
    6050 		if (val !== undefined) {
    6051 			while (i--) {
    6052 				setScrollOffset(nodeList[i], isLeft, val);
    6053 			}
    6054 			return nodeList;
    6055 		} else {
    6056 			return getScrollOffset(nodeList[0], isLeft);
    6057 		}
    6058 	}
    6059 	/**
    6060 	@name glow.NodeList#hide
    6061 	@function
    6062 	@description Hides all items in the NodeList.
    6063 		
    6064 	@returns {glow.NodeList}
    6065 		
    6066 	@example
    6067 		// Hides all list items within #myList
    6068 		glow("#myList li").hide();
    6069 	*/
    6070 	NodeListProto.hide = function() {
    6071 		return this.css('display', 'none').css('visibility', 'hidden');	
    6072 	};
    6073 	
    6074 	/**
    6075 	@name glow.NodeList#show
    6076 	@function
    6077 	@description Shows all hidden items in the NodeList.
    6078 		
    6079 	@returns {glow.NodeList}
    6080 		
    6081 	@example
    6082 		// Show element with ID myDiv
    6083 		glow("#myDiv").show();
    6084 			
    6085 	@example
    6086 		// Show all list items within #myList
    6087 		glow("#myList li").show();
    6088 	*/
    6089 	NodeListProto.show = function() {
    6090 		var i = this.length,
    6091 			len = this.length,
    6092 			currItem,
    6093 			itemStyle;
    6094 		while (i--) {
    6095 			/* Create a NodeList for the current item */
    6096 			currItem = new glow.NodeList(this[i]);
    6097 			itemStyle = currItem[0].style;
    6098 			if (currItem.css('display') == 'none') {
    6099 				itemStyle.display = '';
    6100 				itemStyle.visibility = 'visible';
    6101 			/* If display is still none, set to block */
    6102 			if (currItem.css('display') == 'none') {
    6103 				itemStyle.display = 'block';
    6104 				}
    6105 			}	
    6106 		}
    6107 		return this;	
    6108 	};
    6109 
    6110 	/**
    6111 	@name glow.NodeList#offset
    6112 	@function
    6113 	@description Gets the offset from the top left of the document.
    6114 		If the NodeList contains multiple items, the offset of the
    6115 		first item is returned.
    6116 			
    6117 	@returns {Object}
    6118 		Returns an object with "top" & "left" properties in pixels
    6119 			
    6120 	@example
    6121 		glow("#myDiv").offset().top
    6122 	*/
    6123 	NodeListProto.offset = function() {
    6124 				// http://weblogs.asp.net/bleroy/archive/2008/01/29/getting-absolute-coordinates-from-a-dom-element.aspx - great bit of research, most bugfixes identified here (and also jquery trac)
    6125 		var elm = this[0],
    6126 		doc = document,
    6127 		docElm = doc.documentElement,
    6128 			docScrollPos = {
    6129 				x: getScrollOffset(window, true),
    6130 				y: getScrollOffset(window, false)
    6131 			}
    6132 
    6133 		//this is simple(r) if we can use 'getBoundingClientRect'
    6134 		// Sorry but the sooper dooper simple(r) way is not accurate in Safari 4
    6135 		if (!glow.env.webkit && elm.getBoundingClientRect) {
    6136 			var rect = elm.getBoundingClientRect();
    6137 			return {
    6138 				top: Math.floor(rect.top)
    6139 				/*
    6140 				 getBoundingClientRect is realive to top left of
    6141 				 the viewport, so we need to sort out scrolling offset
    6142 				*/
    6143 				+ docScrollPos.y
    6144 				/*
    6145 				IE adds the html element's border to the value. We can
    6146 				deduct this value using client(Top|Left). However, if
    6147 				the user has done html{border:0} clientTop will still
    6148 				report a 2px border in IE quirksmode so offset will be off by 2.
    6149 				Hopefully this is an edge case but we may have to revisit this
    6150 				in future
    6151 				*/
    6152 				- docElm.clientTop,
    6153 
    6154 				left: Math.floor(rect.left) //see above for docs on all this stuff
    6155 				+ docScrollPos.x
    6156 				- docElm.clientLeft
    6157 			};
    6158 		} else { //damnit, let's go the long way around
    6159 			var top = elm.offsetTop,
    6160 			left = elm.offsetLeft,
    6161 			originalElm = elm,
    6162 			nodeNameLower,
    6163 			docBody = document.body,
    6164 			//does the parent chain contain a position:fixed element
    6165 			involvesFixedElement = false,
    6166 			offsetParentBeforeBody = elm;
    6167 
    6168 			//add up all the offset positions
    6169 			while (elm = elm.offsetParent) {
    6170 				left += elm.offsetLeft;
    6171 				top += elm.offsetTop;
    6172 
    6173 				//if css position is fixed, we need to add in the scroll offset too, catch it here
    6174 				if (getCssValue(elm, 'position') == 'fixed') {
    6175 					involvesFixedElement = true;
    6176 				}
    6177 
    6178 				//gecko & webkit (safari 3) don't add on the border for positioned items
    6179 				if (glow.env.gecko || glow.env.webkit > 500) {
    6180 					left += parseInt(getCssValue(elm, 'border-left-width')) || 0;
    6181 					top  += parseInt(getCssValue(elm, 'border-top-width'))  || 0;
    6182 				}
    6183 				
    6184 				//we need the offset parent (before body) later
    6185 				if (elm.nodeName.toLowerCase() != 'body') {
    6186 					offsetParentBeforeBody = elm;
    6187 				}
    6188 			}
    6189 
    6190 			//deduct all the scroll offsets
    6191 			elm = originalElm;
    6192 			while ((elm = elm.parentNode) && (elm != docBody) && (elm != docElm)) {
    6193 				left -= elm.scrollLeft;
    6194 				top -= elm.scrollTop;
    6195 
    6196 				//FIXES
    6197 				//gecko doesn't add the border of contained elements to the offset (overflow!=visible)
    6198 				if (glow.env.gecko && getCssValue(elm, 'overflow') != 'visible') {
    6199 					left += parseInt(getCssValue(elm, 'border-left-width'));
    6200 					top += parseInt(getCssValue(elm, 'border-top-width'));
    6201 				}
    6202 			}
    6203 
    6204 			//if we found a fixed position element we need to add the scroll offsets
    6205 			if (involvesFixedElement) {
    6206 				left += docScrollPos.x;
    6207 				top += docScrollPos.y;
    6208 			}
    6209 
    6210 			//FIXES
    6211 			// Webkit < 500 body's offset gets counted twice for absolutely-positioned elements (or if there's a fixed element)
    6212 			// Gecko - non-absolutely positioned elements that are direct children of body get the body offset counted twice
    6213 			if (
    6214 				(glow.env.webkit < 500 && (involvesFixedElement || getCssValue(offsetParentBeforeBody, 'position') == 'absolute')) ||
    6215 				(glow.env.gecko && getCssValue(offsetParentBeforeBody, 'position') != 'absolute')
    6216 			) {
    6217 				left -= docBody.offsetLeft;
    6218 				top -= docBody.offsetTop;
    6219 			}
    6220 
    6221 			return {left:left, top:top};
    6222 		}
    6223 	};
    6224 	
    6225 	/**
    6226 	@name glow.NodeList#position
    6227 	@function
    6228 	@description Get the top & left position of an element relative to its positioned parent
    6229 		This is useful if you want to make a position:static element position:absolute
    6230 		and retain the original position of the element
    6231 			
    6232 	@returns {Object}
    6233 		An object with 'top' and 'left' number properties
    6234 		
    6235 	@example
    6236 		// get the top distance from the positioned parent
    6237 		glow("#elm").position().top
    6238 	*/
    6239 	NodeListProto.position = function() {
    6240 		var positionedParent = new glow.NodeList( getPositionedParent(this[0]) ),
    6241 			hasPositionedParent = !!positionedParent[0],
    6242 					
    6243 			// element margins to deduct
    6244 			marginLeft = parseInt( this.css('margin-left') ) || 0,
    6245 			marginTop  = parseInt( this.css('margin-top')  ) || 0,
    6246 					
    6247 			// offset parent borders to deduct, set to zero if there's no positioned parent
    6248 			positionedParentBorderLeft = ( hasPositionedParent && parseInt( positionedParent.css('border-left-width') ) ) || 0,
    6249 			positionedParentBorderTop  = ( hasPositionedParent && parseInt( positionedParent.css('border-top-width')  ) ) || 0,
    6250 					
    6251 			// element offsets
    6252 		elOffset = this.offset(),
    6253 		positionedParentOffset = hasPositionedParent ? positionedParent.offset() : {top: 0, left: 0};
    6254 				
    6255 		return {
    6256 			left: elOffset.left - positionedParentOffset.left - marginLeft - positionedParentBorderLeft,
    6257 			top:  elOffset.top  - positionedParentOffset.top  - marginTop  - positionedParentBorderTop
    6258 		}	
    6259 	};
    6260 	/*
    6261 		Get the 'real' positioned parent for an element, otherwise return null.
    6262 	*/
    6263 	function getPositionedParent(elm) {
    6264 		var offsetParent = elm.offsetParent,
    6265 		doc = document,
    6266 		docElm = doc.documentElement;
    6267 			
    6268 		// get the real positioned parent
    6269 		// IE places elements with hasLayout in the offsetParent chain even if they're position:static
    6270 		// Also, <body> and <html> can appear in the offsetParent chain, but we don't want to return them if they're position:static
    6271 		while (offsetParent && new glow.NodeList(offsetParent).css('position') == 'static') {	
    6272 			offsetParent = offsetParent.offsetParent;
    6273 		}
    6274 			
    6275 		// sometimes the <html> element doesn't appear in the offsetParent chain, even if it has position:relative
    6276 		if (!offsetParent && new glow.NodeList(docElm).css('position') != 'static') {
    6277 			offsetParent = docElm;
    6278 		}
    6279 			
    6280 		return offsetParent || null;
    6281 	}
    6282 });
    6283 Glow.provide(function(glow) {
    6284 	var NodeListProto = glow.NodeList.prototype,
    6285 		document = window.document,
    6286 		undefined,
    6287 		keyEventNames = ' keypress keydown keyup ';
    6288 	
    6289 	/**
    6290 		@name glow.NodeList#on
    6291 		@function
    6292 		@description Listen for an event.
    6293 		   This will listen for a particular event on each dom node
    6294 		   in the NodeList.
    6295 		   
    6296 		   If you're listening to many children of a particular item,
    6297 		   you may get better performance from {@link glow.NodeList#delegate}.
    6298 		
    6299 		@param {String} eventName Name of the event to listen for.
    6300 		   This can be any regular DOM event ('click', 'mouseover' etc) or
    6301 		   a special event of NodeList.
    6302 		   
    6303 		@param {Function} callback Function to call when the event fires.
    6304 		   The callback is passed a single event object. The type of this
    6305 		   object is {@link glow.DomEvent} unless otherwise stated.
    6306 		   
    6307 		@param {Object} [thisVal] Value of 'this' within the callback.
    6308 		   By default, this is the dom node being listened to.
    6309 		
    6310 		@returns this
    6311 		
    6312 		@example
    6313 		   glow.get('#testLink').on('click', function(domEvent) {
    6314 			   // do stuff
    6315 			   
    6316 			   // if you want to cancel the default action (following the link)...
    6317 			   return false;
    6318 		   });
    6319 	*/
    6320 	NodeListProto.on = function(eventName, callback, thisVal) {
    6321 		var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
    6322 			
    6323 		if (isKeyEvent) {
    6324 			glow.events._addKeyListener(this, eventName, callback, thisVal);
    6325 		}
    6326 		else { // assume it's a DOM event
    6327 			glow.events._addDomEventListener(this, eventName, callback, thisVal);
    6328 		}
    6329 		
    6330 		return this;
    6331 	}
    6332 	
    6333 	/**
    6334 		@name glow.NodeList#detach
    6335 		@function
    6336 		@description detach a listener from elements
    6337 		   This will detach the listener from each dom node in the NodeList.
    6338 		
    6339 		@param {String} eventName Name of the event to detach the listener from
    6340 		   
    6341 		@param {Function} callback Listener callback to detach
    6342 		
    6343 		@returns this
    6344 		
    6345 		@example
    6346 			function clickListener(domEvent) {
    6347 				// ...
    6348 			}
    6349 			
    6350 			// adding listeners
    6351 			glow.get('a').on('click', clickListener);
    6352 			
    6353 			// removing listeners
    6354 			glow.get('a').detach('click', clickListener);
    6355 	*/
    6356 	NodeListProto.detach = function(eventName, callback, thisVal) {
    6357 		var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
    6358 		if (isKeyEvent) {
    6359 			glow.events._removeKeyListener(this, eventName, callback);
    6360 		}
    6361 		else { // assume it's a DOM event
    6362 			glow.events._removeDomEventListener(this, eventName, callback, thisVal);
    6363 		}
    6364 		
    6365 		return this;
    6366 	}
    6367 	
    6368 	/**
    6369 		@name glow.NodeList#delegate
    6370 		@function
    6371 		@description Listen for an event occurring on child elements matching a selector.
    6372 			'delegate' will catch events which occur on matching items created after
    6373 			the listener was added. 
    6374 		
    6375 		@param {String} eventName Name of the event to listen for.
    6376 			This can be any regular DOM event ('click', 'mouseover' etc) or
    6377 			a special event of NodeList.
    6378 		
    6379 		@param {String} selector CSS selector of child elements to listen for events on
    6380 			For example, if you were wanting to hear events from links, this
    6381 			would be 'a'.
    6382 		
    6383 		@param {Function} callback Function to call when the event fires.
    6384 			The callback is passed a single event object. The type of this
    6385 			object is {@link glow.DomEvent} unless otherwise stated.
    6386 		
    6387 		@param {Object} [thisVal] Value of 'this' within the callback.
    6388 			By default, this is the dom node matched by 'selector'.
    6389 		
    6390 		@returns this
    6391 		
    6392 		@example
    6393 			// Using 'on' to catch clicks on links in a list
    6394 			glow.get('#nav a').on('click', function() {
    6395 				// do stuff
    6396 			});
    6397 			
    6398 			// The above adds a listener to each link, any links created later
    6399 			// will not have this listener, so we won't hear about them.
    6400 			
    6401 			// Using 'delegate' to catch clicks on links in a list
    6402 			glow.get('#nav').delegate('click', 'a', function() {
    6403 				// do stuff
    6404 			});
    6405 			
    6406 			// The above only adds one listener to #nav which tracks clicks
    6407 			// to any links within. This includes elements created after 'delegate'
    6408 			// was called.
    6409 		
    6410 		@example
    6411 			// Using delegate to change class names on table rows so :hover
    6412 			// behaviour can be emulated in IE6
    6413 			glow.get('#contactData').delegate('mouseover', 'tr', function() {
    6414 				glow.get(this).addClass('hover');
    6415 			});
    6416 			
    6417 			glow.get('#contactData').delegate('mouseout', 'tr', function() {
    6418 				glow.get(this).removeClass('hover');
    6419 			});
    6420 	*/
    6421 	NodeListProto.delegate = function(eventName, selector, callback, thisVal) {
    6422 		var i = this.length,
    6423 			attachTo,
    6424 			isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1);
    6425 		
    6426 		while (i--) {
    6427 			attachTo = this[i];
    6428 			
    6429 			if (isKeyEvent) {
    6430 // 					glow.events._addKeyListener([attachTo], eventName, handler);
    6431 			}
    6432 			else { // assume it's a DOM event
    6433 				glow.events._addDomEventListener([attachTo], eventName, callback, thisVal, selector);
    6434 			}
    6435 		}
    6436 		
    6437 		return this;
    6438 	}
    6439 	
    6440 	/**
    6441 		@name glow.NodeList#detachDelegate
    6442 		@function
    6443 		@description detach a delegated listener from elements
    6444 		   This will detach the listener from each dom node in the NodeList.
    6445 		
    6446 		@param {String} eventName Name of the event to detach the listener from
    6447 		
    6448 		@param {String} selector CSS selector of child elements the listener is listening to
    6449 		
    6450 		@param {Function} callback Listener callback to detach
    6451 		
    6452 		@returns this
    6453 		
    6454 		@example
    6455 			function clickListener(domEvent) {
    6456 				// ...
    6457 			}
    6458 			
    6459 			// adding listeners
    6460 			glow.get('#nav').delegate('click', 'a', clickListener);
    6461 			
    6462 			// removing listeners
    6463 			glow.get('#nav').detachDelegate('click', 'a', clickListener);
    6464 	*/
    6465 	NodeListProto.detachDelegate = function(eventName, selector, callback, thisVal) {
    6466 		var i = this.length,
    6467 			attachTo,
    6468 			isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1),
    6469 			handler;
    6470 		
    6471 		while (i--) {
    6472 			attachTo = this[i];
    6473 			
    6474 			if (isKeyEvent) {
    6475 // 				glow.events._removeKeyListener([attachTo], eventName, handler);
    6476  			}
    6477  			else {
    6478  				glow.events._removeDomEventListener([attachTo], eventName, callback, selector);
    6479  			}
    6480 		}
    6481 		
    6482 		return this;
    6483 	}
    6484 	
    6485 	/**
    6486 		@name glow.NodeList#fire
    6487 		@function
    6488 		@param {String} eventName Name of the event to fire
    6489 		@param {glow.events.Event} [event] Event object to pass into listeners.
    6490 		   You can provide a simple object of key / value pairs which will
    6491 		   be added as properties of a glow.events.Event instance.
    6492 		
    6493 		@description Fire an event on dom nodes within the NodeList
    6494 		   Note, this will only trigger event listeners to be called, it won't
    6495 		   for example, move the mouse or click a link on the page.
    6496 		
    6497 		@returns glow.events.Event
    6498 		
    6499 		@example
    6500 		   glow.get('#testLink').on('click', function() {
    6501 			   alert('Link clicked!');
    6502 		   });
    6503 		   
    6504 		   // The following causes 'Link clicked!' to be alerted, but doesn't
    6505 		   // cause the browser to follow the link
    6506 		   glow.get('#testLink').fire('click');
    6507 	*/
    6508 	NodeListProto.fire = function(eventName, event) {
    6509 		return glow.events.fire(this, eventName, event);
    6510 	}
    6511 	
    6512 	/**
    6513 		@name glow.NodeList#event:mouseenter
    6514 		@event
    6515 		@description Fires when the mouse enters the element specifically, does not bubble
    6516 		
    6517 		@param {glow.events.DomEvent} event Event Object
    6518 	*/
    6519 	
    6520 	/**
    6521 		@name glow.NodeList#event:mouseleave
    6522 		@event
    6523 		@description Fires when the mouse leaves the element specifically, does not bubble
    6524 		
    6525 		@param {glow.events.DomEvent} event Event Object
    6526 	*/
    6527 	
    6528 	/**
    6529 		@name glow.NodeList#event:keydown
    6530 		@event
    6531 		@description Fires when the user presses a key
    6532 			Only fires if the element has focus, listen for this event on
    6533 			the document to catch all keydowns.
    6534 			
    6535 			This event related to the user pressing a key on the keyboard,
    6536 			if you're more concerned about the character entered, see the
    6537 			{@link glow.NodeList#event:keypress keypress} event.
    6538 			
    6539 			keydown will only fire once, when the user presses the key.
    6540 			
    6541 			The order of events is keydown, keypress*, keyup. keypress may
    6542 			fire many times if the user holds the key down.
    6543 		
    6544 		@param {glow.events.KeyboardEvent} event Event Object
    6545 	*/
    6546 	
    6547 	/**
    6548 		@name glow.NodeList#event:keypress
    6549 		@event
    6550 		@description Fires when a key's command executes.
    6551 			For instance, if you hold down a key, it's action will occur many
    6552 			times. This event will fire on each action.
    6553 			
    6554 			This event is useful when you want to react to keyboard repeating, or
    6555 			to detect when a character is entered into a field.
    6556 			
    6557 			The order of events is keydown, keypress*, keyup. keypress may
    6558 			fire many times if the user holds the key down.
    6559 		
    6560 		@param {glow.events.KeyboardEvent} event Event Object
    6561 	*/
    6562 	
    6563 	/**
    6564 		@name glow.NodeList#event:keyup
    6565 		@event
    6566 		@description Fires when the user releases a key
    6567 			Only fires if the element has focus, listen for this event on
    6568 			the document to catch all keyups.
    6569 			
    6570 			This event related to the user pressing a key on the keyboard,
    6571 			if you're more concerned about the character entered, see the
    6572 			{@link glow.NodeList#event:keypress keypress} event.
    6573 			
    6574 			The order of events is keydown, keypress*, keyup. keypress may
    6575 			fire many times if the user holds the key down.
    6576 		
    6577 		@param {glow.events.KeyboardEvent} event Event Object
    6578 	*/
    6579 });
    6580 Glow.complete('core', '2.0.0-alpha1');
    6581 
    docs/symbols/src/build_2.0.0-alpha1_glow.js.html100644 0 0 226334 11343467400 17001 0ustar 0 0
      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 (function() {
     17 	var glowMap,
     18 		defaultBase,
     19 		document = window.document,
     20 		thisScriptSrc = ( document.body || document.getElementsByTagName('head')[0] ).lastChild.src;
     21 	
     22 	// get default base from last script element
     23 	defaultBase = thisScriptSrc.slice( 0, thisScriptSrc.lastIndexOf('/') ) + '/../';
     24 		
     25 	// track when document is ready, must run before the page is finished loading
     26 	if (!document.readyState) {
     27 		if (document.addEventListener) { // like Mozilla
     28 			document.addEventListener('DOMContentLoaded',
     29 				function () {
     30 					document.removeEventListener('DOMContentLoaded', arguments.callee, false);
     31 					document.readyState = 'complete';
     32 				},
     33 				false
     34 			);
     35 		}
     36 	}
     37 	
     38 	/**
     39 		@public
     40 		@name Glow
     41 		@function
     42 		@description Creates an instance of the Glow JavaScript Library.
     43 		@param {string} [version]
     44 		@param {object} [opts]
     45 		@param {string} [opts.base] The path to the base folder, in which the Glow versions are kept.
     46 		@param {boolean} [opts.debug] Have all filenames modified to point to debug versions.
     47 	*/
     48 	window.Glow = function(version, opts) { /*debug*///log.info('new Glow("'+Array.prototype.join.call(arguments, '", "')+'")');
     49 		opts = opts || {};
     50 		
     51 		var glowInstance,
     52 			debug = (opts.debug)? '.debug' : '',
     53 			base = opts.base || defaultBase;
     54 
     55 		glowMap = {
     56 			versions: ['2.0.0-alpha1', '@'+'SRC@'],
     57 			'2.0.0-alpha1': {
     58 				'core':    ['core'+debug+'.js'],
     59 				'widgets': ['core', 'widgets'+debug+'.js', 'widgets'+debug+'.css']
     60 			}
     61 		};
     62 		
     63 		if (opts._map) { glowMap = opts._map; } // for testing purposes map can be overridden
     64 		
     65 		version = getVersion(version); /*debug*///log.info('Version is "'+version+'"');
     66 		
     67 		if (Glow._build.instances[version]) { /*debug*///log.info('instance for "'+version+'" already exists.');
     68 			return Glow._build.instances[version];
     69 		}
     70 		
     71 		// opts.base should be formatted like a directory
     72 		if (base.slice(-1) !== '/') {
     73 			base += '/';
     74 		}
     75 		
     76 		glowInstance = createGlowInstance(version, base);
     77 		Glow._build.instances[version] = glowInstance;
     78 		
     79 		glowInstance.UID = 'glow' + Math.floor(Math.random() * (1<<30));
     80 
     81  		glowInstance.load('core'); // core is always loaded;
     82  		 		
     83 		return glowInstance;
     84 	}
     85 	
     86 	/**
     87 		@private
     88 		@name getVersion
     89 		@function
     90 		@param {string} version A (possibly imprecise) version identifier, like "2".
     91 		@description Find the most recent, available version of glow that matches.
     92 	 */
     93 	var getVersion = function(version) { /*debug*///log.info('getVersion("'+version+'")');
     94 		var versions = glowMap.versions,
     95 			matchThis = version + '.';
     96 		
     97 		// TODO: an empty version means: the very latest version
     98 		
     99 		var i = versions.length;
    100 		while (i--) {
    101 			if ( ( versions[i] + '.').indexOf(matchThis) === 0 ) {
    102 				return versions[i];
    103 			}
    104 		}
    105 		
    106 		throw new Error('Version "'+version+'" does not exist');
    107 	}
    108 	
    109 	/**
    110 		@private
    111 		@name getMap
    112 		@function
    113 		@description Find the file map for a given version.
    114 		@param {string} version Resolved identifier, like '2.0.0'.
    115 		@returns {object} A map of package names to files list.
    116 	 */
    117 	var getMap = function(version, debug) { /*debug*///log.info('getMap("'+version+'")');
    118 		var versions = glowMap.versions,
    119 			map = null,
    120 			versionFound = false;
    121 		
    122 		var i = versions.length;
    123 		while (--i > -1) {
    124 			if (glowMap[versions[i]]) { map = glowMap[versions[i]]; }
    125 			if (versions[i] === version) { versionFound = true; }
    126 			if (versionFound && map) { return map; }
    127 		}
    128 		
    129 		throw new Error('No map available for version "' + version + '".');
    130 	}
    131 	
    132 	/**
    133 		@private
    134 		@name injectJs
    135 		@function
    136 		@description Start asynchronously loading an external JavaScript file.
    137 	 */
    138 	var injectJs = function(src) { /*debug*///log.info('injectJs("'+src+'")');
    139 		var head,
    140 			script;
    141 		
    142 		head = document.getElementsByTagName('head')[0];
    143 		script = document.createElement('script');
    144 		script.src = src;
    145 		script.type = 'text/javascript';
    146 		
    147 		head.insertBefore(script, head.firstChild); // rather than appendChild() to avoid IE bug when injecting SCRIPTs after BASE tag opens. see: http://shauninman.com/archive/2007/04/13/operation_aborted
    148 	}
    149 	
    150 	/**
    151 		@private
    152 		@name injectCss
    153 		@function
    154 		@description Start asynchronously loading an external CSS file.
    155 	 */
    156 	var injectCss = function(src) { /*debug*///log.info('injectCss("'+src+'")');
    157 		var head,
    158 			link;
    159 			
    160 		head = document.getElementsByTagName('head')[0];
    161 		link = document.createElement('link');
    162 		link.href = src;
    163 		link.type = 'text/css';
    164 		link.rel = 'stylesheet';
    165 		
    166 		head.insertBefore(link, head.firstChild);
    167 	}
    168 	
    169 	/** @private */
    170 	Glow._build = {
    171 		provided: [], // provided but not yet complete
    172 		instances: {} // built
    173 	}
    174 	
    175 	/**
    176 		@private
    177 		@name Glow.provide
    178 		@function
    179 		@param {function} builder A function to run, given an instance of glow, and will add a feature to glow.
    180 		@description Provide a builder function to Glow as part of a package.
    181 	 */
    182 	Glow.provide = function(builder) { /*debug*///log.info('Glow.provide('+typeof builder+')');
    183 		Glow._build.provided.push(builder);
    184 	}
    185 	
    186 	/**
    187 		@private
    188 		@name Glow.complete
    189 		@function
    190 		@param {string} name The name of the completed package.
    191 		@param {string} version The version of the completed package.
    192 		@description Signals that no more builder functions will be provided by this package.
    193 	 */
    194 	Glow.complete = function(name, version) { /*debug*///log.info('complete('+name+', '+version+')');
    195 		var glow,
    196 			loading,
    197 			builders;
    198 
    199 		// now that we have the name and version we can move the builders out of provided cache
    200 		glow = Glow._build.instances[version];
    201 		if (!glow) { /*debug*///log.info('Cannot complete, unknown version of glow: '+version);
    202 			throw new Error('Cannot complete, unknown version of glow: '+version);
    203 		}
    204 		glow._build.builders[name] = Glow._build.provided;
    205 		Glow._build.provided = [];
    206 
    207 		// shortcuts
    208 		loading   = glow._build.loading;
    209 		builders = glow._build.builders;
    210 		
    211 		// try to build packages, in the same order they were loaded
    212 		for (var i = 0; i < loading.length; i++) { // loading.length may change during loop
    213 			if (!builders[loading[i]]) { /*debug*///log.info(loading[i]+' has no builders.');
    214 				break;
    215 			}
    216 			
    217 			// run the builders for this package in the same order they were loaded
    218 			for (var j = 0, jlen = builders[loading[i]].length; j < jlen; j++) { /*debug*///log.info('running builder '+j+ ' for '+loading[i]+' version '+glow.version);
    219 				builders[loading[i]][j](glow); // builder will modify glow
    220 			}
    221 			
    222 			// remove this package from the loaded and builders list, now that it's built
    223 			if (glow._removeReadyBlock) { glow._removeReadyBlock('glow_loading_'+loading[i]); }
    224 			builders[loading[i]] = undefined;
    225 			loading.splice(i, 1);
    226 			i--;
    227 			
    228 			
    229 		}
    230 		
    231 		// try to run onLoaded callbacks
    232 		glow._release();
    233 	}
    234 	
    235 	/**
    236 		@name createGlowInstance
    237 		@private
    238 		@function
    239 		@description Creates an instance of the Glow library. 
    240 		@param {string} version
    241 		@param {string} base
    242 	 */
    243 	var createGlowInstance = function(version, base) { /*debug*///log.info('new glow("'+Array.prototype.join.call(arguments, '", "')+'")');
    244 		var glow = function(nodeListContents) {
    245 			return new glow.NodeList(nodeListContents);
    246 		};
    247 		
    248 		glow.version = version;
    249 		glow.base = base;
    250 		glow.map = getMap(version);
    251 		glow._build = {
    252 			loading: [],   // names of packages requested but not yet built, in same order as requested.
    253 			builders: {},  // completed but not yet built (waiting on dependencies). Like _build.builders[packageName]: [function, function, ...].
    254 			history: {},   // names of every package ever loaded for this instance
    255 			callbacks: []
    256 		};
    257 		
    258 		// copy properties from glowInstanceMembers
    259 		for (var prop in glowInstanceMembers) {
    260 			glow[prop] = glowInstanceMembers[prop];
    261 		}
    262 		
    263 		return glow;
    264 	}
    265 	
    266 	
    267 	/**
    268 		@name glowInstanceMembers
    269 		@private
    270 		@description All members of this object will be copied onto little-glow instances
    271 		@type {Object}
    272 	*/
    273 	var glowInstanceMembers = {
    274 		/**
    275 			@public
    276 			@name glow#load
    277 			@function
    278 			@description Add a package to this instance of the Glow library.
    279 			@param {string[]} ... The names of 1 or more packages to add.
    280 		 */
    281 		load: function() { /*debug*///log.info('glow.load("'+Array.prototype.join.call(arguments, '", "')+'") for version '+this.version);
    282 			var name = '',
    283 				src,
    284 				depends;
    285 			
    286 			for (var i = 0, len = arguments.length; i < len; i++) {
    287 				name = arguments[i];
    288 				
    289 				if (this._build.history[name]) { /*debug*///log.info('already loaded package "'+name+'" for version '+this.version+', skipping.');
    290 					continue;
    291 				}
    292 				
    293 				this._build.history[name] = true;
    294 				
    295 				// packages have dependencies, listed in the map: a single js file, css files, or even other packages
    296 				depends = this.map[name]; /*debug*///log.info('depends for '+name+' '+this.version+': "'+depends.join('", "')+'"');
    297 				for (var j = 0, lenj = depends.length; j < lenj; j++) {
    298 					
    299 					if (depends[j].slice(-3) === '.js') { /*debug*///log.info('dependent js: "'+depends[j]+'"');
    300 						src = this.base + this.version + '/' + depends[j];
    301 						
    302 						// readyBlocks are removed in _release()
    303 						if (this._addReadyBlock) { this._addReadyBlock('glow_loading_'+name); } // provided by core
    304 						this._build.loading.push(name);
    305 						
    306 						injectJs(src);
    307 					}
    308 					else if (depends[j].slice(-4) === '.css') { /*debug*///log.info('dependent css "'+depends[j]+'"');
    309 						src = this.base + this.version + '/' + depends[j];
    310 						injectCss(src);
    311 					}
    312 					else { /*debug*///log.info('dependent package: "'+depends[j]+'"');
    313 						this.load(depends[j]); // recursively load dependency packages
    314 					}
    315 				}
    316 			}
    317 			
    318 			return this;
    319 		},
    320 		/**
    321 			@public
    322 			@name glow#loaded
    323 			@function
    324 			@param {function} onLoadCallback Called when all the packages load.
    325 			@description Do something when all the packages load.
    326 		 */
    327 		loaded: function(onLoadCallback) { /*debug*///log.info('glow.loaded('+typeof onLoadCallback+') for version '+this.version);
    328 			this._build.callbacks.push(onLoadCallback);
    329 			if (this._addReadyBlock) { this._addReadyBlock('glow_loading_loadedcallback'); }
    330 			
    331 			this._release();
    332 			
    333 			return this;
    334 		},
    335 		/**
    336 			@private
    337 			@name glow#_release
    338 			@function
    339 			@description If all loaded packages are now built, then run the onLoaded callbacks.
    340 		 */
    341 		_release: function() { /*debug*///log.info('glow._release("'+this.version+'")');
    342 			var callback;
    343 			
    344 			if (this._build.loading.length !== 0) { /*debug*///log.info('waiting for '+this._build.loading.length+' to finish.');
    345 				return;
    346 			}
    347 			/*debug*///log.info('running '+this._build.callbacks.length+' loaded callbacks for version "'+this.version+'"');
    348 			
    349 			// run and remove each available _onloaded callback
    350 			while (callback = this._build.callbacks.shift()) {
    351 				callback(this);
    352 				if (this._removeReadyBlock) { this._removeReadyBlock('glow_loading_loadedcallback'); }
    353 			}
    354 		},
    355 		/**
    356 			@name glow#ready
    357 			@function
    358 			@param {function} onReadyCallback Called when all the packages load and the DOM is available.
    359 			@description Do something when all the packages load and the DOM is ready.
    360 		 */
    361 		ready: function(onReadyCallback) { /*debug*///log.info('(ember) glow#ready('+typeof onReadyCallback+') for version '+this.version+'. There are '+this._build.loading.length+' loaded packages waiting to be built.');
    362 			this.loaded(function(glow) {
    363 				glow.ready( function() { onReadyCallback(glow); } );
    364 			});
    365 			
    366 			return this;
    367 		}
    368 	}
    369 })();
    370 
    glow/2.0.0-alpha1/core.debug.js100644 0 0 555740 11343467400 13413 0ustar 0 0 /*! Copyright 2010 British Broadcasting Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /** @name glow @namespace @version @VERSION@ @description The glow library namespace The library can also be used as a function, which is a shortcut to {@link glow.NodeList}. @example var links = glow('a'); // is the same as var links = new glow.NodeList('a'); */ if (!window.Glow) { // loading packages via user SCRIPT tags? window.Glow = { provide: function(f) { f(glow); }, complete: function(n, version) { glow.version = version; } }; window.glow = function(nodeListContents) { return new glow.NodeList(nodeListContents); }; glow.load = function() { throw new Error('Method load() is not available without glow.js'); } } Glow.provide(function(glow) { /*!debug*/ var glowbug = { errors: [] , log: function(message, fileName, lineNumber) { this._add('Log', message, fileName, lineNumber); } , warn: function(message, fileName, lineNumber) { this._add('Warn', message, fileName, lineNumber); } , error: function(message, fileName, lineNumber) { this._add('Error', message, fileName, lineNumber); } , _add: function(level, message, fileName, lineNumber) { var e = new Error(message, fileName, lineNumber); e.message = message; e.name = 'Glow'+level; e.level = level.toLowerCase(); var match = /\[([^\]]+)\]/.exec(message); if (match) e.type = match[1]; // may be undefined else e.type = 'message'; this.errors.push(e); this.out(e); } , out: function(e) { var message = '['+e.level+'] '+e.message; if (window.console) { if (e.level === 'warn' && window.console.warn) { console.warn(message); } else if (e.level === 'error' && window.console.error) { console.error(message); } else if (window.console.log) { console.log(message); } } else if (window.opera && opera.postError) { opera.postError(message); } else { // use our own console glowbug.console.log(e.level, message); } } }; glowbug.console = { messages: [], log: function(level, message) { if (!this._w) { try { this._w = window.open('', 'report', 'width=350,height=250,menubar=0,toolbar=0,location=no,status=0,scrollbars=1,resizable=1'); this._w.document.write( '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>' + '<body style="font: 11px monaco"><code id="messages"><\/code><\/body><\/html>' ) this._w.document.close(); } catch(ignored) { this._w = null; } } if (this._w) { var p = this._w.document.createElement('P'); p.className = 'message ' + level; p.innerHTML = message; this._w.document.getElementById('messages').appendChild(p); var dh = this._w.document.body.scrollHeight var ch = this._w.document.body.clientHeight if (dh > ch) { this._w.scrollTo(0, dh-ch); } } } } if (typeof glowbug != 'undefined') { glow.debug = glowbug; } /*gubed!*/ }); Glow.provide(function(glow) { /** @name glow.env @namespace @description Information about the browser and characteristics */ // parse the useragent string, setting NaN if match isn't found var ua = navigator.userAgent.toLowerCase(), nanArray = [0, NaN], opera = (/opera[\s\/]([\w\.]+)/.exec(ua) || nanArray)[1], ie = opera ? NaN : (/msie ([\w\.]+)/.exec(ua) || nanArray)[1], gecko = (/rv:([\w\.]+).*gecko\//.exec(ua) || nanArray)[1], webkit = (/applewebkit\/([\w\.]+)/.exec(ua) || nanArray)[1], khtml = (/khtml\/([\w\.]+)/.exec(ua) || nanArray)[1], toNumber = parseFloat, env = {}; /** @name glow.env.gecko @type number @description Gecko version number to one decimal place (eg 1.9) or NaN on non-gecko browsers. The most popular browser using the Gecko engine is Firefox. @see <a href="http://en.wikipedia.org/wiki/Gecko_(layout_engine)#Usage">Versions of Gecko used by browsers</a> @example if (glow.env.gecko < 1.9) { // runs in Firefox 2 and other browsers that use Gecko earlier than 1.9 } */ env.gecko = toNumber(gecko); /** @name glow.env.ie @type number @description IE version number to one decimal place (eg 6.0) or NaN on non-IE browsers. This number will also be populated for browser based on IE's trident engine @example if (glow.env.ie < 9) { // runs in IE pre-9.0 glow('#content').css('background', 'deadmoomin.png'); } */ env.ie = toNumber(ie); /** @name glow.env.opera @type number @description Opera version number to one decimal place (eg 10.0) or NaN on non-Opera browsers. @example if (glow.env.opera < 10) { // runs in Opera pre-10.0 } */ env.opera = toNumber(opera); /** @name glow.env.webkit @type number @description Webkit version number to one decimal place (eg 531.9) or NaN on non-Webkit browsers. Safari and Google Chrome are the most popular browsers using Webkit. @see <a href="http://en.wikipedia.org/wiki/Safari_version_history#Release_history">Versions of Webkit used by Safari</a> @see <a href="http://en.wikipedia.org/wiki/Google_Chrome#Release_history">Versions of Webkit used by Google Chrome</a> @example if (glow.env.webkit < 526) { // runs in Safari pre-4.0, and Chrome pre-1.0 } */ env.webkit = toNumber(webkit); /** @name glow.env.khtml @type number @description KHTML version number to one decimal place or NaN on non-KHTML browsers. Konqueror is the most popular browsers using KHTML. */ env.khtml = toNumber(khtml); /** @name glow.env.standardsMode @type boolean @description True if the browser reports itself to be in 'standards mode' Otherwise, the browser is in 'quirks mode' @see <a href="http://en.wikipedia.org/wiki/Quirks_mode">Quirks Mode vs Standards Mode</a> */ env.standardsMode = document.compatMode != "BackCompat" && (!env.ie || env.ie >= 6); /** @name glow.env.version @type string @description Version number of the browser in use as a string. This caters for version numbers that aren't 'real' numbers, like "7b" or "1.9.1" */ env.version = ie || gecko || webkit || opera || khtml || ''; // export glow.env = env; }); // start-source: core/ready.js /*debug*///log.info('executing core/ready.js'); Glow.provide( function(glow) { var readyQueue = [], domReadyQueue = [], blockersActive = 0, processingReadyQueue = false; glow._readyBlockers = {}; /*debug*///log.info('overwriting Glow ready with glow.ready'); glow.ready = function (f) { /*debug*///log.info('glow.ready()'); if (this.isReady) { f(); } else { readyQueue.push(f); } return glow; }; glow.onDomReady = function(f) { //just run function if already ready if (glow.isDomReady) { f(); } else { domReadyQueue.push(f); } }; glow._addReadyBlock = function(name) { /*debug*///log.info('_addReadyBlock('+name+')'); if (typeof glow._readyBlockers[name] === 'undefined') { glow._readyBlockers[name] = 0; } glow._readyBlockers[name]++; glow.isReady = false; blockersActive++; /*debug*///log.info(' » blockersActive '+blockersActive+'.'); return glow; } glow._removeReadyBlock = function(name) { /*debug*///log.info('_removeReadyBlock('+name+')'); if (glow._readyBlockers[name]) { glow._readyBlockers[name]--; blockersActive--; /*debug*///log.info(' » blockersActive '+blockersActive+'.'); // if we're out of blockers if (!blockersActive) { // call our queue glow.isReady = true; runReadyQueue(); } } return glow; } // add blockers for any packages that started loading before core (this package) was built if (glow._build) { // only defined when using big Glow for (var i = 0, len = glow._build.loading.length; i < len; i++) { glow._addReadyBlock('glow_loading_'+glow._build.loading[i]); } for (var j = 0, lenj = glow._build.callbacks.length; j < lenj; j++) { if (glow._addReadyBlock) { glow._addReadyBlock('glow_loading_loadedcallback'); } } } function runDomReadyQueue() { /*debug*///log.info('runDomReadyQueue()'); glow.isDomReady = true; // run all functions in the array for (var i = 0, len = domReadyQueue.length; i < len; i++) { domReadyQueue[i](); } } function runReadyQueue() { /*debug*///log.info('runReadyQueue()'); // if we're already processing the queue, just exit, the other instance will take care of it if (processingReadyQueue) { return; } /*debug*///log.info('readyQueue: '+readyQueue.length); processingReadyQueue = true; while (readyQueue.length) { var callback = readyQueue.shift(); /*debug*///log.info('callback: '+callback); callback(glow); // check if the previous function has created a blocker if (blockersActive) { break; } } processingReadyQueue = false; } /** @private @function @name bindReady @description Add listener to document to detect when page is ready. */ function bindReady() { /*debug*///log.info('bindReady()'); //don't do this stuff if the dom is already ready if (glow.isDomReady) { return; } glow._addReadyBlock('glow_domReady'); // wait for dom to be ready function onReady() { /*debug*///log.info('onReady()'); runReadyQueue(); glow._removeReadyBlock('glow_domReady'); } if (document.readyState == 'complete') { // already here! /*debug*///log.info('already complete'); onReady(); } else if (glow.env.ie && document.attachEvent) { /*debug*///log.info('bindready() - document.attachEvent'); // like IE // not an iframe... if (document.documentElement.doScroll && window == top) { (function() { /*debug*///log.info('doScroll'); try { document.documentElement.doScroll('left'); } catch(error) { setTimeout(arguments.callee, 0); return; } // and execute any waiting functions onReady(); })(); } else { // an iframe... document.attachEvent( 'onreadystatechange', function() { /*debug*///log.info('onreadystatechange'); if (document.readyState == 'complete') { document.detachEvent('onreadystatechange', arguments.callee); onReady(); } } ); } } else if (document.readyState) { /*debug*///log.info('bindready() - document.readyState'); // like pre Safari (function() { /*debug*///log.info('loaded|complete'); if ( /loaded|complete/.test(document.readyState) ) { onReady(); } else { setTimeout(arguments.callee, 0); } })(); } else if (document.addEventListener) {/*debug*///log.info('bindready() - document.addEventListener'); // like Mozilla, Opera and recent webkit document.addEventListener( 'DOMContentLoaded', function(){ /*debug*///log.info('glow DOMContentLoaded'); document.removeEventListener('DOMContentLoaded', arguments.callee, false); onReady(); }, false ); } else { throw new Error('Unable to bind glow ready listener to document.'); } } glow.notSupported = ( // here are the browsers we don't support glow.env.ie < 6 || (glow.env.gecko < 1.9 && !/^1\.8\.1/.test(env.version)) || glow.env.opera < 9 || glow.env.webkit < 412 ); // deprecated glow.isSupported = !glow.notSupported; // block 'ready' if browser isn't supported if (glow.notSupported) { glow._addReadyBlock('glow_browserSupport'); } bindReady(); } ); // end-source: core/ready.js /** @name glow.util @namespace @description Core JavaScript helpers */ Glow.provide(function(glow) { var util = {}, undefined; /** @name glow.util.apply @function @description Copies properties from one object to another All properties from 'source' will be copied onto 'destination', potentially overwriting existing properties on 'destination'. Properties from 'source's prototype chain will not be copied. @param {Object} destination Destination object @param {Object} source Properties of this object will be copied onto the destination @returns {Object} The destination object. @example var obj = glow.util.apply({foo: "hello", bar: "world"}, {bar: "everyone"}); //results in {foo: "hello", bar: "everyone"} */ util.apply = function(destination, source) { /*!debug*/ if (arguments.length != 2) { glow.debug.warn('[wrong count] glow.util.apply expects 2 arguments, not '+arguments.length+'.'); } if (typeof destination != 'object') { glow.debug.warn('[wrong type] glow.util.apply expects argument "destination" to be of type object, not ' + typeof destination + '.'); } if (typeof source != 'object') { glow.debug.warn('[wrong type] glow.util.apply expects argument "source" to be of type object, not ' + typeof source + '.'); } /*gubed!*/ for (var i in source) { if ( source.hasOwnProperty(i) ) { destination[i] = source[i]; } } return destination; }; /** @name glow.util.extend @function @description Copies the prototype of one object to another The 'subclass' can also access the 'base class' via subclass.base @param {Function} sub Class which inherits properties. @param {Function} base Class to inherit from. @param {Object} additionalProperties An object of properties and methods to add to the subclass. @example function MyClass(arg) { this.prop = arg; } MyClass.prototype.showProp = function() { alert(this.prop); }; function MyOtherClass(arg) { //call the base class's constructor arguments.callee.base.apply(this, arguments); } glow.util.extend(MyOtherClass, MyClass, { setProp: function(newProp) { this.prop = newProp; } }); var test = new MyOtherClass("hello"); test.showProp(); // alerts "hello" test.setProp("world"); test.showProp(); // alerts "world" */ util.extend = function(sub, base, additionalProperties) { /*!debug*/ if (arguments.length < 2) { glow.debug.warn('[wrong count] glow.util.extend expects at least 2 arguments, not '+arguments.length+'.'); } if (typeof sub != 'function') { glow.debug.error('[wrong type] glow.util.extend expects argument "sub" to be of type function, not ' + typeof sub + '.'); } if (typeof base != 'function') { glow.debug.error('[wrong type] glow.util.extend expects argument "base" to be of type function, not ' + typeof base + '.'); } /*gubed!*/ var f = function () {}, p; f.prototype = base.prototype; p = new f(); sub.prototype = p; p.constructor = sub; sub.base = base; if (additionalProperties) { util.apply(sub.prototype, additionalProperties); } }; // export glow.util = util; }); Glow.provide(function(glow) { /** @name glow.events @namespace @description Handling custom events */ var events = {}; /* storage variables */ var eventListeners = {}, eventId = 1, /* TODO: camelCase */ objIdCounter = 1, eventKey = '__eventId' + glow.UID; /** @name glow.events.addListeners @function @param {Object[]} attachTo Array of objects to add listeners to. @param {string} name Name of the event to listen for. Event names are case sensitive. @param {function} callback Function to call when the event is fired. The callback will be passed a single event object. The type of this object depends on the event (see documentation for the event you're listening to). @param {Object} [thisVal] Value of 'this' within the callback. By default, this is the object being listened to. @see glow.events.Target#fire @description Convenience method to add listeners to many objects at once. If you want to add a listener to a single object, use its 'on' method. */ events.addListeners = function (attachTo, name, callback, thisVal) { var listenerIds = [], objIdent, listener, eventsOnObject, currentListeners; //attach the event for each element, return an array of listener ids var i = attachTo.length; while (i--) { objIdent = attachTo[i][eventKey]; if (!objIdent){ objIdent = attachTo[i][eventKey] = objIdCounter++; } listener = [ callback, thisVal ]; eventsOnObject = eventListeners[objIdent]; if(!eventsOnObject){ eventsOnObject = eventListeners[objIdent] = {}; } currentListeners = eventsOnObject[name]; if(!currentListeners){ eventsOnObject[name] = [listener]; } else{ currentListeners[currentListeners.length] = listener; } } return events; }; events._getPrivateEventKey = function(node) { if (!node[eventKey]) { node[eventKey] = objid++; } return node[eventKey]; } /** @name glow.events.fire @function @param {Object[]} items Array of objects to add listeners to @param {string} eventName Name of the event to fire @param {glow.events.Event|Object} [event] Event object to pass into listeners. You can provide a simple object of key-value pairs which will be added as properties on the glow.events.Event instance. @description Convenience method to fire events on multiple items at once. If you want to fire events on a single object, use its 'fire' method. */ events.fire = function (items, eventName, event) { if (! event) { event = new events.Event(); } else if ( event.constructor === Object ) { event = new events.Event( event ) } // for loop, because order matters! for(var i = 0, len = items.length; i < len; i++) { callListeners(items[i], eventName, event); } return event; }; /** @name glow.events-callListeners @private */ function callListeners(item, eventName, event, thisVal) { var objIdent = item[eventKey], listenersForEvent, returnedVal; if (!objIdent || !eventListeners[objIdent]) { return event; } listenersForEvent = eventListeners[objIdent][eventName]; if (!listenersForEvent) { return event; } // Slice to make sure we get a unique copy. listenersForEvent = listenersForEvent.slice(0); for (var i = 0, len = listenersForEvent.length; i < len; i++){ returnVal = listenersForEvent[i][0].call((listenersForEvent[i][1] || thisVal || item), event); if (returnVal === false){ event.preventDefault(); } } return event; } events._callListeners = callListeners; /** @name glow.events.removeAllListeners @function @param {Object[]} items Items to remove events from @description Removes all listeners attached to a given object. This removes not only listeners you added, but listeners others added too. For this reason it should only be used as part of a cleanup operation on objects that are about to be destroyed. */ events.removeAllListeners = function (items) { var objIdent, i = items.length; while(i--){ objIdent = items[i][eventKey]; if (!objIdent) { return false; } else { delete eventListeners[objIdent]; } } return true; }; /** @name glow.events.removeListeners @function @param {Object[]} items Items to remove events from. @param {string} eventName Name of the event to remove. @param {function} callback A reference to the original callback used when the listener was added. @decription Removes listeners for an event. */ events.removeListeners = function (item, eventName, callback) { /* TODO: items! */ var objIdent, listenersForEvent, i = item.length; while(i--){ objIdent = item[i][eventKey]; if(!objIdent || !eventListeners[objIdent]){ return events; } listenersForEvent = eventListeners[objIdent][eventName]; if(!listenersForEvent){ return events; } // for loop, because order matters for(var j = 0, lenj = listenersForEvent.length; j < lenj; j++){ if (listenersForEvent[j][0] === callback){ listenersForEvent.splice(j, 1); break; } } } return events; }; /** Copies the events from one nodelist to another @private @name glow.events._copyEvent @see glow.NodeList#clone @function */ events._copyEvent = function(from, to){ var listenersToCopy, i = [from].length, listenersForEvent, name, callback, thisVal; while(i--){ var objIdent = [from][i][eventKey]; listenersForEvent = eventListeners[objIdent]; if(!objIdent){ return false; } else{ for ( var eventName in eventListeners[objIdent] ) { name = eventName; callback = eventListeners[objIdent][eventName][0][0]; thisVal = eventListeners[objIdent][eventName][0][1]; } events._addDomEventListener([to], name, callback, thisVal); } return; } } ///** //@name glow.events.getListeners //@function //@param {Object[]} item Item to find events for //@decription Returns a list of listeners attached for the given item. // //*/ //glow.events.getListeners = function(item){ // var objIdent; // for (var i = 0, len = item.length; i < len; i++) { // // objIdent = item[i][eventKey]; // // if (!objIdent) { // return false; // } // else { // // todo: need to return listeners in a sensible format // return eventListeners[objIdent]; // } // } // // // return false; //}; // ///** //@name glow.events.hasListener //@function //@param {Object[]} item Item to find events for //@param {String} eventName Name of the event to match //@decription Returns true if an event is found for the item supplied // //*/ // //glow.events.hasListener = function (item, eventName) { // var objIdent, // listenersForEvent; // // for (var i = 0, len = item.length; i < len; i++) { // objIdent = item[i][eventKey]; // // if (!objIdent || !eventListeners[objIdent]) { // return false; // } // // listenersForEvent = eventListeners[objIdent][eventName]; // if (!listenersForEvent) { // return false; // } // else { // return true; // } // } // // return false; //}; /** @name glow.events.Target @class @description An object that can have event listeners and fire events. Extend this class to make your own objects have 'on' and 'fire' methods. @example // Ball is our constructor function Ball() { // ... } // make Ball inherit from Target glow.util.extend(Ball, glow.events.Target, { // additional methods for Ball here, eg: bowl: function() { // ... } }); // now instances of Ball can receive event listeners var myBall = new Ball(); myBall.on('bounce', function() { alert('BOING!'); }); // and events can be fired from Ball instances myBall.fire('bounce'); */ events.Target = function () { }; var targetProto = events.Target.prototype; /** @name glow.events.Target.extend @function @param {Object} obj Object to add Target instance methods to. @description Convenience method to add Target instance methods onto an object. If you want to add Target methods to a class, extend glow.events.Target instead. @example var myApplication = {}; glow.events.Target.extend(myApplication); // now myApplication can fire events... myApplication.fire('load'); // and other objects can listen for those events myApplication.on('load', function(e) { alert('App loaded'); }); */ events.Target.extend = function (obj) { glow.util.apply( obj, glow.events.Target.prototype ); }; /** @name glow.events.Target#on @function @param {string} eventName Name of the event to listen for. @param {function} callback Function to call when the event fires. The callback is passed a single event object. The type of this object depends on the event (see documentation for the event you're listening to). @param {Object} [thisVal] Value of 'this' within the callback. By default, this is the object being listened to. @description Listen for an event @returns this @example myObj.on('show', function() { // do stuff }); */ targetProto.on = function(eventName, callback, thisVal) { glow.events.addListeners([this], eventName, callback, thisVal); return this; } /** @name glow.events.Target#detach @function @param {string} eventName Name of the event to remove. @param {function} callback Callback to detach. @param {Object} [thisVal] Value of 'this' within the callback. By default, this is the object being listened to. @description Remove an event listener. @returns this Target object @example function showListener() { // ... } // add listener myObj.on('show', showListener); // remove listener myObj.detach('show', showListener); @example // note the following WILL NOT WORK // add listener myObj.on('show', function() { alert('hi'); }); // remove listener myObj.detach('show', function() { alert('hi'); }); // this is because both callbacks are different function instances */ targetProto.detach = function(eventName, callback) { glow.events.removeListeners(this, eventName, callback); return this; } /** @name glow.events.Target#fire @function @param {string} eventName Name of the event to fire. @param {glow.events.Event|Object} [event] Event object to pass into listeners. You can provide a simple object of key-value pairs which will be added as properties of a glow.events.Event instance. @description Fire an event. @returns glow.events.Event @example myObj.fire('show'); @example // adding properties to the event object myBall.fire('bounce', { velocity: 30 }); @example // BallBounceEvent extends glow.events.Event but has extra methods myBall.fire( 'bounce', new BallBounceEvent(myBall) ); */ targetProto.fire = function(eventName, event) { return callListeners(this, eventName, event); } /** @name glow.events.Event @class @param {Object} [properties] Properties to add to the Event instance. Each key-value pair in the object will be added to the Event as properties. @description Describes an event that occurred. You don't need to create instances of this class if you're simply listening to events. One will be provided as the first argument in your callback. @example // creating a simple event object var event = new glow.events.Event({ velocity: 50, direction: 180 }); // 'velocity' and 'direction' are simple made-up properties // you may want to add to your event object @example // inheriting from glow.events.Event to make a more // specialised event object function RocketEvent() { // ... } // inherit from glow.events.Event glow.util.extend(RocketEvent, glow.events.Event, { getVector: function() { return // ... } }); // firing the event rocketInstance.fire( 'landingGearDown', new RocketEvent() ); // how a user would listen to the event rocketInstance.on('landingGearDown', function(rocketEvent) { var vector = rocketEvent.getVector(); }); */ events.Event = function(obj) { if (obj) { glow.util.apply(this, obj); } }; var eventProto = events.Event.prototype; /** @name glow.events.Event#attachedTo @type {Object} @description The object the listener was attached to. If null, this value will be populated by {@link glow.events.Target#fire} */ /** @name glow.events.Event#source @type Element @description The actual object/element that the event originated from. For example, you could attach a listener to an 'ol' element to listen for clicks. If the user clicked on an 'li' the source property would be the 'li' element, and 'attachedTo' would be the 'ol'. */ /** @name glow.events.Event#preventDefault @function @description Prevent the default action of the event. Eg, if the click event on a link is cancelled, the link is not followed. Returning false from an event listener has the same effect as calling this function. For custom events, it's down to whatever fired the event to decide what to do in this case. See {@link glow.events.Event#defaultPrevented defaultPrevented} @example myLinks.on('click', function(event) { event.preventDefault(); }); // same as... myLinks.on('click', function(event) { return false; }); */ eventProto.preventDefault = function () { this._defaultPrevented = true; }; /** @name glow.events.Event#defaultPrevented @function @description Has the default been prevented for this event? This should be used by whatever fires the event to determine if it should carry out of the default action. @returns {Boolean} Returns true if {@link glow.events.Event#preventDefault preventDefault} has been called for this event. @example // fire the 'show' event // read if the default action has been prevented if ( overlayInstance.fire('show').defaultPrevented() == false ) { // go ahead and show } */ eventProto.defaultPrevented = function () { return this._defaultPrevented; }; /* Export */ glow.events = events; }); Glow.provide(function(glow) { var document = window.document, undef = undefined, domEventHandlers = []; // like: domEventHandlers[uniqueId][eventName].count, domEventHandlers[uniqueId][eventName].callback /** @name glow.events.DomEvent @constructor @extends glow.events.Event @param {Event|string} nativeEvent A native browser event read properties from, or the name of a native event. @param {Object} [properties] Properties to add to the Event instance. Each key-value pair in the object will be added to the Event as properties @description Describes a DOM event that occurred You don't need to create instances of this class if you're simply listening to events. One will be provided as the first argument in your callback. */ function DomEvent(e, properties) { /** @name glow.events.DomEvent#nativeEvent @type {Event | MouseEvent | UIEvent} @description The native event object provided by the browser. */ this.nativeEvent = e; /** @name glow.events.DomEvent#type @type {string} @description The native type of the event, like 'click' or 'keydown'. */ this.type = e.type; /** @name glow.events.DomEvent#source @type {HTMLElement} @description The element that the event originated from. For example, you could attach a listener to an <ol> element to listen for clicks. If the user clicked on an <li> the source property would be the <li> element, and {@link glow.DomEvent#attachedTo attachedTo} would be the <ol>. */ if (e.target) { this.source = e.target; } // like FF else if (e.srcElement) { this.source = e.srcElement; } // like IE if (this.source && this.source.nodeType !== 1) { // like a textNode this.source = this.source.parentNode; } /** @name glow.events.DomEvent#related @type {HTMLElement} @description A related HTMLElement For mouseover / mouseenter events, this will refer to the previous element the mouse was over. For mouseout / mouseleave events, this will refer to the element the mouse is now over. */ this.related = e.relatedTarget || (this.type == 'mouseover' ? e.fromElement : e.toElement); /** @name glow.events.DomEvent#shiftKey @type {boolean | undefined} @description Was the shift key pressed during the event? */ this.shiftKey = (e.shiftKey === undef)? undef : !!e.shiftKey; /** @name glow.events.DomEvent#altKey @type {boolean | undefined} @description Was the alt key pressed during the event? */ this.altKey = (e.altKey === undef)? undef : !!e.altKey; /** @name glow.events.DomEvent#ctrlKey @type {boolean | undefined} @description Was the ctrl key pressed during the event? */ this.ctrlKey = (e.ctrlKey === undef)? undef : !!e.ctrlKey; /** @name glow.events.DomEvent#button @type {number | undefined} @description A number representing which button was pressed. 0 for the left button, 1 for the middle button or 2 for the right button. */ this.button = glow.env.ie ? (e.button & 1 ? 0 : e.button & 2 ? 2 : 1) : e.button; /** @name glow.events.DomEvent#mouseTop @type {number} @description The vertical position of the mouse pointer in the page in pixels. */ /** @name glow.events.DomEvent#mouseLeft @type {number} @description The horizontal position of the mouse pointer in the page in pixels. */ if (e.pageX !== undef || e.pageY !== undef) { this.mouseTop = e.pageY; this.mouseLeft = e.pageX; } else if (e.clientX !== undef || e.clientY !== undef) { this.mouseTop = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; this.mouseLeft = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; } /** @name glow.events.DomEvent#wheelData @type {number} @description The number of clicks the mouse wheel moved. Up values are positive, down values are negative. */ if (this.type == 'mousewheel') { // this works in latest opera, but have read that it needs to be switched in direction // if there was an opera bug, I can't find which version it was fixed in this.wheelDelta = e.wheelDelta ? e.wheelDelta / 120 : e.detail ? - e.detail / 3 : 0; } for (var key in properties) { this[key] = properties[key]; } } glow.util.extend(DomEvent, glow.events.Event); // DomEvent extends Event /** Add listener for an event fired by the browser. @private @name glow.events._addDomEventListener @see glow.NodeList#on @function */ glow.events._addDomEventListener = function(nodeList, eventName, callback, thisVal, selector) { var i = nodeList.length, // TODO: should we check that this nodeList is deduped? attachTo, id, eId = eventName + (selector? '/'+selector : ''); while (i-- && nodeList[i]) { attachTo = nodeList[i]; // will add a unique id to this node, if there is not one already glow.events.addListeners([attachTo], eventName, callback, thisVal); id = glow.events._getPrivateEventKey(attachTo); // check if there is already a handler for this kind of event attached // to this node (which will run all associated callbacks in Glow) if (!domEventHandlers[id]) { domEventHandlers[id] = {}; } if (domEventHandlers[id][eId] && domEventHandlers[id][eId].count > 0) { // already have handler in place domEventHandlers[id][eId].count++; continue; } // no bridge in place yet domEventHandlers[id][eId] = { callback: null, count:1 }; // attach a handler to tell Glow to run all the associated callbacks (function(attachTo) { var handler = domHandle(attachTo, eventName, selector); if (attachTo.addEventListener) { // like DOM2 browsers 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 } else if (attachTo.attachEvent) { // like IE if (eventName === 'focus') attachTo.attachEvent('onfocusin', handler); // see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html else if (eventName === 'blur') attachTo.attachEvent('onfocusout', handler); // cause that's how IE rolls... attachTo.attachEvent('on' + handler.domName, handler); } // older browsers? domEventHandlers[id][eId].callback = handler; })(attachTo); } } function domHandle(attachTo, eventName, selector) { var handler; if (eventName === 'mouseenter') { handler = function(nativeEvent, node) { var e = new glow.events.DomEvent(nativeEvent), container = node || attachTo; if (!new glow.NodeList(container).contains(e.related)) { var result = glow.events._callListeners(attachTo, eventName, e, node); // fire() returns result of callback if (typeof result === 'boolean') { return result; } else { return !e.defaultPrevented(); } } }; if (selector) { handler = delegate(attachTo, eventName, selector, handler); } handler.domName = 'mouseover'; } else if (eventName === 'mouseleave') { handler = function(nativeEvent, node) { var e = new glow.events.DomEvent(nativeEvent), container = node || attachTo; if (!new glow.NodeList(container).contains(e.related)) { var result = glow.events._callListeners(attachTo, eventName, e, node); // fire() returns result of callback if (typeof result === 'boolean') { return result; } else { return !e.defaultPrevented(); } } }; if (selector) { handler = delegate(attachTo, eventName, selector, handler); } handler.domName = 'mouseout'; } else { handler = function(nativeEvent, node) { var domEvent = new glow.events.DomEvent(nativeEvent); var result = glow.events._callListeners(attachTo, eventName, domEvent, node); // fire() returns result of callback if (typeof result === 'boolean') { return result; } else { return !domEvent.defaultPrevented(); } }; if (selector) { handler = delegate(attachTo, eventName, selector, handler); } handler.domName = eventName; } return handler; } // wraps a handler in code to detect delegation function delegate(attachTo, eventName, selector, handler) { return function(nativeEvent) { //console.log('dispatched, selector is: '+selector); var e = new glow.events.DomEvent(nativeEvent); node = e.source; // if the source matches the selector while (node) { if (!!glow._sizzle.matches(selector, [node]).length) { // the wrapped handler is called here, pass in the node that matched so it can be used as `this` return handler(nativeEvent, node); // } if (node === attachTo) { break; } // don't check parents above the attachTo node = node.parentNode; } }; } /** Remove listener for an event fired by the browser. @private @name glow.events._removeDomEventListener @see glow.NodeList#detach @function */ glow.events._removeDomEventListener = function(nodeList, eventName, callback, selector) { var i = nodeList.length, // TODO: should we check that this nodeList is deduped? attachTo, id, eId = eventName + (selector? '/'+selector : ''), bridge, handler; while (i-- && nodeList[i]) { attachTo = nodeList[i]; // skip if there is no bridge for this kind of event attached id = glow.events._getPrivateEventKey(attachTo); if (!domEventHandlers[id] && !domEventHandlers[id][eId]) { continue; } glow.events.removeListeners([attachTo], eventName, callback); bridge = domEventHandlers[id][eId] if (bridge.count > 0) { bridge.count--; // one less listener associated with this event if (bridge.count === 0) { // no more listeners associated with this event // detach bridge handler to tell Glow to run all the associated callbacks handler = bridge.callback; if (attachTo.removeEventListener) { // like DOM2 browsers 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 } else if (attachTo.detachEvent) { // like IE if (eventName === 'focus') attachTo.detachEvent('onfocusin', handler); // see: http://www.quirksmode.org/blog/archives/2008/04/delegating_the.html else if (eventName === 'blur') attachTo.detachEvent('onfocusout', handler); // cause that's how IE rolls... attachTo.detachEvent('on' + handler.domName, handler); } } } } } // see: http://developer.yahoo.com/yui/3/event/#eventsimulation // see: http://developer.yahoo.com/yui/docs/YAHOO.util.UserAction.html // function simulateDomEvent(nodeList, domEvent) { // var i = nodeList.length, // eventName = domEvent.type, // nativeEvent, // node, // fire; // // if (document.createEvent) { // var nativeEvent = document.createEvent('MouseEvent'); // see: // nativeEvent.initEvent(eventName, true, true); // // fire = function(el) { // return !el.dispatchEvent(nativeEvent); // } // } // else { // fire = function(el) { // var nativeEvent = document.createEventObject(); // return el.fireEvent('on'+eventName, nativeEvent); // } // } // // while (i--) { // node = nodeList[i]; // if (node.nodeType !== 1) { continue; } // fire(node); // } // } // export glow.events.DomEvent = DomEvent; }); Glow.provide(function(glow) { var document = window.document, undefined, keyboardEventProto, $env = glow.env, // the keyCode for the last keydown (returned to undefined on keyup) activeKey, // the charCode for the last keypress (returned to undefined on keyup & keydown) activeChar, DomEvent = glow.events.DomEvent, _callListeners = glow.events._callListeners, _getPrivateEventKey = glow.events._getPrivateEventKey, // object of event names & listeners, eg: // { // eventId: [ // 2, // the number of glow listeners added for this node // keydownListener, // keypressListener, // keyupListener // ] // } // This lets us remove these DOM listeners from the node when the glow listeners reaches zero eventKeysRegistered = {}; /** @name glow.events.KeyboardEvent @constructor @extends glow.events.DomEvent @param {Event} nativeEvent A native browser event read properties from @param {Object} [properties] Properties to add to the Event instance. Each key-value pair in the object will be added to the Event as properties @description Describes a keyboard event that occurred You don't need to create instances of this class if you're simply listening to events. One will be provided as the first argument in your callback. */ function KeyboardEvent(nativeEvent) { if (activeKey) { this.key = keyCodeToId(activeKey); } if (activeChar) { this.keyChar = String.fromCharCode(activeChar); } DomEvent.call(this, nativeEvent); } glow.util.extend(KeyboardEvent, DomEvent, { /** @name glow.events.KeyboardEvent#key @type {string} @description The key pressed This is a string representing the key pressed. Alphanumeric keys are represented by 0-9 and A-Z uppercase. Other safe cross-browser values are: <dl> <li>backspace</li> <li>tab</li> <li>return</li> <li>shift</li> <li>alt</li> <li>escape</li> <li>space</li> <li>pageup</li> <li>pagedown</li> <li>end</li> <li>home</li> <li>left</li> <li>up</li> <li>right</li> <li>down</li> <li>insert</li> <li>delete</li> <li>;</li> <li>=</li> <li>-</li> <li>f1</li> <li>f2</li> <li>f3</li> <li>f4</li> <li>f5</li> <li>f6</li> <li>f7</li> <li>f8</li> <li>f9</li> <li>f10</li> <li>f11</li> <li>f12</li> <li>numlock</li> <li>scrolllock</li> <li>pause</li> <li>,</li> <li>.</li> <li>/</li> <li>[</li> <li>\</li> <li>]</li> </dl> Some keys may trigger actions in your browser and operating system, some are not cancelable. @example glow(document).on('keypress', function(event) { switch (event.key) { case 'up': // do stuff break; case 'down': // do stuff break; } }); */ key: '', /** @name glow.events.KeyboardEvent#keyChar @type {string} @description The character entered. This is only available during 'keypress' events. If the user presses shift and 1, event.key will be "1", but event.keyChar will be "!". @example // only allow numbers to be entered into the ageInput field glow('#ageInput').on('keypress', function(event) { return !isNaN( Number(event.keyChar) ); }); */ keyChar: '' }); // add a dom listener function addListener(elm, name, callback) { if (elm.addEventListener) { // like DOM2 browsers elm.addEventListener(name, callback, false); } else if (elm.attachEvent) { // like IE elm.attachEvent('on' + name, callback); } } // remove a dom listener function removeListener(elm, name, callback) { if (elm.removeEventListener) { // like DOM2 browsers elm.removeEventListener(name, callback, false); } else if (elm.detachEvent) { // like IE elm.detachEvent('on' + name, callback); } } // takes a keyCode from a keydown listener and returns true if the browser will also fire a keypress function expectKeypress(keyCode, defaultPrevented) { var keyName; // for browsers that fire keypress for the majority of keys if ($env.gecko || $env.opera) { return !noKeyPress[keyCode]; } // for browsers that only fire keypress for printable chars keyName = keyCodeToId(keyCode); // is this a printable char? if (keyName.length === 1 && !noKeyPress[keyCode]) { // webkit doesn't fire keypress if the keydown has been prevented return !($env.webkit && defaultPrevented); } return false; } // Add the key listeners for firing glow's normalised key events. // returns an entry for eventKeysRegistered function addDomKeyListeners(attachTo) { var keydownHandler, keypressHandler, keyupHandler, // Even though the user may only be interested in one key event, we need all 3 listeners to normalise any of them // hash of which keys are down, keyed by keyCode keysDown = {}; keydownHandler = function(nativeEvent) { var keyCode = nativeEvent.keyCode, preventDefault, preventDefaultKeyPress; // some browsers repeat this event while a key is held down, we don't want to do that if ( !keysDown[keyCode] ) { activeKey = keyCode; activeChar = undefined; preventDefault = _callListeners( attachTo, 'keydown', new KeyboardEvent(nativeEvent) ).defaultPrevented(); keysDown[keyCode] = true; } // we want to fire a keyPress event here if the browser isn't going to fire one itself if ( !expectKeypress(keyCode, preventDefault) ) { preventDefaultKeyPress = _callListeners( attachTo, 'keypress', new KeyboardEvent(nativeEvent) ).defaultPrevented(); } // return false if either the keydown or fake keypress event was cancelled return !(preventDefault || preventDefaultKeyPress); }; keypressHandler = function(nativeEvent) { // some browsers store the charCode in .charCode, some in .keyCode activeChar = nativeEvent.charCode || nativeEvent.keyCode; // some browsers fire this event for non-printable chars, look at the previous keydown and see if we're expecting a printable char if ( keyCodeToId(activeKey).length > 1 ) { // non-printable chars have an ID length greater than 1 activeChar = undefined; } var preventDefault = _callListeners( attachTo, 'keypress', new KeyboardEvent(nativeEvent) ).defaultPrevented(); return !preventDefault; }; keyupHandler = function(nativeEvent) { var keyCode = nativeEvent.keyCode, preventDefault; activeKey = keyCode; activeChar = undefined; preventDefault = _callListeners( attachTo, 'keyup', new KeyboardEvent(nativeEvent) ).defaultPrevented(); keysDown[keyCode] = false; activeKey = undefined; return !preventDefault; }; // add listeners to the dom addListener(attachTo, 'keydown', keydownHandler); addListener(attachTo, 'keypress', keypressHandler); addListener(attachTo, 'keyup', keyupHandler); return [1, keydownHandler, keypressHandler, keyupHandler]; } /** @name glow.events._addKeyListener @private @function @description Add listener for a key event fired by the browser. @see glow.NodeList#on */ glow.events._addKeyListener = function(nodeList, name, callback, thisVal) { var i = nodeList.length, attachTo, eventKey; // will add a unique id to this node, if there is not one already glow.events.addListeners(nodeList, name, callback, thisVal); while (i--) { attachTo = nodeList[i]; // get the ID for this event eventKey = _getPrivateEventKey(attachTo); // if we've already attached DOM listeners for this, don't add them again if ( eventKeysRegistered[eventKey] ) { eventKeysRegistered[eventKey][0]++; continue; } else { eventKeysRegistered[eventKey] = addDomKeyListeners(attachTo); } } } /** Remove listener for an event fired by the browser. @private @name glow.events._removeKeyListener @see glow.NodeList#detach @function */ glow.events._removeKeyListener = function(nodeList, name, callback) { var i = nodeList.length, attachTo, eventKey, eventRegistry; // remove the glow events glow.events.removeListeners(nodeList, name, callback); while (i--) { attachTo = nodeList[i]; // get the ID for this event eventKey = _getPrivateEventKey(attachTo); eventRegistry = eventKeysRegistered[eventKey]; // exist if there are no key events registered for this node if ( !eventRegistry ) { return; } if ( --eventRegistry[0] === 0 ) { // our glow listener count is zero, we have no need for the dom listeners anymore removeListener( attachTo, 'keydown', eventRegistry[1] ); removeListener( attachTo, 'keypress', eventRegistry[2] ); removeListener( attachTo, 'keyup', eventRegistry[3] ); eventKeysRegistered[eventKey] = undefined; } } } // convert a keyCode to a string name for that key function keyCodeToId(keyCode) { // key codes for 0-9 A-Z are the same as their char codes if ( (keyCode >= keyCodeA && keyCode <= keyCodeZ) || (keyCode >= keyCode0 && keyCode <= keyCode9) ) { return String.fromCharCode(keyCode).toLowerCase(); } return keyIds[keyCode] || 'unknown' + keyCode; } // keyCode to key name translation var keyCodeA = 'A'.charCodeAt(0), keyCodeZ = 'Z'.charCodeAt(0), keyCode0 = '0'.charCodeAt(0), keyCode9 = '9'.charCodeAt(0), // key codes for non-alphanumeric keys keyIds = { 8: 'backspace', 9: 'tab', 13: 'return', 16: 'shift', 17: 'control', 18: 'alt', 19: 'pause', 27: 'escape', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down', 44: 'printscreen', // Only fires keyup in firefox, IE. Doesn't fire in webkit, opera. 45: 'insert', 46: 'delete', 59: ';', 61: '=', 91: 'meta', 93: 'menu', // no keycode in opera, doesn't fire in Chrome // these are number pad numbers, but Opera doesn't distinguish them from normal number keys so we normalise on that 96: '0', 97: '1', 98: '2', 99: '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9', 106: '*', // opera fires 2 keypress events 107: '+', // opera fires 2 keypress events 109: '-', // opera sees - as insert 110: '.', // opera sees this as n 111: '/', // end of numpad 112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5', 117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10', 122: 'f11', 123: 'f12', 144: 'numlock', 145: 'scrolllock', 188: ',', 189: '-', 190: '.', 191: '/', 192: "'", 219: '[', 220: '\\', 221: ']', 222: '#', // opera sees # key as 3. Pah. 223: '`', 224: 'meta', // same as [ in opera 226: '\\' // this key appears on a US layout in webkit windows }, noKeyPress = {}; // corrections for particular browsers :( if ($env.gecko) { keyIds[107] = '='; noKeyPress = { 16: 1, // shift 17: 1, // control 18: 1, // alt 144: 1, // numlock 145: 1 // scrolllock }; } else if ($env.opera) { keyIds[42] = '*'; keyIds[43] = '+'; keyIds[47] = '/'; keyIds[222] = "'"; keyIds[192] = '`'; noKeyPress = { 16: 1, // shift 17: 1, // control 18: 1 // alt }; } else if ($env.webkit || $env.ie) { keyIds[186] = ';'; keyIds[187] = '='; } // export glow.events.KeyboardEvent = KeyboardEvent; }); Glow.provide(function(glow) { var NodeListProto, undefined, // shortcuts to aid compression document = window.document, arraySlice = Array.prototype.slice, arrayPush = Array.prototype.push; /** @name glow.NodeList @constructor @description An array-like collection of DOM Nodes It is recommended to create a NodeList using the shortcut function {@link glow}. @param {string | glow.NodeList | Node | Node[] | Window} contents Items to populate the NodeList with. This parameter will be passed to {@link glow.NodeList#push}. Strings will be treated as CSS selectors unless they start with '<', in which case they'll be treated as an HTML string. @example // empty NodeList var myNodeList = glow(); @example // using glow to return a NodeList then chaining methods glow('p').addClass('eg').append('<div>Hello!</div>'); @example // creating an element from a string glow('<div>Hello!</div>').appendTo('body'); @see <a href="http://wiki.github.com/jeresig/sizzle/">Supported CSS selectors</a> */ function NodeList(contents) { // call push if we've been given stuff to add contents && this.push(contents); } NodeListProto = NodeList.prototype; /** @name glow.NodeList#length @type Number @description Number of nodes in the NodeList @example // get the number of paragraphs on the page glow('p').length; */ NodeListProto.length = 0; /** @name glow.NodeList._strToNodes @private @function @description Converts a string to an array of nodes @param {string} str HTML string @returns {Node[]} Array of nodes (including text / comment nodes) */ NodeList._strToNodes = (function() { var tmpDiv = document.createElement('div'), // these wraps are in the format [depth to children, opening html, closing html] tableWrap = [1, '<table>', '</table>'], emptyWrap = [0, '', ''], // webkit won't accept <link> elms to be the only child of an element, // it steals them and hides them in the head for some reason. Using // broken html fixes it for some reason paddingWrap = [1, 'b<div>', '</div>'], trWrap = [3, '<table><tbody><tr>', '</tr></tbody></table>'], wraps = { caption: tableWrap, thead: tableWrap, th: trWrap, colgroup: tableWrap, tbody: tableWrap, tr: [2, '<table><tbody>', '</tbody></table>'], td: trWrap, tfoot: tableWrap, option: [1, '<select multiple="multiple">', '</select>'], legend: [1, '<fieldset>', '</fieldset>'], link: paddingWrap, script: paddingWrap, style: paddingWrap, '!': paddingWrap }; function strToNodes(str) { var r = [], tagName = ( /^<([\w!]+)/.exec(str) || [] )[1], // This matches str content with potential elements that cannot // be a child of <div>. elmFilter declared at top of page. wrap = wraps[tagName] || emptyWrap, nodeDepth = wrap[0], childElm = tmpDiv, exceptTbody, rLen = 0, firstChild; // Create the new element using the node tree contents available in filteredElm. childElm.innerHTML = (wrap[1] + str + wrap[2]); // Strip newElement down to just the required elements' parent while(nodeDepth--) { childElm = childElm.lastChild; } // pull nodes out of child if (wrap === tableWrap && str.indexOf('<tbody') === -1) { // IE7 (and earlier) sometimes gives us a <tbody> even though we didn't ask for one while (firstChild = childElm.firstChild) { if (firstChild.nodeName != 'TBODY') { r[rLen++] = firstChild; } childElm.removeChild(firstChild); } } else { while (firstChild = childElm.firstChild) { r[rLen++] = childElm.removeChild(firstChild); } } return r; } return strToNodes; })(); // takes a collection and returns an array var collectionToArray = function(collection) { return arraySlice.call(collection, 0); }; try { // look out for an IE bug arraySlice.call( document.documentElement.childNodes, 0 ); } catch(e) { collectionToArray = function(collection) { // We can't use this trick on IE collections that are com-based, like HTMLCollections // Thankfully they don't have a constructor, so that's how we detect those if (collection instanceof Object) { return arraySlice.call(collection, 0); } var i = collection.length, arr = []; while (i--) { arr[i] = collection[i]; } return arr; } } /** @name glow.NodeList#push @function @description Adds nodes to the NodeList @param {string | Node | Node[] | glow.NodeList} nodes Node(s) to add to the NodeList Strings will be treated as CSS selectors or HTML strings. @returns {glow.NodeList} @example myNodeList.push('<div>Foo</div>').push('h1'); */ NodeListProto.push = function(nodes) { /*!debug*/ if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#push expects 1 argument, not '+arguments.length+'.'); } /*gubed!*/ if (nodes) { if (typeof nodes === 'string') { // if the string begins <, treat it as html, otherwise it's a selector if (nodes.charAt(0) === '<') { nodes = NodeList._strToNodes(nodes); } else { nodes = glow._sizzle(nodes) } arrayPush.apply(this, nodes); } else if ( nodes.nodeType || nodes.window == nodes ) { if (this.length) { arrayPush.call(this, nodes); } else { this[0] = nodes; this.length = 1; } } else if (nodes.length !== undefined) { if (nodes.constructor != Array) { // convert array-like objects into an array nodes = collectionToArray(nodes); } arrayPush.apply(this, nodes); } /*!debug*/ else { glow.debug.warn('[wrong type] glow.NodeList#push: Ignoring unexpected argument type, failing silently'); } /*gubed!*/ } /*!debug*/ else { glow.debug.warn('[wrong type] glow.NodeList#push: Ignoring false argument type, failing silently'); } /*gubed!*/ return this; }; /** @name glow.NodeList#eq @function @description Compares this NodeList to another Returns true if both NodeLists contain the same items in the same order @param {Node | Node[] | glow.NodeList} nodeList The NodeList to compare to. @returns {boolean} @see {@link glow.NodeList#is} for testing if a NodeList item matches a selector @example // the following returns true glow('#blah').eq( document.getElementById('blah') ); */ NodeListProto.eq = function(nodeList) { /*!debug*/ if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#eq expects 1 argument, not ' + arguments.length + '.'); } if (typeof nodeList !== 'object') { glow.debug.warn('[wrong type] glow.NodeList#eq expects object argument, not ' + typeof nodeList + '.'); } /*gubed!*/ var len = this.length, i = len; // normalise param to NodeList if ( !(nodeList instanceof NodeList) ) { nodeList = new NodeList(nodeList); } // quickly return false if lengths are different if (len != nodeList.length) { return false; } // loop through and return false on inequality while (i--) { if (this[i] !== nodeList[i]) { return false; } } return true; }; /** @name glow.NodeList#slice @function @description Get a section of an NodeList Operates in the same way as an Array's slice method @param {number} start Start index If negative, it specifies a position measured from the end of the list @param {number} [end] End index By default, this is the end of the list. A negative end specifies a position measured from the end of the list. @returns {glow.NodeList} A new sliced NodeList @example var myNodeList = glow("<div></div><p></p>"); myNodeList.slice(1, 2); // selects the paragraph myNodeList.slice(-1); // same thing, selects the paragraph */ NodeListProto.slice = function(/*start, end*/) { return new NodeList( arraySlice.apply(this, arguments) ); }; /** @name glow.NodeList#sort @function @description Sort the elements in the list. Items will already be in document order if a CSS selector was used to fetch them. @param {Function} [func] Function to determine sort order This function will be passed 2 elements (elementA, elementB). The function should return a number less than 0 to sort elementA lower than elementB and greater than 0 to sort elementA higher than elementB. If no function is provided, elements will be sorted in document order. @returns {glow.NodeList} A new sorted NodeList @example //get links in alphabetical (well, lexicographical) order var links = glow("a").sort(function(elementA, elementB) { return glow(elementA).text() < glow(elementB).text() ? -1 : 1; }) */ NodeListProto.sort = function(func) { var items = collectionToArray(this), sortedElms = func ? items.sort(func) : glow._sizzle.uniqueSort(items); return new NodeList(sortedElms); }; /** @name glow.NodeList#item @function @description Get a single item from the list as an NodeList Negative numbers can be used to get items from the end of the list. @param {number} index The numeric index of the node to return. @returns {glow.NodeList} A new NodeList containing a single item @example // get the html from the fourth element myNodeList.item(3).html(); @example // add a class name to the last item myNodeList.item(-1).addClass('last'); */ NodeListProto.item = function(index) { /*!debug*/ if ( arguments.length !== 1 ) { glow.debug.warn('[wrong count] glow.NodeList#item expects 1 argument, got ' + arguments.length); } /*gubed!*/ // TODO: test which of these methods is faster (use the current one unless significantly slower) return this.slice(index, (index + 1) || this.length); // return new NodeList( index < 0 ? this[this.length + index] : this[index] ); }; /** @name glow.NodeList#each @function @description Calls a function for each node in the list. @param {Function} callback The function to call for each node. The function will be passed 2 arguments, the index of the current item, and the NodeList being iterated over. Inside the function 'this' refers to the Node. Returning false from this function stops further iterations @returns {glow.NodeList} @example // add "link number: x" to each link, where x is the index of the link glow("a").each(function(i, nodeList) { glow(this).append(' link number: ' + i); }); @example // breaking out of an each loop glow("a").each(function(i, nodeList) { // do stuff if ( glow(this).hasClass('whatever') ) { // we don't want to process any more links return false; } }); */ NodeListProto.each = function(callback) { /*!debug*/ if ( arguments.length !== 1 ) { glow.debug.warn('[wrong count] glow.NodeList#each expects 1 argument, got ' + arguments.length); } if (typeof callback != 'function') { glow.debug.warn('[wrong type] glow.NodeList#each expects "function", got ' + typeof callback); } /*gubed!*/ for (var i = 0, len = this.length; i<len; i++) { if ( callback.call(this[i], i, this) === false ) { break; } } return this; }; /** @name glow.NodeList#filter @function @description Filter the NodeList @param {Function|string} test Filter test If a string is provided it's treated as a CSS selector. Elements which match the CSS selector are added to the new NodeList. If 'test' is a function, it will be called per node in the NodeList. The function is passed 2 arguments, the index of the current item, and the ElementList being itterated over. Inside the function 'this' refers to the node. Return true to add the element to the new NodeList. @returns {glow.NodeList} A new NodeList containing the filtered nodes @example // return images with a width greater than 320 glow("img").filter(function () { return glow(this).width() > 320; }); @example // Get items that don't have an alt attribute myElementList.filter(':not([alt])'); */ NodeListProto.filter = function(test) { /*!debug*/ if ( arguments.length !== 1 ) { glow.debug.warn('[wrong count] glow.NodeList#filter expects 1 argument, got ' + arguments.length); } if ( !/^(function|string)$/.test(typeof test) ) { glow.debug.warn('[wrong type] glow.NodeList#each expects function/string, got ' + typeof test); } /*gubed!*/ var r = [], ri = 0; if (typeof test === 'string') { r = glow._sizzle.matches(test, this); } else { for (var i = 0, len = this.length; i<len; i++) { if ( test.call(this[i], i, this) ) { r[ri++] = this[i]; } } } return new NodeList(r); }; /** @name glow.NodeList#is @function @description Tests if the first element matches a CSS selector @param {string} selector CSS selector @returns {boolean} @example if ( myNodeList.is(':visible') ) { // ... } */ NodeListProto.is = function(selector) { /*!debug*/ if ( arguments.length !== 1 ) { glow.debug.warn('[wrong count] glow.NodeList#is expects 1 argument, got ' + arguments.length); } if ( typeof selector !== 'string' ) { glow.debug.warn('[wrong type] glow.NodeList#is expects string, got ' + typeof selector); } /*gubed!*/ if ( !this[0] ) { return false; } return !!glow._sizzle.matches( selector, [ this[0] ] ).length; }; // export glow.NodeList = NodeList; }); Glow.provide(function(glow) { var undef , NodeListProto = glow.NodeList.prototype /** @private @name glow.NodeList-dom0PropertyMapping @description Mapping of HTML attribute names to DOM0 property names. */ , dom0PropertyMapping = { // keys must be lowercase 'class' : 'className', 'for' : 'htmlFor', 'maxlength' : 'maxLength' } /** @private @name glow.NodeList-dataPropName @type String @description The property name added to the DomElement by the NodeList#data method. */ , dataPropName = '_uniqueData' + glow.UID /** @private @name glow.NodeList-dataIndex @type String @description The value of the dataPropName added by the NodeList#data method. */ , dataIndex = 1 // must be a truthy value /** @private @name glow.NodeList-dataCache @type Object @description Holds the data used by the NodeList#data method. The structure is like: [ { myKey: "my data" } ] */ , dataCache = []; /** @name glow.NodeList#addClass @function @description Adds a class to each node. @param {string} name The name of the class to add. @returns {glow.NodeList} @example glow("#login a").addClass("highlight"); */ NodeListProto.addClass = function(name) { var i = this.length; /*!debug*/ if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#addClass expects 1 argument, not '+arguments.length+'.'); } else if (typeof arguments[0] !== 'string') { glow.debug.warn('[wrong type] glow.NodeList#addClass expects argument 1 to be of type string, not '+typeof arguments[0]+'.'); } /*gubed!*/ while (i--) { if (this[i].nodeType === 1) { _addClass(this[i], name); } } return this; }; function _addClass(node, name) { // TODO: handle classnames separated by non-space characters? if ( (' ' + node.className + ' ').indexOf(' ' + name + ' ') === -1 ) { node.className += (node.className? ' ' : '') + name; } } /** @name glow.NodeList#attr @function @description Gets or sets attributes. When getting an attribute, it is retrieved from the first node in this NodeList. Setting attributes applies the change to each element in this NodeList. To set an attribute, pass in the name as the first parameter and the value as a second parameter. To set multiple attributes in one call, pass in an object of name/value pairs as a single parameter. For browsers that don't support manipulating attributes using the DOM, this method will try to do the right thing (i.e. don't expect the semantics of this method to be consistent across browsers as this is not possible with currently supported browsers). @param {string | Object} name The name of the attribute, or an object of name/value pairs @param {string} [value] The value to set the attribute to. @returns {string | undefined | glow.NodeList} When setting attributes this method returns its own NodeList, otherwise returns the attribute value. The attribute name is always treated as case-insensitive. When getting, the returned value will be of type string unless that particular attribute was never set and there is no default value, in which case the returned value will be an empty string. @example var myNodeList = glow(".myImgClass"); // get an attribute myNodeList.attr("class"); // set an attribute myNodeList.attr("class", "anotherImgClass"); // set multiple attributes myNodeList.attr({ src: "a.png", alt: "Cat jumping through a field" }); */ // see: http://tobielangel.com/2007/1/11/attribute-nightmare-in-ie/ NodeListProto.attr = function(/*arguments*/) { var args = arguments, argsLen = args.length, thisLen = this.length, name = keyvals = args[0], // using this API: attr(name) or attr({key: val}) ? dom0Property = '', node, attrNode; /*!debug*/ 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]+'.'); } 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.'); } else if (arguments.length === 0 || arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#attr expects 1 or 2 arguments, not '+arguments.length+'.'); } /*gubed!*/ if (this.length === 0) { // is this an empty nodelist? return (argsLen > 1)? this : undef; } if (typeof keyvals === 'object') { // SETting value from {name: value} object for (name in keyvals) { if (!keyvals.hasOwnProperty(name)) { continue; } // in IE6 and IE7 the attribute name needs to be translated into dom property name if (glow.env.ie < 8) { dom0Property = dom0PropertyMapping[name.toLowerCase()]; } var i = thisLen; while (i--) { node = this[i]; if (node.nodeType !== 1) { continue; } if (dom0Property) { node[dom0Property] = keyvals[name]; } else { node.setAttribute(name, keyvals[name], 0); // IE flags, 0: case-insensitive } } } return this; } else { node = this[0]; if (node.nodeType !== 1) { return (argsLen > 1)? this : undef; } if (argsLen === 1) { // GETting value from name if (node.attributes[name]) { // in IE node.getAttributeNode sometimes returns unspecified default values so we look for specified attributes if we can return (!node.attributes[name].specified)? '' : node.attributes[name].value; } else if (node.getAttributeNode) { // in IE getAttribute() does not always work so we use getAttributeNode if we can attrNode = node.getAttributeNode(name, 0); return (attrNode === null)? '' : attrNode.value; } else { value = node.getAttribute(name, 0, 2); // IE flags, 0: case-insensitive, 2: as string return (value === null)? '' : value; } } else { // SETting a single value like attr(name, value), normalize to an keyval object if (glow.env.ie < 8) { dom0Property = dom0PropertyMapping[name.toLowerCase()]; } if (dom0Property) { node[dom0Property] = args[1]; } else { node.setAttribute(name, args[1], 0); // IE flags, 0: case-insensitive } return this; } } }; /** Copies the data from one nodelist to another @private @name glow.NodeList._copyData @see glow.NodeList#clone @function */ glow.NodeList._copyData = function(from, to){ if ( !from[dataPropName] ){ return; } else{ to = new glow.NodeList(to); to.data( dataCache[from[dataPropName]] ); return; } } /** Used to remove the data when a node is destroyed @private @name glow.NodeList._copyData @see glow.NodeList#destroy @function */ glow.NodeList._destroyData = function(removeFrom){ if ( !removeFrom && !removeFrom[0][dataPropName] ){ return; } else{ removeFromNode = new glow.NodeList(removeFrom); removeFromNode.removeData(); return; } } /** @name glow.NodeList#data @function @description Use this to safely attach arbitrary data to any DOM Element. This method is useful when you wish to avoid memory leaks that are possible when adding your own data directly to DOM Elements. When called with no arguments, will return glow's entire data store for the first node in this NodeList. Otherwise, when given a name, will return the associated value from the first node in this NodeList. When given both a name and a value, will store that data on every node in this NodeList. Optionally you can pass in a single object composed of multiple name, value pairs. @param {string|Object} [key] The name of the value in glow's data store. @param {Object} [val] The value you wish to associate with the given name. @see glow.NodeList#removeData @example glow("p").data("tea", "milky"); var colour = glow("p").data("tea"); // milky @returns {Object} When setting a value this method can be chained, as in that case it will return itself. @see glow.NodeList#removeData */ NodeListProto.data = function (key, val) { /*debug*///console.log("data("+key+", "+val+")"); var args = arguments, argsLen = args.length, keyvals = key, // like: data({key: val}) or data(key, val) index, node; /*!debug*/ if (arguments.length === 2 && typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#data expects name argument to be of type string.'); } 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.'); } else if (arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#data expects 0, 1 or 2 arguments.'); } /*gubed!*/ if (argsLen > 1) { // SET key, val on every node var i = this.length; while (i--) { node = this[i]; if (node.nodeType !== 1) { continue; } index = node[dataPropName]; if (!index) { // assumes index is always > 0 index = dataIndex++; node[dataPropName] = index; dataCache[index] = {}; } dataCache[index][key] = val; } return this; // chainable with (key, val) signature } else if (typeof keyvals === 'object') { // SET keyvals on every node var i = this.length; while (i--) { node = this[i]; if (node.nodeType !== 1) { continue; } index = node[dataPropName]; if (!index) { // assumes index is always > 0 index = dataIndex++; node[dataPropName] = index; dataCache[index] = {}; } for (key in keyvals) { dataCache[index][key] = keyvals[key]; } } return this; // chainable with ({key, val}) signature } else { // GET from first node node = this[0]; if (node === undef || node.nodeType !== 1) { return undef; } if ( !(index = node[dataPropName]) ) { return undef; } if (key) { return dataCache[index][key]; } // get the entire data cache object for this node return dataCache[index]; } }; /** @name glow.NodeList#hasAttr @function @description Does the node have a particular attribute? The first node in this NodeList is tested. @param {string} name The name of the attribute to test for. @returns {boolean|undefined} Returns undefined if the first node is not an element, or if the NodeList is empty, otherwise returns true/false to indicate if that attribute exists on the first element. @example if ( glow("#myImg").hasAttr("alt") ){ // ... } */ NodeListProto.hasAttr = function(name) { var node; /*!debug*/ if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#hasAttr expects 1 argument.'); } else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#hasAttr expects argument 1 to be of type string.'); } /*gubed!*/ node = this[0]; if (this.length && node.nodeType === 1) { if (node.attributes[name]) { // is an object in IE, or else: undefined in IE < 8, null in IE 8 return !!node.attributes[name].specified; } if (node.hasAttribute) { return node.hasAttribute(name); } // like FF, Safari, etc else { return node.attributes[name] !== undef; } // like IE7 } }; /** @name glow.NodeList#hasClass @function @description Does the node have a particular class? The first node in this NodeList is tested. @param {string} name The name of the class to test for. @returns {boolean} @example if ( glow("#myInput").hasClass("errored") ){ // ... } */ NodeListProto.hasClass = function (name) { /*!debug*/ if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#hasClass expects 1 argument.'); } else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#hasClass expects argument 1 to be of type string.'); } /*gubed!*/ if (this.length && this[0].nodeType === 1) { return ( (' ' + this[0].className + ' ').indexOf(' ' + name + ' ') > -1 ); } }; /** @name glow.NodeList#prop @function @description Gets or sets node properties. This function gets / sets node properties, to get attributes, see {@link glow.NodeList#attr NodeList#attr}. When getting a property, it is retrieved from the first node in this NodeList. Setting properties to each element in this NodeList. To set multiple properties in one call, pass in an object of name/value pairs. @param {string | Object} name The name of the property, or an object of name/value pairs @param {string} [value] The value to set the property to. @returns {string | glow.NodeList} When setting properties it returns the NodeList, otherwise returns the property value. @example var myNodeList = glow("#formElement"); // get the node name myNodeList.prop("nodeName"); // set a property myNodeList.prop("_secretValue", 10); // set multiple properties myNodeList.prop({ checked: true, _secretValue: 10 }); */ NodeListProto.prop = function(name, val) { var hash = name, argsLen = arguments.length; /*!debug*/ 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.'); } else if (arguments.length === 2 && typeof name !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#prop expects name to be of type string.'); } else if (arguments.length === 0 || arguments.length > 2) { glow.debug.warn('[wrong count] glow.NodeList#prop expects 1 or 2 arguments.'); } /*gubed!*/ if (this.length === 0) return; if (argsLen === 2 && typeof name === 'string') { for (var i = 0, ilen = this.length; i < ilen; i++) { if (this[i].nodeType === 1) { this[i][name] = val; } } return this; } else if (argsLen === 1 && hash.constructor === Object) { for (var key in hash) { for (var i = 0, ilen = this.length; i < ilen; i++) { if (this[i].nodeType === 1) { this[i][key] = hash[key]; } } } return this; } else if (argsLen === 1 && typeof name === 'string') { if (this[0].nodeType === 1) { return this[0][name]; } } else { throw new Error('Invalid parameters.'); } }; /** @name glow.NodeList#removeAttr @function @description Removes an attribute from each node. @param {string} name The name of the attribute to remove. @returns {glow.NodeList} @example glow("a").removeAttr("target"); */ NodeListProto.removeAttr = function (name) { var dom0Property; /*!debug*/ if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#removeAttr expects 1 argument.'); } else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeAttr expects argument 1 to be of type string.'); } /*gubed!*/ for (var i = 0, leni = this.length; i < leni; i++) { if (this[i].nodeType === 1) { if (glow.env.ie < 8) { if ( (dom0Property = dom0PropertyMapping[name.toLowerCase()]) ) { this[i][dom0Property] = ''; } } if (this[i].removeAttribute) this[i].removeAttribute(name); } } return this; }; /** @name glow.NodeList#removeClass @function @description Removes a class from each node. @param {string} name The name of the class to remove. @returns {glow.NodeList} @example glow("#footer #login a").removeClass("highlight"); */ NodeListProto.removeClass = function(name) { var node; /*!debug*/ if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#removeClass() expects 1 argument.'); } else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#removeClass() expects argument 1 to be of type string.'); } /*gubed!*/ var i = this.length; while (i--) { node = this[i]; if (node.className) { _removeClass(node, name); } } return this; }; function _removeClass(node, name) { var oldClasses = node.className.split(' '), newClasses = []; oldClasses = node.className.split(' '); newClasses = []; var i = oldClasses.length; while (i--) { if (oldClasses[i] !== name) { oldClasses[i] && newClasses.unshift(oldClasses[i]); // unshift to maintain original order } } node.className = (newClasses.length)? newClasses.join(' ') : ''; } /** @name glow.NodeList#removeData @function @description Removes data previously added by {@link glow.NodeList#data} from each node in this NodeList. When called with no arguments, will delete glow's entire data store for each node in this NodeList. Otherwise, when given a name, will delete the associated value from each node in this NodeList. @param {string} [key] The name of the value in glow's data store. @see glow.NodeList#data */ NodeListProto.removeData = function(key) { var elm, i = this.length, index; // uses private scoped variables: dataCache, dataPropName /*!debug*/ if (arguments.length > 1) { glow.debug.warn('[wrong count] glow.NodeList#removeData expects 0 or 1 arguments.'); } 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.'); } /*gubed!*/ while (i--) { elm = this[i]; index = elm[dataPropName]; if (index !== undef) { switch (arguments.length) { case 0: dataCache[index] = undef; elm[dataPropName] = undef; try { delete elm[dataPropName]; // IE 6 goes wobbly here } catch(e) { // remove expando from IE 6 elm.removeAttribute && elm.removeAttribute(dataPropName); } break; case 1: dataCache[index][key] = undef; delete dataCache[index][key]; break; } } } return this; // chainable }; /** @name glow.NodeList#toggleClass @function @description Toggles a class on each node. @param {string} name The name of the class to toggle. @returns {glow.NodeList} @example glow(".onOffSwitch").toggleClass("on"); */ NodeListProto.toggleClass = function(name) { var node; /*!debug*/ if (arguments.length !== 1) { glow.debug.warn('[wrong count] glow.NodeList#toggleClass() expects 1 argument.'); } else if (typeof arguments[0] !== 'string') {glow.debug.warn('[wrong type] glow.NodeList#toggleClass() expects argument 1 to be of type string.'); } /*gubed!*/ for (var i = 0, leni = this.length; i < leni; i++) { node = this[i]; if (node.className) { if ( (' ' + node.className + ' ').indexOf(' ' + name + ' ') > -1 ) { _removeClass(node, name); } else { _addClass(node, name); } } } return this; }; /** @name glow.NodeList#val @function @description Gets or sets form values for the first node. <p><em>This method is not applicable to XML NodeLists.</em></p> <p><em>Getting values from form elements</em></p> The returned value depends on the type of element, see below: <dl> <dt>Radio button or checkbox</dt> <dd>If checked, then the contents of the value attribute, otherwise an empty string.</dd> <dt>Select</dt> <dd>The contents of value attribute of the selected option</dd> <dt>Select (multiple)</dt> <dd>An array of selected option values.</dd> <dt>Other form element</dt> <dd>The value of the input.</dd> </dl> <p><em>Getting values from a form</em></p> If the first element in the NodeList is a form, then an object is returned containing the form data. Each item property of the object is a value as above, apart from when multiple elements of the same name exist, in which case the it will contain an array of values. <p><em>Setting values for form elements</em></p> If a value is passed and the first element of the NodeList is a form element, then the form element is given that value. For select elements, this means that the first option that matches the value will be selected. For selects that allow multiple selection, the options which have a value that exists in the array of values/match the value will be selected and others will be deselected. Currently checkboxes and radio buttons are not checked or unchecked, just their value is changed. This does mean that this does not do exactly the reverse of getting the value from the element (see above) and as such may be subject to change <p><em>Setting values for forms</em></p> If the first element in the NodeList is a form and the value is an object, then each element of the form has its value set to the corresponding property of the object, using the method described above. @param {String | Object} [value] The value to set the form element/elements to. @returns {glow.dom.NodeList | String | Object} When used to set a value it returns the NodeList, otherwise returns the value as described above. @example // get a value var username = glow.dom.get("input#username").val(); @example / get values from a form var userDetails = glow.dom.get("form").val(); @example // set a value glow.dom.get("input#username").val("example username"); @example // set values in a form glow.dom.get("form").val({ username : "another", name : "A N Other" }); */ NodeListProto.val = function(){ /* PrivateFunction: elementValue Get a value for a form element. */ function elementValue (el) { var elType = el.type, elChecked = el.checked, elValue = el.value, vals = [], i = 0; if (elType == "radio") { return elChecked ? elValue : ""; } else if (elType == "checkbox") { return elChecked ? elValue : ""; } else if (elType == "select-one") { return el.selectedIndex > -1 ? el.options[el.selectedIndex].value : ""; } else if (elType == "select-multiple") { for (var length = el.options.length; i < length; i++) { if (el.options[i].selected) { vals[vals.length] = el.options[i].value; } } return vals; } else { return elValue; } } /* PrivateMethod: formValues Get an object containing form data. */ function formValues (form) { var vals = {}, radios = {}, formElements = form.elements, i = 0, length = formElements.length, name, formElement, j, radio, nodeName; for (; i < length; i++) { formElement = formElements[i]; nodeName = formElement.nodeName.toLowerCase(); name = formElement.name; // fieldsets & objects come back as form elements, but we don't care about these // we don't bother with fields that don't have a name // switch to whitelist? if ( nodeName == "fieldset" || nodeName == "object" || !name ) { continue; } if (formElement.type == "checkbox" && ! formElement.checked) { if (! name in vals) { vals[name] = undefined; } } else if (formElement.type == "radio") { if (radios[name]) { radios[name][radios[name].length] = formElement; } else { radios[name] = [formElement]; } } else { var value = elementValue(formElement); if (name in vals) { if (vals[name].push) { vals[name][vals[name].length] = value; } else { vals[name] = [vals[name], value]; } } else { vals[name] = value; } } } for (i in radios) { j = 0; for (length = radios[i].length; j < length; j++) { radio = radios[i][j]; name = radio.name; if (radio.checked) { vals[radio.name] = radio.value; break; } } if (! name in vals) { vals[name] = undefined; } } return vals; } /* PrivateFunction: setFormValues Set values of a form to those in passed in object. */ function setFormValues (form, vals) { var prop, currentField, fields = {}, storeType, i = 0, n, len, foundOne, currentFieldType; for (prop in vals) { currentField = form[prop]; if (currentField && currentField[0] && !currentField.options) { // is array of fields //normalise values to array of vals vals[prop] = vals[prop] && vals[prop].push ? vals[prop] : [vals[prop]]; //order the fields by types that matter fields.radios = []; fields.checkboxesSelects = []; fields.multiSelects = []; fields.other = []; for (i = 0; currentField[i]; i++) { currentFieldType = currentField[i].type; if (currentFieldType == "radio") { storeType = "radios"; } else if (currentFieldType == "select-one" || currentFieldType == "checkbox") { storeType = "checkboxesSelects"; } else if (currentFieldType == "select-multiple") { storeType = "multiSelects"; } else { storeType = "other"; } //add it to the correct array fields[storeType][fields[storeType].length] = currentField[i]; } for (i = 0; fields.multiSelects[i]; i++) { vals[prop] = setValue(fields.multiSelects[i], vals[prop]); } for (i = 0; fields.checkboxesSelects[i]; i++) { setValue(fields.checkboxesSelects[i], ""); for (n = 0, len = vals[prop].length; n < len; n++) { if (setValue(fields.checkboxesSelects[i], vals[prop][n])) { vals[prop].slice(n, 1); break; } } } for (i = 0; fields.radios[i]; i++) { fields.radios[i].checked = false; foundOne = false; for (n = 0, len = vals[prop].length; n < len; n++) { if (setValue(fields.radios[i], vals[prop][n])) { vals[prop].slice(n, 1); foundOne = true; break; } if (foundOne) { break; } } } for (i = 0; fields.other[i] && vals[prop][i] !== undefined; i++) { setValue(fields.other[i], vals[prop][i]); } } else if (currentField && currentField.nodeName) { // is single field, easy setValue(currentField, vals[prop]); } } } /* PrivateFunction: setValue 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 */ function setValue (el, val) { var i = 0, length, n = 0, nlen, elOption, optionVal; if (el.type == "select-one") { for (length = el.options.length; i < length; i++) { if (el.options[i].value == val) { el.selectedIndex = i; return true; } } return false; } else if (el.type == "select-multiple") { var isArray = !!val.push; for (i = 0, length = el.options.length; i < length; i++) { elOption = el.options[i]; optionVal = elOption.value; if (isArray) { elOption.selected = false; for (nlen = val.length; n < nlen; n++) { if (optionVal == val[n]) { elOption.selected = true; val.splice(n, 1); break; } } } else { return elOption.selected = val == optionVal; } } return false; } else if (el.type == "radio" || el.type == "checkbox") { el.checked = val == el.value; return val == el.value; } else { el.value = val; return true; } } // toplevel implementation var args = arguments, val = args[0], that = this, i = 0, length = that.length; if (args.length === 0) { return that[0].nodeName == 'FORM' ? formValues(that[0]) : elementValue(that[0]); } if (that[0].nodeName == 'FORM') { if (! typeof val == 'object') { throw 'value for FORM must be object'; } setFormValues(that[0], val); } else { for (; i < length; i++) { setValue(that[i], val); } } return that; }; }); /*! * Sizzle CSS Selector Engine - v1.0 * Copyright 2009, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ (function(){ var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, done = 0, toString = Object.prototype.toString, hasDuplicate = false, baseHasDuplicate = true; // Here we check if the JavaScript engine is using some sort of // optimization where it does not always call our comparision // function. If that is the case, discard the hasDuplicate value. // Thus far that includes Google Chrome. [0, 0].sort(function(){ baseHasDuplicate = false; return 0; }); var Sizzle = function(selector, context, results, seed) { results = results || []; var origContext = context = context || document; if ( context.nodeType !== 1 && context.nodeType !== 9 ) { return []; } if ( !selector || typeof selector !== "string" ) { return results; } var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context), soFar = selector; // Reset the position of the chunker regexp (start from head) while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { soFar = m[3]; parts.push( m[1] ); if ( m[2] ) { extra = m[3]; break; } } if ( parts.length > 1 && origPOS.exec( selector ) ) { if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { set = posProcess( parts[0] + parts[1], context ); } else { set = Expr.relative[ parts[0] ] ? [ context ] : Sizzle( parts.shift(), context ); while ( parts.length ) { selector = parts.shift(); if ( Expr.relative[ selector ] ) { selector += parts.shift(); } set = posProcess( selector, set ); } } } else { // Take a shortcut and set the context if the root selector is an ID // (but not if it'll be faster if the inner selector is an ID) if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { var ret = Sizzle.find( parts.shift(), context, contextXML ); context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; } if ( context ) { var ret = seed ? { expr: parts.pop(), set: makeArray(seed) } : Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; if ( parts.length > 0 ) { checkSet = makeArray(set); } else { prune = false; } while ( parts.length ) { var cur = parts.pop(), pop = cur; if ( !Expr.relative[ cur ] ) { cur = ""; } else { pop = parts.pop(); } if ( pop == null ) { pop = context; } Expr.relative[ cur ]( checkSet, pop, contextXML ); } } else { checkSet = parts = []; } } if ( !checkSet ) { checkSet = set; } if ( !checkSet ) { throw "Syntax error, unrecognized expression: " + (cur || selector); } if ( toString.call(checkSet) === "[object Array]" ) { if ( !prune ) { results.push.apply( results, checkSet ); } else if ( context && context.nodeType === 1 ) { for ( var i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { results.push( set[i] ); } } } else { for ( var i = 0; checkSet[i] != null; i++ ) { if ( checkSet[i] && checkSet[i].nodeType === 1 ) { results.push( set[i] ); } } } } else { makeArray( checkSet, results ); } if ( extra ) { Sizzle( extra, origContext, results, seed ); Sizzle.uniqueSort( results ); } return results; }; Sizzle.uniqueSort = function(results){ if ( sortOrder ) { hasDuplicate = baseHasDuplicate; results.sort(sortOrder); if ( hasDuplicate ) { for ( var i = 1; i < results.length; i++ ) { if ( results[i] === results[i-1] ) { results.splice(i--, 1); } } } } return results; }; Sizzle.matches = function(expr, set){ return Sizzle(expr, null, null, set); }; Sizzle.find = function(expr, context, isXML){ var set, match; if ( !expr ) { return []; } for ( var i = 0, l = Expr.order.length; i < l; i++ ) { var type = Expr.order[i], match; if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { var left = match[1]; match.splice(1,1); if ( left.substr( left.length - 1 ) !== "\\" ) { match[1] = (match[1] || "").replace(/\\/g, ""); set = Expr.find[ type ]( match, context, isXML ); if ( set != null ) { expr = expr.replace( Expr.match[ type ], "" ); break; } } } } if ( !set ) { set = context.getElementsByTagName("*"); } return {set: set, expr: expr}; }; Sizzle.filter = function(expr, set, inplace, not){ var old = expr, result = [], curLoop = set, match, anyFound, isXMLFilter = set && set[0] && isXML(set[0]); while ( expr && set.length ) { for ( var type in Expr.filter ) { if ( (match = Expr.match[ type ].exec( expr )) != null ) { var filter = Expr.filter[ type ], found, item; anyFound = false; if ( curLoop === result ) { result = []; } if ( Expr.preFilter[ type ] ) { match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); if ( !match ) { anyFound = found = true; } else if ( match === true ) { continue; } } if ( match ) { for ( var i = 0; (item = curLoop[i]) != null; i++ ) { if ( item ) { found = filter( item, match, i, curLoop ); var pass = not ^ !!found; if ( inplace && found != null ) { if ( pass ) { anyFound = true; } else { curLoop[i] = false; } } else if ( pass ) { result.push( item ); anyFound = true; } } } } if ( found !== undefined ) { if ( !inplace ) { curLoop = result; } expr = expr.replace( Expr.match[ type ], "" ); if ( !anyFound ) { return []; } break; } } } // Improper expression if ( expr === old ) { if ( anyFound == null ) { throw "Syntax error, unrecognized expression: " + expr; } else { break; } } old = expr; } return curLoop; }; var Expr = Sizzle.selectors = { order: [ "ID", "NAME", "TAG" ], match: { ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ }, leftMatch: {}, attrMap: { "class": "className", "for": "htmlFor" }, attrHandle: { href: function(elem){ return elem.getAttribute("href"); } }, relative: { "+": function(checkSet, part){ var isPartStr = typeof part === "string", isTag = isPartStr && !/\W/.test(part), isPartStrNotTag = isPartStr && !isTag; if ( isTag ) { part = part.toLowerCase(); } for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { if ( (elem = checkSet[i]) ) { while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? elem || false : elem === part; } } if ( isPartStrNotTag ) { Sizzle.filter( part, checkSet, true ); } }, ">": function(checkSet, part){ var isPartStr = typeof part === "string"; if ( isPartStr && !/\W/.test(part) ) { part = part.toLowerCase(); for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { var parent = elem.parentNode; checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; } } } else { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { checkSet[i] = isPartStr ? elem.parentNode : elem.parentNode === part; } } if ( isPartStr ) { Sizzle.filter( part, checkSet, true ); } } }, "": function(checkSet, part, isXML){ var doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !/\W/.test(part) ) { var nodeCheck = part = part.toLowerCase(); checkFn = dirNodeCheck; } checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); }, "~": function(checkSet, part, isXML){ var doneName = done++, checkFn = dirCheck; if ( typeof part === "string" && !/\W/.test(part) ) { var nodeCheck = part = part.toLowerCase(); checkFn = dirNodeCheck; } checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); } }, find: { ID: function(match, context, isXML){ if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); return m ? [m] : []; } }, NAME: function(match, context){ if ( typeof context.getElementsByName !== "undefined" ) { var ret = [], results = context.getElementsByName(match[1]); for ( var i = 0, l = results.length; i < l; i++ ) { if ( results[i].getAttribute("name") === match[1] ) { ret.push( results[i] ); } } return ret.length === 0 ? null : ret; } }, TAG: function(match, context){ return context.getElementsByTagName(match[1]); } }, preFilter: { CLASS: function(match, curLoop, inplace, result, not, isXML){ match = " " + match[1].replace(/\\/g, "") + " "; if ( isXML ) { return match; } for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { if ( elem ) { if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { if ( !inplace ) { result.push( elem ); } } else if ( inplace ) { curLoop[i] = false; } } } return false; }, ID: function(match){ return match[1].replace(/\\/g, ""); }, TAG: function(match, curLoop){ return match[1].toLowerCase(); }, CHILD: function(match){ if ( match[1] === "nth" ) { // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); // calculate the numbers (first)n+(last) including if they are negative match[2] = (test[1] + (test[2] || 1)) - 0; match[3] = test[3] - 0; } // TODO: Move to normal caching system match[0] = done++; return match; }, ATTR: function(match, curLoop, inplace, result, not, isXML){ var name = match[1].replace(/\\/g, ""); if ( !isXML && Expr.attrMap[name] ) { match[1] = Expr.attrMap[name]; } if ( match[2] === "~=" ) { match[4] = " " + match[4] + " "; } return match; }, PSEUDO: function(match, curLoop, inplace, result, not){ if ( match[1] === "not" ) { // If we're dealing with a complex expression, or a simple one if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { match[3] = Sizzle(match[3], null, null, curLoop); } else { var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); if ( !inplace ) { result.push.apply( result, ret ); } return false; } } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { return true; } return match; }, POS: function(match){ match.unshift( true ); return match; } }, filters: { enabled: function(elem){ return elem.disabled === false && elem.type !== "hidden"; }, disabled: function(elem){ return elem.disabled === true; }, checked: function(elem){ return elem.checked === true; }, selected: function(elem){ // Accessing this property makes selected-by-default // options in Safari work properly elem.parentNode.selectedIndex; return elem.selected === true; }, parent: function(elem){ return !!elem.firstChild; }, empty: function(elem){ return !elem.firstChild; }, has: function(elem, i, match){ return !!Sizzle( match[3], elem ).length; }, header: function(elem){ return /h\d/i.test( elem.nodeName ); }, text: function(elem){ return "text" === elem.type; }, radio: function(elem){ return "radio" === elem.type; }, checkbox: function(elem){ return "checkbox" === elem.type; }, file: function(elem){ return "file" === elem.type; }, password: function(elem){ return "password" === elem.type; }, submit: function(elem){ return "submit" === elem.type; }, image: function(elem){ return "image" === elem.type; }, reset: function(elem){ return "reset" === elem.type; }, button: function(elem){ return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; }, input: function(elem){ return /input|select|textarea|button/i.test(elem.nodeName); } }, setFilters: { first: function(elem, i){ return i === 0; }, last: function(elem, i, match, array){ return i === array.length - 1; }, even: function(elem, i){ return i % 2 === 0; }, odd: function(elem, i){ return i % 2 === 1; }, lt: function(elem, i, match){ return i < match[3] - 0; }, gt: function(elem, i, match){ return i > match[3] - 0; }, nth: function(elem, i, match){ return match[3] - 0 === i; }, eq: function(elem, i, match){ return match[3] - 0 === i; } }, filter: { PSEUDO: function(elem, match, i, array){ var name = match[1], filter = Expr.filters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } else if ( name === "contains" ) { return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; } else if ( name === "not" ) { var not = match[3]; for ( var i = 0, l = not.length; i < l; i++ ) { if ( not[i] === elem ) { return false; } } return true; } else { throw "Syntax error, unrecognized expression: " + name; } }, CHILD: function(elem, match){ var type = match[1], node = elem; switch (type) { case 'only': case 'first': while ( (node = node.previousSibling) ) { if ( node.nodeType === 1 ) { return false; } } if ( type === "first" ) { return true; } node = elem; case 'last': while ( (node = node.nextSibling) ) { if ( node.nodeType === 1 ) { return false; } } return true; case 'nth': var first = match[2], last = match[3]; if ( first === 1 && last === 0 ) { return true; } var doneName = match[0], parent = elem.parentNode; if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { var count = 0; for ( node = parent.firstChild; node; node = node.nextSibling ) { if ( node.nodeType === 1 ) { node.nodeIndex = ++count; } } parent.sizcache = doneName; } var diff = elem.nodeIndex - last; if ( first === 0 ) { return diff === 0; } else { return ( diff % first === 0 && diff / first >= 0 ); } } }, ID: function(elem, match){ return elem.nodeType === 1 && elem.getAttribute("id") === match; }, TAG: function(elem, match){ return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; }, CLASS: function(elem, match){ return (" " + (elem.className || elem.getAttribute("class")) + " ") .indexOf( match ) > -1; }, ATTR: function(elem, match){ var name = match[1], result = Expr.attrHandle[ name ] ? Expr.attrHandle[ name ]( elem ) : elem[ name ] != null ? elem[ name ] : elem.getAttribute( name ), value = result + "", type = match[2], check = match[4]; return result == null ? type === "!=" : type === "=" ? value === check : type === "*=" ? value.indexOf(check) >= 0 : type === "~=" ? (" " + value + " ").indexOf(check) >= 0 : !check ? value && result !== false : type === "!=" ? value !== check : type === "^=" ? value.indexOf(check) === 0 : type === "$=" ? value.substr(value.length - check.length) === check : type === "|=" ? value === check || value.substr(0, check.length + 1) === check + "-" : false; }, POS: function(elem, match, i, array){ var name = match[2], filter = Expr.setFilters[ name ]; if ( filter ) { return filter( elem, i, match, array ); } } } }; var origPOS = Expr.match.POS; for ( var type in Expr.match ) { Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source ); } var makeArray = function(array, results) { array = Array.prototype.slice.call( array, 0 ); if ( results ) { results.push.apply( results, array ); return results; } return array; }; // Perform a simple check to determine if the browser is capable of // converting a NodeList to an array using builtin methods. try { Array.prototype.slice.call( document.documentElement.childNodes, 0 ); // Provide a fallback method if it does not work } catch(e){ makeArray = function(array, results) { var ret = results || []; if ( toString.call(array) === "[object Array]" ) { Array.prototype.push.apply( ret, array ); } else { if ( typeof array.length === "number" ) { for ( var i = 0, l = array.length; i < l; i++ ) { ret.push( array[i] ); } } else { for ( var i = 0; array[i]; i++ ) { ret.push( array[i] ); } } } return ret; }; } var sortOrder; if ( document.documentElement.compareDocumentPosition ) { sortOrder = function( a, b ) { if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { if ( a == b ) { hasDuplicate = true; } return a.compareDocumentPosition ? -1 : 1; } var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } else if ( "sourceIndex" in document.documentElement ) { sortOrder = function( a, b ) { if ( !a.sourceIndex || !b.sourceIndex ) { if ( a == b ) { hasDuplicate = true; } return a.sourceIndex ? -1 : 1; } var ret = a.sourceIndex - b.sourceIndex; if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } else if ( document.createRange ) { sortOrder = function( a, b ) { if ( !a.ownerDocument || !b.ownerDocument ) { if ( a == b ) { hasDuplicate = true; } return a.ownerDocument ? -1 : 1; } var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); aRange.setStart(a, 0); aRange.setEnd(a, 0); bRange.setStart(b, 0); bRange.setEnd(b, 0); var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); if ( ret === 0 ) { hasDuplicate = true; } return ret; }; } // Utility function for retreiving the text value of an array of DOM nodes function getText( elems ) { var ret = "", elem; for ( var i = 0; elems[i]; i++ ) { elem = elems[i]; // Get the text from text nodes and CDATA nodes if ( elem.nodeType === 3 || elem.nodeType === 4 ) { ret += elem.nodeValue; // Traverse everything else, except comment nodes } else if ( elem.nodeType !== 8 ) { ret += getText( elem.childNodes ); } } return ret; } // Check to see if the browser returns elements by name when // querying by getElementById (and provide a workaround) (function(){ // We're going to inject a fake input element with a specified name var form = document.createElement("div"), id = "script" + (new Date).getTime(); form.innerHTML = "<a name='" + id + "'/>"; // Inject it into the root element, check its status, and remove it quickly var root = document.documentElement; root.insertBefore( form, root.firstChild ); // The workaround has to do additional checks after a getElementById // Which slows things down for other browsers (hence the branching) if ( document.getElementById( id ) ) { Expr.find.ID = function(match, context, isXML){ if ( typeof context.getElementById !== "undefined" && !isXML ) { var m = context.getElementById(match[1]); return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; } }; Expr.filter.ID = function(elem, match){ var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); return elem.nodeType === 1 && node && node.nodeValue === match; }; } root.removeChild( form ); root = form = null; // release memory in IE })(); (function(){ // Check to see if the browser returns only elements // when doing getElementsByTagName("*") // Create a fake element var div = document.createElement("div"); div.appendChild( document.createComment("") ); // Make sure no comments are found if ( div.getElementsByTagName("*").length > 0 ) { Expr.find.TAG = function(match, context){ var results = context.getElementsByTagName(match[1]); // Filter out possible comments if ( match[1] === "*" ) { var tmp = []; for ( var i = 0; results[i]; i++ ) { if ( results[i].nodeType === 1 ) { tmp.push( results[i] ); } } results = tmp; } return results; }; } // Check to see if an attribute returns normalized href attributes div.innerHTML = "<a href='#'></a>"; if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && div.firstChild.getAttribute("href") !== "#" ) { Expr.attrHandle.href = function(elem){ return elem.getAttribute("href", 2); }; } div = null; // release memory in IE })(); if ( document.querySelectorAll ) { (function(){ var oldSizzle = Sizzle, div = document.createElement("div"); div.innerHTML = "<p class='TEST'></p>"; // Safari can't handle uppercase or unicode characters when // in quirks mode. if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { return; } Sizzle = function(query, context, extra, seed){ context = context || document; // Only use querySelectorAll on non-XML documents // (ID selectors don't work in non-HTML documents) if ( !seed && context.nodeType === 9 && !isXML(context) ) { try { return makeArray( context.querySelectorAll(query), extra ); } catch(e){} } return oldSizzle(query, context, extra, seed); }; for ( var prop in oldSizzle ) { Sizzle[ prop ] = oldSizzle[ prop ]; } div = null; // release memory in IE })(); } (function(){ var div = document.createElement("div"); div.innerHTML = "<div class='test e'></div><div class='test'></div>"; // Opera can't find a second classname (in 9.6) // Also, make sure that getElementsByClassName actually exists if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { return; } // Safari caches class attributes, doesn't catch changes (in 3.2) div.lastChild.className = "e"; if ( div.getElementsByClassName("e").length === 1 ) { return; } Expr.order.splice(1, 0, "CLASS"); Expr.find.CLASS = function(match, context, isXML) { if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { return context.getElementsByClassName(match[1]); } }; div = null; // release memory in IE })(); function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { elem = elem[dir]; var match = false; while ( elem ) { if ( elem.sizcache === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 && !isXML ){ elem.sizcache = doneName; elem.sizset = i; } if ( elem.nodeName.toLowerCase() === cur ) { match = elem; break; } elem = elem[dir]; } checkSet[i] = match; } } } function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { for ( var i = 0, l = checkSet.length; i < l; i++ ) { var elem = checkSet[i]; if ( elem ) { elem = elem[dir]; var match = false; while ( elem ) { if ( elem.sizcache === doneName ) { match = checkSet[elem.sizset]; break; } if ( elem.nodeType === 1 ) { if ( !isXML ) { elem.sizcache = doneName; elem.sizset = i; } if ( typeof cur !== "string" ) { if ( elem === cur ) { match = true; break; } } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { match = elem; break; } } elem = elem[dir]; } checkSet[i] = match; } } } var contains = document.compareDocumentPosition ? function(a, b){ return a.compareDocumentPosition(b) & 16; } : function(a, b){ return a !== b && (a.contains ? a.contains(b) : true); }; var isXML = function(elem){ // documentElement is verified for cases where it doesn't yet exist // (such as loading iframes in IE - #4833) var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; return documentElement ? documentElement.nodeName !== "HTML" : false; }; var posProcess = function(selector, context){ var tmpSet = [], later = "", match, root = context.nodeType ? [context] : context; // Position selectors must be done after the filter // And so must :not(positional) so we move all PSEUDOs to the end while ( (match = Expr.match.PSEUDO.exec( selector )) ) { later += match[0]; selector = selector.replace( Expr.match.PSEUDO, "" ); } selector = Expr.relative[selector] ? selector + "*" : selector; for ( var i = 0, l = root.length; i < l; i++ ) { Sizzle( selector, root[i], tmpSet ); } return Sizzle.filter( later, tmpSet ); }; // Add Sizzle to Glow // This file is injected into sizzle.js by the ant "deps" target Glow.provide(function(glow) { glow._sizzle = Sizzle; }); return; window.Sizzle = Sizzle; })(); Glow.provide(function(glow) { var NodeListProto = glow.NodeList.prototype /* PrivateVar: ucheck Used by unique(), increased by 1 on each use */ , ucheck = 1 /* PrivateVar: ucheckPropName This is the property name used by unique checks */ , ucheckPropName = "_unique" + glow.UID; /* PrivateMethod: unique Get an array of nodes without duplicate nodes from an array of nodes. Arguments: aNodes - (Array|<NodeList>) Returns: An array of nodes without duplicates. */ //worth checking if it's an XML document? if (glow.env.ie) { var unique = function(aNodes) { if (aNodes.length == 1) { return aNodes; } //remove duplicates var r = [], ri = 0, i = 0; for (; aNodes[i]; i++) { if (aNodes[i].getAttribute(ucheckPropName) != ucheck && aNodes[i].nodeType == 1) { r[ri++] = aNodes[i]; } aNodes[i].setAttribute(ucheckPropName, ucheck); } for (i=0; aNodes[i]; i++) { aNodes[i].removeAttribute(ucheckPropName); } ucheck++; return r; } } else { var unique = function(aNodes) { if (aNodes.length == 1) { return aNodes; } //remove duplicates var r = [], ri = 0, i = 0; for (; aNodes[i]; i++) { if (aNodes[i][ucheckPropName] != ucheck && aNodes[i].nodeType == 1) { r[ri++] = aNodes[i]; } aNodes[i][ucheckPropName] = ucheck; } ucheck++; return r; } }; /** @name glow.NodeList#parent @function @description Gets the unique parent nodes of each node as a new NodeList. @param {string | HTMLElement | NodeList} [search] Search value If provided, will seek the next parent element until a match is found @returns {glow.NodeList} Returns a new NodeList containing the parent nodes, with duplicates removed @example // elements which contain links var parents = glow.dom.get("a").parent(); */ NodeListProto.parent = function(search) { var ret = [], ri = 0, i = this.length, node; while (i--) { node = this[i]; if (node.nodeType == 1) { if(search){ while(node = node.parentNode){ if (glow._sizzle.filter(search, [node]).length) { ret[ri++] = node; break; } } } else if(node = node.parentNode){ ret[ri++] = node; } } } return new glow.NodeList(unique(ret)); }; /* Private method for prev() and next() */ function getNextOrPrev(nodelist, dir, search) { var ret = [], ri = 0, node, i = 0, length = nodelist.length; while (i < length) { node = nodelist[i]; if(search){ while (node = node[dir + 'Sibling']) { if (node.nodeType == 1 && node.nodeName != '!') { if (glow._sizzle.filter(search, [node]).length) { ret[ri++] = node; break; } } } } else{ while (node = node[dir + 'Sibling']) { if (node.nodeType == 1 && node.nodeName != '!') { ret[ri++] = node; break; } } } i++; } return new glow.NodeList(ret); } /** @name glow.NodeList#prev @function @description Gets the previous sibling element for each node in the ElementList. If a filter is provided, the previous item that matches the filter is returned, or none if no match is found. @param {string | HTMLElement | NodeList} [search] Search value If provided, will seek the previous sibling element until a match is found @returns {glow.ElementList} A new ElementList containing the previous sibling elements that match the (optional) filter. @example // gets the element before #myLink (if there is one) var next = glow.get("#myLink").prev(); @example // get the previous sibling link element before #skipLink glow.get('#skipLink').prev('a') */ NodeListProto.prev = function(search) { return getNextOrPrev(this, 'previous', search); }; /** @name glow.NodeList#next @function @description Gets the next sibling element for each node in the ElementList. If a filter is provided, the next item that matches the filter is returned, or none if no match is found. @param {string | HTMLElement | NodeList} [search] Search value If provided, will seek the next sibling element until a match is found @returns {glow.ElementList} A new ElementList containing the next sibling elements that match the (optional) filter. @example // gets the element following #myLink (if there is one) var next = glow.get("#myLink").next(); @example // get the next sibling link element after #skipLink glow.get('#skipLink').next('a') */ NodeListProto.next = function(search) { return getNextOrPrev(this, 'next', search); }; /** @name glow.NodeList#get @function @description Gets decendents of nodes that match a CSS selector. @param {String} selector CSS selector @returns {glow.NodeList} Returns a new NodeList containing matched elements @example // create a new NodeList var myNodeList = glow.dom.create("<div><a href='s.html'>Link</a></div>"); // get 'a' tags that are decendants of the NodeList nodes myNewNodeList = myNodeList.get("a"); */ NodeListProto.get = function(selector) { var ret = [], i = this.length; while (i--) { glow._sizzle(selector, this[i], ret); } // need to remove uniqueSorts because they're slow. Replace with own method for unique. return new glow.NodeList(unique(ret)); }; /** @name glow.NodeList#ancestors @function @description Gets the unique ancestor nodes of each node as a new NodeList. @param {Function|string} [filter] Filter test If a string is provided, it is used in a call to {@link glow.ElementList#is ElementList#is}. If a function is provided it will be passed 2 arguments, the index of the current item, and the ElementList being itterated over. Inside the function 'this' refers to the HTMLElement. Return true to keep the node, or false to remove it. @returns {glow.dom.NodeList} Returns NodeList @example // get ancestor elements for anchor elements var ancestors = glow.dom.get("a").ancestors(); */ NodeListProto.ancestors = function(filter) { var ret = [], ri = 0, i = 0, length = this.length, node; while (i < length) { node = this[i].parentNode; while (node && node.nodeType == 1) { ret[ri++] = node; node = node.parentNode; } i++; } if(filter){ ret = new glow.NodeList(ret); ret = ret.filter(filter); } return new glow.NodeList(unique(ret)); }; /* Private method to get the child elements for an html node (used by children()) */ function getChildElms(node) { var r = [], childNodes = node.childNodes, i = 0, ri = 0; for (; childNodes[i]; i++) { if (childNodes[i].nodeType == 1 && childNodes[i].nodeName != '!') { r[ri++] = childNodes[i]; } } return r; } /** @name glow.NodeList#children @function @description Gets the child elements of each node as a new NodeList. @returns {glow.dom.NodeList} Returns a new NodeList containing all the child nodes @example // get all list items var items = glow.dom.get("ul, ol").children(); */ NodeListProto.children = function() { var ret = [], i = this.length; while(i--) { ret = ret.concat( getChildElms(this[i]) ); } return new glow.NodeList(ret); }; /** @name glow.NodeList#contains @function @description Find if this NodeList contains the given element @param {string | HTMLELement | NodeList} Single element to check for @returns {boolean} myElementList.contains(elm) // Returns true if an element in myElementList contains elm, or IS elm. */ NodeListProto.contains = function(elm) { var i = 0, node = new glow.NodeList(elm)[0], length = this.length, newNodes, toTest; // missing some nodes? Return false if ( !node || !this.length ) { return false; } if (this[0].compareDocumentPosition) { //w3 method while (i < length) { //break out if the two are teh same if(this[i] == node){ break; } //check against bitwise to see if node is contained in this else if (!(this[i].compareDocumentPosition(node) & 16)) { return false; } i++; } } else if(node.contains){ for (; i < length; i++) { if ( !( this[i].contains( node ) ) ) { return false; } } } else { //manual method for last chance corale while (i < length) { toTest = node; while (toTest = toTest.parentNode) { if (this[i] == toTest) { break; } } if (!toTest) { return false; } i++; } } return true; }; }); Glow.provide(function(glow) { var NodeListProto = glow.NodeList.prototype, document = window.document, undefined; // create a fragment and insert a set of nodes into it function createFragment(nodes) { var fragment = document.createDocumentFragment(), i = 0, node; while ( node = nodes[i++] ) { fragment.appendChild(node); } return fragment; } // generate the #before and #after methods // after: 1 for #(insert)after, 0 for #(insert)before // insert: 1 for #insert(After|Before), 0 for #(after|before) function afterAndBefore(after, insert) { return function(elements) { var toAddList, toAddToList, fragmentToAdd, nextFragmentToAdd, item, itemParent; if (!this.length) { return this; } // normalise 'elements' // if we're dealing with append/prepend then strings are always treated as HTML strings if (!insert && typeof elements === 'string') { elements = new glow.NodeList( glow.NodeList._strToNodes(elements) ); } else { elements = new glow.NodeList(elements); } // set the element we're going to add to, and the elements we're going to add if (insert) { toAddToList = elements; toAddList = new glow.NodeList(this); } else { toAddToList = this; toAddList = elements; } nextFragmentToAdd = createFragment(toAddList); for (var i = 0, leni = toAddToList.length, lasti = leni - 1; i < leni; i++) { item = toAddToList[i]; fragmentToAdd = nextFragmentToAdd; // we can only append after if the element has a parent right? if (itemParent = item.parentNode) { if (i !== lasti) { // if not the last item nextFragmentToAdd = fragmentToAdd.cloneNode(true); insert && toAddList.push(nextFragmentToAdd.childNodes); } itemParent.insertBefore(fragmentToAdd, after ? item.nextSibling : item); } } return insert ? toAddList : toAddToList; } } // generate the #append, #appendTo, #prepend and #prependTo methods // append: 1 for #append(To), 0 for #prepend(To) // to: 1 for #(append|prepend)To, 0 for #(append|prepend) function appendAndPrepend(append, to) { return function(elements) { var toAddList, toAddToList, fragmentToAdd, nextFragmentToAdd, item; if (!this.length) { return this; } // normalise 'elements' // if we're dealing with append/prepend then strings are always treated as HTML strings if (!to && typeof elements === 'string') { elements = new glow.NodeList( glow.NodeList._strToNodes(elements) ); } else { elements = new glow.NodeList(elements); } // set the element we're going to add to, and the elements we're going to add if (to) { toAddToList = elements; toAddList = new glow.NodeList(this); } else { toAddToList = this; toAddList = elements; } nextFragmentToAdd = createFragment(toAddList); for (var i = 0, leni = toAddToList.length, lasti = leni - 1; i < leni; i++) { item = toAddToList[i]; fragmentToAdd = nextFragmentToAdd; // avoid trying to append to non-elements if (item.nodeType === 1) { if (i !== lasti) { // if not the last item nextFragmentToAdd = fragmentToAdd.cloneNode(true); // add the clones to the return element for appendTo / prependTo to && toAddList.push(nextFragmentToAdd.childNodes); } item.insertBefore(fragmentToAdd, append ? null : item.firstChild); } } return to ? toAddList : toAddToList; } } /** @name glow.NodeList#after @function @description Insert node(s) after each node in this NodeList. If there is more than one node in this NodeList, 'nodes' will be inserted after the first element and clones will be inserted after each subsequent element. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert Strings will be treated as HTML strings. @returns {glow.NodeList} Original NodeList @example // adds a paragraph after each heading glow('h1, h2, h3').after('<p>That was a nice heading.</p>'); */ NodeListProto.after = afterAndBefore(1); /** @name glow.NodeList#before @function @description Insert node(s) before each node in this NodeList. If there is more than one node in this NodeList, 'nodes' will be inserted before the first element and clones will be inserted before each subsequent element. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert Strings will be treated as HTML strings. @returns {glow.NodeList} Original NodeList @example // adds a div before each paragraph glow('p').before('<div>Here comes a paragraph!</div>'); */ NodeListProto.before = afterAndBefore(0); /** @name glow.NodeList#append @function @description Appends node to each node in this NodeList. If there is more than one node in this NodeList, then the given nodes are appended to the first node and clones are appended to the other nodes. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Nodes(s) to append Strings will be treated as HTML strings. @returns {glow.NodeList} Original NodeList @example // ends every paragraph with '...' glow('p').append('<span>...</span>'); */ NodeListProto.append = appendAndPrepend(1); /** @name glow.NodeList#prepend @function @description Prepends nodes to each node in this NodeList. If there is more than one node in this NodeList, then the given nodes are prepended to the first node and clones are prepended to the other nodes. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Nodes(s) to prepend Strings will be treated as HTML strings. @returns {glow.NodeList} Original NodeList @example // prepends every paragraph with 'Paragraph: ' glow('p').prepend('<span>Paragraph: </span>'); */ NodeListProto.prepend = appendAndPrepend(0); /** @name glow.NodeList#appendTo @function @description Appends nodes in this NodeList to given node(s) If appending to more than one node, the NodeList is appended to the first node and clones are appended to the others. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} node Node(s) to append to. Strings will be treated as CSS selectors or HTML strings. @returns {glow.NodeList} The appended nodes. @example // appends '...' to every paragraph glow('<span>...</span>').appendTo('p'); */ NodeListProto.appendTo = appendAndPrepend(1, 1); /** @name glow.NodeList#prependTo @function @description Prepends nodes in this NodeList to given node(s) If prepending to more than one node, the NodeList is prepended to the first node and clones are prepended to the others. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} node Node(s) to prepend to Strings will be treated as CSS selectors or HTML strings. @returns {glow.NodeList} The prepended nodes. @example // prepends 'Paragraph: ' to every paragraph glow('<span>Paragraph: </span>').prependTo('p'); */ NodeListProto.prependTo = appendAndPrepend(0, 1); /** @name glow.NodeList#insertAfter @function @description Insert this NodeList after the given nodes If inserting after more than one node, the NodeList is inserted after the first node and clones are inserted after the others. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert after Strings will be treated as CSS selectors. @returns {glow.NodeList} Inserted nodes. @example // adds a paragraph after each heading glow('<p>HAI!</p>').insertAfter('h1, h2, h3'); */ NodeListProto.insertAfter = afterAndBefore(1, 1); /** @name glow.NodeList#insertBefore @function @description Insert this NodeList before the given nodes If inserting before more than one node, the NodeList is inserted before the first node and clones are inserted before the others. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} nodes Node(s) to insert before Strings will be treated as CSS selectors. @returns {glow.NodeList} Inserted nodes. @example // adds a div before each paragraph glow('<div>Here comes a paragraph!</div>').insertBefore('p'); */ NodeListProto.insertBefore = afterAndBefore(0, 1); /** @name glow.NodeList#destroy @function @description Removes each element from the document The element, attached listeners & attached data will be destroyed to free up memory. Detroyed elements may not be reused in some browsers. @returns {glow.NodeList} An empty NodeList @example // destroy all links in the document glow("a").destroy(); */ var tmpDiv = document.createElement('div'); NodeListProto.destroy = function() { glow.NodeList._destroyData(this); this.appendTo(tmpDiv); tmpDiv.innerHTML = ''; return new glow.NodeList(); }; /** @name glow.NodeList#remove @function @description Removes each element from the document If you no longer need the elements, consider using {@link glow.NodeList#destroy destroy} @returns {glow.NodeList} The removed elements @example // take all the links out of a document glow("a").remove(); */ NodeListProto.remove = function() { var parent, node, i = this.length; while (i--) { node = this[i]; if (parent = node.parentNode) { parent.removeChild(node); } } return this; }; /** @name glow.NodeList#empty @function @description Removes the nodes' contents @returns {glow.NodeList} Original nodes @example // remove the contents of all textareas glow("textarea").empty(); */ // TODO: is this shortcut worth doing? NodeListProto.empty = glow.env.ie ? // When you clean an element out using innerHTML it destroys its inner text nodes in IE8 and below // Here's an alternative method for IE: function() { var i = this.length, node, child; while (i--) { node = this[i]; while (child = node.firstChild) { node.removeChild(child); } } return this; } : // method for most browsers function() { var i = this.length; while (i--) { this[i].innerHTML = ''; } return this; } /** @name glow.NodeList#replaceWith @function @description Replace elements with another @param {string | HTMLElement | HTMLElement[] | glow.NodeList} elements Element(s) to insert into the document If there is more than one element in the NodeList, then the given elements replace the first element, clones are appended to the other elements. @returns {glow.NodeList} The replaced elements Call {@link glow.NodeList#destroy destroy} on these if you no longer need them. */ NodeListProto.replaceWith = function(elements) { return this.after(elements).remove(); }; /** @name glow.NodeList#wrap @function @description Wraps the given NodeList with the specified element(s). The given NodeList items will always be placed in the first child element that contains no further elements. Each item in a given NodeList will be wrapped individually. @param {string | HTMLElement | HTMLElement[] | glow.NodeList} wrapper Element to use as a wrapper Strings will be treated as HTML strings if they begin with <, else they'll be treated as a CSS selector. @returns {glow.NodeList} The NodeList with new wrapper parents @example // <span id="mySpan">Hello</span> glow("#mySpan").wrap("<div><p></p></div>"); // Makes: // <div> // <p> // <span id="mySpan">Hello</span> // </p> // </div> */ // get first child element node of an element, otherwise undefined function getFirstChildElm(parent) { for (var child = parent.firstChild; child; child = child.nextSibling) { if (child.nodeType == 1) { return child; } } return undefined; } NodeListProto.wrap = function(wrapper) { // normalise input wrapper = new glow.NodeList(wrapper); // escape if the wraper is non-existant or not an element if (!wrapper[0] || wrapper[0].nodeType != 1) { return this; } var toWrap, toWrapTarget, firstChildElm; for (var i = 0, leni = this.length; i<leni; i++) { toWrap = this[i]; // get target element to insert toWrap in toWrapTarget = wrapper[0]; while (toWrapTarget) { firstChildElm = getFirstChildElm(toWrapTarget); if (!firstChildElm) { break; } toWrapTarget = firstChildElm; } if (toWrap.parentNode) { wrapper.insertBefore(toWrap); } // If wrapping multiple nodes, we need to take a clean copy of the wrapping nodes if (i != leni-1) { wrapper = wrapper.clone(); } toWrapTarget.appendChild(toWrap); } return this; }; /** @name glow.NodeList#unwrap @function @description Removes the parent of each item in the list @returns {glow.NodeList} The now unwrapped elements @example // Before: <div><p><span id="mySpan">Hello</span></p></div> // unwrap the given element glow("#mySpan").unwrap(); // After: <div><span id="mySpan">Hello</span></div> */ NodeListProto.unwrap = function() { var parentToRemove, childNodes, // get unique parents parentsToRemove = this.parent(); for (var i = 0, leni = parentsToRemove.length; i < leni; i++) { parentToRemove = parentsToRemove.slice(i, i+1); // make sure we get all children, including text nodes childNodes = new glow.NodeList( parentToRemove[0].childNodes ); // 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 if (!parentToRemove[0].parentNode){ childNodes.remove(); parentToRemove.destroy(); } else { childNodes.insertBefore(parentToRemove); parentToRemove.destroy(); } } return this; }; /** @name glow.NodeList#clone @function @description Clones each node in the NodeList, along with data & event listeners @returns {glow.NodeList} Returns a new NodeList containing clones of all the nodes in the NodeList @example // get a copy of all heading elements var myClones = glow.get("h1, h2, h3, h4, h5, h6").clone(); */ NodeListProto.clone = function() { var nodes = [], eventIdProp = '__eventId' + glow.UID, i = this.length; while (i--) { nodes[i] = this[i].cloneNode(true); // some browsers (ie) also clone node properties as attributes // we need to get rid of the eventId. allCloneElms = new glow.NodeList( nodes ).get("*").push( nodes ); j = allCloneElms.length; while(j--) { nodes[i][eventIdProp] = null; // now copy over the data and events glow.events._copyEvent(this[i], nodes[i]); glow.NodeList._copyData(this[i], nodes[i]); } } // some browsers (ie) also clone node properties as attributes // we need to get rid of the eventId. //allCloneElms = new glow.NodeList( nodes ).get("*").push( nodes ); //j = allCloneElms.length; //while(j--) { //allCloneElms[j][eventIdProp] = null; //} // copy data from base elements to clone elements /*allBaseElms = this.get("*").push( this ); i = allCloneElms.length; while (i--) { allCloneElms[i].removeAttribute(dataPropName); glow.dom.get(allCloneElms[i]).data( glow.dom.get(allBaseElms[i]).data() ); }*/ return new glow.NodeList(nodes); }; /** @name glow.NodeList#copy @function @description Copies each node in the NodeList, excluding data & event listeners @returns {glow.NodeList} Returns a new NodeList containing copies of all the nodes in the NodeList @example // get a copy of all heading elements var myCopies = glow.get("h1, h2, h3, h4, h5, h6").copy(); */ NodeListProto.copy = function() { var nodes = [], i = this.length; while (i--) { nodes[i] = this[i].cloneNode(true); } return new glow.NodeList(nodes); }; /** @name glow.NodeList#html @function @description Gets / sets HTML content Either gets content of the first element, or sets the content for all elements in the list @param {String} [htmlString] String to set as the HTML of elements If omitted, the html for the first element in the list is returned. @returns {glow.NodeList | string} Returns the original NodeList when setting, or the HTML content when getting. @example // get the html in #footer var footerContents = glow("#footer").html(); @example // set a new footer glow("#footer").html("<strong>Hello World!</strong>"); */ NodeListProto.html = function(htmlString) { // getting if (!arguments.length) { return this[0] ? this[0].innerHTML : ''; } // setting var i = this.length, node; // normalise the string htmlString = htmlString ? String(htmlString): ''; while (i--) { node = this[i]; if (node.nodeType == 1) { try { // this has a habit of failing in IE for some elements node.innerHTML = htmlString; } catch (e) { new glow.NodeList(node).empty().append(htmlString); } } } return this; }; /** @name glow.NodeList#text @function @description Gets / set the text content Either gets content of the first element, or sets the content for all elements in the list @param {String} [text] String to set as the text of elements If omitted, the test for the first element in the list is returned. @returns {glow.NodeList | String} Returns the original NodeList when setting, or the text content when getting. @example // set text var div = glow("<div></div>").text("Fun & games!"); // <div>Func & games!</div> @example // get text var mainHeading = glow('#mainHeading').text(); */ NodeListProto.text = function(textString) { var firstNode = this[0], i = this.length, node; // getting if (!arguments.length) { // get the text by checking a load of properties in priority order return firstNode ? firstNode.textContent || firstNode.innerText || firstNode.nodeValue || '' // nodeValue for comment & text nodes : ''; } // setting // normalise the string textString = textString ? String(textString): ''; this.empty(); while (i--) { node = this[i]; if (node.nodeType == 1) { node.appendChild( document.createTextNode(textString) ); } else { node.nodeValue = textString; } } return this; }; }); Glow.provide(function(glow) { var NodeListProto = glow.NodeList.prototype, doc = document, win = window; /********************************PRIVATE METHODS*****************************************/ /* PrivateMethod: toStyleProp Converts a css property name into its javascript name, such as "background-color" to "backgroundColor". Arguments: prop - (String) CSS Property name Returns: String, javascript style property name */ function toStyleProp(prop) { if (prop == 'float') { return glow.env.ie ? 'styleFloat' : 'cssFloat'; } return prop.replace(/-(\w)/g, function(match, p1) { return p1.toUpperCase(); }); } /* PrivateMethod: getCssValue Get a computed css property Arguments: elm - element prop - css property or array of properties to add together Returns: String, value */ function getCssValue(elm, prop) { var r, //return value total = 0, i = 0, /*regex for detecting which css properties need to be calculated relative to the y axis*/ usesYAxis = /height|top/, propLen = prop.length, cssPropRegex = /^(?:(width|height)|(border-(top|bottom|left|right)-width))$/, compStyle = doc.defaultView && (doc.defaultView.getComputedStyle(elm, null) || doc.defaultView.getComputedStyle), elmCurrentStyle = elm.currentStyle, oldDisplay, match, propTest = prop.push || cssPropRegex.exec(prop) || []; if (prop.push) { //multiple properties, add them up for (; i < propLen; i++) { total += parseInt( getCssValue(elm, prop[i]), 10 ) || 0; } return total + 'px'; } if (propTest[1]) { // is width / height if (!isVisible(elm)) { //element may be display: none return tempBlock(elm, function() { return getElmDimension(elm, propTest[1]) + 'px'; }); } return getElmDimension(elm, propTest[1]) + 'px'; } else if (propTest[2] //is border-*-width && glow.env.ie && getCssValue(elm, 'border-' + propTest[3] + '-style') == 'none' ) { return '0'; } else if (compStyle) { //W3 Method //this returns computed values if (typeof compStyle == 'function') { //safari returns null for compStyle when element is display:non oldDisplay = elm.style.display; r = tempBlock(elm, function() { if (prop == 'display') { //get true value for display, since we've just fudged it elm.style.display = oldDisplay; if (!doc.defaultView.getComputedStyle(elm, null)) { return 'none'; } elm.style.display = 'block'; } return getCssValue(elm, prop); }); } else { // assume equal horizontal margins in safari 3 // http://bugs.webkit.org/show_bug.cgi?id=13343 // The above bug doesn't appear to be closed, but it works fine in Safari 4 if (glow.env.webkit > 500 && glow.env.webkit < 526 && prop == 'margin-right' && compStyle.getPropertyValue('position') != 'absolute') { prop = 'margin-left'; } r = compStyle.getPropertyValue(prop); } } else if (elmCurrentStyle) { //IE method if (prop == 'opacity') { match = /alpha\(opacity=([^\)]+)\)/.exec(elmCurrentStyle.filter); return match ? String(parseInt(match[1], 10) / 100) : '1'; } //this returns cascaded values so needs fixing r = String(elmCurrentStyle[toStyleProp(prop)]); if (/^-?[\d\.]+(?!px)[%a-z]+$/i.test(r) && prop != 'font-size') { r = getPixelValue(elm, r, usesYAxis.test(prop)) + 'px'; } } //some results need post processing if (prop.indexOf('color') != -1) { //deal with colour values r = normaliseCssColor(r).toString(); } else if (r.indexOf('url') == 0) { //some browsers put quotes around the url, get rid r = r.replace(/\"/g,''); } return r; } /* PrivateMethod: isVisible Is the element visible? */ function isVisible(elm) { //this is a bit of a guess, if there's a better way to do this I'm interested! return elm.offsetWidth || elm.offsetHeight; } /* PrivateMethod: normaliseCssColor Converts a CSS colour into "rgb(255, 255, 255)" or "transparent" format */ function normaliseCssColor(val) { if (/^(transparent|rgba\(0, ?0, ?0, ?0\))$/.test(val)) { return 'transparent'; } var match, //tmp regex match holder r, g, b, //final colour vals hex, //tmp hex holder mathRound = Math.round, parseIntFunc = parseInt, parseFloatFunc = parseFloat, htmlColorNames = { black: 0, silver: 0xc0c0c0, gray: 0x808080, white: 0xffffff, maroon: 0x800000, red: 0xff0000, purple: 0x800080, fuchsia: 0xff00ff, green: 0x8000, lime: 0xff00, olive: 0x808000, yellow: 0xffff00, navy: 128, blue: 255, teal: 0x8080, aqua: 0xffff, orange: 0xffa500 }, colorRegex = /^rgb\(([\d\.]+)(%?),\s*([\d\.]+)(%?),\s*([\d\.]+)(%?)/i; if (match = colorRegex.exec(val)) { //rgb() format, cater for percentages r = match[2] ? mathRound(((parseFloatFunc(match[1]) / 100) * 255)) : parseIntFunc(match[1]); g = match[4] ? mathRound(((parseFloatFunc(match[3]) / 100) * 255)) : parseIntFunc(match[3]); b = match[6] ? mathRound(((parseFloatFunc(match[5]) / 100) * 255)) : parseIntFunc(match[5]); } else { if (typeof val == 'number') { hex = val; } else if (val.charAt(0) == '#') { if (val.length == '4') { //deal with #fff shortcut val = '#' + val.charAt(1) + val.charAt(1) + val.charAt(2) + val.charAt(2) + val.charAt(3) + val.charAt(3); } hex = parseIntFunc(val.slice(1), 16); } else { hex = htmlColorNames[val]; } r = (hex) >> 16; g = (hex & 0x00ff00) >> 8; b = (hex & 0x0000ff); } val = new String('rgb(' + r + ', ' + g + ', ' + b + ')'); val.r = r; val.g = g; val.b = b; return val; } /* PrivateMethod: getElmDimension Gets the size of an element as an integer, not including padding or border */ var horizontalBorderPadding = [ 'border-left-width', 'border-right-width', 'padding-left', 'padding-right' ], verticalBorderPadding = [ 'border-top-width', 'border-bottom-width', 'padding-top', 'padding-bottom' ]; function getElmDimension(elm, cssProp /* (width|height) */) { var r, doc = document, docElm = doc.documentElement, docBody = document.body, docElmOrBody = glow.env.standardsMode ? docElm : docBody, isWidth = (cssProp == 'width'), cssPropCaps = isWidth ? 'Width' : 'Height', cssBorderPadding; if (elm.window) { // is window r = glow.env.webkit < 522.11 ? (isWidth ? elm.innerWidth : elm.innerHeight) : glow.env.webkit ? (isWidth ? docBody.clientWidth : elm.innerHeight) : glow.env.opera < 9.5 ? (isWidth ? docBody.clientWidth : docBody.clientHeight) : /* else */ (isWidth ? docElmOrBody.clientWidth : docElmOrBody.clientHeight); } else if (elm.getElementById) { // is document // we previously checked offsetWidth & clientWidth here // but they returned values too large in IE6 scrollWidth seems enough r = Math.max( docBody['scroll' + cssPropCaps], docElm['scroll' + cssPropCaps] ) } else { // get an array of css borders & padding cssBorderPadding = isWidth ? horizontalBorderPadding : verticalBorderPadding; r = elm['offset' + cssPropCaps] - parseInt( getCssValue(elm, cssBorderPadding) ); } return r; } /* PrivateMethod: setElmsSize Set element's size Arguments: elms - (<NodeList>) Elements val - (Mixed) Set element height / width. In px unless stated type - (String) "height" or "width" Returns: Nowt. */ function setElmsSize(elms, val, type) { if (typeof val == 'number' || /\d$/.test(val)) { val += 'px'; } for (var i = 0, len = elms.length; i < len; i++) { elms[i].style[type] = val; } } /* PrivateMethod: tempBlock Gives an element display:block (but keeps it hidden) and runs a function, then sets the element back how it was Arguments: elm - element func - function to run Returns: Return value of the function */ function tempBlock(elm, func) { //TODO: rather than recording individual style properties, just cache cssText? This was faster for getting the element size var r, elmStyle = elm.style, oldDisp = elmStyle.display, oldVis = elmStyle.visibility, oldPos = elmStyle.position; elmStyle.visibility = 'hidden'; elmStyle.position = 'absolute'; elmStyle.display = 'block'; if (!isVisible(elm)) { elmStyle.position = oldPos; r = tempBlock(elm.parentNode, func); elmStyle.display = oldDisp; elmStyle.visibility = oldVis; } else { r = func(); elmStyle.display = oldDisp; elmStyle.position = oldPos; elmStyle.visibility = oldVis; } return r; } /* PrivateMethod: getPixelValue Converts a relative value into an absolute pixel value. Only works in IE with Dimension value (not stuff like relative font-size). Based on some Dean Edwards' code Arguments: element - element used to calculate relative values value - (string) relative value useYAxis - (string) calulate relative values to the y axis rather than x Returns: Number */ function getPixelValue(element, value, useYAxis) { // Remember the original values var axisPos = useYAxis ? 'top' : 'left', axisPosUpper = useYAxis ? 'Top' : 'Left', elmStyle = element.style, positionVal = elmStyle[axisPos], runtimePositionVal = element.runtimeStyle[axisPos], r; // copy to the runtime type to prevent changes to the display element.runtimeStyle[axisPos] = element.currentStyle[axisPos]; // set value to left / top elmStyle[axisPos] = value; // get the pixel value r = elmStyle['pixel' + axisPosUpper]; // revert values elmStyle[axisPos] = positionVal; element.runtimeStyle[axisPos] = runtimePositionVal; return r; } /*************************************** API METHODS ******************************************/ /** @name glow.NodeList#css @function @description Get / set a CSS property value @param {string | Object} property The CSS property name, or object of property-value pairs to set @param {string | number} [value] The value to apply Number values will be treated as 'px' unless the CSS property accepts a unitless value. If value is omitted, the value for the given property will be returned @returns {glow.NodeList | string} Returns the NodeList when setting value, or the CSS value when getting values. CSS values are strings. For instance, "height" will return "25px" for an element 25 pixels high. You can use parseInt to convert these values. @example // get value from first node glow("#subNav").css("display"); @example // set left padding to 10px on all nodes glow("#subNav li").css("padding-left", "2em"); @example // where appropriate, px is assumed when no unit is passed glow("#mainPromo").css("margin-top", 300); @example // set multiple CSS values at once // NOTE: Property names containing a hyphen such as font-weight must be quoted glow("#myDiv").css({ 'font-weight': 'bold', 'padding' : '10px', 'color' : '#00cc99' }); */ NodeListProto.css = function(prop, val) { var thisStyle, i = this.length, len = this.length, originalProp = prop, hasUnits = /width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/, style; if (prop.constructor === Object) { // set multiple values for (style in prop) { this.css(style, prop[style]); } return this; } else if (val != undefined) { //set one CSS value prop = toStyleProp(prop); while (i--) { thisStyle = this[i].style; if (typeof val == 'number' && hasUnits.test(originalProp)) { val = val.toString() + 'px'; } if (prop == 'opacity' && glow.env.ie) { //in IE the element needs hasLayout for opacity to work thisStyle.zoom = '1'; if (val === '') { thisStyle.filter = ''; } else { thisStyle.filter = 'alpha(opacity=' + Math.round(Number(val, 10) * 100) + ')'; } } else { thisStyle[prop] = val; } } return this; } else { //getting stuff if (!len) { return; } return getCssValue(this[0], prop); } }; /** @name glow.NodeList#height @function @description Gets / set element height Return value does not include the padding or border of the element in browsers supporting the correct box model. You can use this to easily get the height of the document or window, see example below. @param {Number} [height] New height in pixels for each element in the list If ommited, the height of the first element is returned @returns {glow.NodeList | number} Height of first element, or original NodeList when setting heights. @example // get the height of #myDiv glow("#myDiv").height(); @example // set the height of list items in #myList to 200 pixels glow("#myList > li").height(200); @example // get the height of the document glow(document).height(); @example // get the height of the window glow(window).height(); */ NodeListProto.height = function(height) { if (height == undefined) { return getElmDimension(this[0], 'height'); } setElmsSize(this, height, 'height'); return this; }; /** @name glow.NodeList#width @function @description Gets / set element width Return value does not include the padding or border of the element in browsers supporting the correct box model. You can use this to easily get the width of the document or window, see example below. @param {Number} [width] New width in pixels for each element in the list If ommited, the width of the first element is returned @returns {glow.NodeList | number} width of first element, or original NodeList when setting widths. @example // get the width of #myDiv glow("#myDiv").width(); @example // set the width of list items in #myList to 200 pixels glow("#myList > li").width(200); @example // get the width of the document glow(document).width(); @example // get the width of the window glow(window).width(); */ NodeListProto.width = function(width) { if (width == undefined) { return getElmDimension(this[0], 'width'); } setElmsSize(this, width, 'width'); return this; }; /** @name glow.NodeList#scrollLeft @function @description Gets/sets the number of pixels the element has scrolled horizontally To get/set the scroll position of the window, use this method on a nodelist containing the window object. @param {Number} [val] New left scroll position Omit this to get the current scroll position @returns {glow.NodeList | number} Current scrollLeft value, or NodeList when setting scroll position. @example // get the scroll left value of #myDiv var scrollPos = glow("#myDiv").scrollLeft(); // scrollPos is a number, eg: 45 @example // set the scroll left value of #myDiv to 20 glow("#myDiv").scrollLeft(20); @example // get the scrollLeft of the window glow(window).scrollLeft(); // scrollPos is a number, eg: 45 */ NodeListProto.scrollLeft = function(val) { return scrollOffset(this, true, val); }; /** @name glow.NodeList#scrollTop @function @description Gets/sets the number of pixels the element has scrolled vertically To get/set the scroll position of the window, use this method on a nodelist containing the window object. @param {Number} [val] New top scroll position Omit this to get the current scroll position @returns {glow.NodeList | number} Current scrollTop value, or NodeList when setting scroll position. @example // get the scroll top value of #myDiv var scrollPos = glow("#myDiv").scrollTop(); // scrollPos is a number, eg: 45 @example // set the scroll top value of #myDiv to 20 glow("#myDiv").scrollTop(20); @example // get the scrollTop of the window glow(window).scrollTop(); // scrollPos is a number, eg: 45 */ NodeListProto.scrollTop = function(val) { return scrollOffset(this, false, val); }; /** @name glow.dom-getScrollOffset @private @description Get the scrollTop / scrollLeft of a particular element @param {Element} elm Element (or window object) to get the scroll position of @param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top */ function getScrollOffset(elm, isLeft) { var r, scrollProp = 'scroll' + (isLeft ? 'Left' : 'Top'); // are we dealing with the window object or the document object? if (elm.window) { // get the scroll of the documentElement or the pageX/Yoffset // - some browsers use one but not the other r = elm.document.documentElement[scrollProp] || (isLeft ? elm.pageXOffset : elm.pageYOffset) || 0; } else { r = elm[scrollProp]; } return r; } /** @name glow.dom-setScrollOffset @private @description Set the scrollTop / scrollLeft of a particular element @param {Element} elm Element (or window object) to get the scroll position of @param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top @param {Number} newVal New scroll value */ function setScrollOffset(elm, isLeft, newVal) { // are we dealing with the window object or the document object? if (elm.window) { // we need to get whichever value we're not setting elm.scrollTo( isLeft ? newVal : getScrollOffset(elm, true), !isLeft ? newVal : getScrollOffset(elm, false) ); } else { elm['scroll' + (isLeft ? 'Left' : 'Top')] = newVal; } } /** @name glow.dom-scrollOffset @private @description Set/get the scrollTop / scrollLeft of a NodeList @param {glow.dom.NodeList} nodeList Elements to get / set the position of @param {Boolean} isLeft True if we're dealing with left scrolling, otherwise top @param {Number} [val] Val to set (if not provided, we'll get the value) @returns NodeList for sets, Number for gets */ function scrollOffset(nodeList, isLeft, val) { var i = nodeList.length; if (val !== undefined) { while (i--) { setScrollOffset(nodeList[i], isLeft, val); } return nodeList; } else { return getScrollOffset(nodeList[0], isLeft); } } /** @name glow.NodeList#hide @function @description Hides all items in the NodeList. @returns {glow.NodeList} @example // Hides all list items within #myList glow("#myList li").hide(); */ NodeListProto.hide = function() { return this.css('display', 'none').css('visibility', 'hidden'); }; /** @name glow.NodeList#show @function @description Shows all hidden items in the NodeList. @returns {glow.NodeList} @example // Show element with ID myDiv glow("#myDiv").show(); @example // Show all list items within #myList glow("#myList li").show(); */ NodeListProto.show = function() { var i = this.length, len = this.length, currItem, itemStyle; while (i--) { /* Create a NodeList for the current item */ currItem = new glow.NodeList(this[i]); itemStyle = currItem[0].style; if (currItem.css('display') == 'none') { itemStyle.display = ''; itemStyle.visibility = 'visible'; /* If display is still none, set to block */ if (currItem.css('display') == 'none') { itemStyle.display = 'block'; } } } return this; }; /** @name glow.NodeList#offset @function @description Gets the offset from the top left of the document. If the NodeList contains multiple items, the offset of the first item is returned. @returns {Object} Returns an object with "top" & "left" properties in pixels @example glow("#myDiv").offset().top */ NodeListProto.offset = function() { // 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) var elm = this[0], doc = document, docElm = doc.documentElement, docScrollPos = { x: getScrollOffset(window, true), y: getScrollOffset(window, false) } //this is simple(r) if we can use 'getBoundingClientRect' // Sorry but the sooper dooper simple(r) way is not accurate in Safari 4 if (!glow.env.webkit && elm.getBoundingClientRect) { var rect = elm.getBoundingClientRect(); return { top: Math.floor(rect.top) /* getBoundingClientRect is realive to top left of the viewport, so we need to sort out scrolling offset */ + docScrollPos.y /* IE adds the html element's border to the value. We can deduct this value using client(Top|Left). However, if the user has done html{border:0} clientTop will still report a 2px border in IE quirksmode so offset will be off by 2. Hopefully this is an edge case but we may have to revisit this in future */ - docElm.clientTop, left: Math.floor(rect.left) //see above for docs on all this stuff + docScrollPos.x - docElm.clientLeft }; } else { //damnit, let's go the long way around var top = elm.offsetTop, left = elm.offsetLeft, originalElm = elm, nodeNameLower, docBody = document.body, //does the parent chain contain a position:fixed element involvesFixedElement = false, offsetParentBeforeBody = elm; //add up all the offset positions while (elm = elm.offsetParent) { left += elm.offsetLeft; top += elm.offsetTop; //if css position is fixed, we need to add in the scroll offset too, catch it here if (getCssValue(elm, 'position') == 'fixed') { involvesFixedElement = true; } //gecko & webkit (safari 3) don't add on the border for positioned items if (glow.env.gecko || glow.env.webkit > 500) { left += parseInt(getCssValue(elm, 'border-left-width')) || 0; top += parseInt(getCssValue(elm, 'border-top-width')) || 0; } //we need the offset parent (before body) later if (elm.nodeName.toLowerCase() != 'body') { offsetParentBeforeBody = elm; } } //deduct all the scroll offsets elm = originalElm; while ((elm = elm.parentNode) && (elm != docBody) && (elm != docElm)) { left -= elm.scrollLeft; top -= elm.scrollTop; //FIXES //gecko doesn't add the border of contained elements to the offset (overflow!=visible) if (glow.env.gecko && getCssValue(elm, 'overflow') != 'visible') { left += parseInt(getCssValue(elm, 'border-left-width')); top += parseInt(getCssValue(elm, 'border-top-width')); } } //if we found a fixed position element we need to add the scroll offsets if (involvesFixedElement) { left += docScrollPos.x; top += docScrollPos.y; } //FIXES // Webkit < 500 body's offset gets counted twice for absolutely-positioned elements (or if there's a fixed element) // Gecko - non-absolutely positioned elements that are direct children of body get the body offset counted twice if ( (glow.env.webkit < 500 && (involvesFixedElement || getCssValue(offsetParentBeforeBody, 'position') == 'absolute')) || (glow.env.gecko && getCssValue(offsetParentBeforeBody, 'position') != 'absolute') ) { left -= docBody.offsetLeft; top -= docBody.offsetTop; } return {left:left, top:top}; } }; /** @name glow.NodeList#position @function @description Get the top & left position of an element relative to its positioned parent This is useful if you want to make a position:static element position:absolute and retain the original position of the element @returns {Object} An object with 'top' and 'left' number properties @example // get the top distance from the positioned parent glow("#elm").position().top */ NodeListProto.position = function() { var positionedParent = new glow.NodeList( getPositionedParent(this[0]) ), hasPositionedParent = !!positionedParent[0], // element margins to deduct marginLeft = parseInt( this.css('margin-left') ) || 0, marginTop = parseInt( this.css('margin-top') ) || 0, // offset parent borders to deduct, set to zero if there's no positioned parent positionedParentBorderLeft = ( hasPositionedParent && parseInt( positionedParent.css('border-left-width') ) ) || 0, positionedParentBorderTop = ( hasPositionedParent && parseInt( positionedParent.css('border-top-width') ) ) || 0, // element offsets elOffset = this.offset(), positionedParentOffset = hasPositionedParent ? positionedParent.offset() : {top: 0, left: 0}; return { left: elOffset.left - positionedParentOffset.left - marginLeft - positionedParentBorderLeft, top: elOffset.top - positionedParentOffset.top - marginTop - positionedParentBorderTop } }; /* Get the 'real' positioned parent for an element, otherwise return null. */ function getPositionedParent(elm) { var offsetParent = elm.offsetParent, doc = document, docElm = doc.documentElement; // get the real positioned parent // IE places elements with hasLayout in the offsetParent chain even if they're position:static // Also, <body> and <html> can appear in the offsetParent chain, but we don't want to return them if they're position:static while (offsetParent && new glow.NodeList(offsetParent).css('position') == 'static') { offsetParent = offsetParent.offsetParent; } // sometimes the <html> element doesn't appear in the offsetParent chain, even if it has position:relative if (!offsetParent && new glow.NodeList(docElm).css('position') != 'static') { offsetParent = docElm; } return offsetParent || null; } }); Glow.provide(function(glow) { var NodeListProto = glow.NodeList.prototype, document = window.document, undefined, keyEventNames = ' keypress keydown keyup '; /** @name glow.NodeList#on @function @description Listen for an event. This will listen for a particular event on each dom node in the NodeList. If you're listening to many children of a particular item, you may get better performance from {@link glow.NodeList#delegate}. @param {String} eventName Name of the event to listen for. This can be any regular DOM event ('click', 'mouseover' etc) or a special event of NodeList. @param {Function} callback Function to call when the event fires. The callback is passed a single event object. The type of this object is {@link glow.DomEvent} unless otherwise stated. @param {Object} [thisVal] Value of 'this' within the callback. By default, this is the dom node being listened to. @returns this @example glow.get('#testLink').on('click', function(domEvent) { // do stuff // if you want to cancel the default action (following the link)... return false; }); */ NodeListProto.on = function(eventName, callback, thisVal) { var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1); if (isKeyEvent) { glow.events._addKeyListener(this, eventName, callback, thisVal); } else { // assume it's a DOM event glow.events._addDomEventListener(this, eventName, callback, thisVal); } return this; } /** @name glow.NodeList#detach @function @description detach a listener from elements This will detach the listener from each dom node in the NodeList. @param {String} eventName Name of the event to detach the listener from @param {Function} callback Listener callback to detach @returns this @example function clickListener(domEvent) { // ... } // adding listeners glow.get('a').on('click', clickListener); // removing listeners glow.get('a').detach('click', clickListener); */ NodeListProto.detach = function(eventName, callback, thisVal) { var isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1); if (isKeyEvent) { glow.events._removeKeyListener(this, eventName, callback); } else { // assume it's a DOM event glow.events._removeDomEventListener(this, eventName, callback, thisVal); } return this; } /** @name glow.NodeList#delegate @function @description Listen for an event occurring on child elements matching a selector. 'delegate' will catch events which occur on matching items created after the listener was added. @param {String} eventName Name of the event to listen for. This can be any regular DOM event ('click', 'mouseover' etc) or a special event of NodeList. @param {String} selector CSS selector of child elements to listen for events on For example, if you were wanting to hear events from links, this would be 'a'. @param {Function} callback Function to call when the event fires. The callback is passed a single event object. The type of this object is {@link glow.DomEvent} unless otherwise stated. @param {Object} [thisVal] Value of 'this' within the callback. By default, this is the dom node matched by 'selector'. @returns this @example // Using 'on' to catch clicks on links in a list glow.get('#nav a').on('click', function() { // do stuff }); // The above adds a listener to each link, any links created later // will not have this listener, so we won't hear about them. // Using 'delegate' to catch clicks on links in a list glow.get('#nav').delegate('click', 'a', function() { // do stuff }); // The above only adds one listener to #nav which tracks clicks // to any links within. This includes elements created after 'delegate' // was called. @example // Using delegate to change class names on table rows so :hover // behaviour can be emulated in IE6 glow.get('#contactData').delegate('mouseover', 'tr', function() { glow.get(this).addClass('hover'); }); glow.get('#contactData').delegate('mouseout', 'tr', function() { glow.get(this).removeClass('hover'); }); */ NodeListProto.delegate = function(eventName, selector, callback, thisVal) { var i = this.length, attachTo, isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1); while (i--) { attachTo = this[i]; if (isKeyEvent) { // glow.events._addKeyListener([attachTo], eventName, handler); } else { // assume it's a DOM event glow.events._addDomEventListener([attachTo], eventName, callback, thisVal, selector); } } return this; } /** @name glow.NodeList#detachDelegate @function @description detach a delegated listener from elements This will detach the listener from each dom node in the NodeList. @param {String} eventName Name of the event to detach the listener from @param {String} selector CSS selector of child elements the listener is listening to @param {Function} callback Listener callback to detach @returns this @example function clickListener(domEvent) { // ... } // adding listeners glow.get('#nav').delegate('click', 'a', clickListener); // removing listeners glow.get('#nav').detachDelegate('click', 'a', clickListener); */ NodeListProto.detachDelegate = function(eventName, selector, callback, thisVal) { var i = this.length, attachTo, isKeyEvent = (keyEventNames.indexOf(' ' + eventName + ' ') > -1), handler; while (i--) { attachTo = this[i]; if (isKeyEvent) { // glow.events._removeKeyListener([attachTo], eventName, handler); } else { glow.events._removeDomEventListener([attachTo], eventName, callback, selector); } } return this; } /** @name glow.NodeList#fire @function @param {String} eventName Name of the event to fire @param {glow.events.Event} [event] Event object to pass into listeners. You can provide a simple object of key / value pairs which will be added as properties of a glow.events.Event instance. @description Fire an event on dom nodes within the NodeList Note, this will only trigger event listeners to be called, it won't for example, move the mouse or click a link on the page. @returns glow.events.Event @example glow.get('#testLink').on('click', function() { alert('Link clicked!'); }); // The following causes 'Link clicked!' to be alerted, but doesn't // cause the browser to follow the link glow.get('#testLink').fire('click'); */ NodeListProto.fire = function(eventName, event) { return glow.events.fire(this, eventName, event); } /** @name glow.NodeList#event:mouseenter @event @description Fires when the mouse enters the element specifically, does not bubble @param {glow.events.DomEvent} event Event Object */ /** @name glow.NodeList#event:mouseleave @event @description Fires when the mouse leaves the element specifically, does not bubble @param {glow.events.DomEvent} event Event Object */ /** @name glow.NodeList#event:keydown @event @description Fires when the user presses a key Only fires if the element has focus, listen for this event on the document to catch all keydowns. This event related to the user pressing a key on the keyboard, if you're more concerned about the character entered, see the {@link glow.NodeList#event:keypress keypress} event. keydown will only fire once, when the user presses the key. The order of events is keydown, keypress*, keyup. keypress may fire many times if the user holds the key down. @param {glow.events.KeyboardEvent} event Event Object */ /** @name glow.NodeList#event:keypress @event @description Fires when a key's command executes. For instance, if you hold down a key, it's action will occur many times. This event will fire on each action. This event is useful when you want to react to keyboard repeating, or to detect when a character is entered into a field. The order of events is keydown, keypress*, keyup. keypress may fire many times if the user holds the key down. @param {glow.events.KeyboardEvent} event Event Object */ /** @name glow.NodeList#event:keyup @event @description Fires when the user releases a key Only fires if the element has focus, listen for this event on the document to catch all keyups. This event related to the user pressing a key on the keyboard, if you're more concerned about the character entered, see the {@link glow.NodeList#event:keypress keypress} event. The order of events is keydown, keypress*, keyup. keypress may fire many times if the user holds the key down. @param {glow.events.KeyboardEvent} event Event Object */ }); Glow.complete('core', '2.0.0-alpha1'); ��������������������������������glow/2.0.0-alpha1/core.js���������������������������������������������������������������������������100644 � 0 � 0 � 130372 11343467400 12314� 0����������������������������������������������������������������������������������������������������ustar������������������������������������������������������������������� 0 � 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright 2010 British Broadcasting Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ if(!window.Glow){window.Glow={provide:function(a){a(glow);},complete:function(b,a){glow.version=a;}};window.glow=function(a){return new glow.NodeList(a);};glow.load=function(){throw new Error("Method load() is not available without glow.js");};}Glow.provide(function(a){});Glow.provide(function(g){var b=navigator.userAgent.toLowerCase(),i=[0,NaN],e=(/opera[\s\/]([\w\.]+)/.exec(b)||i)[1],a=e?NaN:(/msie ([\w\.]+)/.exec(b)||i)[1],c=(/rv:([\w\.]+).*gecko\//.exec(b)||i)[1],h=(/applewebkit\/([\w\.]+)/.exec(b)||i)[1],d=(/khtml\/([\w\.]+)/.exec(b)||i)[1],k=parseFloat,f={};f.gecko=k(c);f.ie=k(a);f.opera=k(e);f.webkit=k(h);f.khtml=k(d);f.standardsMode=document.compatMode!="BackCompat"&&(!f.ie||f.ie>=6);f.version=a||c||h||e||d||"";g.env=f;});Glow.provide(function(g){var k=[],e=[],m=0,n=false;g._readyBlockers={};g.ready=function(i){if(this.isReady){i();}else{k.push(i);}return g;};g.onDomReady=function(i){if(g.isDomReady){i();}else{e.push(i);}};g._addReadyBlock=function(i){if(typeof g._readyBlockers[i]==="undefined"){g._readyBlockers[i]=0;}g._readyBlockers[i]++;g.isReady=false;m++;return g;};g._removeReadyBlock=function(i){if(g._readyBlockers[i]){g._readyBlockers[i]--;m--;if(!m){g.isReady=true;h();}}return g;};if(g._build){for(var d=0,f=g._build.loading.length;d<f;d++){g._addReadyBlock("glow_loading_"+g._build.loading[d]);}for(var b=0,l=g._build.callbacks.length;b<l;b++){if(g._addReadyBlock){g._addReadyBlock("glow_loading_loadedcallback");}}}function a(){g.isDomReady=true;for(var p=0,o=e.length;p<o;p++){e[p]();}}function h(){if(n){return;}n=true;while(k.length){var i=k.shift();i(g);if(m){break;}}n=false;}function c(){if(g.isDomReady){return;}g._addReadyBlock("glow_domReady");function i(){h();g._removeReadyBlock("glow_domReady");}if(document.readyState=="complete"){i();}else{if(g.env.ie&&document.attachEvent){if(document.documentElement.doScroll&&window==top){(function(){try{document.documentElement.doScroll("left");}catch(o){setTimeout(arguments.callee,0);return;}i();})();}else{document.attachEvent("onreadystatechange",function(){if(document.readyState=="complete"){document.detachEvent("onreadystatechange",arguments.callee);i();}});}}else{if(document.readyState){(function(){if(/loaded|complete/.test(document.readyState)){i();}else{setTimeout(arguments.callee,0);}})();}else{if(document.addEventListener){document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);i();},false);}else{throw new Error("Unable to bind glow ready listener to document.");}}}}}g.notSupported=(g.env.ie<6||(g.env.gecko<1.9&&!/^1\.8\.1/.test(env.version))||g.env.opera<9||g.env.webkit<412);g.isSupported=!g.notSupported;if(g.notSupported){g._addReadyBlock("glow_browserSupport");}c();});Glow.provide(function(c){var a={},b;a.apply=function(d,f){for(var e in f){if(f.hasOwnProperty(e)){d[e]=f[e];}}return d;};a.extend=function(e,h,d){var g=function(){},i;g.prototype=h.prototype;i=new g();e.prototype=i;i.constructor=e;e.base=h;if(d){a.apply(e.prototype,d);}};c.util=a;});Glow.provide(function(g){var i={};var c={},a=1,e=1,b="__eventId"+g.UID;i.addListeners=function(q,k,s,t){var r=[],l,o,m,n;var p=q.length;while(p--){l=q[p][b];if(!l){l=q[p][b]=e++;}o=[s,t];m=c[l];if(!m){m=c[l]={};}n=m[k];if(!n){m[k]=[o];}else{n[n.length]=o;}}return i;};i._getPrivateEventKey=function(k){if(!k[b]){k[b]=objid++;}return k[b];};i.fire=function(m,l,o){if(!o){o=new i.Event();}else{if(o.constructor===Object){o=new i.Event(o);}}for(var n=0,k=m.length;n<k;n++){f(m[n],l,o);}return o;};function f(r,n,k,s){var l=r[b],p,q;if(!l||!c[l]){return k;}p=c[l][n];if(!p){return k;}p=p.slice(0);for(var m=0,o=p.length;m<o;m++){returnVal=p[m][0].call((p[m][1]||s||r),k);if(returnVal===false){k.preventDefault();}}return k;}i._callListeners=f;i.removeAllListeners=function(l){var k,m=l.length;while(m--){k=l[m][b];if(!k){return false;}else{delete c[k];}}return true;};i.removeListeners=function(q,n,r){var m,l,p=q.length;while(p--){m=q[p][b];if(!m||!c[m]){return i;}l=c[m][n];if(!l){return i;}for(var o=0,k=l.length;o<k;o++){if(l[o][0]===r){l.splice(o,1);break;}}}return i;};i._copyEvent=function(q,r){var p,m=[q].length,o,l,s,t;while(m--){var k=[q][m][b];o=c[k];if(!k){return false;}else{for(var n in c[k]){l=n;s=c[k][n][0][0];t=c[k][n][0][1];}i._addDomEventListener([r],l,s,t);}return;}};i.Target=function(){};var d=i.Target.prototype;i.Target.extend=function(k){g.util.apply(k,g.events.Target.prototype);};d.on=function(k,m,l){g.events.addListeners([this],k,m,l);return this;};d.detach=function(k,l){g.events.removeListeners(this,k,l);return this;};d.fire=function(k,l){return f(this,k,l);};i.Event=function(k){if(k){g.util.apply(this,k);}};var h=i.Event.prototype;h.preventDefault=function(){this._defaultPrevented=true;};h.defaultPrevented=function(){return this._defaultPrevented;};g.events=i;});Glow.provide(function(g){var a=window.document,d=undefined,f=[];function e(k,i){this.nativeEvent=k;this.type=k.type;if(k.target){this.source=k.target;}else{if(k.srcElement){this.source=k.srcElement;}}if(this.source&&this.source.nodeType!==1){this.source=this.source.parentNode;}this.related=k.relatedTarget||(this.type=="mouseover"?k.fromElement:k.toElement);this.shiftKey=(k.shiftKey===d)?d:!!k.shiftKey;this.altKey=(k.altKey===d)?d:!!k.altKey;this.ctrlKey=(k.ctrlKey===d)?d:!!k.ctrlKey;this.button=g.env.ie?(k.button&1?0:k.button&2?2:1):k.button;if(k.pageX!==d||k.pageY!==d){this.mouseTop=k.pageY;this.mouseLeft=k.pageX;}else{if(k.clientX!==d||k.clientY!==d){this.mouseTop=k.clientY+a.body.scrollTop+a.documentElement.scrollTop;this.mouseLeft=k.clientX+a.body.scrollLeft+a.documentElement.scrollLeft;}}if(this.type=="mousewheel"){this.wheelDelta=k.wheelDelta?k.wheelDelta/120:k.detail?-k.detail/3:0;}for(var h in i){this[h]=i[h];}}g.util.extend(e,g.events.Event);g.events._addDomEventListener=function(l,p,q,r,m){var n=l.length,o,h,k=p+(m?"/"+m:"");while(n--&&l[n]){o=l[n];g.events.addListeners([o],p,q,r);h=g.events._getPrivateEventKey(o);if(!f[h]){f[h]={};}if(f[h][k]&&f[h][k].count>0){f[h][k].count++;continue;}f[h][k]={callback:null,count:1};(function(i){var s=b(i,p,m);if(i.addEventListener){i.addEventListener(s.domName,s,(p==="focus"||p==="blur"));}else{if(i.attachEvent){if(p==="focus"){i.attachEvent("onfocusin",s);}else{if(p==="blur"){i.attachEvent("onfocusout",s);}}i.attachEvent("on"+s.domName,s);}}f[h][k].callback=s;})(o);}};function b(k,i,h){var l;if(i==="mouseenter"){l=function(n,p){var q=new g.events.DomEvent(n),o=p||k;if(!new g.NodeList(o).contains(q.related)){var m=g.events._callListeners(k,i,q,p);if(typeof m==="boolean"){return m;}else{return !q.defaultPrevented();}}};if(h){l=c(k,i,h,l);}l.domName="mouseover";}else{if(i==="mouseleave"){l=function(n,p){var q=new g.events.DomEvent(n),o=p||k;if(!new g.NodeList(o).contains(q.related)){var m=g.events._callListeners(k,i,q,p);if(typeof m==="boolean"){return m;}else{return !q.defaultPrevented();}}};if(h){l=c(k,i,h,l);}l.domName="mouseout";}else{l=function(n,p){var o=new g.events.DomEvent(n);var m=g.events._callListeners(k,i,o,p);if(typeof m==="boolean"){return m;}else{return !o.defaultPrevented();}};if(h){l=c(k,i,h,l);}l.domName=i;}}return l;}function c(k,i,h,l){return function(m){var n=new g.events.DomEvent(m);node=n.source;while(node){if(!!g._sizzle.matches(h,[node]).length){return l(m,node);}if(node===k){break;}node=node.parentNode;}};}g.events._removeDomEventListener=function(m,q,r,n){var o=m.length,p,h,k=q+(n?"/"+n:""),l,s;while(o--&&m[o]){p=m[o];h=g.events._getPrivateEventKey(p);if(!f[h]&&!f[h][k]){continue;}g.events.removeListeners([p],q,r);l=f[h][k];if(l.count>0){l.count--;if(l.count===0){s=l.callback;if(p.removeEventListener){p.removeEventListener(s.domName,s,(q==="focus"||q==="blur"));}else{if(p.detachEvent){if(q==="focus"){p.detachEvent("onfocusin",s);}else{if(q==="blur"){p.detachEvent("onfocusout",s);}}p.detachEvent("on"+s.domName,s);}}}}}};g.events.DomEvent=e;});Glow.provide(function(l){var t=window.document,g,i,e=l.env,b,m,u=l.events.DomEvent,c=l.events._callListeners,q=l.events._getPrivateEventKey,h={};function r(y){if(b){this.key=a(b);}if(m){this.keyChar=String.fromCharCode(m);}u.call(this,y);}l.util.extend(r,u,{key:"",keyChar:""});function p(A,y,z){if(A.addEventListener){A.addEventListener(y,z,false);}else{if(A.attachEvent){A.attachEvent("on"+y,z);}}}function f(A,y,z){if(A.removeEventListener){A.removeEventListener(y,z,false);}else{if(A.detachEvent){A.detachEvent("on"+y,z);}}}function v(A,y){var z;if(e.gecko||e.opera){return !s[A];}z=a(A);if(z.length===1&&!s[A]){return !(e.webkit&&y);}return false;}function w(z){var y,B,A,C={};y=function(D){var G=D.keyCode,F,E;if(!C[G]){b=G;m=g;F=c(z,"keydown",new r(D)).defaultPrevented();C[G]=true;}if(!v(G,F)){E=c(z,"keypress",new r(D)).defaultPrevented();}return !(F||E);};B=function(D){m=D.charCode||D.keyCode;if(a(b).length>1){m=g;}var E=c(z,"keypress",new r(D)).defaultPrevented();return !E;};A=function(D){var F=D.keyCode,E;b=F;m=g;E=c(z,"keyup",new r(D)).defaultPrevented();C[F]=false;b=g;return !E;};p(z,"keydown",y);p(z,"keypress",B);p(z,"keyup",A);return[1,y,B,A];}l.events._addKeyListener=function(A,z,E,D){var C=A.length,B,y;l.events.addListeners(A,z,E,D);while(C--){B=A[C];y=q(B);if(h[y]){h[y][0]++;continue;}else{h[y]=w(B);}}};l.events._removeKeyListener=function(A,z,E){var C=A.length,B,y,D;l.events.removeListeners(A,z,E);while(C--){B=A[C];y=q(B);D=h[y];if(!D){return;}if(--D[0]===0){f(B,"keydown",D[1]);f(B,"keypress",D[2]);f(B,"keyup",D[3]);h[y]=g;}}};function a(y){if((y>=x&&y<=o)||(y>=k&&y<=d)){return String.fromCharCode(y).toLowerCase();}return n[y]||"unknown"+y;}var x="A".charCodeAt(0),o="Z".charCodeAt(0),k="0".charCodeAt(0),d="9".charCodeAt(0),n={8:"backspace",9:"tab",13:"return",16:"shift",17:"control",18:"alt",19:"pause",27:"escape",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",44:"printscreen",45:"insert",46:"delete",59:";",61:"=",91:"meta",93:"menu",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scrolllock",188:",",189:"-",190:".",191:"/",192:"'",219:"[",220:"\\",221:"]",222:"#",223:"`",224:"meta",226:"\\"},s={};if(e.gecko){n[107]="=";s={16:1,17:1,18:1,144:1,145:1};}else{if(e.opera){n[42]="*";n[43]="+";n[47]="/";n[222]="'";n[192]="`";s={16:1,17:1,18:1};}else{if(e.webkit||e.ie){n[186]=";";n[187]="=";}}}l.events.KeyboardEvent=r;});Glow.provide(function(k){var f,c,i=window.document,b=Array.prototype.slice,h=Array.prototype.push;function a(e){e&&this.push(e);}f=a.prototype;f.length=0;a._strToNodes=(function(){var m=i.createElement("div"),o=[1,"<table>","</table>"],p=[0,"",""],l=[1,"b<div>","</div>"],e=[3,"<table><tbody><tr>","</tr></tbody></table>"],n={caption:o,thead:o,th:e,colgroup:o,tbody:o,tr:[2,"<table><tbody>","</tbody></table>"],td:e,tfoot:o,option:[1,'<select multiple="multiple">',"</select>"],legend:[1,"<fieldset>","</fieldset>"],link:l,script:l,style:l,"!":l};function q(y){var s=[],v=(/^<([\w!]+)/.exec(y)||[])[1],t=n[v]||p,u=t[0],A=m,w,z=0,x;A.innerHTML=(t[1]+y+t[2]);while(u--){A=A.lastChild;}if(t===o&&y.indexOf("<tbody")===-1){while(x=A.firstChild){if(x.nodeName!="TBODY"){s[z++]=x;}A.removeChild(x);}}else{while(x=A.firstChild){s[z++]=A.removeChild(x);}}return s;}return q;})();var d=function(e){return b.call(e,0);};try{b.call(i.documentElement.childNodes,0);}catch(g){d=function(m){if(m instanceof Object){return b.call(m,0);}var l=m.length,e=[];while(l--){e[l]=m[l];}return e;};}f.push=function(e){if(e){if(typeof e==="string"){if(e.charAt(0)==="<"){e=a._strToNodes(e);}else{e=k._sizzle(e);}h.apply(this,e);}else{if(e.nodeType||e.window==e){if(this.length){h.call(this,e);}else{this[0]=e;this.length=1;}}else{if(e.length!==c){if(e.constructor!=Array){e=d(e);}h.apply(this,e);}}}}return this;};f.eq=function(l){var e=this.length,m=e;if(!(l instanceof a)){l=new a(l);}if(e!=l.length){return false;}while(m--){if(this[m]!==l[m]){return false;}}return true;};f.slice=function(){return new a(b.apply(this,arguments));};f.sort=function(m){var l=d(this),e=m?l.sort(m):k._sizzle.uniqueSort(l);return new a(e);};f.item=function(e){return this.slice(e,(e+1)||this.length);};f.each=function(m){for(var l=0,e=this.length;l<e;l++){if(m.call(this[l],l,this)===false){break;}}return this;};f.filter=function(o){var n=[],l=0;if(typeof o==="string"){n=k._sizzle.matches(o,this);}else{for(var m=0,e=this.length;m<e;m++){if(o.call(this[m],m,this)){n[l++]=this[m];}}}return new a(n);};f.is=function(e){if(!this[0]){return false;}return !!k._sizzle.matches(e,[this[0]]).length;};k.NodeList=a;});Glow.provide(function(i){var d,f=i.NodeList.prototype,e={"class":"className","for":"htmlFor",maxlength:"maxLength"},b="_uniqueData"+i.UID,h=1,c=[];f.addClass=function(k){var l=this.length;while(l--){if(this[l].nodeType===1){g(this[l],k);}}return this;};function g(l,k){if((" "+l.className+" ").indexOf(" "+k+" ")===-1){l.className+=(l.className?" ":"")+k;}}f.attr=function(){var n=arguments,l=n.length,k=this.length,m=keyvals=n[0],r="",p,q;if(this.length===0){return(l>1)?this:d;}if(typeof keyvals==="object"){for(m in keyvals){if(!keyvals.hasOwnProperty(m)){continue;}if(i.env.ie<8){r=e[m.toLowerCase()];}var o=k;while(o--){p=this[o];if(p.nodeType!==1){continue;}if(r){p[r]=keyvals[m];}else{p.setAttribute(m,keyvals[m],0);}}}return this;}else{p=this[0];if(p.nodeType!==1){return(l>1)?this:d;}if(l===1){if(p.attributes[m]){return(!p.attributes[m].specified)?"":p.attributes[m].value;}else{if(p.getAttributeNode){q=p.getAttributeNode(m,0);return(q===null)?"":q.value;}else{value=p.getAttribute(m,0,2);return(value===null)?"":value;}}}else{if(i.env.ie<8){r=e[m.toLowerCase()];}if(r){p[r]=n[1];}else{p.setAttribute(m,n[1],0);}return this;}}};i.NodeList._copyData=function(l,k){if(!l[b]){return;}else{k=new i.NodeList(k);k.data(c[l[b]]);return;}};i.NodeList._destroyData=function(k){if(!k&&!k[0][b]){return;}else{removeFromNode=new i.NodeList(k);removeFromNode.removeData();return;}};f.data=function(p,r){var n=arguments,l=n.length,k=p,m,q;if(l>1){var o=this.length;while(o--){q=this[o];if(q.nodeType!==1){continue;}m=q[b];if(!m){m=h++;q[b]=m;c[m]={};}c[m][p]=r;}return this;}else{if(typeof k==="object"){var o=this.length;while(o--){q=this[o];if(q.nodeType!==1){continue;}m=q[b];if(!m){m=h++;q[b]=m;c[m]={};}for(p in k){c[m][p]=k[p];}}return this;}else{q=this[0];if(q===d||q.nodeType!==1){return d;}if(!(m=q[b])){return d;}if(p){return c[m][p];}return c[m];}}};f.hasAttr=function(k){var l;l=this[0];if(this.length&&l.nodeType===1){if(l.attributes[k]){return !!l.attributes[k].specified;}if(l.hasAttribute){return l.hasAttribute(k);}else{return l.attributes[k]!==d;}}};f.hasClass=function(k){if(this.length&&this[0].nodeType===1){return((" "+this[0].className+" ").indexOf(" "+k+" ")>-1);}};f.prop=function(l,q){var p=l,k=arguments.length;if(this.length===0){return;}if(k===2&&typeof l==="string"){for(var o=0,n=this.length;o<n;o++){if(this[o].nodeType===1){this[o][l]=q;}}return this;}else{if(k===1&&p.constructor===Object){for(var m in p){for(var o=0,n=this.length;o<n;o++){if(this[o].nodeType===1){this[o][m]=p[m];}}}return this;}else{if(k===1&&typeof l==="string"){if(this[0].nodeType===1){return this[0][l];}}else{throw new Error("Invalid parameters.");}}}};f.removeAttr=function(l){var n;for(var m=0,k=this.length;m<k;m++){if(this[m].nodeType===1){if(i.env.ie<8){if((n=e[l.toLowerCase()])){this[m][n]="";}}if(this[m].removeAttribute){this[m].removeAttribute(l);}}}return this;};f.removeClass=function(k){var m;var l=this.length;while(l--){m=this[l];if(m.className){a(m,k);}}return this;};function a(n,l){var k=n.className.split(" "),o=[];k=n.className.split(" ");o=[];var m=k.length;while(m--){if(k[m]!==l){k[m]&&o.unshift(k[m]);}}n.className=(o.length)?o.join(" "):"";}f.removeData=function(m){var o,l=this.length,k;while(l--){o=this[l];k=o[b];if(k!==d){switch(arguments.length){case 0:c[k]=d;o[b]=d;try{delete o[b];}catch(n){o.removeAttribute&&o.removeAttribute(b);}break;case 1:c[k][m]=d;delete c[k][m];break;}}}return this;};f.toggleClass=function(l){var n;for(var m=0,k=this.length;m<k;m++){n=this[m];if(n.className){if((" "+n.className+" ").indexOf(" "+l+" ")>-1){a(n,l);}else{g(n,l);}}}return this;};f.val=function(){function o(w){var t=w.type,u=w.checked,y=w.value,z=[],v=0;if(t=="radio"){return u?y:"";}else{if(t=="checkbox"){return u?y:"";}else{if(t=="select-one"){return w.selectedIndex>-1?w.options[w.selectedIndex].value:"";}else{if(t=="select-multiple"){for(var x=w.options.length;v<x;v++){if(w.options[v].selected){z[z.length]=w.options[v].value;}}return z;}else{return y;}}}}}function p(u){var B={},A={},x=u.elements,z=0,w=x.length,t,D,y,v,C;for(;z<w;z++){D=x[z];C=D.nodeName.toLowerCase();t=D.name;if(C=="fieldset"||C=="object"||!t){continue;}if(D.type=="checkbox"&&!D.checked){if(!t in B){B[t]=undefined;}}else{if(D.type=="radio"){if(A[t]){A[t][A[t].length]=D;}else{A[t]=[D];}}else{var E=o(D);if(t in B){if(B[t].push){B[t][B[t].length]=E;}else{B[t]=[B[t],E];}}else{B[t]=E;}}}}for(z in A){y=0;for(w=A[z].length;y<w;y++){v=A[z][y];t=v.name;if(v.checked){B[v.name]=v.value;break;}}if(!t in B){B[t]=undefined;}}return B;}function q(u,C){var t,D,z={},x,y=0,v,A,B,w;for(t in C){D=u[t];if(D&&D[0]&&!D.options){C[t]=C[t]&&C[t].push?C[t]:[C[t]];z.radios=[];z.checkboxesSelects=[];z.multiSelects=[];z.other=[];for(y=0;D[y];y++){w=D[y].type;if(w=="radio"){x="radios";}else{if(w=="select-one"||w=="checkbox"){x="checkboxesSelects";}else{if(w=="select-multiple"){x="multiSelects";}else{x="other";}}}z[x][z[x].length]=D[y];}for(y=0;z.multiSelects[y];y++){C[t]=m(z.multiSelects[y],C[t]);}for(y=0;z.checkboxesSelects[y];y++){m(z.checkboxesSelects[y],"");for(v=0,A=C[t].length;v<A;v++){if(m(z.checkboxesSelects[y],C[t][v])){C[t].slice(v,1);break;}}}for(y=0;z.radios[y];y++){z.radios[y].checked=false;B=false;for(v=0,A=C[t].length;v<A;v++){if(m(z.radios[y],C[t][v])){C[t].slice(v,1);B=true;break;}if(B){break;}}}for(y=0;z.other[y]&&C[t][y]!==undefined;y++){m(z.other[y],C[t][y]);}}else{if(D&&D.nodeName){m(D,C[t]);}}}}function m(v,x){var y=0,u,w=0,B,t,A;if(v.type=="select-one"){for(u=v.options.length;y<u;y++){if(v.options[y].value==x){v.selectedIndex=y;return true;}}return false;}else{if(v.type=="select-multiple"){var z=!!x.push;for(y=0,u=v.options.length;y<u;y++){t=v.options[y];A=t.value;if(z){t.selected=false;for(B=x.length;w<B;w++){if(A==x[w]){t.selected=true;x.splice(w,1);break;}}}else{return t.selected=x==A;}}return false;}else{if(v.type=="radio"||v.type=="checkbox"){v.checked=x==v.value;return x==v.value;}else{v.value=x;return true;}}}}var s=arguments,l=s[0],r=this,n=0,k=r.length;if(s.length===0){return r[0].nodeName=="FORM"?p(r[0]):o(r[0]);}if(r[0].nodeName=="FORM"){if(!typeof l=="object"){throw"value for FORM must be object";}q(r[0],l);}else{for(;n<k;n++){m(r[n],l);}}return r;};}); /* * Sizzle CSS Selector Engine - v1.0 * Copyright 2009, The Dojo Foundation * Released under the MIT, BSD, and GPL Licenses. * More information: http://sizzlejs.com/ */ (function(){var n=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,o=0,q=Object.prototype.toString,h=false,g=true;[0,0].sort(function(){g=false;return 0;});var c=function(y,e,B,C){B=B||[];var E=e=e||document;if(e.nodeType!==1&&e.nodeType!==9){return[];}if(!y||typeof y!=="string"){return B;}var z=[],v,G,J,u,x=true,w=d(e),D=y;while((n.exec(""),v=n.exec(D))!==null){D=v[3];z.push(v[1]);if(v[2]){u=v[3];break;}}if(z.length>1&&i.exec(y)){if(z.length===2&&k.relative[z[0]]){G=r(z[0]+z[1],e);}else{G=k.relative[z[0]]?[e]:c(z.shift(),e);while(z.length){y=z.shift();if(k.relative[y]){y+=z.shift();}G=r(y,G);}}}else{if(!C&&z.length>1&&e.nodeType===9&&!w&&k.match.ID.test(z[0])&&!k.match.ID.test(z[z.length-1])){var F=c.find(z.shift(),e,w);e=F.expr?c.filter(F.expr,F.set)[0]:F.set[0];}if(e){var F=C?{expr:z.pop(),set:m(C)}:c.find(z.pop(),z.length===1&&(z[0]==="~"||z[0]==="+")&&e.parentNode?e.parentNode:e,w);G=F.expr?c.filter(F.expr,F.set):F.set;if(z.length>0){J=m(G);}else{x=false;}while(z.length){var I=z.pop(),H=I;if(!k.relative[I]){I="";}else{H=z.pop();}if(H==null){H=e;}k.relative[I](J,H,w);}}else{J=z=[];}}if(!J){J=G;}if(!J){throw"Syntax error, unrecognized expression: "+(I||y);}if(q.call(J)==="[object Array]"){if(!x){B.push.apply(B,J);}else{if(e&&e.nodeType===1){for(var A=0;J[A]!=null;A++){if(J[A]&&(J[A]===true||J[A].nodeType===1&&l(e,J[A]))){B.push(G[A]);}}}else{for(var A=0;J[A]!=null;A++){if(J[A]&&J[A].nodeType===1){B.push(G[A]);}}}}}else{m(J,B);}if(u){c(u,E,B,C);c.uniqueSort(B);}return B;};c.uniqueSort=function(u){if(p){h=g;u.sort(p);if(h){for(var e=1;e<u.length;e++){if(u[e]===u[e-1]){u.splice(e--,1);}}}}return u;};c.matches=function(e,u){return c(e,null,null,u);};c.find=function(A,e,B){var z,x;if(!A){return[];}for(var w=0,v=k.order.length;w<v;w++){var y=k.order[w],x;if((x=k.leftMatch[y].exec(A))){var u=x[1];x.splice(1,1);if(u.substr(u.length-1)!=="\\"){x[1]=(x[1]||"").replace(/\\/g,"");z=k.find[y](x,e,B);if(z!=null){A=A.replace(k.match[y],"");break;}}}}if(!z){z=e.getElementsByTagName("*");}return{set:z,expr:A};};c.filter=function(D,C,G,w){var v=D,I=[],A=C,y,e,z=C&&C[0]&&d(C[0]);while(D&&C.length){for(var B in k.filter){if((y=k.match[B].exec(D))!=null){var u=k.filter[B],H,F;e=false;if(A===I){I=[];}if(k.preFilter[B]){y=k.preFilter[B](y,A,G,I,w,z);if(!y){e=H=true;}else{if(y===true){continue;}}}if(y){for(var x=0;(F=A[x])!=null;x++){if(F){H=u(F,y,x,A);var E=w^!!H;if(G&&H!=null){if(E){e=true;}else{A[x]=false;}}else{if(E){I.push(F);e=true;}}}}}if(H!==undefined){if(!G){A=I;}D=D.replace(k.match[B],"");if(!e){return[];}break;}}}if(D===v){if(e==null){throw"Syntax error, unrecognized expression: "+D;}else{break;}}v=D;}return A;};var k=c.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(e){return e.getAttribute("href");}},relative:{"+":function(z,u){var w=typeof u==="string",y=w&&!/\W/.test(u),A=w&&!y;if(y){u=u.toLowerCase();}for(var v=0,e=z.length,x;v<e;v++){if((x=z[v])){while((x=x.previousSibling)&&x.nodeType!==1){}z[v]=A||x&&x.nodeName.toLowerCase()===u?x||false:x===u;}}if(A){c.filter(u,z,true);}},">":function(z,u){var x=typeof u==="string";if(x&&!/\W/.test(u)){u=u.toLowerCase();for(var v=0,e=z.length;v<e;v++){var y=z[v];if(y){var w=y.parentNode;z[v]=w.nodeName.toLowerCase()===u?w:false;}}}else{for(var v=0,e=z.length;v<e;v++){var y=z[v];if(y){z[v]=x?y.parentNode:y.parentNode===u;}}if(x){c.filter(u,z,true);}}},"":function(w,u,y){var v=o++,e=s;if(typeof u==="string"&&!/\W/.test(u)){var x=u=u.toLowerCase();e=a;}e("parentNode",u,v,w,x,y);},"~":function(w,u,y){var v=o++,e=s;if(typeof u==="string"&&!/\W/.test(u)){var x=u=u.toLowerCase();e=a;}e("previousSibling",u,v,w,x,y);}},find:{ID:function(u,v,w){if(typeof v.getElementById!=="undefined"&&!w){var e=v.getElementById(u[1]);return e?[e]:[];}},NAME:function(v,y){if(typeof y.getElementsByName!=="undefined"){var u=[],x=y.getElementsByName(v[1]);for(var w=0,e=x.length;w<e;w++){if(x[w].getAttribute("name")===v[1]){u.push(x[w]);}}return u.length===0?null:u;}},TAG:function(e,u){return u.getElementsByTagName(e[1]);}},preFilter:{CLASS:function(w,u,v,e,z,A){w=" "+w[1].replace(/\\/g,"")+" ";if(A){return w;}for(var x=0,y;(y=u[x])!=null;x++){if(y){if(z^(y.className&&(" "+y.className+" ").replace(/[\t\n]/g," ").indexOf(w)>=0)){if(!v){e.push(y);}}else{if(v){u[x]=false;}}}}return false;},ID:function(e){return e[1].replace(/\\/g,"");},TAG:function(u,e){return u[1].toLowerCase();},CHILD:function(e){if(e[1]==="nth"){var u=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(u[1]+(u[2]||1))-0;e[3]=u[3]-0;}e[0]=o++;return e;},ATTR:function(x,u,v,e,y,z){var w=x[1].replace(/\\/g,"");if(!z&&k.attrMap[w]){x[1]=k.attrMap[w];}if(x[2]==="~="){x[4]=" "+x[4]+" ";}return x;},PSEUDO:function(x,u,v,e,y){if(x[1]==="not"){if((n.exec(x[3])||"").length>1||/^\w/.test(x[3])){x[3]=c(x[3],null,null,u);}else{var w=c.filter(x[3],u,v,true^y);if(!v){e.push.apply(e,w);}return false;}}else{if(k.match.POS.test(x[0])||k.match.CHILD.test(x[0])){return true;}}return x;},POS:function(e){e.unshift(true);return e;}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden";},disabled:function(e){return e.disabled===true;},checked:function(e){return e.checked===true;},selected:function(e){e.parentNode.selectedIndex;return e.selected===true;},parent:function(e){return !!e.firstChild;},empty:function(e){return !e.firstChild;},has:function(v,u,e){return !!c(e[3],v).length;},header:function(e){return/h\d/i.test(e.nodeName);},text:function(e){return"text"===e.type;},radio:function(e){return"radio"===e.type;},checkbox:function(e){return"checkbox"===e.type;},file:function(e){return"file"===e.type;},password:function(e){return"password"===e.type;},submit:function(e){return"submit"===e.type;},image:function(e){return"image"===e.type;},reset:function(e){return"reset"===e.type;},button:function(e){return"button"===e.type||e.nodeName.toLowerCase()==="button";},input:function(e){return/input|select|textarea|button/i.test(e.nodeName);}},setFilters:{first:function(u,e){return e===0;},last:function(v,u,e,w){return u===w.length-1;},even:function(u,e){return e%2===0;},odd:function(u,e){return e%2===1;},lt:function(v,u,e){return u<e[3]-0;},gt:function(v,u,e){return u>e[3]-0;},nth:function(v,u,e){return e[3]-0===u;},eq:function(v,u,e){return e[3]-0===u;}},filter:{PSEUDO:function(z,v,w,A){var u=v[1],x=k.filters[u];if(x){return x(z,w,v,A);}else{if(u==="contains"){return(z.textContent||z.innerText||b([z])||"").indexOf(v[3])>=0;}else{if(u==="not"){var y=v[3];for(var w=0,e=y.length;w<e;w++){if(y[w]===z){return false;}}return true;}else{throw"Syntax error, unrecognized expression: "+u;}}}},CHILD:function(e,w){var z=w[1],u=e;switch(z){case"only":case"first":while((u=u.previousSibling)){if(u.nodeType===1){return false;}}if(z==="first"){return true;}u=e;case"last":while((u=u.nextSibling)){if(u.nodeType===1){return false;}}return true;case"nth":var v=w[2],C=w[3];if(v===1&&C===0){return true;}var y=w[0],B=e.parentNode;if(B&&(B.sizcache!==y||!e.nodeIndex)){var x=0;for(u=B.firstChild;u;u=u.nextSibling){if(u.nodeType===1){u.nodeIndex=++x;}}B.sizcache=y;}var A=e.nodeIndex-C;if(v===0){return A===0;}else{return(A%v===0&&A/v>=0);}}},ID:function(u,e){return u.nodeType===1&&u.getAttribute("id")===e;},TAG:function(u,e){return(e==="*"&&u.nodeType===1)||u.nodeName.toLowerCase()===e;},CLASS:function(u,e){return(" "+(u.className||u.getAttribute("class"))+" ").indexOf(e)>-1;},ATTR:function(y,w){var v=w[1],e=k.attrHandle[v]?k.attrHandle[v](y):y[v]!=null?y[v]:y.getAttribute(v),z=e+"",x=w[2],u=w[4];return e==null?x==="!=":x==="="?z===u:x==="*="?z.indexOf(u)>=0:x==="~="?(" "+z+" ").indexOf(u)>=0:!u?z&&e!==false:x==="!="?z!==u:x==="^="?z.indexOf(u)===0:x==="$="?z.substr(z.length-u.length)===u:x==="|="?z===u||z.substr(0,u.length+1)===u+"-":false;},POS:function(x,u,v,y){var e=u[2],w=k.setFilters[e];if(w){return w(x,v,u,y);}}}};var i=k.match.POS;for(var f in k.match){k.match[f]=new RegExp(k.match[f].source+/(?![^\[]*\])(?![^\(]*\))/.source);k.leftMatch[f]=new RegExp(/(^(?:.|\r|\n)*?)/.source+k.match[f].source);}var m=function(u,e){u=Array.prototype.slice.call(u,0);if(e){e.push.apply(e,u);return e;}return u;};try{Array.prototype.slice.call(document.documentElement.childNodes,0);}catch(t){m=function(x,w){var u=w||[];if(q.call(x)==="[object Array]"){Array.prototype.push.apply(u,x);}else{if(typeof x.length==="number"){for(var v=0,e=x.length;v<e;v++){u.push(x[v]);}}else{for(var v=0;x[v];v++){u.push(x[v]);}}}return u;};}var p;if(document.documentElement.compareDocumentPosition){p=function(u,e){if(!u.compareDocumentPosition||!e.compareDocumentPosition){if(u==e){h=true;}return u.compareDocumentPosition?-1:1;}var v=u.compareDocumentPosition(e)&4?-1:u===e?0:1;if(v===0){h=true;}return v;};}else{if("sourceIndex" in document.documentElement){p=function(u,e){if(!u.sourceIndex||!e.sourceIndex){if(u==e){h=true;}return u.sourceIndex?-1:1;}var v=u.sourceIndex-e.sourceIndex;if(v===0){h=true;}return v;};}else{if(document.createRange){p=function(w,u){if(!w.ownerDocument||!u.ownerDocument){if(w==u){h=true;}return w.ownerDocument?-1:1;}var v=w.ownerDocument.createRange(),e=u.ownerDocument.createRange();v.setStart(w,0);v.setEnd(w,0);e.setStart(u,0);e.setEnd(u,0);var x=v.compareBoundaryPoints(Range.START_TO_END,e);if(x===0){h=true;}return x;};}}}function b(e){var u="",w;for(var v=0;e[v];v++){w=e[v];if(w.nodeType===3||w.nodeType===4){u+=w.nodeValue;}else{if(w.nodeType!==8){u+=b(w.childNodes);}}}return u;}(function(){var u=document.createElement("div"),v="script"+(new Date).getTime();u.innerHTML="<a name='"+v+"'/>";var e=document.documentElement;e.insertBefore(u,e.firstChild);if(document.getElementById(v)){k.find.ID=function(x,y,z){if(typeof y.getElementById!=="undefined"&&!z){var w=y.getElementById(x[1]);return w?w.id===x[1]||typeof w.getAttributeNode!=="undefined"&&w.getAttributeNode("id").nodeValue===x[1]?[w]:undefined:[];}};k.filter.ID=function(y,w){var x=typeof y.getAttributeNode!=="undefined"&&y.getAttributeNode("id");return y.nodeType===1&&x&&x.nodeValue===w;};}e.removeChild(u);e=u=null;})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){k.find.TAG=function(u,y){var x=y.getElementsByTagName(u[1]);if(u[1]==="*"){var w=[];for(var v=0;x[v];v++){if(x[v].nodeType===1){w.push(x[v]);}}x=w;}return x;};}e.innerHTML="<a href='#'></a>";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){k.attrHandle.href=function(u){return u.getAttribute("href",2);};}e=null;})();if(document.querySelectorAll){(function(){var e=c,v=document.createElement("div");v.innerHTML="<p class='TEST'></p>";if(v.querySelectorAll&&v.querySelectorAll(".TEST").length===0){return;}c=function(z,y,w,x){y=y||document;if(!x&&y.nodeType===9&&!d(y)){try{return m(y.querySelectorAll(z),w);}catch(A){}}return e(z,y,w,x);};for(var u in e){c[u]=e[u];}v=null;})();}(function(){var e=document.createElement("div");e.innerHTML="<div class='test e'></div><div class='test'></div>";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return;}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return;}k.order.splice(1,0,"CLASS");k.find.CLASS=function(u,v,w){if(typeof v.getElementsByClassName!=="undefined"&&!w){return v.getElementsByClassName(u[1]);}};e=null;})();function a(u,z,y,C,A,B){for(var w=0,v=C.length;w<v;w++){var e=C[w];if(e){e=e[u];var x=false;while(e){if(e.sizcache===y){x=C[e.sizset];break;}if(e.nodeType===1&&!B){e.sizcache=y;e.sizset=w;}if(e.nodeName.toLowerCase()===z){x=e;break;}e=e[u];}C[w]=x;}}}function s(u,z,y,C,A,B){for(var w=0,v=C.length;w<v;w++){var e=C[w];if(e){e=e[u];var x=false;while(e){if(e.sizcache===y){x=C[e.sizset];break;}if(e.nodeType===1){if(!B){e.sizcache=y;e.sizset=w;}if(typeof z!=="string"){if(e===z){x=true;break;}}else{if(c.filter(z,[e]).length>0){x=e;break;}}}e=e[u];}C[w]=x;}}}var l=document.compareDocumentPosition?function(u,e){return u.compareDocumentPosition(e)&16;}:function(u,e){return u!==e&&(u.contains?u.contains(e):true);};var d=function(e){var u=(e?e.ownerDocument||e:0).documentElement;return u?u.nodeName!=="HTML":false;};var r=function(e,A){var w=[],x="",y,v=A.nodeType?[A]:A;while((y=k.match.PSEUDO.exec(e))){x+=y[0];e=e.replace(k.match.PSEUDO,"");}e=k.relative[e]?e+"*":e;for(var z=0,u=v.length;z<u;z++){c(e,v[z],w);}return c.filter(x,w);};Glow.provide(function(e){e._sizzle=c;});return;window.Sizzle=c;})();Glow.provide(function(g){var b=g.NodeList.prototype,e=1,d="_unique"+g.UID;if(g.env.ie){var f=function(k){if(k.length==1){return k;}var m=[],h=0,l=0;for(;k[l];l++){if(k[l].getAttribute(d)!=e&&k[l].nodeType==1){m[h++]=k[l];}k[l].setAttribute(d,e);}for(l=0;k[l];l++){k[l].removeAttribute(d);}e++;return m;};}else{var f=function(k){if(k.length==1){return k;}var m=[],h=0,l=0;for(;k[l];l++){if(k[l][d]!=e&&k[l].nodeType==1){m[h++]=k[l];}k[l][d]=e;}e++;return m;};}b.parent=function(m){var k=[],h=0,l=this.length,n;while(l--){n=this[l];if(n.nodeType==1){if(m){while(n=n.parentNode){if(g._sizzle.filter(m,[n]).length){k[h++]=n;break;}}}else{if(n=n.parentNode){k[h++]=n;}}}}return new g.NodeList(f(k));};function c(q,l,n){var k=[],h=0,p,m=0,o=q.length;while(m<o){p=q[m];if(n){while(p=p[l+"Sibling"]){if(p.nodeType==1&&p.nodeName!="!"){if(g._sizzle.filter(n,[p]).length){k[h++]=p;break;}}}}else{while(p=p[l+"Sibling"]){if(p.nodeType==1&&p.nodeName!="!"){k[h++]=p;break;}}}m++;}return new g.NodeList(k);}b.prev=function(h){return c(this,"previous",h);};b.next=function(h){return c(this,"next",h);};b.get=function(h){var k=[],l=this.length;while(l--){g._sizzle(h,this[l],k);}return new g.NodeList(f(k));};b.ancestors=function(m){var k=[],h=0,l=0,o=this.length,n;while(l<o){n=this[l].parentNode;while(n&&n.nodeType==1){k[h++]=n;n=n.parentNode;}l++;}if(m){k=new g.NodeList(k);k=k.filter(m);}return new g.NodeList(f(k));};function a(m){var l=[],n=m.childNodes,k=0,h=0;for(;n[k];k++){if(n[k].nodeType==1&&n[k].nodeName!="!"){l[h++]=n[k];}}return l;}b.children=function(){var h=[],k=this.length;while(k--){h=h.concat(a(this[k]));}return new g.NodeList(h);};b.contains=function(o){var k=0,m=new g.NodeList(o)[0],l=this.length,h,n;if(!m||!this.length){return false;}if(this[0].compareDocumentPosition){while(k<l){if(this[k]==m){break;}else{if(!(this[k].compareDocumentPosition(m)&16)){return false;}}k++;}}else{if(m.contains){for(;k<l;k++){if(!(this[k].contains(m))){return false;}}}else{while(k<l){n=m;while(n=n.parentNode){if(this[k]==n){break;}}if(!n){return false;}k++;}}}return true;};});Glow.provide(function(g){var d=g.NodeList.prototype,f=window.document,b;function e(k){var l=f.createDocumentFragment(),m=0,n;while(n=k[m++]){l.appendChild(n);}return l;}function h(l,k){return function(m){var p,r,v,s,u,n;if(!this.length){return this;}if(!k&&typeof m==="string"){m=new g.NodeList(g.NodeList._strToNodes(m));}else{m=new g.NodeList(m);}if(k){r=m;p=new g.NodeList(this);}else{r=this;p=m;}s=e(p);for(var q=0,t=r.length,o=t-1;q<t;q++){u=r[q];v=s;if(n=u.parentNode){if(q!==o){s=v.cloneNode(true);k&&p.push(s.childNodes);}n.insertBefore(v,l?u.nextSibling:u);}}return k?p:r;};}function c(k,l){return function(m){var o,q,u,r,s;if(!this.length){return this;}if(!l&&typeof m==="string"){m=new g.NodeList(g.NodeList._strToNodes(m));}else{m=new g.NodeList(m);}if(l){q=m;o=new g.NodeList(this);}else{q=this;o=m;}r=e(o);for(var p=0,t=q.length,n=t-1;p<t;p++){s=q[p];u=r;if(s.nodeType===1){if(p!==n){r=u.cloneNode(true);l&&o.push(r.childNodes);}s.insertBefore(u,k?null:s.firstChild);}}return l?o:q;};}d.after=h(1);d.before=h(0);d.append=c(1);d.prepend=c(0);d.appendTo=c(1,1);d.prependTo=c(0,1);d.insertAfter=h(1,1);d.insertBefore=h(0,1);var i=f.createElement("div");d.destroy=function(){g.NodeList._destroyData(this);this.appendTo(i);i.innerHTML="";return new g.NodeList();};d.remove=function(){var l,m,k=this.length;while(k--){m=this[k];if(l=m.parentNode){l.removeChild(m);}}return this;};d.empty=g.env.ie?function(){var k=this.length,l,m;while(k--){l=this[k];while(m=l.firstChild){l.removeChild(m);}}return this;}:function(){var k=this.length;while(k--){this[k].innerHTML="";}return this;};d.replaceWith=function(k){return this.after(k).remove();};function a(k){for(var l=k.firstChild;l;l=l.nextSibling){if(l.nodeType==1){return l;}}return b;}d.wrap=function(p){p=new g.NodeList(p);if(!p[0]||p[0].nodeType!=1){return this;}var m,n,o;for(var l=0,k=this.length;l<k;l++){m=this[l];n=p[0];while(n){o=a(n);if(!o){break;}n=o;}if(m.parentNode){p.insertBefore(m);}if(l!=k-1){p=p.clone();}n.appendChild(m);}return this;};d.unwrap=function(){var m,o,k=this.parent();for(var n=0,l=k.length;n<l;n++){m=k.slice(n,n+1);o=new g.NodeList(m[0].childNodes);if(!m[0].parentNode){o.remove();m.destroy();}else{o.insertBefore(m);m.destroy();}}return this;};d.clone=function(){var k=[],m="__eventId"+g.UID,l=this.length;while(l--){k[l]=this[l].cloneNode(true);allCloneElms=new g.NodeList(k).get("*").push(k);j=allCloneElms.length;while(j--){k[l][m]=null;g.events._copyEvent(this[l],k[l]);g.NodeList._copyData(this[l],k[l]);}}return new g.NodeList(k);};d.copy=function(){var k=[],l=this.length;while(l--){k[l]=this[l].cloneNode(true);}return new g.NodeList(k);};d.html=function(n){if(!arguments.length){return this[0]?this[0].innerHTML:"";}var k=this.length,l;n=n?String(n):"";while(k--){l=this[k];if(l.nodeType==1){try{l.innerHTML=n;}catch(m){new g.NodeList(l).empty().append(n);}}}return this;};d.text=function(k){var n=this[0],l=this.length,m;if(!arguments.length){return n?n.textContent||n.innerText||n.nodeValue||"":"";}k=k?String(k):"";this.empty();while(l--){m=this[l];if(m.nodeType==1){m.appendChild(f.createTextNode(k));}else{m.nodeValue=k;}}return this;};});Glow.provide(function(e){var a=e.NodeList.prototype,s=document,c=window;function k(t){if(t=="float"){return e.env.ie?"styleFloat":"cssFloat";}return t.replace(/-(\w)/g,function(u,v){return v.toUpperCase();});}function i(B,w){var u,D=0,z=0,E=/height|top/,y=w.length,F=/^(?:(width|height)|(border-(top|bottom|left|right)-width))$/,x=s.defaultView&&(s.defaultView.getComputedStyle(B,null)||s.defaultView.getComputedStyle),v=B.currentStyle,C,A,t=w.push||F.exec(w)||[];if(w.push){for(;z<y;z++){D+=parseInt(i(B,w[z]),10)||0;}return D+"px";}if(t[1]){if(!o(B)){return l(B,function(){return m(B,t[1])+"px";});}return m(B,t[1])+"px";}else{if(t[2]&&e.env.ie&&i(B,"border-"+t[3]+"-style")=="none"){return"0";}else{if(x){if(typeof x=="function"){C=B.style.display;u=l(B,function(){if(w=="display"){B.style.display=C;if(!s.defaultView.getComputedStyle(B,null)){return"none";}B.style.display="block";}return i(B,w);});}else{if(e.env.webkit>500&&e.env.webkit<526&&w=="margin-right"&&x.getPropertyValue("position")!="absolute"){w="margin-left";}u=x.getPropertyValue(w);}}else{if(v){if(w=="opacity"){A=/alpha\(opacity=([^\)]+)\)/.exec(v.filter);return A?String(parseInt(A[1],10)/100):"1";}u=String(v[k(w)]);if(/^-?[\d\.]+(?!px)[%a-z]+$/i.test(u)&&w!="font-size"){u=n(B,u,E.test(w))+"px";}}}}}if(w.indexOf("color")!=-1){u=q(u).toString();}else{if(u.indexOf("url")==0){u=u.replace(/\"/g,"");}}return u;}function o(t){return t.offsetWidth||t.offsetHeight;}function q(v){if(/^(transparent|rgba\(0, ?0, ?0, ?0\))$/.test(v)){return"transparent";}var A,u,B,C,w,y=Math.round,D=parseInt,x=parseFloat,z={black:0,silver:12632256,gray:8421504,white:16777215,maroon:8388608,red:16711680,purple:8388736,fuchsia:16711935,green:32768,lime:65280,olive:8421376,yellow:16776960,navy:128,blue:255,teal:32896,aqua:65535,orange:16753920},t=/^rgb\(([\d\.]+)(%?),\s*([\d\.]+)(%?),\s*([\d\.]+)(%?)/i;if(A=t.exec(v)){u=A[2]?y(((x(A[1])/100)*255)):D(A[1]);B=A[4]?y(((x(A[3])/100)*255)):D(A[3]);C=A[6]?y(((x(A[5])/100)*255)):D(A[5]);}else{if(typeof v=="number"){w=v;}else{if(v.charAt(0)=="#"){if(v.length=="4"){v="#"+v.charAt(1)+v.charAt(1)+v.charAt(2)+v.charAt(2)+v.charAt(3)+v.charAt(3);}w=D(v.slice(1),16);}else{w=z[v];}}u=(w)>>16;B=(w&65280)>>8;C=(w&255);}v=new String("rgb("+u+", "+B+", "+C+")");v.r=u;v.g=B;v.b=C;return v;}var f=["border-left-width","border-right-width","padding-left","padding-right"],d=["border-top-width","border-bottom-width","padding-top","padding-bottom"];function m(z,w){var u,A=document,C=A.documentElement,x=document.body,B=e.env.standardsMode?C:x,t=(w=="width"),y=t?"Width":"Height",v;if(z.window){u=e.env.webkit<522.11?(t?z.innerWidth:z.innerHeight):e.env.webkit?(t?x.clientWidth:z.innerHeight):e.env.opera<9.5?(t?x.clientWidth:x.clientHeight):(t?B.clientWidth:B.clientHeight);}else{if(z.getElementById){u=Math.max(x["scroll"+y],C["scroll"+y]);}else{v=t?f:d;u=z["offset"+y]-parseInt(i(z,v));}}return u;}function p(w,x,v){if(typeof x=="number"||/\d$/.test(x)){x+="px";}for(var u=0,t=w.length;u<t;u++){w[u].style[v]=x;}}function l(z,x){var w,y=z.style,v=y.display,t=y.visibility,u=y.position;y.visibility="hidden";y.position="absolute";y.display="block";if(!o(z)){y.position=u;w=l(z.parentNode,x);y.display=v;y.visibility=t;}else{w=x();y.display=v;y.position=u;y.visibility=t;}return w;}function n(y,A,w){var u=w?"top":"left",x=w?"Top":"Left",B=y.style,v=B[u],z=y.runtimeStyle[u],t;y.runtimeStyle[u]=y.currentStyle[u];B[u]=A;t=B["pixel"+x];B[u]=v;y.runtimeStyle[u]=z;return t;}a.css=function(A,w){var z,u=this.length,t=this.length,y=A,x=/width|height|top$|bottom$|left$|right$|spacing$|indent$|font-size/,v;if(A.constructor===Object){for(v in A){this.css(v,A[v]);}return this;}else{if(w!=undefined){A=k(A);while(u--){z=this[u].style;if(typeof w=="number"&&x.test(y)){w=w.toString()+"px";}if(A=="opacity"&&e.env.ie){z.zoom="1";if(w===""){z.filter="";}else{z.filter="alpha(opacity="+Math.round(Number(w,10)*100)+")";}}else{z[A]=w;}}return this;}else{if(!t){return;}return i(this[0],A);}}};a.height=function(t){if(t==undefined){return m(this[0],"height");}p(this,t,"height");return this;};a.width=function(t){if(t==undefined){return m(this[0],"width");}p(this,t,"width");return this;};a.scrollLeft=function(t){return h(this,true,t);};a.scrollTop=function(t){return h(this,false,t);};function g(w,v){var u,t="scroll"+(v?"Left":"Top");if(w.window){u=w.document.documentElement[t]||(v?w.pageXOffset:w.pageYOffset)||0;}else{u=w[t];}return u;}function b(v,u,t){if(v.window){v.scrollTo(u?t:g(v,true),!u?t:g(v,false));}else{v["scroll"+(u?"Left":"Top")]=t;}}function h(t,w,v){var u=t.length;if(v!==undefined){while(u--){b(t[u],w,v);}return t;}else{return g(t[0],w);}}a.hide=function(){return this.css("display","none").css("visibility","hidden");};a.show=function(){var v=this.length,u=this.length,t,w;while(v--){t=new e.NodeList(this[v]);w=t[0].style;if(t.css("display")=="none"){w.display="";w.visibility="visible";if(t.css("display")=="none"){w.display="block";}}}return this;};a.offset=function(){var z=this[0],C=document,D=C.documentElement,x={x:g(window,true),y:g(window,false)};if(!e.env.webkit&&z.getBoundingClientRect){var B=z.getBoundingClientRect();return{top:Math.floor(B.top)+x.y-D.clientTop,left:Math.floor(B.left)+x.x-D.clientLeft};}else{var A=z.offsetTop,t=z.offsetLeft,u=z,E,w=document.body,y=false,v=z;while(z=z.offsetParent){t+=z.offsetLeft;A+=z.offsetTop;if(i(z,"position")=="fixed"){y=true;}if(e.env.gecko||e.env.webkit>500){t+=parseInt(i(z,"border-left-width"))||0;A+=parseInt(i(z,"border-top-width"))||0;}if(z.nodeName.toLowerCase()!="body"){v=z;}}z=u;while((z=z.parentNode)&&(z!=w)&&(z!=D)){t-=z.scrollLeft;A-=z.scrollTop;if(e.env.gecko&&i(z,"overflow")!="visible"){t+=parseInt(i(z,"border-left-width"));A+=parseInt(i(z,"border-top-width"));}}if(y){t+=x.x;A+=x.y;}if((e.env.webkit<500&&(y||i(v,"position")=="absolute"))||(e.env.gecko&&i(v,"position")!="absolute")){t-=w.offsetLeft;A-=w.offsetTop;}return{left:t,top:A};}};a.position=function(){var u=new e.NodeList(r(this[0])),z=!!u[0],y=parseInt(this.css("margin-left"))||0,x=parseInt(this.css("margin-top"))||0,w=(z&&parseInt(u.css("border-left-width")))||0,t=(z&&parseInt(u.css("border-top-width")))||0,A=this.offset(),v=z?u.offset():{top:0,left:0};return{left:A.left-v.left-y-w,top:A.top-v.top-x-t};};function r(w){var u=w.offsetParent,v=document,t=v.documentElement;while(u&&new e.NodeList(u).css("position")=="static"){u=u.offsetParent;}if(!u&&new e.NodeList(t).css("position")!="static"){u=t;}return u||null;}});Glow.provide(function(e){var b=e.NodeList.prototype,a=window.document,d,c=" keypress keydown keyup ";b.on=function(f,i,h){var g=(c.indexOf(" "+f+" ")>-1);if(g){e.events._addKeyListener(this,f,i,h);}else{e.events._addDomEventListener(this,f,i,h);}return this;};b.detach=function(f,i,h){var g=(c.indexOf(" "+f+" ")>-1);if(g){e.events._removeKeyListener(this,f,i);}else{e.events._removeDomEventListener(this,f,i,h);}return this;};b.delegate=function(g,f,n,m){var l=this.length,k,h=(c.indexOf(" "+g+" ")>-1);while(l--){k=this[l];if(h){}else{e.events._addDomEventListener([k],g,n,m,f);}}return this;};b.detachDelegate=function(g,f,o,n){var l=this.length,k,h=(c.indexOf(" "+g+" ")>-1),m;while(l--){k=this[l];if(h){}else{e.events._removeDomEventListener([k],g,o,f);}}return this;};b.fire=function(f,g){return e.events.fire(this,f,g);};});Glow.complete("core","2.0.0-alpha1");����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������glow/2.0.0-alpha1/glow.debug.js���������������������������������������������������������������������100644 � 0 � 0 � 27312 11343467400 13400� 0����������������������������������������������������������������������������������������������������ustar������������������������������������������������������������������� 0 � 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*! Copyright 2010 British Broadcasting Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ (function() { var glowMap, defaultBase, document = window.document, thisScriptSrc = ( document.body || document.getElementsByTagName('head')[0] ).lastChild.src; // get default base from last script element defaultBase = thisScriptSrc.slice( 0, thisScriptSrc.lastIndexOf('/') ) + '/../'; // track when document is ready, must run before the page is finished loading if (!document.readyState) { if (document.addEventListener) { // like Mozilla document.addEventListener('DOMContentLoaded', function () { document.removeEventListener('DOMContentLoaded', arguments.callee, false); document.readyState = 'complete'; }, false ); } } /** @public @name Glow @function @description Creates an instance of the Glow JavaScript Library. @param {string} [version] @param {object} [opts] @param {string} [opts.base] The path to the base folder, in which the Glow versions are kept. @param {boolean} [opts.debug] Have all filenames modified to point to debug versions. */ window.Glow = function(version, opts) { /*debug*///log.info('new Glow("'+Array.prototype.join.call(arguments, '", "')+'")'); opts = opts || {}; var glowInstance, debug = (opts.debug)? '.debug' : '', base = opts.base || defaultBase; glowMap = { versions: ['2.0.0-alpha1', '@'+'SRC@'], '2.0.0-alpha1': { 'core': ['core'+debug+'.js'], 'widgets': ['core', 'widgets'+debug+'.js', 'widgets'+debug+'.css'] } }; if (opts._map) { glowMap = opts._map; } // for testing purposes map can be overridden version = getVersion(version); /*debug*///log.info('Version is "'+version+'"'); if (Glow._build.instances[version]) { /*debug*///log.info('instance for "'+version+'" already exists.'); return Glow._build.instances[version]; } // opts.base should be formatted like a directory if (base.slice(-1) !== '/') { base += '/'; } glowInstance = createGlowInstance(version, base); Glow._build.instances[version] = glowInstance; glowInstance.UID = 'glow' + Math.floor(Math.random() * (1<<30)); glowInstance.load('core'); // core is always loaded; return glowInstance; } /** @private @name getVersion @function @param {string} version A (possibly imprecise) version identifier, like "2". @description Find the most recent, available version of glow that matches. */ var getVersion = function(version) { /*debug*///log.info('getVersion("'+version+'")'); var versions = glowMap.versions, matchThis = version + '.'; // TODO: an empty version means: the very latest version var i = versions.length; while (i--) { if ( ( versions[i] + '.').indexOf(matchThis) === 0 ) { return versions[i]; } } throw new Error('Version "'+version+'" does not exist'); } /** @private @name getMap @function @description Find the file map for a given version. @param {string} version Resolved identifier, like '2.0.0'. @returns {object} A map of package names to files list. */ var getMap = function(version, debug) { /*debug*///log.info('getMap("'+version+'")'); var versions = glowMap.versions, map = null, versionFound = false; var i = versions.length; while (--i > -1) { if (glowMap[versions[i]]) { map = glowMap[versions[i]]; } if (versions[i] === version) { versionFound = true; } if (versionFound && map) { return map; } } throw new Error('No map available for version "' + version + '".'); } /** @private @name injectJs @function @description Start asynchronously loading an external JavaScript file. */ var injectJs = function(src) { /*debug*///log.info('injectJs("'+src+'")'); var head, script; head = document.getElementsByTagName('head')[0]; script = document.createElement('script'); script.src = src; script.type = 'text/javascript'; head.insertBefore(script, head.firstChild); // rather than appendChild() to avoid IE bug when injecting SCRIPTs after BASE tag opens. see: http://shauninman.com/archive/2007/04/13/operation_aborted } /** @private @name injectCss @function @description Start asynchronously loading an external CSS file. */ var injectCss = function(src) { /*debug*///log.info('injectCss("'+src+'")'); var head, link; head = document.getElementsByTagName('head')[0]; link = document.createElement('link'); link.href = src; link.type = 'text/css'; link.rel = 'stylesheet'; head.insertBefore(link, head.firstChild); } /** @private */ Glow._build = { provided: [], // provided but not yet complete instances: {} // built } /** @private @name Glow.provide @function @param {function} builder A function to run, given an instance of glow, and will add a feature to glow. @description Provide a builder function to Glow as part of a package. */ Glow.provide = function(builder) { /*debug*///log.info('Glow.provide('+typeof builder+')'); Glow._build.provided.push(builder); } /** @private @name Glow.complete @function @param {string} name The name of the completed package. @param {string} version The version of the completed package. @description Signals that no more builder functions will be provided by this package. */ Glow.complete = function(name, version) { /*debug*///log.info('complete('+name+', '+version+')'); var glow, loading, builders; // now that we have the name and version we can move the builders out of provided cache glow = Glow._build.instances[version]; if (!glow) { /*debug*///log.info('Cannot complete, unknown version of glow: '+version); throw new Error('Cannot complete, unknown version of glow: '+version); } glow._build.builders[name] = Glow._build.provided; Glow._build.provided = []; // shortcuts loading = glow._build.loading; builders = glow._build.builders; // try to build packages, in the same order they were loaded for (var i = 0; i < loading.length; i++) { // loading.length may change during loop if (!builders[loading[i]]) { /*debug*///log.info(loading[i]+' has no builders.'); break; } // run the builders for this package in the same order they were loaded for (var j = 0, jlen = builders[loading[i]].length; j < jlen; j++) { /*debug*///log.info('running builder '+j+ ' for '+loading[i]+' version '+glow.version); builders[loading[i]][j](glow); // builder will modify glow } // remove this package from the loaded and builders list, now that it's built if (glow._removeReadyBlock) { glow._removeReadyBlock('glow_loading_'+loading[i]); } builders[loading[i]] = undefined; loading.splice(i, 1); i--; } // try to run onLoaded callbacks glow._release(); } /** @name createGlowInstance @private @function @description Creates an instance of the Glow library. @param {string} version @param {string} base */ var createGlowInstance = function(version, base) { /*debug*///log.info('new glow("'+Array.prototype.join.call(arguments, '", "')+'")'); var glow = function(nodeListContents) { return new glow.NodeList(nodeListContents); }; glow.version = version; glow.base = base; glow.map = getMap(version); glow._build = { loading: [], // names of packages requested but not yet built, in same order as requested. builders: {}, // completed but not yet built (waiting on dependencies). Like _build.builders[packageName]: [function, function, ...]. history: {}, // names of every package ever loaded for this instance callbacks: [] }; // copy properties from glowInstanceMembers for (var prop in glowInstanceMembers) { glow[prop] = glowInstanceMembers[prop]; } return glow; } /** @name glowInstanceMembers @private @description All members of this object will be copied onto little-glow instances @type {Object} */ var glowInstanceMembers = { /** @public @name glow#load @function @description Add a package to this instance of the Glow library. @param {string[]} ... The names of 1 or more packages to add. */ load: function() { /*debug*///log.info('glow.load("'+Array.prototype.join.call(arguments, '", "')+'") for version '+this.version); var name = '', src, depends; for (var i = 0, len = arguments.length; i < len; i++) { name = arguments[i]; if (this._build.history[name]) { /*debug*///log.info('already loaded package "'+name+'" for version '+this.version+', skipping.'); continue; } this._build.history[name] = true; // packages have dependencies, listed in the map: a single js file, css files, or even other packages depends = this.map[name]; /*debug*///log.info('depends for '+name+' '+this.version+': "'+depends.join('", "')+'"'); for (var j = 0, lenj = depends.length; j < lenj; j++) { if (depends[j].slice(-3) === '.js') { /*debug*///log.info('dependent js: "'+depends[j]+'"'); src = this.base + this.version + '/' + depends[j]; // readyBlocks are removed in _release() if (this._addReadyBlock) { this._addReadyBlock('glow_loading_'+name); } // provided by core this._build.loading.push(name); injectJs(src); } else if (depends[j].slice(-4) === '.css') { /*debug*///log.info('dependent css "'+depends[j]+'"'); src = this.base + this.version + '/' + depends[j]; injectCss(src); } else { /*debug*///log.info('dependent package: "'+depends[j]+'"'); this.load(depends[j]); // recursively load dependency packages } } } return this; }, /** @public @name glow#loaded @function @param {function} onLoadCallback Called when all the packages load. @description Do something when all the packages load. */ loaded: function(onLoadCallback) { /*debug*///log.info('glow.loaded('+typeof onLoadCallback+') for version '+this.version); this._build.callbacks.push(onLoadCallback); if (this._addReadyBlock) { this._addReadyBlock('glow_loading_loadedcallback'); } this._release(); return this; }, /** @private @name glow#_release @function @description If all loaded packages are now built, then run the onLoaded callbacks. */ _release: function() { /*debug*///log.info('glow._release("'+this.version+'")'); var callback; if (this._build.loading.length !== 0) { /*debug*///log.info('waiting for '+this._build.loading.length+' to finish.'); return; } /*debug*///log.info('running '+this._build.callbacks.length+' loaded callbacks for version "'+this.version+'"'); // run and remove each available _onloaded callback while (callback = this._build.callbacks.shift()) { callback(this); if (this._removeReadyBlock) { this._removeReadyBlock('glow_loading_loadedcallback'); } } }, /** @name glow#ready @function @param {function} onReadyCallback Called when all the packages load and the DOM is available. @description Do something when all the packages load and the DOM is ready. */ ready: function(onReadyCallback) { /*debug*///log.info('(ember) glow#ready('+typeof onReadyCallback+') for version '+this.version+'. There are '+this._build.loading.length+' loaded packages waiting to be built.'); this.loaded(function(glow) { glow.ready( function() { onReadyCallback(glow); } ); }); return this; } } })(); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������glow/2.0.0-alpha1/glow.js���������������������������������������������������������������������������100644 � 0 � 0 � 7202 11343467400 12267� 0����������������������������������������������������������������������������������������������������ustar������������������������������������������������������������������� 0 � 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright 2010 British Broadcasting Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ (function(){var d,j,f=window.document,c=(f.body||f.getElementsByTagName("head")[0]).lastChild.src;j=c.slice(0,c.lastIndexOf("/"))+"/../";if(!f.readyState){if(f.addEventListener){f.addEventListener("DOMContentLoaded",function(){f.removeEventListener("DOMContentLoaded",arguments.callee,false);f.readyState="complete";},false);}}window.Glow=function(l,n){n=n||{};var k,m=(n.debug)?".debug":"",o=n.base||j;d={versions:["2.0.0-alpha1","@SRC@"],"2.0.0-alpha1":{core:["core"+m+".js"],widgets:["core","widgets"+m+".js","widgets"+m+".css"]}};if(n._map){d=n._map;}l=a(l);if(Glow._build.instances[l]){return Glow._build.instances[l];}if(o.slice(-1)!=="/"){o+="/";}k=h(l,o);Glow._build.instances[l]=k;k.UID="glow"+Math.floor(Math.random()*(1<<30));k.load("core");return k;};var a=function(l){var k=d.versions,n=l+".";var m=k.length;while(m--){if((k[m]+".").indexOf(n)===0){return k[m];}}throw new Error('Version "'+l+'" does not exist');};var e=function(l,m){var k=d.versions,p=null,n=false;var o=k.length;while(--o>-1){if(d[k[o]]){p=d[k[o]];}if(k[o]===l){n=true;}if(n&&p){return p;}}throw new Error('No map available for version "'+l+'".');};var b=function(m){var l,k;l=f.getElementsByTagName("head")[0];k=f.createElement("script");k.src=m;k.type="text/javascript";l.insertBefore(k,l.firstChild);};var i=function(m){var k,l;k=f.getElementsByTagName("head")[0];l=f.createElement("link");l.href=m;l.type="text/css";l.rel="stylesheet";k.insertBefore(l,k.firstChild);};Glow._build={provided:[],instances:{}};Glow.provide=function(k){Glow._build.provided.push(k);};Glow.complete=function(n,l){var r,q,p;r=Glow._build.instances[l];if(!r){throw new Error("Cannot complete, unknown version of glow: "+l);}r._build.builders[n]=Glow._build.provided;Glow._build.provided=[];q=r._build.loading;p=r._build.builders;for(var o=0;o<q.length;o++){if(!p[q[o]]){break;}for(var m=0,k=p[q[o]].length;m<k;m++){p[q[o]][m](r);}if(r._removeReadyBlock){r._removeReadyBlock("glow_loading_"+q[o]);}p[q[o]]=undefined;q.splice(o,1);o--;}r._release();};var h=function(k,l){var n=function(o){return new n.NodeList(o);};n.version=k;n.base=l;n.map=e(k);n._build={loading:[],builders:{},history:{},callbacks:[]};for(var m in g){n[m]=g[m];}return n;};var g={load:function(){var n="",q,p;for(var o=0,k=arguments.length;o<k;o++){n=arguments[o];if(this._build.history[n]){continue;}this._build.history[n]=true;p=this.map[n];for(var m=0,l=p.length;m<l;m++){if(p[m].slice(-3)===".js"){q=this.base+this.version+"/"+p[m];if(this._addReadyBlock){this._addReadyBlock("glow_loading_"+n);}this._build.loading.push(n);b(q);}else{if(p[m].slice(-4)===".css"){q=this.base+this.version+"/"+p[m];i(q);}else{this.load(p[m]);}}}}return this;},loaded:function(k){this._build.callbacks.push(k);if(this._addReadyBlock){this._addReadyBlock("glow_loading_loadedcallback");}this._release();return this;},_release:function(){var k;if(this._build.loading.length!==0){return;}while(k=this._build.callbacks.shift()){k(this);if(this._removeReadyBlock){this._removeReadyBlock("glow_loading_loadedcallback");}}},ready:function(k){this.loaded(function(l){l.ready(function(){k(l);});});return this;}};})();��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������deList(nodeListContents); }; glow.version = version; glow.base = base; glow.map = getMap(version); glow._build = { loading: [], // names of packages requested but not yet built, in same order as requested. builders: {}, // completed but not yet built (waiting on dependencies). Like _build.builders[packageName]: [function, function, ...]. history: {}, // names of every package ever loaded for this instance callbacks: [] }; // copy properties from glowInstanceMembers for (var prop in glowInstanceMembers) { glow[prop] = glowInstanceMembers[prop]; } return glow; } /** @name glowInstanceMembers @private @description All members of this object will be copied onto little-glow instances @type {Object} */ var glowInstanceMembers = { /** @public @name glow#load @function @description Add a package to this instance of the Glow library. @param {string[]} ... The names of 1 or more packages to add. */ load: function() { /*debug*///log.info('glow.load("'+Array.prototype.join.call(arguments, '", "')+'") for version '+this.version); var name = '', src, depends; for (var i = 0, len = arguments.length; i < len; i++) { name = arguments[i]; if (this._build.history[name]) { /*debug*///log.info('already loaded package "'+name+'" for version '+this.version+', skipping.'); continue; } this._build.history[name] = true; // packages have dependencies, listed in the map: a single js file, css files, or even other packages depends = this.map[name]; /*debug*///log.info('depends for '+name+' '+this.version+': "'+depends.join('", "')+'"'); for (var j = 0, lenj = depends.length; j < lenj; j++) { if (depends[j].slice(-3) === '.js') { /*debug*///log.info('dependent js: "'+depends[j]+'"'); src = this.base + this.version + '/' + depends[j]; // readyBlocks are removed in _release() if (this._addReadyBlock) { this._addReadyBlock('glow_loading_'+name); } // provided by core this._build.loading.push(name); injectJs(src); } else if (depends[j].slice(-4) === '.css') { /*debug*///log.info('dependent css "'+depends[j]+'"'); src = this.base + this.version + '/' + depends[j]; injectCss(src); } else { /*debug*///log.info('dependent package: "'+depends[j]+'"'); this.load(depends[j]); // recursively load dependency packages } } } return this; }, /** @public @name glow#loaded @function @param {function} onLoadCallback Called when all the packages load. @description Do something when all the packages load. */ loaded: function(onLoadCallback) { /*debug*///log.info('glow.loaded('+typeof onLoadCallback+') for version '+this.version); this._build.callbacks.push(onLoadCallback); if (this._addReadyBlock) { this._addReadyBlock('glow_loading_loadedcallback'); } this._release(); return this; }, /** @private @name glow#_release @function @description If all loaded packages are now built, then run the onLoaded callbacks. */ _release: function() { /*debug*///log.info('glow._release("'+this.version+'")'); var callback; if (this._build.loading.length !== 0) { /*debug*///log.info('waiting for '+this._build.loading.length+' to finish.'); return; } /*debug*///log.info('running '+this._build.callbacks.length+' loaded callbacks for version "'+this.version+'"'); // run and remove each available _onloaded callback while (callback = this._build.callbacks.shift()) { callback(this); if (this._removeReadyBlock) { this._removeReadyBlock('glow_loading_loadedcallback'); } } }, /** @name glow#ready @function @param {function} onReadyCallback Called when all the packages load and the DOM is available. @description Do something when all the packages load and the DOM is ready. */ ready: function(onReadyCallback) { /*debug*///log.info('(ember) glow#ready('+typeof onReadyCallback+') for version '+this.version+'. There are '+this._build.loading.length+' loaded packages waiting to be built.'); this.loaded(function(glow) { glow.ready( function() { onReadyCallback(glow); } ); }); return this; } } })(); ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������glow/2.0.0-alpha1/glow.js���������������������������������������������������������������������������100644 � 0 � 0 � 7202 11343467400 12267� 0����������������������������������������������������������������������������������������������������ustar������������������������������������������������������������������� 0 � 0 ������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* Copyright 2010 British Broadcasting Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ (function(){var d,j,f=window.document,c=(f.body||f.getElementsByTagName("head")[0]).lastChild.src;j=c.slice(0,c.lastIndexOf("/"))+"/../";if(!f.readyState){if(f.addEventListener){f.addEventListener("DOMContentLoaded",function(){f.removeEventListener("DOMContentLoaded",arguments.callee,false);f.readyState="complete";},false);}}window.Glow=function(l,n){n=n||{};var k,m=(n.debug)?".debug":"",o=n.base||j;d={versions:["2.0.0-alpha1","@SRC@"],"2.0.0-alpha1":{core:["core"+m+".js"],widgets:["core","widgets"+m+".js","widgets"+m+".css"]}};if(n._map){d=n._map;}l=a(l);if(Glow._build.instances[l]){return Glow._build.instances[l];}if(o.slice(-1)!=="/"){o+="/";}k=h(l,o);Glow._build.instances[l]=k;k.UID="glow"+Math.floor(Math.random()*(1<<30));k.load("core");return k;};var a=function(l){var k=d.versions,n=l+".";var m=k.length;while(m--){if((k[m]+".").indexOf(n)===0){return k[m];}}throw new Error('Version "'+l+'" does not exist');};var e=function(l,m){var k=d.versions,p=null,n=false;var o=k.length;while(--o>-1){if(d[k[o]]){p=d[k[o]];}if(k[o]===l){n=true;}if(n&&p){return p;}}throw new Error('No map available for version "'+l+'".');};var b=function(m){var l,k;l=f.getElementsByTagName("head")[0];k=f.createElement("script");k.src=m;k.type="text/javascript";l.insertBefore(k,l.firstChild);};var i=function(m){var k,l;k=f.getElementsByTagName("head")[0];l=f.createElement("link");l.href=m;l.type="text/css";l.rel="stylesheet";k.insertBefore(l,k.firstChild);};Glow._build={provided:[],instances:{}};Glow.provide=function(k){Glow._build.provided.push(k);};Glow.complete=function(n,l){var r,q,p;r=Glow._build.instances[l];if(!r){throw new Error("Cannot complete, unknown version of glow: "+l);}r._build.builders[n]=Glow._build.provided;Glow._build.provided=[];q=r._build.loading;p=r._build.builders;for(var o=0;o<q.length;o++){if(!p[q[o]]){break;}for(var m=0,k=p[q[o]].length;m<k;m++){p[q[o]][m](r);}if(r._removeReadyBlock){r._removeReadyBlock("glow_loading_"+q[o]);}p[q[o]]=undefined;q.splice(o,1);o--;}r._release();};var h=function(k,l){var n=function(o){return new n.NodeList(o);};n.version=k;n.base=l;n.map=e(k);n._build={loading:[],builders:{},history:{},callbacks:[]};for(var m in g){n[m]=g[m];}return n;};var g={load:function(){var n="",q,p;for(var o=0,k=arguments.length;o<k;o++){n=arguments[o];if(this._build.history[n]){continue;}this._build.history[n]=true;p=this.map[n];for(var m=0,l=p.length;m<l;m++){if(p[m].slice(-3)===".js"){q=this.base+this.version+"/"+p[m];if(this._addReadyBlock){this._addReadyBlock("glow_loading_"+n);}this._build.loading.push(n);b(q);}else{if(p[m].slice(-4)===".css"){q=this.base+this.version+"/"+p[m];i(q);}else{this.load(p[m]);}}}}return this;},loaded:function(k){this._build.callbacks.push(k);if(this._addReadyBlock){this._addReadyBlock("glow_loading_loadedcallback");}this._release();return this;},_release:function(){var k;if(this._build.loading.length!==0){return;}while(k=this._build.callbacks.shift()){k(this);if(this._removeReadyBlock){this._removeReadyBlock("glow_loading_loadedcallback");}}},ready:function(k){this.loaded(function(l){l.ready(function(){k(l);});});return this;}};})();������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������