/* 
   Peppy - A lightning fast CSS 3 Compliant selector engine.   	 
   http://www.w3.org/TR/css3-selectors/#selectors
   
   version 0.1.2	
   
   Author: James Donaghue - james.donaghue@gmail.com

   Copyright (c) 2008 James Donaghue (jamesdonaghue.com)	
   Licenced under the FreeBSD (http://www.freebsd.org/copyright/freebsd-license.html) licence.

*/
(function(){	
	var doc = document;
	var isIE = /(?!.*?opera.*?)msie(?!.*?opera.*?)/i.test( navigator.userAgent );
	var isWebKit = /webkit/i.test( navigator.userAgent );
	var cache = {};
	var cacheOn = !isIE && !isWebKit;
	var persistCache = {};		
	var _uid = 0;		
	
	var reg = {
		trim : /^\s+|\s+$/g,
		quickTest : /^[^:\[>+~ ,]+$/,
		typeSelector : /(^[^\[:]+?)(?:\[|\:|$)/,
		tag : /^(\w+|\*)/,		
		id : /^(\w*|\*)#/,
		classRE : /^(\w*|\*)\./,
		attributeName : /(\w+)(?:[!+~*\^$|=])|\w+/,
		attributeValue : /(?:[!+~*\^$|=]=*)(.+)(?:\])/, 
		pseudoName :  /(\:[^\(]+)/,
		pseudoArgs : /(?:\()(.+)(?:\))/,				
		nthParts : /([+-]?\d)*(n)([+-]\d+)*/i,		
		combinatorTest : /[+>~ ](?![^\(]+\)|[^\[]+\])/,
		combinator :  /\s*[>~]\s*(?![=])|\s*\+\s*(?![0-9)])|\s+/g, 						
		recursive : /:(not|has)\((\w+|\*)?([#.](\w|\d)+)*(\:(\w|-)+(\([^\)]+\))?|\[[^\}]+\])*(\s*,\s*(\w+|\*)?([#.](\w|\d)+)*(\:(\w|-)+(\([^\)]+\))?|\[[^\}]+\])*)*\)/gi		
	}
	
	var arrayIt = function(a){
		if( !!(window.attachEvent && !window.opera) ) {
			return function(a){
				if( a instanceof Array ) return a;
				for( var i=0, result = [], m; m = a[ i ]; i++){
					result[ result.length ] = m;
				}
				return result;
			};
		} else {
			return function(a){
				return Array.prototype.slice.call(a);
			};
		}
	}();	
	
	// Filters a list of elements for uniqueness.
	function filter( a, tag ) {
		var r = [], 
			uids = {};
		if( tag ) tag = new RegExp( "^" + tag + "$", "i" );
		for( var i = 0, ae; ae = a[ i++ ]; ) {
			ae.uid = ae.uid || _uid++;
			if( !uids[ae.uid] && (!tag || ae.nodeName.search( tag ) !== -1) ) {
				r[r.length] = uids[ae.uid] = ae;
			}
		}
		return r;
	}
	
	// getAttribute - inspired by EXT -> http://extjs.com
	// Copyright(c) 2006-2008, Ext JS, LLC.
 	// http://extjs.com/license
	function getAttribute( e, a ) {
		if( !e ) return null;
		if( a === "class" || a === "className" )
			return e.className;
		if( a === "for" ) 
			return e.htmlFor;	
		return e.getAttribute( a ) || e[a];
	}		
	
	function getByClass( selector, selectorRE, root, includeRoot, cacheKey, tag, flat ) {
		var result = [];
		
		if( !!flat ) {
			return selectorRE.test( root.className ) ? [root] : [];
		}
		
		if( root.getElementsByClassName ) {
			result = arrayIt( root.getElementsByClassName( selector) );			
			
			if( !!includeRoot ) {
				if( selectorRE.test( root.className ) ) result[ result.length ] = root;
			}
			
			if( tag != "*" ) result = filter( result, tag );
			cache[ cacheKey ] = result.slice(0);
			return result;
			
		} else if( doc.getElementsByClassName ) {
			result = arrayIt( doc.getElementsByClassName( selector ) ); 
			
			if( tag != "*" ) result = filter( result, tag );
			cache[ cacheKey ] = result.slice(0);
			return result;
		}
		
		var es = (tag == "*" && root.all) ? root.all : root.getElementsByTagName( tag );		
		
		if( !!includeRoot ) es[ es.length ] = root ;		
		
		for( var index = 0, e; e = es[ index++ ]; ) {
			if( selectorRE.test( e.className ) ) {
				result[ result.length ] = e;
			}
		}
		return result;
	}
	
	function getById( selector, root, includeRoot, cacheKey, tag, flat ) {
		var rs, 
			result = [];
		
		if( !!flat ) {
			return getAttribute( root, "id" ) === selector ? [root] : [];
		}
		
		if( root.getElementById ) {
			rs = root.getElementById( selector );
		} else {
			rs = doc.getElementById( selector );
		}
		
		if( rs && getAttribute( rs, "id" ) === selector ) {			
			result[ result.length ] = rs;
			cache[ cacheKey ] = result.slice(0);
			return result;
		}
		
		var es = root.getElementsByTagName( tag );
		
		if( !!includeRoot ) es[ es.length ] = root ;
		
		for( var index = 0, e; e = es[ index++ ]; ) {
			if( getAttribute( e, "id" ) === selector ) {
				result[ result.length ] = e;
				break;
			}
		}
		return result;
	} 

	function getContextFromSequenceSelector( selector, roots, includeRoot, flat ) {
		var context, 
			tag, 
			contextType = "", 
			result = [], 
			tResult = [], 
			root, 
			rootCount, 
			rootsLength;
			
		reg.id.lastIndex = reg.typeSelector.lastIndex = reg.classRE.lastIndex = 0;
		if( !reg.tag.test( selector ) ) selector = "*" + selector;
		context = reg.typeSelector.exec( selector )[1];
		roots = roots instanceof Array ? roots.slice(0) : [roots];
		rootsLength = roots.length;
		rootCount = rootsLength - 1;

		if( reg.id.test( context ) ) {			
			contextType = "id";
			tag = (tag = context.match( /^\w+/ )) ? tag[0] : "*";
			context = context.replace( reg.id, "");						
		} else if( reg.classRE.test( context ) ) {
			contextType = "class";
			tag = (tag = context.match( reg.tag )) ? tag[0] : "*";
			context = context.replace( reg.tag, "" );
			contextRE = persistCache[context + "RegExp"] || 
						(persistCache[context + "RegExp"] = new RegExp( "(?:^|\\s)" + context.replace( /\./g, "\\s*" ) + "(?:\\s|$)" ));
			context = context.replace( /\./g, " " )
		}			
		
		while( rootCount > -1 ) { 
			root = roots[ rootCount-- ];
			root.uid = root.uid || _uid++;
			var cacheKey = selector + root.uid;
			
			if( cacheOn && cache[ cacheKey ] ) {
				result = result.concat( cache[ cacheKey ] );
				continue;
			}
			
			if( contextType === "id" ) {
				tResult = getById( context, root, includeRoot, cacheKey, tag, flat );
			} else if( contextType === "class" ) {
				tResult = getByClass( context, contextRE, root, includeRoot, cacheKey, tag, flat );
			} else { /* tagname */
				tResult = arrayIt( root.getElementsByTagName( context ) );
				if( !!includeRoot && (root.nodeName.toUpperCase() === context.toUpperCase() || context === "*") ) tResult[tResult.length] = root;
			}
			
			result = rootsLength > 1 ? result.concat( tResult ) : tResult;
			cache[ cacheKey ] = result.slice(0);
		}
		return result;
	}
	
	peppy = {
		query : function( selectorGroups, root, includeRoot, recursed, flat ) {
			var elements = [];						
			if( !recursed ) {  // TODO: try to clean this up. 
				selectorGroups = selectorGroups.replace( reg.trim, "" ) // get rid of leading and trailing spaces												 
											   .replace( /(\[)\s+/g, "$1") // remove spaces around '['  of attributes
											   .replace( /\s+(\])/g, "$1") // remove spaces around ']' of attributes
											   .replace( /(\[[^\] ]+)\s+/g, "$1") // remove spaces to the left of operator inside of attributes
											   .replace( /\s+([^ \[]+\])/g, "$1" ) // remove spaces to the right of operator inside of attributes
											   .replace( /(\()\s+/g, "$1") // remove spaces around '(' of pseudos											   
											   .replace( /(\+)([^0-9])/g, "$1 $2") // add space after + combinator
											   .replace( /['"]/g, "") // remove all quotations
											   .replace( /\(\s*even\s*\)/gi, "(2n)") // replace (even) with (2n) - pseudo arg (for caching)
											   .replace( /\(\s*odd\s*\)/gi, "(2n+1)"); // replace (odd) with (2n+1) - pseudo arg (for caching)
			}			
			
			if( typeof root === "string" ) {
				root = (root = getContextFromSequenceSelector( root, doc )).length > 0 ? root : undefined;
			}

			root = root || doc;
			root.uid = root.uid || _uid++;
			
			var cacheKey = selectorGroups + root.uid;
			if( cacheOn && cache[ cacheKey ] ) return cache[ cacheKey ];
			
			reg.quickTest.lastIndex = 0;
			if( reg.quickTest.test( selectorGroups ) ) {
				elements = getContextFromSequenceSelector( selectorGroups, root, includeRoot, flat );
				return (cache[ cacheKey ] = elements.slice(0));
			}
			
			var groupsWorker, 
				groups, 
				selector, 
				parts = [], 
				part;
				
			groupsWorker = selectorGroups.split( /\s*,\s*/g );
			groups = groupsWorker.length > 1 ? [""] : groupsWorker;
			
			// validate groups
			for( var gwi = 0, tc = 0, gi = 0, g; groupsWorker.length > 1 && (g = groupsWorker[ gwi++ ]) !== undefined;) {
				tc += (((l = g.match( /\(/g )) ? l.length : 0) - ((r = g.match( /\)/g )) ? r.length : 0));
				groups[gi] = groups[gi] || "";
				groups[gi] += (groups[gi] === "" ? g : "," + g);
				if( tc === 0 ) gi++;
			}
			
			var gCount = 0;				
			while( (selector = groups[gCount++]) !== undefined ) {
				reg.quickTest.lastIndex = 0;
				if( reg.quickTest.test( selector ) ) {
					result = getContextFromSequenceSelector( selector, root, includeRoot, flat )
					elements = groups.length > 1 ? elements.concat( result ) : result;
					continue;
				}
				reg.combinatorTest.lastIndex = 0;
				if( reg.combinatorTest.test( selector ) ) {
					var parts, 
						pLength, 
						pCount = 0, 
						combinators, 
						cLength, 
						cCount = 0, 
						result;
						
					parts = selector.split( reg.combinator );
					pLength = parts.length;
					
					combinators = selector.match( reg.combinator ) || [""];					
					cLength = combinators.length;
					
					while( pCount < pLength ) {
						var c, 
							part1, 
							part2;
							
						c = combinators[ cCount++ ].replace( reg.trim, "");
						
						part1 = result || peppy.query( parts[pCount++], root, includeRoot, true, flat );								
						part2 = peppy.query( parts[ pCount++ ], 
											c == "" || c == ">" ? part1 : root, 
											c == "" || c == ">", 
											true,
											flat );
											
 						result = peppy.queryCombinator( part1, part2, c );
					}
					
					elements = groups.length > 1 ? elements.concat( result ) : result;							   
					result = undefined;
				} else {
					result = peppy.querySelector( selector, root, includeRoot, flat );
					elements = groups.length > 1 ? elements.concat( result ) : result;
				}
			}	
			
			if( groups.length > 1 ) elements = filter(elements);
			
			return ( cache[ cacheKey ] = elements.slice(0));
		},
		queryCombinator: function( l, r, c ) {
			var result = [], 
				uids = {}, 
				proc = {}, 
				succ = {}, 
				fail = {}, 
				combinatorCheck = peppy.simpleSelector.combinator[c];
				
			for( var li = 0, le; le = l[ li++ ]; ) {
				le.uid = le.uid || _uid++
				uids[ le.uid ] = le;
			}	
					
			for( var ri = 0, re; re = r[ ri++ ]; ) {
				re.uid = re.uid || _uid++; 
				if( !proc[ re.uid ] && combinatorCheck( re, uids, fail, succ ) ) {
					result[ result.length ] = re;
				}
				proc[ re.uid ] = re;
			}
			return result;
		},
		querySelector : function( selector, root, includeRoot, flat ) {
			var context, 
				passed = [],				 
				count, 
				totalCount, 
				e, 
				first = true, 
				localCache = {};

			context = getContextFromSequenceSelector( selector, root, includeRoot, flat ); 	
			count = context.length;
			totalCount = count - 1;			
						
			var tests, recursive;
			if( /:(not|has)/i.test( selector ) ) {
				recursive = selector.match( reg.recursive );
				selector = selector.replace( reg.recursive, "" );
			}
			
			// Get the tests (if there aren't any just set tests to an empty array).
			if( !(tests = selector.match( /:(\w|-)+(\([^\(]+\))*|\[[^\[]+\]/g )) ) tests = [];	
				
			// If there were any recursive tests put them in the tests array (they were removed above).
			if( recursive ) tests = tests.concat( recursive );			

			// Process each tests for all elements.
			var aTest;
			while( (aTest = tests.pop()) !== undefined ) {				
				var pc = persistCache[ aTest ], 
					testFuncScope,
				 	testFunc, 
				 	testFuncKey,				 	
				 	testFuncArgs = [],
					isTypeTest = false, 
					isCountTest = false;
					
				passed = [];
				
				if( pc ) {
					testFuncKey = pc[ 0 ];
					testFuncScope = pc[ 1 ];					
					testFuncArgs = pc.slice( 2 );
					testFunc = testFuncScope[ testFuncKey ];											
				} else if( !/^:/.test( aTest ) ) { // attribute																
					var n = aTest.match( reg.attributeName );
					var v = aTest.match( reg.attributeValue );
										
					testFuncArgs[ 1 ] = n[ 1 ] || n[ 0 ];
					testFuncArgs[ 2 ] = v ? v[ 1 ] : "";						
					testFuncKey = "" + aTest.match( /[~!+*\^$|=]/ );
					testFuncScope = peppy.simpleSelector.attribute;	
					testFunc = testFuncScope[ testFuncKey ];						
					persistCache[ aTest ] = [ testFuncKey, testFuncScope ].concat( testFuncArgs );					
				} else { // pseudo						
					var pa = aTest.match( reg.pseudoArgs );					
					testFuncArgs[ 1 ] = pa ? pa[ 1 ] : "";						
					testFuncKey = aTest.match( reg.pseudoName )[ 1 ];
					testFuncScope = peppy.simpleSelector.pseudos;
					
					if( /nth-(?!.+only)/i.test( aTest ) ) {											
						var a, 
							b, 
							nArg = testFuncArgs[ 1 ],
							nArgPC = persistCache[ nArg ];
							
						if( nArgPC ) {
							a = nArgPC[ 0 ];
							b = nArgPC[ 1 ];
						} else {								
							var nParts = nArg.match( reg.nthParts );
							if( nParts ) {								
								a = parseInt( nParts[1],10 ) || 0;
								b = parseInt( nParts[3],10 ) || 0;
								
								if( /^\+n|^n/i.test( nArg ) ) {
									a = 1;
								} else if( /^-n/i.test( nArg ) ) {
									a = -1;
								}
								
								testFuncArgs[ 2 ] = a;
								testFuncArgs[ 3 ] = b;
								persistCache[ nArg ] = [a, b];									
							}
						}
					} else if( /^:contains/.test( aTest ) ) {
						var cArg = testFuncArgs[1];
						var cArgPC = persistCache[ cArg ];
						
						if( cArgPC ) {
							testFuncArgs[1] = cArgPC;
						} else {
							testFuncArgs[1] = persistCache[ cArg ] = new RegExp( cArg );	
						}
					}
					testFunc = testFuncScope[ testFuncKey ];						
					persistCache[ aTest ] = [ testFuncKey, testFuncScope ].concat( testFuncArgs );	
				}				
				
				isTypeTest = /:(\w|-)+type/i.test( aTest);
				isCountTest = /^:(nth[^-]|eq|gt|lt|first|last)/i.test( aTest );					
				if( isCountTest ) testFuncArgs[ 3 ] = totalCount;	
				
				// Now run the test on each element (keep only those that pass)								
				var cLength = context.length, cCount = cLength -1 ;
				while( cCount > -1 ) {
					e = context[ cCount-- ];
  					if( first ) {
 	 					e.peppyCount = cCount + 1;
  					}
					var pass = true;
 					testFuncArgs[ 0 ] = e;
 					if( isCountTest ) 
 						testFuncArgs[2] = e.peppyCount;

					if( !testFunc.apply( testFuncScope, testFuncArgs ) ) {
						pass = false;
					}						
					if( pass ) {
						passed.push(e);
					}
				}
				context = passed;
				first = false;
			}
			return passed;
		},
		simpleSelector: {
			attribute: {
				"null": function( e, a, v ) { return !!getAttribute(e,a); },
				"=" : function( e, a, v ) { return getAttribute(e,a) == v; },
				"~" : function( e, a, v ) { return getAttribute(e,a).match(new RegExp('\\b'+v+'\\b')) },
				"^" : function( e, a, v ) { return getAttribute(e,a).indexOf( v ) === 0; },
				"$" : function( e, a, v ) { var attr = getAttribute(e,a); return attr.lastIndexOf( v ) === attr.length - v.length; },
				"*" : function( e, a, v ) { return getAttribute(e,a).indexOf( v ) != -1; },
				"|" : function( e, a, v ) { return getAttribute(e,a).match( '^'+v+'-?(('+v+'-)*('+v+'$))*' ); },
				"!" : function( e, a, v ) { return getAttribute(e,a) !== v; }
			},
			pseudos: {
				":root" : function( e ) { return e === doc.getElementsByTagName( "html" )[0] ? true : false; },
				":nth-child" : function( e, n, a, b, t ) {	
// Unobtrusive version									
// 					var parent = e.parentNode;
// 					if( !parent ) return false;
// 					
// 					e.uid = e.uid || _uid++;
// 					parent.uid = parent.uid || _uid++;
// 					
// 					var parentCache = cache[ "pos" + parent.uid ];										
// 					
// 					if( !parentCache ) {
// 						var node = e.parentNode.firstChild, 
// 							count = 0, 
// 							last,
// 							cacheHash = {},
// 							cacheArr = [];
// 						for( ; node; node = node.nextSibling ) {
// 							if( node.nodeType == 1 ) {								
// 								node.uid = node.uid || _uid++;																
// 								cacheArr[ count ] = node.uid;
// 								cacheHash[ node.uid ] = ++count;								
// 							}
// 						}
// 						parentCache = cache[ "pos" +  parent.uid ] = { posList : cacheArr, 
// 																	   posHash : cacheHash,
// 																	   length : count};
// 					}					
// 					
// 					var position = parentCache.posHash[ e.uid ];	
// 					if( n == "first" ) 
// 						return position == 1;
// 					if( n == "last" )
// 						return position == parentCache.length;
// 					if( n == "only" )
// 						return parentCache.length == 1;	
// 					return (!a && !b && position == n) || 
// 						   ((a == 0 ? position == b : 
// 							 		  a > 0 ? position >= b && (position - b) % a == 0 :
// 							 			  	  position <= b && (position + b) % a == 0));
		

// Obtrusive but faster version	- the problem with this version ( which is similar to what 
// is seen in other libraries ) is that as soon as the DOM changes results will potentially be incorrect.
// Specifically, if a node in question gets a new sibling, its position will then be different if it is 
// anything but the first or last child. The nature of this code (to keep it performant) is to not recalculate 
// a position. The unobtrusive version above is very similar to this, the only difference is that it stores
// positions in cache instead of as a property of the element. As soon as the DOM changes the cache is cleared
// so with the unobtrusive version the position will be recalculated.		
					if( !e.nodeIndex ) {
						var node = e.parentNode.firstChild, count = 0, last;
						for( ; node; node = node.nextSibling ) {
							if( node.nodeType == 1 ) {
								last = node;								
								node.nodeIndex = ++count;
							}
						}
						last.IsLastNode = true;
						if( count == 1 ) last.IsOnlyChild = true;
					}
					var position = e.nodeIndex;
					if( n == "first" ) 
						return position == 1;
					if( n == "last" )
						return !!e.IsLastNode;
					if( n == "only" )
						return !!e.IsOnlyChild;
					return (!a && !b && position == n) || 
						   ((a == 0 ? position == b : 
							 		  a > 0 ? position >= b && (position - b) % a == 0 :
							 			  	  position <= b && (position + b) % a == 0));
				},				
				":nth-last-child" : function( e, n ) { return this[ ":nth-child" ]( e, n, a, b ); },  // TODO: n is not right.
				":nth-of-type" : function( e, n, t ) { return this[ ":nth-child" ]( e, n, a, b, t); },
				":nth-last-of-type" : function( e, n, t ) { return this[ ":nth-child" ](e, n, a, b, t ); }, // TODO: n is not right.
				":first-child" : function( e ) { return this[ ":nth-child" ]( e, "first" ); },
				":last-child" : function( e ) { return this[ ":nth-child" ]( e, "last" ); },
				":first-of-type" : function( e, n, t ) { return this[ ":nth-child" ]( e, "first", null, null, t ); },
				":last-of-type" : function( e, n, t ) { return this[ ":nth-child" ]( e, "last", null, null, t ); },
				":only-child" : function( e ) { return this[ ":nth-child" ]( e, "only" ); },
				":only-of-type" : function( e, n, t ) { return this[ ":nth-child" ]( e, "only", null, null, t ); },
				":empty" : function( e ) { 
					for( var node = e.firstChild, count = 0; node !== null; node = node.nextSibling ) {
						if( node.nodeType === 1 || node.nodeType === 3 ) return false;
					}
					return true;
				},
				":not" : function( e, s ) { return peppy.query( s, e, true, true, true ).length === 0; },
				":has" : function( e, s ) { return peppy.query( s, e, true, true, true ).length > 0; },
				":selected" : function( e ) { return e.selected; },
				":hidden" : function( e ) { return e.type === "hidden" || e.style.display === "none"; },
				":visible" : function( e ) { return e.type !== "hidden" && e.style.display !== "none"; },
				":input" : function( e ) { return e.nodeName.search( /input|select|textarea|button/i ) !== -1; },
				":radio" : function( e ) { return e.type === "radio"; },
				":checkbox" : function( e ) { return e.type === "checkbox"; },
				":text" : function( e ) { return e.type === "text"; },
				":header" : function( e ) { return e.nodeName.search( /h\d/i ) !== -1; },
				":enabled" : function( e ) { return !e.disabled && e.type !== "hidden"; },
				":disabled" : function( e ) { return e.disabled; },
				":checked" : function( e ) { return e.checked; },
				":contains" : function( e, s ) { return s.test( (e.textContent || e.innerText || "") ); },
				":parent" : function( e ) { return !!e.firstChild; },
				":odd" : function( e ) { return this[ ":nth-child" ]( e, "2n+2", 2, 2 ); },
				":even" : function( e ) { return this[ ":nth-child" ]( e, "2n+1", 2, 1 ); },
				":nth" : function( e, s, i ) { return s == i; },
				":eq" : function( e, s, i ) { return s == i; },
				":gt" : function( e, s, i ) { return i > s; },
				":lt" : function( e, s, i ) { return i < s; },
				":first" : function( e, s, i ) { return i == 0 },
				":last" : function( e, s, i, end ) { return i == end; }
			},
			combinator : {
				"" : function( r, u, f, s ) {
					var rUID = r.uid;
					while( (r = r.parentNode) !== null && !f[ r.uid ]) {
						if( !!u[ r.uid ] || !!s[ r.uid ] ) {
							return (s[ rUID ] = true);
						}
					}
					return (f[ rUID ] = false);
				},
				">" : function( r, u, f, s ) {
					return r.parentNode && u[ r.parentNode.uid ] ;
				},
				"+" : function( r, u, f, s ) {
					while( (r = r.previousSibling) !== null && !f[ r.uid ] ) {
						if( r.nodeType === 1 )
							return r.uid in u;
					}
					return false;
				},
				"~" : function( r, u, f, s ) {
					var rUID = r.uid;
					while( (r = r.previousSibling) !== null && !f[ r.uid ] ) {
						if( !!u[ r.uid ] || !!s[ r.uid ] ) {
							return (s[ rUID ] = true);
						}
					}
					return (f[ rUID ] = false);
				}
			}
		}
	}
	
	// From John Resig -> http://ejohn.org/blog/thoughts-on-queryselectorall/
	// Copyright 2008, John Resig (http://ejohn.org/)
	// released under the MIT License
	if ( doc.querySelectorAll ) {
		(function(){
			var oldpeppy = peppy.query;
			
			peppy.query = function(sel, context){
				context = context || doc;
				if ( context === doc ) {
					try {
						return context.querySelectorAll(sel);
					} catch(e){}
				}
				
				return oldpeppy.apply(oldpeppy, arrayIt(arguments));
			};
		})();
	} else {
		// If the DOM changes we need to clear the cache because it will no longer be reliable. 
		// Inspired by code from Sizzle -> http://github.com/jeresig/sizzle/tree/master.
		// Copyright 2008, John Resig (http://ejohn.org/)
		// released under the MIT License	
		var aEvent = doc.addEventListener || doc.attachEvent;
		function clearCache(){ cache = {}; }
		aEvent("DOMAttrModified", clearCache, false);
		aEvent("DOMNodeInserted", clearCache, false);
		aEvent("DOMNodeRemoved", clearCache, false);	
	}
	
	//if( !($ = window.$) ) $ = peppy.query;
})();


// ---------------------------------------------------------------------	glock

var glock = function(){
	var that = {};
	
	that.emptyFunction = function(){};
	that.idfu = function(x) { return x;};
	
	that.isElement = function(object) {
		return object && object.nodeType == 1;
	},
	
	that.isArray = function(object) {
		return object != null && typeof object == "object" &&
	  'splice' in object && 'join' in object;
	},	
	
	that.isFunction = function(object) {
	    return typeof object == "function";
	};
	
	that.isString = function(object) {
	    return typeof object == "string";
	};
	
	that.isNumber = function(object) {
	    return typeof object == "number";
	};
	
	that.isDef = function(object) {
	    return typeof object !== "undefined";
	};
	
	that.isUndefined = function(object) {
	    return typeof object == "undefined";
	};
	
	that.isObject = function(object) {
		return (typeof object).toLowerCase() == "object";
	}
	
	that.xW = function(string) {
	  if (!glock.isString(string)) return [];
	  string = string.strip();
	  return string ? string.split(/\s+/) : [];
	}		
	
	that.ScriptFragment = '<script[^>]*>([\\S\\s]*?)<\/script>';
	that.JSONFilter = /^\/\*-secure-([\s\S]*)\*\/\s*$/;
	
	// pluging in CSS selection engine
	
	that.x = function(element) {
		if (this.isString(element)) {
			element = document.getElementById(element);
		}

		if (glock.isObject(element)){
			element = glock.extElement(element);
		}
		
		return element;
	}
	
	that.oxx = function() {
		return Selector.findChildElements(document, $A(arguments));
	};	
	
	that.xx = function(selector, context) {
		if(peppy) {
			var elements = peppy.query(selector,context);
			for(var i=0; i< elements.length; i++){
				elements[i] = glock.extElement(elements[i]);
			}
			return elements;
		} else {
			return [];
		}		
	}	

	return that;	
}();

// ---------------------------------------------------------------------	glock.nodeType

glock.nodeType = {
  ELEMENT: 1,
  ATTRIBUTE: 2,
  TEXT: 3,
  CDATA_SECTION: 4,
  ENTITY_REFERENCE: 5,
  ENTITY: 6,
  PROCESSING_INSTRUCTION: 7,
  COMMENT: 8,
  DOCUMENT: 9,
  DOCUMENT_TYPE: 10,
  DOCUMENT_FRAGMENT: 11,
  NOTATION: 12
};

// ---------------------------------------------------------------------	glock.env

glock.env = {
	ie: /MSIE/i.test(navigator.userAgent),
	ie6: /^[^(]*?\([^(]*?MSIE 6/i.test(navigator.userAgent),
	ie7: /^[^(]*?\([^(]*?MSIE 7/i.test(navigator.userAgent),
	ie8: /^[^(]*?\([^(]*?MSIE 8/i.test(navigator.userAgent),
	firefox: /Firefox/i.test(navigator.userAgent),
	opera: /Opera/i.test(navigator.userAgent),
	webkit: /Webkit/i.test(navigator.userAgent),
    gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
    mobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)	
};


// ---------------------------------------------------------------------	glock.browserFeatures

glock.browserFeatures = {
    XPath: !!document.evaluate,
    ElementExtensions: !!window.HTMLElement,
    SpecificElementExtensions:
      document.createElement('div').__proto__ &&
      document.createElement('div').__proto__ !==
        document.createElement('form').__proto__
};

// ---------------------------------------------------------------------	glock.obj
	
glock.extObj = function(destination, source) {
		if(destination && source){
	  		for (var property in source) {
			    destination[property] = source[property];
			}
		}
	  return destination;
	};
	
glock.obj = {
  toJSON: function(object) {
    var type = typeof object;
    switch (type) {
      case 'undefined':
      case 'function':
      case 'unknown': return;
      case 'boolean': return object.toString();
    }

    if (object === null) return 'null';
    if (object.toJSON) return object.toJSON();
    if (glock.isElement(object)) return;

    var results = [];
    for (var property in object) {
      var value = glock.obj.toJSON(object[property]);
      if (!glock.isUndefined(value))
        results.push(property.toJSON() + ': ' + value);
    }

    return '{' + results.join(', ') + '}';
  },

//TODO: !!! hash function
  toQueryString: function(object) {
    return $H(object).toQueryString();
  },

  toHTML: function(object) {
    return object && object.toHTML ? object.toHTML() : glock.string.interpret(object);
  },

  keys: function(object) {
    var keys = [];
    for (var property in object)
      keys.push(property);
    return keys;
  },

  values: function(object) {
    var values = [];
    for (var property in object)
      values.push(object[property]);
    return values;
  },

  clone: function(object) {
    return glock.extObj({ }, object);
  },

  isElement: function(object) {
    return object && object.nodeType == 1;
  },

  isArray: function(object) {
    return object != null && typeof object == "object" &&
      'splice' in object && 'join' in object;
  },

  isHash: function(object) {
    return object instanceof glock.hash;
  },

  isFunction: function(object) {
    return typeof object == "function";
  },

  isString: function(object) {
    return typeof object == "string";
  },

  isNumber: function(object) {
    return typeof object == "number";
  },

  isUndefined: function(object) {
    return typeof object == "undefined";
  }
};	
	
// ---------------------------------------------------------------------	glock.string
//String.prototype
//!!!
glock.string = {
  grep: function(pattern, replacement) {
    var result = '', source = this, match;
    //replacement = arguments.callee.prepareReplacement(replacement);

    while (source.length > 0) {
		match = source.match(pattern)
      if (match) {
        result += source.slice(0, match.index);
        result += glock.string.interpret(replacement(match));
        source  = source.slice(match.index + match[0].length);
      } else {
        result += source;
		source = '';
      }
    }
    return result;
  },

  sub: function(pattern, replacement, count) {
    //replacement = this.grep.prepareReplacement(replacement);
    count = glock.isUndefined(count) ? 1 : count;

    return this.grep(pattern, function(match) {
      if (--count < 0) return match[0];
      return replacement(match);
    });
  },

  scan: function(pattern, iterator) {
    this.grep(pattern, iterator);
    return String(this);
  },

  truncate: function(length, truncation) {
    length = length || 30;
    truncation = glock.isUndefined(truncation) ? '...' : truncation;
    return this.length > length ?
      this.slice(0, length - truncation.length) + truncation : String(this);
  },

  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },

  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(glock.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(glock.ScriptFragment, 'img');
    var matchOne = new RegExp(glock.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(function(script) { return eval(script) });
  },
  
  escapeHTML: function() {
    return this.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  },
  unescapeHTML: function() {
    return this.replace(/&amp;/g,'&').replace(/&lt;/g,'<').replace(/&gt;/g,'>');
  },
  
  toQueryParams: function(separator) {
    var match = this.strip().match(/([^?#]*)(#.*)?$/);
    if (!match) return { };

    return match[1].split(separator || '&').inject({ }, function(hash, pair) {
      if ((pair = pair.split('='))[0]) {
        var key = decodeURIComponent(pair.shift());
        var value = pair.length > 1 ? pair.join('=') : pair[0];
        if (value != undefined) value = decodeURIComponent(value);

        if (key in hash) {
          if (!glock.isArray(hash[key])) hash[key] = [hash[key]];
          hash[key].push(value);
        }
        else hash[key] = value;
      }
      return hash;
    });
  },

  toArray: function() {
    return this.split('');
  },

  succ: function() {
    return this.slice(0, this.length - 1) +
      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
  },

  times: function(count) {
    return count < 1 ? '' : new Array(count + 1).join(this);
  },

  camelize: function() {
    var parts = this.split('-'), len = parts.length;
    if (len == 1) return parts[0];

    var camelized = this.charAt(0) == '-'
      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
      : parts[0];

    for (var i = 1; i < len; i++)
      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);

    return camelized;
  },

  capitalize: function() {
    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
  },

  underscore: function() {
    return this.grep(/::/, '/').grep(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').grep(/([a-z\d])([A-Z])/,'#{1}_#{2}').grep(/-/,'_').toLowerCase();
  },

  dasherize: function() {
    return this.grep(/_/,'-');
  },

  inspect: function(useDoubleQuotes) {
    var escapedString = this.grep(/[\x00-\x1f\\]/, function(match) {
      var character = String.specialChar[match[0]];
      return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
    });
    if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
    return "'" + escapedString.replace(/'/g, '\\\'') + "'";
  },

  toJSON: function() {
    return this.inspect(true);
  },

  unfilterJSON: function(filter) {
    return this.sub(filter || glock.JSONFilter, '#{1}');
  },

  isJSON: function() {
    var str = this;
    if (str.blank()) return false;
    str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
    return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
  },

  evalJSON: function(sanitize) {
    var json = this.unfilterJSON();
    try {
      if (!sanitize || json.isJSON()) return eval('(' + json + ')');
    } catch (e) { }
    throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
  },

  include: function(pattern) {
    return this.indexOf(pattern) > -1;
  },

  startsWith: function(pattern) {
    return this.indexOf(pattern) === 0;
  },

  endsWith: function(pattern) {
    var d = this.length - pattern.length;
    return d >= 0 && this.lastIndexOf(pattern) === d;
  },

  empty: function() {
    return this == '';
  },

  blank: function() {
    return /^\s*$/.test(this);
  },

  interpolate: function(object, pattern) {
    return new Template(this, pattern).evaluate(object);
  }
};

glock.string.trim = glock.string.strip;

glock.extObj(glock.string, {
  interpret: function(value) {
    return value == null ? '' : String(value);
  },
  specialChar: {
    '\b': '\\b',
    '\t': '\\t',
    '\n': '\\n',
    '\f': '\\f',
    '\r': '\\r',
    '\\': '\\\\'
  }
});

glock.extObj(String.prototype, glock.string);

//String.prototype.grep.prepareReplacement = function(replacement) {
//  if (glock.isFunction(replacement)) return replacement;
//  var template = new Template(replacement);
//  return function(match) { return template.evaluate(match) };
//};
//
//String.prototype.parseQuery = String.prototype.toQueryParams;

// ---------------------------------------------------------------------	glock.function

glock.efunction = {
  argumentNames: function() {
    var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
    return names.length == 1 && !names[0] ? [] : names;
  },

  bind: function() {
    if (arguments.length < 2 && glock.isUndefined(arguments[0])) return this;
    var __method = this, args = glock.xA(arguments), object = args.shift();
    return function() {
      return __method.apply(object, args.concat(glock.xA(arguments)));
    }
  },

  bindAsEventListener: function() {
    var __method = this, args = glock.xA(arguments), object = args.shift();
    return function(event) {
      return __method.apply(object, [event || window.event].concat(args));
    }
  },

  curry: function() {
    if (!arguments.length) return this;
    var __method = this, args = glock.xA(arguments);
    return function() {
      return __method.apply(this, args.concat(glock.xA(arguments)));
    }
  },

  gldelay: function() {
    var __method = this, args = glock.xA(arguments), timeout = args.shift() * 1000;
    return window.setTimeout(function() {
      return __method.apply(__method, args);
    }, timeout);
  },

  wrap: function(wrapper) {
    var __method = this;
    return function() {
      return wrapper.apply(this, [__method.bind(this)].concat(glock.xA(arguments)));
    }
  },

  methodize: function() {
    if (this._methodized) return this._methodized;
    var __method = this;
    this._methodized = function() {
      return __method.apply(null, [this].concat(glock.xA(arguments)));
    };
	return this._methodized;
  }
};

//!!!
glock.extObj(Function.prototype,glock.efunction);

// ---------------------------------------------------------------------	glock.enumerable

glock.enumerable = {
  oBreak : {},
  each: function(iterator, context) {
    var index = 0;
    iterator = iterator.bind(context);
    try {
      this._each(function(value) {
        iterator(value, index++);
      });
    } catch (e) {
      if (e != this.oBreak) throw e;
    }
    return this;
  },

  eachSlice: function(number, iterator, context) {
    iterator = iterator ? iterator.bind(context) : glock.idfu;
    var index = -number, slices = [], array = this.toArray();
    while ((index += number) < array.length)
      slices.push(array.slice(index, index+number));
    return slices.collect(iterator, context);
  },

  all: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : glock.idfu;
    var result = true;
    this.each(function(value, index) {
      result = result && !!iterator(value, index);
      if (!result) throw this.oBreak;
    });
    return result;
  },

  any: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : glock.idfu;
    var result = false;
    this.each(function(value, index) {
	  result = !!iterator(value, index);
      if (result) {
        throw this.oBreak;
	  }	
    });
    return result;
  },

  collect: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : glock.idfu;
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },

  detect: function(iterator, context) {
    iterator = iterator.bind(context);
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw this.oBreak;
      }
    });
    return result;
  },

  findAll: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(filter, iterator, context) {
    iterator = iterator ? iterator.bind(context) : glock.idfu;
    var results = [];

    if (glock.isString(filter))
      filter = new RegExp(filter);

    this.each(function(value, index) {
      if (filter.match(value))
        results.push(iterator(value, index));
    });
    return results;
  },

  include: function(object) {
    if (glock.isFunction(this.indexOf))
      if (this.indexOf(object) != -1) return true;

    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw this.oBreak;
      }
    });
    return found;
  },

  inGroupsOf: function(number, fillWith) {
    fillWith = glock.isUndefined(fillWith) ? null : fillWith;
    return this.eachSlice(number, function(slice) {
      while(slice.length < number) slice.push(fillWith);
      return slice;
    });
  },

  inject: function(memo, iterator, context) {
    iterator = iterator.bind(context);
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = glock.xA(arguments).slice(1);
    return this.map(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : glock.idfu;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == null || value >= result)
        result = value;
    });
    return result;
  },

  min: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : glock.idfu;
    var result;
    this.each(function(value, index) {
      value = iterator(value, index);
      if (result == null || value < result)
        result = value;
    });
    return result;
  },

  partition: function(iterator, context) {
    iterator = iterator ? iterator.bind(context) : glock.idfu;
    var trues = [], falses = [];
    this.each(function(value, index) {
      (iterator(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator, context) {
    iterator = iterator.bind(context);
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator, context) {
    iterator = iterator.bind(context);
    return this.map(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {	
    return this.map();
  },

  zip: function() {
    var iterator = glock.idfu, args = glock.xA(arguments);
    if (glock.isFunction(args.last()))
      iterator = args.pop();

    var collections = [this].concat(args).map(glock.xA);
    return this.map(function(value, index) {
      return iterator(collections.pluck(index));
    });
  },

  size: function() {
    return this.toArray().length;
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  }
};

glock.extObj(glock.enumerable, {
  map:     glock.enumerable.collect,
  find:    glock.enumerable.detect,
  select:  glock.enumerable.findAll,
  filter:  glock.enumerable.findAll,
  member:  glock.enumerable.include,
  entries: glock.enumerable.toArray,
  every:   glock.enumerable.all,
  some:    glock.enumerable.any
});

//!!!
glock.extObj(Array.prototype, glock.enumerable);

// ---------------------------------------------------------------------	glock.array		

// we should save original reverse function
if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
	
glock.eArray = {
  _each: function(iterator) {
    for (var i = 0, length = this.length; i < length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(glock.isArray(value) ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = glock.xA(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  reverse: function(inline) {
		var x = (inline !== false ? this : this.toArray());
    return x._reverse();
  },

  reduce: function() {
    return this.length > 1 ? this : this[0];
  },

  uniq: function(sorted) {
    return this.inject([], function(array, value, index) {
      if (0 == index || (sorted ? array.last() != value : !array.include(value)))
        array.push(value);
      return array;
    });
  },

  intersect: function(array) {
    return this.uniq().findAll(function(item) {
      return array.detect(function(value) { return item === value });
    });
  },

  clone: function() {
    return [].concat(this);
  },

  size: function() {
    return this.length;
  },

  inspect: function() {
    return '[' + this.map(glock.inspect).join(', ') + ']';
  },

  toJSON: function() {
    var results = [];
    this.each(function(object) {
      var value = glock.obj.toJSON(object);
      if (!glock.isUndefined(value)) results.push(value);
    });
    return '[' + results.join(', ') + ']';
  }
};	

//!!!
glock.extObj(Array.prototype, glock.eArray);

glock.xA = function (iterable) {
  if (!iterable) return [];
  if (iterable.toArray) return iterable.toArray();
  var length = iterable.length || 0, results = new Array(length);
  while (length--) results[length] = iterable[length];
  return results;
}

// ---------------------------------------------------------------------	glock.hash

//TODO: !!! implement hash object

// ---------------------------------------------------------------------	glock.event
	
	
glock.event = {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,
  KEY_HOME:     36,
  KEY_END:      35,
  KEY_PAGEUP:   33,
  KEY_PAGEDOWN: 34,
  KEY_INSERT:   45,

  cache: {},
	
	relatedTarget: function(event) {
    var element;
    switch(event.type) {
      case 'mouseover': element = event.fromElement; break;
      case 'mouseout':  element = event.toElement;   break;
      default: return null;
    }
    return glock.extElement(element);
  }
};

//---------
glock.extObj(glock.event,(function() {
  var isButton;

  if (glock.env.ie) {
    var buttonMap = { 0: 1, 1: 4, 2: 2 };
    isButton = function(event, code) {
      return event.button == buttonMap[code];
    };

  } else if (glock.env.webkit) {
    isButton = function(event, code) {
      switch (code) {
        case 0: return event.which == 1 && !event.metaKey;
        case 1: return event.which == 1 && event.metaKey;
        default: return false;
      }
    };

  } else {
    isButton = function(event, code) {
      return event.which ? (event.which === code + 1) : (event.button === code);
    };
  }

  return {
    isLeftClick:   function(event) { return isButton(event, 0) },
    isMiddleClick: function(event) { return isButton(event, 1) },
    isRightClick:  function(event) { return isButton(event, 2) },

    element: function(event) {
      event = glock.extEvent(event);

      var node          = event.target,
          type          = event.type,
          currentTarget = event.currentTarget;

      if (currentTarget && currentTarget.tagName) {
        // Firefox screws up the "click" event when moving between radio buttons
        // via arrow keys. It also screws up the "load" and "error" events on images,
        // reporting the document as the target instead of the original image.
        if (type === 'load' || type === 'error' ||
          (type === 'click' && currentTarget.tagName.toLowerCase() === 'input'
            && currentTarget.type === 'radio'))
              node = currentTarget;
      }
      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
      return glock.extElement(node);
    },
/*
    findElement: function(event, expression) {
      var element = glock.event.element(event);
      if (!expression) return element;
      var elements = [element].concat(element.ancestors());
      return glock.xx(elements, expression, 0);
    },
*/
    pointer: function(event) {
      var docElement = document.documentElement,
      body = document.body || { scrollLeft: 0, scrollTop: 0 };
      return {
        x: event.pageX || (event.clientX +
          (docElement.scrollLeft || body.scrollLeft) -
          (docElement.clientLeft || 0)),
        y: event.pageY || (event.clientY +
          (docElement.scrollTop || body.scrollTop) -
          (docElement.clientTop || 0))
      };
    },

    pointerX: function(event) { return glock.event.pointer(event).x },
    pointerY: function(event) { return glock.event.pointer(event).y },

    stop: function(event) {
			glock.extEvent(event);
      event.preventDefault();
      event.stopPropagation();
      event.stopped = true;
    }
  };
})());

glock.extEvent = (function() {
  var methods = glock.obj.keys(glock.event).inject({ }, function(m, name) {

		if (glock.isFunction(glock.event[name])) {
	    m[name] = glock.event[name].methodize();
		}
    return m;
  });

  if (glock.env.ie) {
    glock.extObj(methods, {
      stopPropagation: function() { this.cancelBubble = true },
      preventDefault:  function() { this.returnValue = false },
      inspect: function() { return "[object Event]" }
    });

    return function(event) {
      if (!event) return false;
      if (event._extendedByPrototype) return event;

      event._extendedByPrototype = glock.emptyFunction;
      var pointer =glock.event.pointer(event);
      glock.extObj(event, {
        target: event.srcElement,
        relatedTarget: glock.event.relatedTarget(event),
        pageX:  pointer.x,
        pageY:  pointer.y
      });
      return glock.extObj(event, methods);
    };

  } else {
    glock.eventPrototype = glock.eventPrototype || document.createEvent("HTMLEvents")['__proto__'];
		glock.extObj(glock.eventPrototype, methods);
    return glock.idfu;
  }
})();
//---------

glock.extObj(glock.event, (function() {
  var cache = glock.event.cache;

  function getEventID(element) {
    if (element._prototypeEventID) return element._prototypeEventID[0];
    arguments.callee.id = arguments.callee.id || 1;
	element._prototypeEventID = [++arguments.callee.id];
    return element._prototypeEventID;
  }

  function getDOMEventName(eventName) {
    if (eventName && eventName.include(':')) return "dataavailable";
    return eventName;
  }

  function getCacheForID(id) {
	cache[id] = cache[id] || { };
    return cache[id];
  }

  function getWrappersForEventName(id, eventName) {
    var c = getCacheForID(id);
	c[eventName] = c[eventName] || [];
    return c[eventName];
  }

  function createWrapper(element, eventName, handler) {
    var id = getEventID(element);
    var c = getWrappersForEventName(id, eventName);

    if (c.pluck("handler").include(handler)) return false;

    var wrapper = function(event) {
//TODO: implement event extension
/*
      if (!glock.event || !glock.event.ext ||
        (event.eventName && event.eventName != eventName))
          return false;

      glock.event.ext(event);
//*/
      handler.call(element, event);
    };

    wrapper.handler = handler;
    c.push(wrapper);
    return wrapper;
  }

  function findWrapper(id, eventName, handler) {
    var c = getWrappersForEventName(id, eventName);
    return c.find(function(wrapper) { return wrapper.handler == handler });
  }

  function destroyWrapper(id, eventName, handler) {
    var c = getCacheForID(id);
    if (!c[eventName]) return false;
    c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
  }

  function destroyCache() {
    for (var id in cache)
      for (var eventName in cache[id])
        cache[id][eventName] = null;
  }

  if (window.attachEvent) {
    window.attachEvent("onunload", destroyCache);
  }

  return {
    observe: function(element, eventName, handler) {
      element = glock.x(element);
      var name = getDOMEventName(eventName);

      var wrapper = createWrapper(element, eventName, handler);
      if (!wrapper) return element;

      if (element.addEventListener) {
        element.addEventListener(name, wrapper, false);
      } else {
        element.attachEvent("on" + name, wrapper);
      }

      return element;
    },

    stopObserving: function(element, eventName, handler) {
      element = glock.x(element);
      var id = getEventID(element), name = getDOMEventName(eventName);

      if (!handler && eventName) {
        getWrappersForEventName(id, eventName).each(function(wrapper) {
          element.stopObserving(eventName, wrapper.handler);
        });
        return element;

      } else if (!eventName) {
        glock.keys(getCacheForID(id)).each(function(eventName) {
          element.stopObserving(eventName);
        });
        return element;
      }

      var wrapper = findWrapper(id, eventName, handler);
      if (!wrapper) return element;

      if (element.removeEventListener) {
        element.removeEventListener(name, wrapper, false);
      } else {
        element.detachEvent("on" + name, wrapper);
      }

      destroyWrapper(id, eventName, handler);

      return element;
    },

    fire: function(element, eventName, memo) {
      element = glock.x(element);
      if (element == document && document.createEvent && !element.dispatchEvent)
        element = document.documentElement;

      var event;
      if (document.createEvent) {
        event = document.createEvent("HTMLEvents");
        event.initEvent("dataavailable", true, true);
      } else {
        event = document.createEventObject();
        event.eventType = "ondataavailable";
      }

      event.eventName = eventName;
      event.memo = memo || { };

      if (document.createEvent) {
        element.dispatchEvent(event);
      } else {
        element.fireEvent(event.eventType, event);
      }

      return Event.extend(event);
    }
  };
})());

// ---------------------------------------------------------------------	glock.element

glock.element = {
  visible: function(element) {
    return glock.x(element).style.display != 'none';
  },

  toggle: function(element) {
    element = glock.x(element);		
    glock.element[glock.element.visible(element) ? 'hide' : 'show'](element);
    return element;
  },

  hide: function(element) {
    glock.x(element).style.display = 'none';
    return element;
  },

  show: function(element) {
    glock.x(element).style.display = '';
    return element;
  },

  remove: function(element) {
    element = glock.x(element);
    element.parentNode.removeChild(element);
    return element;
  },

  update: function(element, content) {
    element = glock.x(element);
    if (content && content.toElement) content = content.toElement();
    if (glock.obj.isElement(content)) return element.update().insert(content);
    content = glock.obj.toHTML(content);
    element.innerHTML = content.stripScripts();
    content.evalScripts.bind(content).defer();
    return element;
  },

  replace: function(element, content) {
    element = glock.x(element);
    if (content && content.toElement) content = content.toElement();
    else if (!glock.obj.isElement(content)) {
      content = glock.obj.toHTML(content);
      var range = element.ownerDocument.createRange();
      range.selectNode(element);
      content.evalScripts.bind(content).defer();
      content = range.createContextualFragment(content.stripScripts());
    }
    element.parentNode.replaceChild(content, element);
    return element;
  },

  insert: function(element, insertions) {
    element = glock.x(element);

    if (glock.isString(insertions) || glock.isNumber(insertions) ||
        glock.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
          insertions = {bottom:insertions};

    var content, insert, tagName, childNodes;

    for (var position in insertions) {
      content  = insertions[position];
      position = position.toLowerCase();
      insert = glock.element._insertionTranslations[position];

      if (content && content.toElement) content = content.toElement();
      if (glock.isElement(content)) {
        insert(element, content);
        continue;
      }

      content = glock.obj.toHTML(content);

      tagName = ((position == 'before' || position == 'after')
        ? element.parentNode : element).tagName.toUpperCase();

      childNodes = glock.element._getContentFromAnonymousElement(tagName, content.stripScripts());

      if (position == 'top' || position == 'after') childNodes.reverse();
      childNodes.each(insert.curry(element));

      content.evalScripts.bind(content).defer();
    }

    return element;
  },

  wrap: function(element, wrapper, attributes) {
    element = glock.x(element);
    if (glock.isElement(wrapper)) {

      glock.x(wrapper).writeAttribute(attributes || { });
		} else if (glock.isString(wrapper)) {
			wrapper = glock.builder.node(wrapper, attributes);
		} else { 
			wrapper = glock.builder.node('div');
		}
    if (element.parentNode)
      element.parentNode.replaceChild(wrapper, element);
    wrapper.appendChild(element);
    return wrapper;
  },

  inspect: function(element) {
    element = glock.x(element);
    var result = '<' + element.tagName.toLowerCase();
    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
      var property = pair.first(), attribute = pair.last();
      var value = (element[property] || '').toString();
      if (value) result += ' ' + attribute + '=' + value.inspect(true);
    });
    return result + '>';
  },

  recursivelyCollect: function(element, property) {
    element = glock.x(element);
    var elements = [];
    while (element = element[property])
      if (element.nodeType == 1)
        elements.push(glock.extElement(element));
    return elements;
  },

  ancestors: function(element) {
    return glock.x(element).recursivelyCollect('parentNode');
  },

  descendants: function(element) {
    return glock.x(element).select("*");
  },

  firstDescendant: function(element) {
    element = glock.x(element).firstChild;
    while (element && element.nodeType != 1) element = element.nextSibling;
    return glock.x(element);
  },

  immediateDescendants: function(element) {
    if (!(element = glock.x(element).firstChild)) return [];
    while (element && element.nodeType != 1) element = element.nextSibling;
    if (element) return [element].concat(glock.x(element).nextSiblings());
    return [];
  },

  previousSiblings: function(element) {
    return glock.x(element).recursivelyCollect('previousSibling');
  },

  nextSiblings: function(element) {
    return glock.x(element).recursivelyCollect('nextSibling');
  },

  siblings: function(element) {
    element = glock.x(element);
    return element.previousSiblings().reverse().concat(element.nextSiblings());
  },

  match: function(element, selector) {
    if (Object.isString(selector))
      selector = new glock.Selector(selector);
    return selector.match(glock.x(element));
  },

  up: function(element, expression, index) {
	//TODO: !!! broken plug peppy
    element = glock.x(element);
    if (arguments.length == 1) return glock.x(element.parentNode);
    var ancestors = element.ancestors();
    return glock.isNumber(expression) ? ancestors[expression] :
      glock.selector.findElement(ancestors, expression, index);
  },

  down: function(element, expression, index) {
    element = glock.x(element);
    if (arguments.length == 1) return element.firstDescendant();
    return glock.isNumber(expression) ? element.descendants()[expression] :
      glock.xx(expression,element)[index || 0];
  },

  previous: function(element, expression, index) {
    element = glock.x(element);
    if (arguments.length == 1) return glock.x(glock.selector.handlers.previousElementSibling(element));
    var previousSiblings = element.previousSiblings();
    return glock.isNumber(expression) ? previousSiblings[expression] :
      glock.selector.findElement(previousSiblings, expression, index);
  },

  next: function(element, expression, index) {
    element = glock.x(element);
    if (arguments.length == 1) return glock.x(glock.selector.handlers.nextElementSibling(element));
    var nextSiblings = element.nextSiblings();
    return glock.isNumber(expression) ? nextSiblings[expression] :
      glock.selector.findElement(nextSiblings, expression, index);
  },

  select: function(element,expr) {
    //var args = glock.xA(arguments), element = glock.x(args.shift());
	return glock.xx(expr,element);
    //returnglock.selector.findChildElements(element, args);
  },

  adjacent: function() {
    var args = glock.xA(arguments), element = glock.x(args.shift());
    return glock.selector.findChildElements(element.parentNode, args).without(element);
  },

  identify: function(element) {
    element = glock.x(element);
    var id = element.readAttribute('id'), self = arguments.callee;
    if (id) return id;
    do { id = 'anonymous_element_' + self.counter++ } while (glock.x(id));
    element.writeAttribute('id', id);
    return id;
  },

  readAttribute: function(element, name) {
    element = glock.x(element);
    if (glock.ebv.ie) {
      var t = glock.element._attributeTranslations.read;
      if (t.values[name]) return t.values[name](element, name);
      if (t.names[name]) name = t.names[name];
      if (name.include(':')) {
        return (!element.attributes || !element.attributes[name]) ? null :
         element.attributes[name].value;
      }
    }
    return element.getAttribute(name);
  },

  writeAttribute: function(element, name, value) {
    element = glock.x(element);

    var attributes = { }, t = glock.builder.ATTR_MAP;

    if (typeof name == 'object') attributes = name;
    else attributes[name] = glock.isUndefined(value) ? true : value;

    for (var attr in attributes) {
      name = t[attr] || attr;
      value = attributes[attr];
      // if (t.values[attr]) name = t.values[attr](element, value);
      if (value === false || value === null)
        element.removeAttribute(name);
      else if (value === true)
        element.setAttribute(name, name);
      else element.setAttribute(name, value);
    }
    return element;
  },

  getHeight: function(element) {
    return glock.x(element).getDimensions().height;
  },

  getWidth: function(element) {
    return glock.x(element).getDimensions().width;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = glock.x(element))) return;
    var elementClassName = element.className;
    return (elementClassName.length > 0 && (elementClassName == className ||
      new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
  },

  addClassName: function(element, className) {
    if (!(element = glock.x(element))) return;
    if (!element.hasClassName(className))
      element.className += (element.className ? ' ' : '') + className;
    return element;
  },

  removeClassName: function(element, className) {
    if (!(element = glock.x(element))) return;
    element.className = element.className.replace(
      new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
    return element;
  },

  toggleClassName: function(element, className) {
    if (!(element = glock.x(element))) return;
    return element[element.hasClassName(className) ?
      'removeClassName' : 'addClassName'](className);
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = glock.x(element);
    var node = element.firstChild;
    while (node) {
      var nextNode = node.nextSibling;
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        element.removeChild(node);
      node = nextNode;
    }
    return element;
  },

  empty: function(element) {
    return glock.x(element).innerHTML.blank();
  },

  descendantOf: function(element, ancestor) {
    element = glock.x(element), ancestor = glock.x(ancestor);
    var originalAncestor = ancestor;

    if (element.compareDocumentPosition)
      return (element.compareDocumentPosition(ancestor) & 8) === 8;

    if (element.sourceIndex && !glock.env.opera) {
      var e = element.sourceIndex, a = ancestor.sourceIndex,
       nextAncestor = ancestor.nextSibling;
      if (!nextAncestor) {
        do { ancestor = ancestor.parentNode; }
        while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
      }
      if (nextAncestor && nextAncestor.sourceIndex)
       return (e > a && e < nextAncestor.sourceIndex);
    }

    while (element = element.parentNode)
      if (element == originalAncestor) return true;
    return false;
  },

  scrollTo: function(element) {
    element = glock.x(element);
    var pos = element.cumulativeOffset();
    window.scrollTo(pos[0], pos[1]);
    return element;
  },

  getStyle: function(element, style) {
    element = glock.x(element);
    style = style == 'float' ? 'cssFloat' : style.camelize();
    var value = element.style[style];
    if (!value) {		
      var css = document.defaultView.getComputedStyle(element, null);
      value = css ? css[style] : null;
    }
    if (style == 'opacity') return value ? parseFloat(value) : 1.0;
    return value == 'auto' ? null : value;
  },

  getOpacity: function(element) {
    return glock.x(element).getStyle('opacity');
  },

  setStyle: function(element, styles) {
    element = glock.x(element);
    var elementStyle = element.style, match;
    if (Object.isString(styles)) {
      element.style.cssText += ';' + styles;
      return styles.include('opacity') ?
        element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
    }
    for (var property in styles)
      if (property == 'opacity') element.setOpacity(styles[property]);
      else
        elementStyle[(property == 'float' || property == 'cssFloat') ?
          (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
            property] = styles[property];

    return element;
  },

  setOpacity: function(element, value) {
    element = glock.x(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;
    return element;
  },

  getDimensions: function(element) {
    element = glock.x(element);
    var display = glock.x(element).getStyle('display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = glock.x(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
    return element;
  },

  undoPositioned: function(element) {
    element = glock.x(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
    return element;
  },

  makeClipping: function(element) {
    element = glock.x(element);
    if (element._overflow) return element;
    element._overflow = Element.getStyle(element, 'overflow') || 'auto';
    if (element._overflow !== 'hidden')
      element.style.overflow = 'hidden';
    return element;
  },

  undoClipping: function(element) {
    element = glock.x(element);
    if (!element._overflow) return element;
    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
    element._overflow = null;
    return element;
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        if (element.tagName == 'BODY') break;
        var p = Element.getStyle(element, 'position');
        if (p !== 'static') break;
      }
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  absolutize: function(element) {
    element = glock.x(element);
    if (element.getStyle('position') == 'absolute') return;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    var offsets = element.positionedOffset();
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.width  = width + 'px';
    element.style.height = height + 'px';
    return element;
  },

  relativize: function(element) {
    element = glock.x(element);
    if (element.getStyle('position') == 'relative') return;
    // Position.prepare(); // To be done manually by Scripty when it needs it.

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
    return element;
  },

  cumulativeScrollOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return Element._returnOffset(valueL, valueT);
  },

  getOffsetParent: function(element) {
    if (element.offsetParent) return glock.x(element.offsetParent);
    if (element == document.body) return glock.x(element);

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return glock.x(element);

    return glock.x(document.body);
  },

  viewportOffset: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent == document.body &&
        Element.getStyle(element, 'position') == 'absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      if (!glock.env.opera || element.tagName == 'BODY') {
        valueT -= element.scrollTop  || 0;
        valueL -= element.scrollLeft || 0;
      }
    } while (element = element.parentNode);

    return glock.element._returnOffset(valueL, valueT);
  },

  clonePosition: function(element, source) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || { });

    // find page position of source
    source = glock.x(source);
    var p = source.viewportOffset();

    // find coordinate system to use
    element = glock.x(element);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(element, 'position') == 'absolute') {
      parent = element.getOffsetParent();
      delta = parent.viewportOffset();
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if (options.setLeft)   element.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if (options.setTop)    element.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if (options.setWidth)  element.style.width = source.offsetWidth + 'px';
    if (options.setHeight) element.style.height = source.offsetHeight + 'px';
    return element;
  },
  _returnOffset : function(l, t) {
	  var result = [l, t];
	  result.left = l;
	  result.top = t;
	  return result;
  }
};

glock.element._getContentFromAnonymousElement = function(tagName, html) {
  var div = new Element('div'), t = glock.element._insertionTranslations.tags[tagName];
  if (t) {
    div.innerHTML = t[0] + html + t[1];
    t[2].times(function() { div = div.firstChild });
  } else div.innerHTML = html;
  return glock.xA(div.childNodes);
};

glock.element._insertionTranslations = {
  before: function(element, node) {
    element.parentNode.insertBefore(node, element);
  },
  top: function(element, node) {
    element.insertBefore(node, element.firstChild);
  },
  bottom: function(element, node) {
    element.appendChild(node);
  },
  after: function(element, node) {
    element.parentNode.insertBefore(node, element.nextSibling);
  },
  tags: {
    TABLE:  ['<table>',                '</table>',                   1],
    TBODY:  ['<table><tbody>',         '</tbody></table>',           2],
    TR:     ['<table><tbody><tr>',     '</tr></tbody></table>',      3],
    TD:     ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
    SELECT: ['<select>',               '</select>',                  1]
  }
};

(function() {
  glock.extObj(this.tags, {
    THEAD: this.tags.TBODY,
    TFOOT: this.tags.TBODY,
    TH:    this.tags.TD
  });
}).call(glock.element._insertionTranslations);

glock.element.Simulated = {
  hasAttribute: function(element, attribute) {
    attribute = glock.element._attributeTranslations.has[attribute] || attribute;
    var node = glock.x(element).getAttributeNode(attribute);
    return node && node.specified;
  }
};


//--
if (glock.env.opera) {
	glock.element.getStyle = glock.element.getStyle.wrap(
    function(proceed, element, style) {
      switch (style) {
        case 'left': case 'top': case 'right': case 'bottom':
          if (proceed(element, 'position') === 'static') return null;
        case 'height': case 'width':
          // returns '0px' for hidden elements; we want it to return null
          if (!glock.element.visible(element)) return null;

          // returns the border-box dimensions rather than the content-box
          // dimensions, so we subtract padding and borders from the value
          var dim = parseInt(proceed(element, style), 10);

          if (dim !== element['offset' + style.capitalize()])
            return dim + 'px';

          var properties;
          if (style === 'height') {
            properties = ['border-top-width', 'padding-top',
             'padding-bottom', 'border-bottom-width'];
          }
          else {
            properties = ['border-left-width', 'padding-left',
             'padding-right', 'border-right-width'];
          }
          return properties.inject(dim, function(memo, property) {
            var val = proceed(element, property);
            return val === null ? memo : memo - parseInt(val, 10);
          }) + 'px';
        default: return proceed(element, style);
      }
    }
  );
  glock.element.readAttribute = glock.element.readAttribute.wrap(
    function(proceed, element, attribute) {
      if (attribute === 'title') return element.title;
      return proceed(element, attribute);
    }
  );
}

else if (glock.env.ie) {
  // IE doesn't report offsets correctly for static elements, so we change them
  // to "relative" to get the values, then change them back.
  
  glock.element.getOffsetParent = glock.element.getOffsetParent.wrap(
    function(proceed, element) {
      element = glock.x(element);
      var position = element.getStyle('position');
      if (position !== 'static') return proceed(element);
      element.setStyle({ position: 'relative' });
      var value = proceed(element);
      element.setStyle({ position: position });
      return value;
    }
  );

  glock.xW('positionedOffset viewportOffset').each(function(method) {
	glock.element[method] = glock.element[method].wrap(
      function(proceed, element) {
        element = glock.x(element);
        var position = element.getStyle('position');
        if (position !== 'static') return proceed(element);
        // Trigger hasLayout on the offset parent so that IE6 reports
        // accurate offsetTop and offsetLeft values for position: fixed.
        var offsetParent = element.getOffsetParent();
        if (offsetParent && offsetParent.getStyle('position') === 'fixed')
          offsetParent.setStyle({ zoom: 1 });
        element.setStyle({ position: 'relative' });
        var value = proceed(element);
        element.setStyle({ position: position });
        return value;
      }
    );
  });

  glock.element.getStyle = function(element, style) {
    element = glock.x(element);
    style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
    var value = element.style[style];
    if (!value && element.currentStyle) value = element.currentStyle[style];

    if (style == 'opacity') {
      if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
        if (value[1]) return parseFloat(value[1]) / 100;
      return 1.0;
    }

    if (value == 'auto') {
      if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
        return element['offset' + style.capitalize()] + 'px';
      return null;
    }
    return value;
  };

  glock.element.setOpacity = function(element, value) {
    function stripAlpha(filter){
      return filter.replace(/alpha\([^\)]*\)/gi,'');
    }
    element = glock.x(element);
    var currentStyle = element.currentStyle;
    if ((currentStyle && !currentStyle.hasLayout) ||
      (!currentStyle && element.style.zoom == 'normal'))
        element.style.zoom = 1;

    var filter = element.getStyle('filter'), style = element.style;
    if (value == 1 || value === '') {
      (filter = stripAlpha(filter)) ?
        style.filter = filter : style.removeAttribute('filter');
      return element;
    } else if (value < 0.00001) value = 0;
    style.filter = stripAlpha(filter) +
      'alpha(opacity=' + (value * 100) + ')';
    return element;
  };
  
  glock._elementAttributeTranslations = {
    read: {
      names: {
        'class': 'className',
        'for':   'htmlFor'
      },
      values: {
        _getAttr: function(element, attribute) {
          return element.getAttribute(attribute, 2);
        },
        _getAttrNode: function(element, attribute) {
          var node = element.getAttributeNode(attribute);
          return node ? node.value : "";
        },
        _getEv: function(element, attribute) {
          attribute = element.getAttribute(attribute);
          return attribute ? attribute.toString().slice(23, -2) : null;
        },
        _flag: function(element, attribute) {
          return glock.x(element).hasAttribute(attribute) ? attribute : null;
        },
        style: function(element) {
          return element.style.cssText.toLowerCase();
        },
        title: function(element) {
          return element.title;
        }
      }
    }
  };

  glock._elementAttributeTranslations.write = {
    names: glock.extObj({
      cellpadding: 'cellPadding',
      cellspacing: 'cellSpacing'
    }, glock._elementAttributeTranslations.read.names),
    values: {
      checked: function(element, value) {
        element.checked = !!value;
      },

      style: function(element, value) {
        element.style.cssText = value ? value : '';
      }
    }
  };

  glock._elementAttributeTranslations.has = {};

  glock.xW('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
      'encType maxLength readOnly longDesc').each(function(attr) {
    glock._elementAttributeTranslations.write.names[attr.toLowerCase()] = attr;
    glock._elementAttributeTranslations.has[attr.toLowerCase()] = attr;
  });

  (function(v) {
    glock.extObj(v, {
      href:        v._getAttr,
      src:         v._getAttr,
      type:        v._getAttr,
      action:      v._getAttrNode,
      disabled:    v._flag,
      checked:     v._flag,
      readonly:    v._flag,
      multiple:    v._flag,
      onload:      v._getEv,
      onunload:    v._getEv,
      onclick:     v._getEv,
      ondblclick:  v._getEv,
      onmousedown: v._getEv,
      onmouseup:   v._getEv,
      onmouseover: v._getEv,
      onmousemove: v._getEv,
      onmouseout:  v._getEv,
      onfocus:     v._getEv,
      onblur:      v._getEv,
      onkeypress:  v._getEv,
      onkeydown:   v._getEv,
      onkeyup:     v._getEv,
      onsubmit:    v._getEv,
      onreset:     v._getEv,
      onselect:    v._getEv,
      onchange:    v._getEv
    });
  })(glock._elementAttributeTranslations.read.values);
}

else if (glock.env.gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
  glock.element.setOpacity = function(element, value) {
    element = glock.x(element);
    element.style.opacity = (value == 1) ? 0.999999 :
      (value === '') ? '' : (value < 0.00001) ? 0 : value;
    return element;
  };
}

else if (glock.env.webkit) {
  glock.element.setOpacity = function(element, value) {
    element = glock.x(element);
    element.style.opacity = (value == 1 || value === '') ? '' :
      (value < 0.00001) ? 0 : value;

    if (value == 1)
      if(element.tagName == 'IMG' && element.width) {
        element.width++; element.width--;
      } else try {
        var n = document.createTextNode(' ');
        element.appendChild(n);
        element.removeChild(n);
      } catch (e) { }

    return element;
  };

  // Safari returns margins on body which is incorrect if the child is absolutely
  // positioned.  For performance reasons, redefine Element#cumulativeOffset for
  // KHTML/WebKit only.
  glock.element.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (glock.element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return glock.element._returnOffset(valueL, valueT);
  };
}

if (glock.env.ie || glock.env.opera) {
  // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
  glock.element.update = function(element, content) {
    element = glock.x(element);

    if (content && content.toElement) content = content.toElement();
    if (glock.isElement(content)) return element.update().insert(content);
    
    content = glock.obj.toHTML(content);
    var tagName = element.tagName.toUpperCase();

    if (tagName in Element._insertionTranslations.tags) {
      glock.xA(element.childNodes).each(function(node) { element.removeChild(node) });
      glock.element._getContentFromAnonymousElement(tagName, content.stripScripts())
        .each(function(node) { element.appendChild(node) });
    }
    else element.innerHTML = content.stripScripts();

    content.evalScripts.bind(content).defer();
    return element;
  };
}
//--


glock.extObj(glock.element, {
  getElementsBySelector: glock.element.select,
  childElements: glock.element.immediateDescendants
});


if (!glock.browserFeatures.elementExtensions &&
    document.createElement('div').__proto__) {
  window.HTMLElement = { };
  window.HTMLElement.prototype = document.createElement('div').__proto__;
  glock.browserFeatures.elementExtensions = true;
}

glock.element.ByTag = {};


glock.extElement = function(element) {	
	var property, 
		methods,
		value;
    if (!element || element._extendedByPrototype ||
        element.nodeType != glock.nodeType.ELEMENT || element == window) return element;
		
	methods = glock.obj.clone(glock.element);
    for (property in methods) {
      value = methods[property];
      if (glock.isFunction(value) && !(property in element))
        element[property] = value.methodize();
    }

    element._extendedByPrototype = glock.idfu;		
	return element;
};

// ---------------------------------------------------------------------	glock.builder
// builder is based on source of Scriptaculous builder by Thomas Fuchs
// Copyright (c) Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)

glock.builder = {
  NODEMAP: {
    AREA: 'map',
    CAPTION: 'table',
    COL: 'table',
    COLGROUP: 'table',
    LEGEND: 'fieldset',
    OPTGROUP: 'select',
    OPTION: 'select',
    PARAM: 'object',
    TBODY: 'table',
    TD: 'table',
    TFOOT: 'table',
    TH: 'table',
    THEAD: 'table',
    TR: 'table'
  },
  // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
  //       due to a Firefox bug
  node: function(elementName) {
    elementName = elementName.toUpperCase();
    
    // try innerHTML approach
    var parentTag = this.NODEMAP[elementName] || 'div';
    var parentElement = document.createElement(parentTag);
    try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
      parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
    } catch(e) {}
    var element = parentElement.firstChild || null;
      
    // see if browser added wrapping tags
    if(element && (element.tagName.toUpperCase() != elementName))
      element = element.getElementsByTagName(elementName)[0];
    
    // fallback to createElement approach
    if(!element) element = document.createElement(elementName);
    
    // abort if nothing could be created
    if(!element) return;

    // attributes (or text)
    if(arguments[1])
      if(this._isStringOrNumber(arguments[1]) ||
        (arguments[1] instanceof Array) ||
        arguments[1].tagName) {
          this._children(element, arguments[1]);
        } else {
          var attrs = this._attributes(arguments[1]);
          if(attrs.length) {
            try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
              parentElement.innerHTML = "<" +elementName + " " +
                attrs + "></" + elementName + ">";
            } catch(e) {}
            element = parentElement.firstChild || null;
            // workaround firefox 1.0.X bug
            if(!element) {
              element = document.createElement(elementName);
              for(attr in arguments[1]) 
                element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
            }
            if(element.tagName.toUpperCase() != elementName)
              element = parentElement.getElementsByTagName(elementName)[0];
          }
        } 

    // text, or array of children
    if(arguments[2])
      this._children(element, arguments[2]);

     return glock.x(element);
  },
  _text: function(text) {
     return document.createTextNode(text);
  },

  ATTR_MAP: {
    'className': 'class',
    'htmlFor': 'for'
  },

  _attributes: function(attributes) {
    var attrs = [];
    for(attribute in attributes)
      attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
          '="' + attributes[attribute].toString().escapeHTML().grep(/"/,'&quot;') + '"');
    return attrs.join(" ");
  },
  _children: function(element, children) {
    if(children.tagName) {
      element.appendChild(children);
      return;
    }
    if(typeof children=='object') { // array can hold nodes and text
      children.flatten().each( function(e) {
        if(typeof e=='object')
          element.appendChild(e);
        else
          if(glock.builder._isStringOrNumber(e))
            element.appendChild(glock.builder._text(e));
      });
    } else
      if(glock.builder._isStringOrNumber(children))
        element.appendChild(glock.builder._text(children));
  },
  _isStringOrNumber: function(param) {
    return(typeof param=='string' || typeof param=='number');
  },
  build: function(html) {
    var element = this.node('div');
    glock.x(element).update(html.strip());
    return element.down();
  },
  dump: function(scope) { 
    if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope 
  
    var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
      "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
      "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
      "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
      "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
      "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
  
    tags.each( function(tag){ 
      scope[tag] = function() { 
        return glock.builder.node.apply(glock.builder, [tag].concat(glock.xA(arguments)));  
      };
    });
  }
};

// ---------------------------------------------------------------------	glock.selector

glock.Selector = function(expression) {
	var that = glock.selector;
    that.expression = expression.strip();
    that.compileMatcher();
};

glock.selector = {
 	shouldUseXPath : function() {	
    if (!glock.browserFeatures.XPath) return false;

    var e = that.expression;

    // Safari 3 chokes on :*-of-type and :empty	
    if (glock.env.webkit &&
     (e.include("-of-type") || e.include(":empty")))
      return false;

    // XPath can't do namespaced attributes, nor can it read
    // the "checked" property from DOM nodes
    if ((/(\[[\w-]*?:|:checked)/).test(that.expression))
      return false;

    return true;
  },

  compileMatcher : function() {
    if (that.shouldUseXPath())
      return that.compileXPathMatcher();

    var e = that.expression, ps = glock.selector.patterns, h = glock.selector.handlers,
        c = glock.selector.criteria, le, p, m;

    if (Selector._cache[e]) {
      that.matcher = glock.selector._cache[e];
      return;
    }

    that.matcher = ["that.matcher = function(root) {",
                    "var r = root, h = glock.selector.handlers, c = false, n;"];

    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          that.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
    	      new Template(c[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    that.matcher.push("return h.unique(n);\n}");
    eval(that.matcher.join('\n'));
    glock.selector._cache[that.expression] = that.matcher;
  },

  compileXPathMatcher : function() {
    var e = that.expression, ps = glock.selector.patterns,
        x = glock.selector.xpath, le, m;

    if (Selector._cache[e]) {
      that.xpath = glock.selector._cache[e]; return;
    }

    that.matcher = ['.//*'];
    while (e && le != e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        if (m = e.match(ps[i])) {
          that.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
            new Template(x[i]).evaluate(m));
          e = e.replace(m[0], '');
          break;
        }
      }
    }

    that.xpath = that.matcher.join('');
    glock.selector._cache[that.expression] = that.xpath;
  },

  findElements : function(root) {
    root = root || document;
    if (that.xpath) return document._getElementsByXPath(that.xpath, root);
    return that.matcher(root);
  },

  match : function(element) {
    that.tokens = [];

    var e = that.expression, ps = glock.selector.patterns, as = glock.selector.assertions;
    var le, p, m;

    while (e && le !== e && (/\S/).test(e)) {
      le = e;
      for (var i in ps) {
        p = ps[i];
        if (m = e.match(p)) {
          // use the glock.selector.assertions methods unless the selector
          // is too complex.
          if (as[i]) {
            that.tokens.push([i, Object.clone(m)]);
            e = e.replace(m[0], '');
          } else {
            // reluctantly do a document-wide search
            // and look for a match in the array
            return that.findElements(document).include(element);
          }
        }
      }
    }

    var match = true, name, matches;
    for (var i = 0, token; token = that.tokens[i]; i++) {
      name = token[0], matches = token[1];
      if (!Selector.assertions[name](element, matches)) {
        match = false; break;
      }
    }

    return match;
  },

  toString : function() {
    return that.expression;
  },

  inspect : function() {
    return "#<Selector:" + that.expression.inspect() + ">";
  },
//};

//glock.extObj(glock.selector, {
  _cache: { },

  xpath: {
    descendant:   "//*",
    child:        "/*",
    adjacent:     "/following-sibling::*[1]",
    laterSibling: '/following-sibling::*',
    tagName:      function(m) {
      if (m[1] == '*') return '';
      return "[local-name()='" + m[1].toLowerCase() +
             "' or local-name()='" + m[1].toUpperCase() + "']";
    },
    className:    "[contains(concat(' ', @class, ' '), ' #{1} ')]",
    id:           "[@id='#{1}']",
    attrPresence: function(m) {
      m[1] = m[1].toLowerCase();
      return new Template("[@#{1}]").evaluate(m);
    },
    attr: function(m) {
      m[1] = m[1].toLowerCase();
      m[3] = m[5] || m[6];
      return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
    },
    pseudo: function(m) {
      var h = glock.selector.xpath.pseudos[m[1]];
      if (!h) return '';
      if (Object.isFunction(h)) return h(m);
      return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
    },
    operators: {
      '=':  "[@#{1}='#{3}']",
      '!=': "[@#{1}!='#{3}']",
      '^=': "[starts-with(@#{1}, '#{3}')]",
      '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
      '*=': "[contains(@#{1}, '#{3}')]",
      '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
      '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
    },
    pseudos: {
      'first-child': '[not(preceding-sibling::*)]',
      'last-child':  '[not(following-sibling::*)]',
      'only-child':  '[not(preceding-sibling::* or following-sibling::*)]',
      'empty':       "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
      'checked':     "[@checked]",
      'disabled':    "[@disabled]",
      'enabled':     "[not(@disabled)]",
      'not': function(m) {
        var e = m[6], p = glock.selector.patterns,
            x = glock.selector.xpath, le, v;

        var exclusion = [];
        while (e && le != e && (/\S/).test(e)) {
          le = e;
          for (var i in p) {
            if (m = e.match(p[i])) {
              v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
              exclusion.push("(" + v.substring(1, v.length - 1) + ")");
              e = e.replace(m[0], '');
              break;
            }
          }
        }
        return "[not(" + exclusion.join(" and ") + ")]";
      },
      'nth-child':      function(m) {
        return glock.selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
      },
      'nth-last-child': function(m) {
        return glock.selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
      },
      'nth-of-type':    function(m) {
        return glock.selector.xpath.pseudos.nth("position() ", m);
      },
      'nth-last-of-type': function(m) {
        return glock.selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
      },
      'first-of-type':  function(m) {
        m[6] = "1"; return glock.selector.xpath.pseudos['nth-of-type'](m);
      },
      'last-of-type':   function(m) {
        m[6] = "1"; return glock.selector.xpath.pseudos['nth-last-of-type'](m);
      },
      'only-of-type':   function(m) {
        var p = glock.selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
      },
      nth: function(fragment, m) {
        var mm, formula = m[6], predicate;
        if (formula == 'even') formula = '2n+0';
        if (formula == 'odd')  formula = '2n+1';
        if (mm = formula.match(/^(\d+)$/)) // digit only
          return '[' + fragment + "= " + mm[1] + ']';
        if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
          if (mm[1] == "-") mm[1] = -1;
          var a = mm[1] ? Number(mm[1]) : 1;
          var b = mm[2] ? Number(mm[2]) : 0;
          predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
          "((#{fragment} - #{b}) div #{a} >= 0)]";
          return new Template(predicate).evaluate({
            fragment: fragment, a: a, b: b });
        }
      }
    }
  },

  criteria: {
    tagName:      'n = h.tagName(n, r, "#{1}", c);      c = false;',
    className:    'n = h.className(n, r, "#{1}", c);    c = false;',
    id:           'n = h.id(n, r, "#{1}", c);           c = false;',
    attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
    attr: function(m) {
      m[3] = (m[5] || m[6]);
      return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
    },
    pseudo: function(m) {
      if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
      return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
    },
    descendant:   'c = "descendant";',
    child:        'c = "child";',
    adjacent:     'c = "adjacent";',
    laterSibling: 'c = "laterSibling";'
  },

  patterns: {
    // combinators must be listed first
    // (and descendant needs to be last combinator)
    laterSibling: /^\s*~\s*/,
    child:        /^\s*>\s*/,
    adjacent:     /^\s*\+\s*/,
    descendant:   /^\s/,

    // selectors follow
    tagName:      /^\s*(\*|[\w\-]+)(\b|$)?/,
    id:           /^#([\w\-\*]+)(\b|$)/,
    className:    /^\.([\w\-\*]+)(\b|$)/,
    pseudo:
/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
    attrPresence: /^\[([\w]+)\]/,
    attr:         /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
  },

  // for glock.selector.match and Element#match
  assertions: {
    tagName: function(element, matches) {
      return matches[1].toUpperCase() == element.tagName.toUpperCase();
    },

    className: function(element, matches) {
      return Element.hasClassName(element, matches[1]);
    },

    id: function(element, matches) {
      return element.id === matches[1];
    },

    attrPresence: function(element, matches) {
      return Element.hasAttribute(element, matches[1]);
    },

    attr: function(element, matches) {
      var nodeValue = Element.readAttribute(element, matches[1]);
      return nodeValue && glock.selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
    }
  },

  handlers: {
    // UTILITY FUNCTIONS
    // joins two collections
    concat: function(a, b) {
      for (var i = 0, node; node = b[i]; i++)
        a.push(node);
      return a;
    },

    // marks an array of nodes for counting
    mark: function(nodes) {
      var _true = glock.emptyFunction;
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = _true;
      return nodes;
    },

    unmark: function(nodes) {
      for (var i = 0, node; node = nodes[i]; i++)
        node._countedByPrototype = undefined;
      return nodes;
    },

    // mark each child node with its position (for nth calls)
    // "ofType" flag indicates whether we're indexing for nth-of-type
    // rather than nth-child
    index: function(parentNode, reverse, ofType) {
      parentNode._countedByPrototype = glock.emptyFunction;
      if (reverse) {
        for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
          var node = nodes[i];
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
        }
      } else {
        for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
          if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
      }
    },

    // filters out duplicates and extends all nodes
    unique: function(nodes) {
      if (nodes.length == 0) return nodes;
      var results = [], n;
      for (var i = 0, l = nodes.length; i < l; i++)
        if (!(n = nodes[i])._countedByPrototype) {
          n._countedByPrototype = glock.emptyFunction;
          results.push(Element.extend(n));
        }
      return glock.selector.handlers.unmark(results);
    },

    // COMBINATOR FUNCTIONS
    descendant: function(nodes) {
      var h = glock.selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, node.getElementsByTagName('*'));
      return results;
    },

    child: function(nodes) {
      var h = glock.selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        for (var j = 0, child; child = node.childNodes[j]; j++)
          if (child.nodeType == 1 && child.tagName != '!') results.push(child);
      }
      return results;
    },

    adjacent: function(nodes) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        var next = this.nextElementSibling(node);
        if (next) results.push(next);
      }
      return results;
    },

    laterSibling: function(nodes) {
      var h = glock.selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        h.concat(results, Element.nextSiblings(node));
      return results;
    },

    nextElementSibling: function(node) {
      while (node = node.nextSibling)
	      if (node.nodeType == 1) return node;
      return null;
    },

    previousElementSibling: function(node) {
      while (node = node.previousSibling)
        if (node.nodeType == 1) return node;
      return null;
    },

    // TOKEN FUNCTIONS
    tagName: function(nodes, root, tagName, combinator) {
      var uTagName = tagName.toUpperCase();
      var results = [], h = glock.selector.handlers;
      if (nodes) {
        if (combinator) {
          // fastlane for ordinary descendant combinators
          if (combinator == "descendant") {
            for (var i = 0, node; node = nodes[i]; i++)
              h.concat(results, node.getElementsByTagName(tagName));
            return results;
          } else nodes = this[combinator](nodes);
          if (tagName == "*") return nodes;
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.tagName.toUpperCase() === uTagName) results.push(node);
        return results;
      } else return root.getElementsByTagName(tagName);
    },

    id: function(nodes, root, id, combinator) {
      var targetNode = neo(id), h = glock.selector.handlers;
      if (!targetNode) return [];
      if (!nodes && root == document) return [targetNode];
      if (nodes) {
        if (combinator) {
          if (combinator == 'child') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (targetNode.parentNode == node) return [targetNode];
          } else if (combinator == 'descendant') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Element.descendantOf(targetNode, node)) return [targetNode];
          } else if (combinator == 'adjacent') {
            for (var i = 0, node; node = nodes[i]; i++)
              if (Selector.handlers.previousElementSibling(targetNode) == node)
                return [targetNode];
          } else nodes = h[combinator](nodes);
        }
        for (var i = 0, node; node = nodes[i]; i++)
          if (node == targetNode) return [targetNode];
        return [];
      }
      return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
    },

    className: function(nodes, root, className, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      return glock.selector.handlers.byClassName(nodes, root, className);
    },

    byClassName: function(nodes, root, className) {
      if (!nodes) nodes = glock.selector.handlers.descendant([root]);
      var needle = ' ' + className + ' ';
      for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
        nodeClassName = node.className;
        if (nodeClassName.length == 0) continue;
        if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
          results.push(node);
      }
      return results;
    },

    attrPresence: function(nodes, root, attr, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var results = [];
      for (var i = 0, node; node = nodes[i]; i++)
        if (Element.hasAttribute(node, attr)) results.push(node);
      return results;
    },

    attr: function(nodes, root, attr, value, operator, combinator) {
      if (!nodes) nodes = root.getElementsByTagName("*");
      if (nodes && combinator) nodes = this[combinator](nodes);
      var handler = glock.selector.operators[operator], results = [];
      for (var i = 0, node; node = nodes[i]; i++) {
        var nodeValue = Element.readAttribute(node, attr);
        if (nodeValue === null) continue;
        if (handler(nodeValue, value)) results.push(node);
      }
      return results;
    },

    pseudo: function(nodes, name, value, root, combinator) {
      if (nodes && combinator) nodes = this[combinator](nodes);
      if (!nodes) nodes = root.getElementsByTagName("*");
      return glock.selector.pseudos[name](nodes, value, root);
    }
  },

  pseudos: {
    'first-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.previousElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'last-child': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        if (Selector.handlers.nextElementSibling(node)) continue;
          results.push(node);
      }
      return results;
    },
    'only-child': function(nodes, value, root) {
      var h = glock.selector.handlers;
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
          results.push(node);
      return results;
    },
    'nth-child':        function(nodes, formula, root) {
      return glock.selector.pseudos.nth(nodes, formula, root);
    },
    'nth-last-child':   function(nodes, formula, root) {
      return glock.selector.pseudos.nth(nodes, formula, root, true);
    },
    'nth-of-type':      function(nodes, formula, root) {
      return glock.selector.pseudos.nth(nodes, formula, root, false, true);
    },
    'nth-last-of-type': function(nodes, formula, root) {
      return glock.selector.pseudos.nth(nodes, formula, root, true, true);
    },
    'first-of-type':    function(nodes, formula, root) {
      return glock.selector.pseudos.nth(nodes, "1", root, false, true);
    },
    'last-of-type':     function(nodes, formula, root) {
      return glock.selector.pseudos.nth(nodes, "1", root, true, true);
    },
    'only-of-type':     function(nodes, formula, root) {
      var p = glock.selector.pseudos;
      return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
    },

    // handles the an+b logic
    getIndices: function(a, b, total) {
      if (a == 0) return b > 0 ? [b] : [];
      return $R(1, total).inject([], function(memo, i) {
        if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
        return memo;
      });
    },

    // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
    nth: function(nodes, formula, root, reverse, ofType) {
      if (nodes.length == 0) return [];
      if (formula == 'even') formula = '2n+0';
      if (formula == 'odd')  formula = '2n+1';
      var h = glock.selector.handlers, results = [], indexed = [], m;
      h.mark(nodes);
      for (var i = 0, node; node = nodes[i]; i++) {
        if (!node.parentNode._countedByPrototype) {
          h.index(node.parentNode, reverse, ofType);
          indexed.push(node.parentNode);
        }
      }
      if (formula.match(/^\d+$/)) { // just a number
        formula = Number(formula);
        for (var i = 0, node; node = nodes[i]; i++)
          if (node.nodeIndex == formula) results.push(node);
      } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
        if (m[1] == "-") m[1] = -1;
        var a = m[1] ? Number(m[1]) : 1;
        var b = m[2] ? Number(m[2]) : 0;
        var indices = glock.selector.pseudos.getIndices(a, b, nodes.length);
        for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
          for (var j = 0; j < l; j++)
            if (node.nodeIndex == indices[j]) results.push(node);
        }
      }
      h.unmark(nodes);
      h.unmark(indexed);
      return results;
    },

    'empty': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++) {
        // IE treats comments as element nodes
        if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
        results.push(node);
      }
      return results;
    },

    'not': function(nodes, selector, root) {
      var h = glock.selector.handlers, selectorType, m;
      var exclusions = new glock.Selector(selector).findElements(root);
      h.mark(exclusions);
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node._countedByPrototype) results.push(node);
      h.unmark(exclusions);
      return results;
    },

    'enabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (!node.disabled) results.push(node);
      return results;
    },

    'disabled': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.disabled) results.push(node);
      return results;
    },

    'checked': function(nodes, value, root) {
      for (var i = 0, results = [], node; node = nodes[i]; i++)
        if (node.checked) results.push(node);
      return results;
    }
  },

  operators: {
    '=':  function(nv, v) { return nv == v; },
    '!=': function(nv, v) { return nv != v; },
    '^=': function(nv, v) { return nv.startsWith(v); },
    '$=': function(nv, v) { return nv.endsWith(v); },
    '*=': function(nv, v) { return nv.include(v); },
    '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
    '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
  },

  split: function(expression) {
    var expressions = [];
    expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
      expressions.push(m[1].strip());
    });
    return expressions;
  },

  matchElements: function(elements, expression) {
    var matches = glock.oxx(expression), h = glock.selector.handlers;
    h.mark(matches);
    for (var i = 0, results = [], element; element = elements[i]; i++)
      if (element._countedByPrototype) results.push(element);
    h.unmark(matches);
    return results;
  },

  findElement: function(elements, expression, index) {
    if (glock.isNumber(expression)) {
      index = expression; expression = false;
    }
    return glock.selector.matchElements(elements, expression || '*')[index || 0];
  },

  findChildElements: function(element, expressions) {
    expressions = glock.selector.split(expressions.join(','));
    var results = [], h = glock.selector.handlers;
    for (var i = 0, l = expressions.length, selector; i < l; i++) {
      selector = new glock.Selector(expressions[i].strip());
      h.concat(results, selector.findElements(element));
    }
    return (l > 1) ? h.unique(results) : results;
  }
};  
	if (glock.env.ie) {
	  glock.extObj(glock.selector.handlers, {
	    // IE returns comment nodes on getElementsByTagName("*").
	    // Filter them out.
	    concat: function(a, b) {
	      for (var i = 0, node; node = b[i]; i++)
	        if (node.tagName !== "!") a.push(node);
	      return a;
	    },
	
	    // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
	    unmark: function(nodes) {
	      for (var i = 0, node; node = nodes[i]; i++)
	        node.removeAttribute('_countedByPrototype');
	      return nodes;
	    }
	  });
	}

// ---------------------------------------------------------------------	glock.ui

glock.ui = {}

glock.ui.flatButton = function(args) {

    if (!args) { args = {}; }
    var b = glock.builder.node.bind(glock.builder),
    		imgsrc = typeof args.imgsrc !== 'undefined' ? args.imgsrc : false,
    		onclick = typeof args.onclick !== 'undefined' ? args.onclick : false,
    		width = typeof args.width !== 'undefined' ? args.width : false,
    		caption = typeof args.caption !== 'undefined' ? args.caption : 'Untitled',
				captionStyle = typeof args.captionStyle !== 'undefined' ? args.captionStyle : '',
				captionClassName = typeof args.captionClassName !== 'undefined' ? args.captionClassName : '',
				className = typeof args.className !== 'undefined' ? args.className : '',
	
				style = typeof args.style !== 'undefined' ? args.style : '',
				highLight = typeof args.highLight !== 'undefined' ? args.highLight : true,
				el,
				dstyle = width ? ' width:'+width+'px;' : '',
				ibn;
	
	captionStyle = captionStyle + ' ';
	
	if(imgsrc) {
	    el = b('img', {
				className: 'glfb-ico',
	      src: imgsrc
	    });	    
	}
    ibtn = b('div', {className: 'gl-flat-btn '+className, style: style},[
		b('div',{className:'glfb-leftside'}),
		b('div',{className:'glfb-middle', style:dstyle},[
			el,
			b('span',{style:captionStyle,className:captionClassName},caption)
		]),
		b('div',{className:'glfb-rightside'})
	]);
	
	if(onclick)
	{
	    glock.event.observe(ibtn, 'click', onclick,true);
	}
	    
	if(highLight)
	{
		glock.event.observe(ibtn,'mouseover',function (){
				glock.x(this).addClassName('gl-flat-btn-act');
		}.bindAsEventListener(ibtn));
		
		glock.event.observe(ibtn,'mouseout',function (){
			if(glock.x(this).hasClassName('gl-flat-btn-act'))
				{
					glock.x(this).removeClassName('gl-flat-btn-act');
				}	
		});
	}    
	return ibtn;
}

// ------------- glock.ui.popupMenu

glock.ui.popupMenu = function(args){
	that = {};

    if (!args) { args = {}; }
		
	var parent = typeof args.parent !== 'undefined' ? args.parent : false,
			listItems = typeof args.items !== 'undefined' ? args.items : [],
			ibtn = glock.x(parent),
			lst;
	
	that.BuildList = function (args)
	{
		// argments
	    if (!args){ args = {}; }
		var b = glock.builder.node.bind(glock.builder),	
	  		items = glock.isDef(args.items) ? args.items : false,
				left = glock.isDef(args.left) ? args.left : 0,
				top = glock.isDef(args.top) ? args.top : 0,
				el;		
		
	    if (!items) 
			items = [
						{caption:'Item Number One'},
						{caption:'Item Number Two'}							
					];

		lst = Builder.node('ul',{style:'display:none; left:'+left+'px; '+'top: '+top+'px;',className:'gl-dropdown'});
		for (var i = 0; i < items.length; i++)		
		{
			var xdiv;
			var d = new Date();
			var ddlirid = 'ddli'+d.getTime();
			xdiv = b('div',{id:ddlirid, className:'gl-dropdown-item'}, items[i].caption);
			if (typeof items[i].onclick !== 'undefined') {
				glock.event.observe(xdiv,'click',items[i].onclick);
			}
				
			glock.event.observe(xdiv,'click',function (){
				glock.x(this).hide();
			}.bindAsEventListener(lst));					
			lst.appendChild(b('li',[
				xdiv		
			]));
		}
		
		document.body.appendChild(lst);
		
		lst.show();
	}
	
    	
	glock.event.observe(ibtn,'click',function (){
		if (lst)
		{
			if (glock.x(lst).visible()) {
				lst.hide();
			} else {
				lst.show();
			}
				
			return;
		}
		
		var el = ibtn;
		if(el)		
		{
			var offset = glock.element.positionedOffset(el);
			offset.top += glock.element.getHeight(el) + 1; 			
		}
		this.BuildList({items: this.listItems,left: offset.left,top:offset.top});
	}.bindAsEventListener(that));
	
  	glock.event.observe(document.body,'click',function (event){
		var element = glock.event.element(event);
		var p = glock.x(element).up('ul.gl-dropdown');
		if (p) return;
		
		//p = $(element).up('div#gsom-dropdown-add-field');
		//if (p) return;
	
		if (lst) 		
		{
			if (glock.x(lst).visible())
				lst.hide();
		}
	}.bindAsEventListener(that));
	
	return that;
}






