try{
/* a work in progress....2009-?
primary targets: opera 10, webkit (nightly, palm pre, mobile safari, safari-current?, android 1.?), firefox 3.1b3+, ...
possibly all others via base2 + flash ??

what about ie8?
*/
window.bootstrap = function(){
	window.blah = function(){
			return window.blah.prototype.getElements.apply(new window.blah.prototype.NodeList(), arguments);
	};
	// TODO load/ready watchers for all element types
	// TODO error event (to trap and dispatch script errors so that we can do $(script).bind('error'...);

// + means recommended, * means buggy/not recommended
	var undef, uid = function(){
		var i = 0;
		return function(element, forcenew){
			if(!element || (element.uid && !forcenew)) return element ? element.uid:false;
			++i;
			var uid = 'uid_'.concat(i);
			if(element.setAttribute) element.setAttribute('uid',uid);
			return element.uid = uid;
		}
		}(),
		
		eventType = function(){
		var def = { // normalize events, placeholder for custom types
		'generic': function(o){
				var e = document.createEvent('Event');
				o = o || {};
				e.initEvent(
					o.type || 'generic', // any string
					o.bubbles || true, // e.stopPropagation
					o.cancelable || true // e.preventDefault
				);
		return e;
		},
		'ui': function(o){
				var e = document.createEvent('UIEvent');
				o = o || {};
				e.initUIEvent(
					o.type || 'ui', // SVGZoomEvent, click, DOMActivate, DOMFocusIn, DOMFocusOut, blur, focus, focusin, focusout, activate
					o.bubbles || true,
					o.cancelable || true,
					o.view || window,
					o.detail || 0
				);
		return e;
		},
		'mouse': function(o){
			var e = document.createEvent('MouseEvent');
			o = o || {};
			e.initMouseEvent(
					o.typet || 'mouse', // MouseScrollEvents, click, mousemove, mouseover, mouseout, mousedown, mouseup, 
					o.bubbles || true,
					o.cancelable || true,
					o.view || window,
					o.detail || 0,
					o.screenX || 0, o.screenY || 0, o.clientX || 0, o.clientY || 0,
					o.ctrlKey || false, o.altKey || false, o.shiftKey || false, o.metaKey || false,
					o.button || 0, // mouse-button: 0 left, 1 middle, 2 right
					o.relatedTarget || null // dom-element; eg with mouseover it's previously-over domEl; for mouseout it's moving-onto domEl
			);
		return e;
		},
		'key': function(o){
		// http://www.quirksmode.org/js/keys.html TODO fix some object values
			var e = document.createEvent('KeyboardEvent');
			o = o || {};
			if(e.initKeyboardEvent) // webkit
				e.initKeyboardEvent(
					o.type || 'key', // keypress(or textInput??), keydown, keyup+
					o.bubbles || true, 
					o.cancelable || true,
					o.view || window,
					o.keyCode || 0, o.charCode || 0, // int code for key pressed, optional int char code
					o.ctrlKey || false, o.altKey || false, o.shiftKey || false, o.metaKey || false
				);
			else if(e.initKeyEvent) // firefox
				e.initKeyEvent(
					o.type || 'key', // keypress*(or textInput??), keydown, keyup+
					o.bubbles || true, 
					o.cancelable || true,
					o.view || window,
					o.ctrlKey || false, o.altKey || false, o.shiftKey || false, o.metaKey || false,
					o.keyCode || 0, o.charCode || 0 // int code for key pressed, optional int char code
				);
		return e;
		},
		'gesture':'TODO',
		'animation':'TODO',
/*
XMLHttpRequestProgressEvent
ProgressEvent


gesture:
	types: gesturestart(2 or more fingers touch surface), gesturechange(fingers move), gestureend(1 or 0 fingers on surface)
	GestureEvent
	initGestureEvent
		t,b,c,v,d, screenX,screenY, clientX,clientY, ctrlKey,altKey,shiftKey,metaKey, target, scale, rotation
				0,0, 0,0, false,false,false,false, domElTarget, <floating pt dist btw fingers: init:1.0, >1 further apart and zoom-in, <1 closer zoom-out>, <floating pt rotation value init:0.0, negative values are counter-clockwise>

touchevents occur before gestures, for example, for a two finger multi-touch gesture, the events occur in the following sequence:
1.  touchstart for finger 1. Sent when the first finger touches the surface.
2.  gesturestart. Sent when the second finger touches the surface.
3.  touchstart for finger 2. Sent immediately after gesturestart when the second finger touches the surface.
4.  gesturechange for current gesture. Sent when both fingers move while still touching the surface.
5.  gestureend. Sent when the second finger lifts from the surface.
6.  touchend for finger 2. Sent immediately after gestureend when the second finger lifts from the surface.
7.  touchend for finger 1. Sent when the first finger lifts from the surface.

webkitConvertPointFromPageToNode
webkitConvertPointFromNodeToPage

animationEvents: occur for individual/for each -webkit-animation-name
animationEvents and transitionEvents: last event takes precedence

animation:
	type: webkitAnimationStart (elapseTime is not set by default), webkitAnimationEnd, webkitAnimationIteration (by default only occurs when -webkit-animation-iteration-count is greater than 1)
	WebKitAnimationEvent
	initWebKitAnimationEvent
		t,b,c, animationName, elapsedTime
				<name>, <floating pt seconds>

transition:
	type: webkitTransitionEnd
	WebKitTransitionEvent
	initWebKitTransitionEvent
		t,b,c, propertyName, elapsedTime
				<name>, <floating pt seconds>

key:
	types: keypress(or textInput??), keydown, keyup
	KeyboardEvent
	(firefox)initKeyEvent
		t,b,c,v, ctrlKey,altKey,shiftKey,metaKey, keyCode, charCode
				false,false,false,false, <int code for key pressed or default:0>, <int optional character code or default:0>
	(webkit)initKeyboardEvent
		t,b,c,v, ?,?, ctrlKey,altKey,shiftKey,metaKey
				false,false,false,false, <int code for key pressed or default:0>, <int optional character code or default:0>

	TextEvent
	initTextEvent
		???
		???

mutation:
	types: DOMSubtreeModified, DOMNodeInserted, DOMNodeRemoved, DOMNodeRemovedFromDocument, DOMNodeInsertedIntoDocument, DOMAttrModified, DOMCharacterDataModified, DOMElementNameChanged, DOMAttributeNameChanged
	MutationEvent
	iniMutationEvent
		(firefox)t,b,c,v,d
		(webkit)t,b,c, relatedNode, prevValue, newValue, attrName, 	??int attrChange?

.....

NOTE touch events fire BEFORE gesture events
touchEvent{touches:[all-of-them], targetTouches:[on-target], changedTouches:[those-that-changed]}

orientationchange
touchstart
touchmove
touchend
touchcanel (when does this happen???)
gesturestart >=2 touches
gesturechange
gestureend <2 touches

for click-ables (respectively): mouseover, mousemove, mousedown, mouseup, and click

type The type of event that occurred.
canBubble Indicates whether an event can bubble. If true, the event can bubble; otherwise, it cannot.
cancelable Indicates whether an event can have its default action prevented. If true, the default action can be prevented; otherwise, it cannot.
view The view (DOM window) where the event occurred.
detail Specifies some detail information about the event depending on the type of event.
screenX The x-coordinate of the event’s location in screen coordinates.
screenY The y-coordinate of the event’s location in screen coordinates.
clientX The x-coordinate of the event’s location relative to the window's viewport.
clientY The y-coordinate of the event’s location relative to the window's viewport.
ctrlKey If true, the control key is pressed; otherwise, it is not.
altKey If true, the alt key is pressed; otherwise, it is not.
shiftKey If true, the Shift key is pressed; otherwise, it is not.
metaKey If true, the meta key is pressed; otherwise, it is not.
target The target of this gesture.
scale The distance between two fingers since the start of an event as a multiplier of the initial distance. The initial value is 1.0. If less than 1.0, the gesture is pinch close (to zoom out). If greater than 1.0, the gesture is pinch open (to zoom in).
rotation The delta rotation since the start of an event, in degrees, where clockwise is positive and counter-clockwise is negative. The initial value is 0.0.

TODO drag events dragstart conflicts with mouse/related...https://developer.mozilla.org/en/Drag_and_Drop
TODO redraw (including suspendRedraw)
TODO tap event????
*/
		'touch': function(){
// TODO try catch for appropriate touch translation
// and also for other event-types (orientation, gestures, etc)
/*
	var touch;
		try{
			touch = document.createEvent('TouchEvent');
		}catch(err){
			// rewrite touchevents
			def.touch = def.touchstart = 'mousedown';
			def.touchmove = 'mousemove';
			def.touchup = 'mouseup';
		};
*/
		var fn = function(o){
			var e = document.createEvent('TouchEvent');
			o = o || {};
				e.initTouchEvent(
					o.type || 'touch', // touchstart, touchmove, touchend, touchcancel
					o.bubbles || true, 
					o.cancelable || true,
					o.view || window,
					o.detail || 0,
					o.screenX || 0, o.screenY || 0, o.clientX || 0, o.clientY || 0,
					o.ctrlKey || false, o.altKey || false, o.shiftKey || false, o.metaKey || false,
					o.touches || [], // all
					o.targetTouches || [], // on target
					o.changedTouches || [] // changed
				);
			return e;
		};
		return fn;
		},
		'ready':function(){ //'DOMContentReady',
			var e = document.createEvent('Event');
			e.initEvent('ready', true, true);
		return e;
		},
		'load':function(){
			var e = document.createEvent('Event');
			e.initEvent('load', true, true);
		return e;
		},
		'orientation':function(o){ // accelerometer events
			var e = document.createEvent('Event');
			o = o || {};
				e.initEvent(
					o.type || 'orientation', // orientationchange
					o.bubbles || true, 
					o.cancelable || true
				);
				e.raw = o.raw || 0,
				e.x = o.x || 0,
				e.y = o.y || 0,
				e.z = o.z || 0,
				e.acceleration = o.acceleration || 0;
		return e;
		}
		}; // def

// possibly initialize timers for ready event here, along with  any initial event translation 

		return def;
		}();
/*
blur
change
click
contextmenu*
copy*
cut*
dblclick*
error*
focus*
keydown
keypress*
keyup
mousedown
mousemove
mouseout
mouseover
mouseup
mousewheel*
paste*
reset
resize?(iphone:use scroll--or both scroll and resize?)
scroll?(iphone: fires on zoom event)
select
submit
DOMActivate*
DOMAttrModified*
DOMCharacterDataModified*
DOMFocusIn*
DOMFocusOut*
DOMMouseScroll*
DOMNodeInserted
DOMNodeRemoved?
DOMSubtreeModified
textInput*

touchstart: mousedown
touchmove: mousemove
touchend: mouseup

*/
	var prototype = {
		event: function(type, init){ // create custom event or normalize an event object
			if(!type) return;
			else if(type.constructor == String){
				return init ? (eventType[type] = init):eventType[type]();
			}else if(type.constructor.toString().indexOf('Event')) return type; // normalize or just use custom events
			// event objects always have truthy test:	{event}.constructor.toString().indexOf('Event');
		},
		http:function(){
/*
protocol:
derived from uri?


xhr==iframe,script,

method: *get|post
+url: string
+handler: function (errors passed here too)
ignorefailure: *false || true
xhr: *false || true
header: {name:value,name1:value1}

*default
+required
*/

		},
		xhr: function(){


		},
		NodeList: function(){
			this.length = 0;
			this.dom = [];
			
		return this;
		},
		item: function(){ // this can duplicate retured items eg: (0, 0, 0)
			var i = 0, l = arguments.length, list = [], item;
			while(i<l){
				item = this.dom[i++] || false;
				if(item) list[list.length] = item;
			};
			l = list.length;
		return l ? (l > 1 ? list:list[0]) : null;
		},
		xpath: function(){
// opera supports xpath, won't support querySelectorAll until v10
// TODO
		},
		'uid': uid,
		query: function(){ // css queries
/* TODO
namespaced, currently just does straight css selectors:
	*|ELEMENT.className{rules} // html
	namespace|EL.class{rules} // namespace
	svg|EL.class{rules} // svg
....doesn't work... @namespace [der_space]
*/
			var selector = Array.prototype.concat.apply([],arguments).join(', ');
			if(!selector) return this;
/*
2 cases with our selectors and usage of uid's on each element (in the current dom):
	selector has white-space " <something>", which becomes "#uid <something>"
		eg (using body tag): ' :first-child' becomes 'body *:first-child'
	selector does not have preceeding white-space: "<something>" becomes: "#uid<something>"
		eg (again with body tag): ':first-child' becomes 'body:first-child'
	so we'll insert a space (ie "#uid <something>") ONLY when the query starts with an explicit stand-alone selector such as a letter (for tags, namespaced things, etc) or wildcard "*"

3 cases:
	no attribute selectors: [^,[]+
	unquoted attribute selectors: \[[^'"\]]*?\]
	quoted attribute selectors (assumes no ['"]\] until end of each): \[.*?['"]\]
test sample:
',p[title="[ping,,,, ping], ping"][id][hint=,], p[title=\'1, 2\'], p[id=",this"]:not(#this), p[],'.match(/(?:[^,[]+|(\[[^'"\]]*?\])|(\[.*?['"]\]))+(,|$)?/g));
*/
			if(this.dom.length){
				// add trailing ',' in case there is just one query (so that we end up with #a <query>, #b <query>, and not #a <query> #b <query>)
				var elementSelector = /^(?:[a-zA-Z]|\*)/, query, querylist = selector.concat(',').match(/(?:[^,[]+|(\[[^'"\]]*?\])|(\[.*?['"]\]))+,/g);
				selector = '';
				var i = 0, j, item, uid = 'uid_N';
				while(item = this.dom[i++]){
					uid = item.uid || uid(item);
					j=0;
					// prepend each query with uid's: #uid1 <query0>, #uid2 <query0>..., #uid1 <queryN>, #uid2 <queryN>...
					while(query=querylist[j++]) selector =  selector.concat('[uid="', uid, '"] ', elementSelector.test(query) ? ' ':'', query); // space only if starts with an element selector (TAG or *)
				};
			};
/*
incoming:
selector is always 1 of these 2 cases:
	'#uid <stuff>, #uid2 <stuff>, #uid3 <stuff>'
	'<stuff>'

operation:
document<query>

result:
dom = NodeList[a#uid,b#uid,c#uid] || NodeList[]

assumed:
#uid is always in query when this.dom.length = 0
! therefore => must reset dom when query has it built in
*/
		try{
			this.dom = document.querySelectorAll(selector.replace(/,\s*$/,'')); // any uid's are included in query, loose comma's give errors; TODO possibly clean query further
		}catch(err){
			this.dom = [];
		}
			this.length = this.dom.length;
		return this;
		},
		prev: function(){
			var i=0, item, list = [];
			while(item=this.dom[i++]){
				while(item=item.previousSibling){
					if(item.nodeType == 1){
						list[list.length] = '[uid="'.concat((item.uid || uid(item)), '"]');
						break;
					};
				};
			};
			this.dom = [];
			this.query(list.join(','));
		return this;
		},
		next: function(){
			var i=0, item, list = [];
			while(item=this.dom[i++]){
				while(item=item.nextSibling){
					if(item.nodeType == 1){
						list[list.length] = '[uid="'.concat((item.uid || uid(item)), '"]');
						break;
					};
				};
			};
			this.dom = [];
			this.query(list.join(','));
		return this;
		},
		children: function(selector){
			var i=0, item, list = [];
			selector = '>'.concat(selector || '*');
			while(item=this.dom[i++]){
				list[list.length] = '[uid="'.concat((item.uid || uid(item)), '"]', selector);
			};
			this.dom = list;
			this.length = list.length;
		return this;
		},
		parent: function(){
			var i=0, item, list = [];
			while(item=this.dom[i++]){
				item=item.parentNode;
				if(item.nodeType != 1) continue;
				list[list.length] = '[uid="'.concat((item.uid || uid(item)), '"]');
			};
			this.dom = [];
			this.query(list.join(',')); // get and merge each
		return this;
		},
		parents: function(){
			var i=0, item, list = [];
			while(item=this.dom[i++]){
				while(item=item.parentNode){
					if(!item || item.nodeType != 1) break; // document
					list[list.length] = '[uid="'.concat((item.uid || uid(item)), '"]');
				};
			};
			this.dom = [];
			this.query(list.join(','));
		return this;
		},
		siblings: function(){
			var i=0, j=0, item, p, list = [];
			while(item=this.dom[i++]){
				p = item.parentNode;
				if(!p || p.nodeType != 1) continue;
				list[list.length] = '[uid="'.concat((p.uid || uid(p)), '"]>:not([uid="',(item.uid || uid(item)),'"])');

			};
			this.dom = [];
			this.query(list.join(','));
		return this;
		},
		style: function(rules){
// TODO
		return this;
		// what should this be? or should it even exist?
			var i = 0, item, list = [];
			if(rules){
				while(item=this.dom[i++]) if(item.setAttribute) item.setAttribute('style', item.getAttribute('style').concat(rules));
			return this;
			}else{
				while(item=this.dom[i++]) if(item.getAttribute) list[i-1] = item.getAttribute('style');
			return list.length && list.length < 2 ? list[0]:list;
			};
		},
		text: function(txt){ // set textContent on each + return text or return text from first??? or else do array with all the text?
// TODO
		//return this;
		// what should this be...or should it event exist (is it necessary)??
			var i = 0, item, list = [];
			if(txt){
				while(item=this.dom[i++]) item.textContent = txt;
			return this;
			}else{
				while(item=this.dom[i++]) list[list.length] = item.textContent;
			return list.length && list.length < 2 ? list[0]:list;
			};
		},
		html: function(){
// TODO
		},
// TODO: add, remove, append, insert(before), prepend
		create: function(){
// TODO
		},
		clone: function(copyContents){
// TODO
		},
		unbind: function(type, handler, capture){
		/* unbind:type-only (remove everything associated with this type)
			unbind:type, handler (remove regardless of capture)
			unbind:type, handler, capture (respect capture)
				TODO any desired type translation
			*/
			if(!type || type.constructor != String) return this;

			var i=0, j, h, item, handlers, handler = handler || false, capture = capture == undef ? undef:(!!capture); // TODO ??? change undef to a default based on type ??

			while(item=this.dom[i++]){
				if(!item.event || !item.event[type]){ // try to remove anyway
					if(handler){
						item.removeEventListener(type, handler, capture);
						if(capture == undef) item.removeEventListener(type, handler, !capture);
					};
					continue;
				}
				j=0, handlers=item.event[type];
				while(h=handlers[j]){
					if(!handler || (h.handler == handler && (capture == undef || h.capure == capture))){ // could also check for h.handler.toString() == handler.toString() to strip anonymous functions's
						item.removeEventListener(type, h.handler, h.capture);
						handlers.splice(j, 1);
					}
					++j;
				}
			};
		return this;
		},
		bind: function(type, handler, capture){
			// TODO any desired type translation
			var i=0, item, events;
			handler = {'handler':handler, 'capture':(capture == undef ? undef:(!!capture))};
			while(item=this.dom[i++]){
				events = item.event = item.event || {};
				events = events[type] = events[type] || [];
				item.addEventListener(type, handler.handler, handler.capture);
				events[events.length] = handler;
			};
		return this;
		},
		trigger: function(o){
			/* o: 'event-type' or {type:'type'...} with all optional name+value pairs
				for more detail see the various event initializers defind on eventType
			*/
			o = o || {};
			if(typeof o == 'string') o = {type:o};
			var e = eventType[ o.type || '' ];
			if(!e || e.constructor != Function) return this;
			e = e(o);

			var i = 0, item;
			while(item = this.dom[i++]) if(item.dispatchEvent) item.dispatchEvent(e);

			return this;
/*
blur
change
click
contextmenu*
copy*
cut*
dblclick*
error*
focus*
keydown
keypress*
keyup
mousedown
mousemove
mouseout
mouseover
mouseup
mousewheel*
paste*
reset
resize?(iphone:use scroll--or both scroll and resize?)
scroll?(iphone: fires on zoom event)
select
submit
DOMActivate*
DOMAttrModified*
DOMCharacterDataModified*
DOMFocusIn*
DOMFocusOut*
DOMMouseScroll*
DOMNodeInserted
DOMNodeRemoved?
DOMSubtreeModified
textInput*

touchstart: mousedown
touchmove: mousemove
touchend: mouseup

TODO when more than one touch registers fire a gesture with the touchList(touch, touch...)
	maybe just combine it with keyboard events?
gesturestart: ???
gesturechange: ???
gestureend: ???

*incomplete, buggy or doesn't exist device
? might work, not sure
*/
		},
		create: function(){
	/*
	arguments list of:
		{tag:{attr:value}} || 'string' => <tag attr="value" /> || textNode
	returns this.dom with:
		if(this.dom) elements cloned onto each
		else this.dom = [documentFragment] with elements attached
	*/
			var l = document.createDocumentFragment();		
		},
		getElements: function(){
			var n, j, i=0, a = arguments, item, list = [];
			while(item=a[i++]){
				if(item.constructor == String)
					list[list.length] = item;
				else if(item.setAttribute)
					list[list.length] = '[uid="'.concat((item.uid || uid(item)), '"]');
				
			}

			this.query(list.join(','));
		return this;
		},
		each: function(){
/*
any number of arguments where each is:
function
-or-
object{
fn: required function (unless the object is a function)
}

both may have optional properties:
	args: *, passed to each iteration of the function
	list: array-like or *, if not provided this.dom is used
	iterate: false || true,
		defaults to true which results in iteration from 0 through the list length
		if false no iteration occurs and the list becomes 'this' within fn

*/
			var a = arguments;
			var item, i=0, j, list, args, fn;
			while(item=a[i++]){
				fn = item.constructor == Function ? item : item.fn;
				if(!fn || fn.constructor != Function) continue;

				list = item.list || this.dom;
				args = item.args || false;
				if(item.iterate === false){
					fn.call(list, args.length ? args:null);
				}else{
					j=0; while(item=list[j++]) fn.call(item, args.length ? args:null);
				}
			};
		return this;
		}
	}; // prototype

	window.blah.prototype = prototype.NodeList.prototype = prototype;
	document.addEventListener('DOMContentLoaded',function(){ $(document.documentElement).trigger('ready'); },false);
	window.$ = window.blah;
	delete window.bootstrap;
}(); // bootstrap

}catch(err){ alert(err); }
