<commit_before>/*
 * This file is part of the KUBEjs package
 *
 * (c) Red Scotch Software Inc <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
(function(KUBE){
	"use strict";

    //Remove Animate from this, will be removing animations from it entirely as they don't belong here
    var dependancyArray = [
        '/Library/DOM/WinDocJack',
        '/Library/DOM/StyleJack',
        '/Library/DOM/FeatureDetect',
        '/Library/Tools/Convert',
        '/Library/Extend/Array',
        '/Library/Extend/Object',
        '/Library/Extend/RegExp'
    ];

	KUBE.LoadFactory('/Library/DOM/DomJack', DomJack,dependancyArray);
	
	//These are global cache variables
	var KUBENodeId,jackCache,cleanPointer,wordToKeyCodeMap,scheduleClean,cleanupCache,nodeInsertedStyleInit;
	
	KUBENodeId = 0;			//Current KUBE Node ID
	jackCache = {};			//Our cache
	jackCache.length = 0;	//Start our cache length
	cleanupCache = [];
	scheduleClean = false;
	nodeInsertedStyleInit = false;
	wordToKeyCodeMap = {
		'backspace':8,
		'tab':9,
		'enter':13,
		'shift':16,
		'ctrl':17,
		'alt':18,
        'left':37,
        'up':38,
        'right':39,
        'down':40
	};
	
	function cleanCache(){
		KUBE.console.log('clean cache fired');
		var offset,id,i,slice,slices = [];
		offset = 0;
		slice = [undefined,0];
		for(i=0;i<cleanupCache.length;i++){
			id = cleanupCache[i];
			if(jackCache[id] && !jackCache[id].GetNode().KUBEDJ.save){
				deleteDJ(id);
				if(slice[0] === undefined){
					slice[0] = i;
					slice[1]++;
				}
			}
			else{
				if(slice[0] !== undefined){
					slices.push(slice);
					slice = [undefined,0];
				}
			}
		}
		
		for(i=0;i<slices.length;i++){
			cleanupCache.splice(slices[0]-offset,slices[1]);
			offset = offset+slices[1];
		}
		scheduleClean = false;
	}
	
	function deleteDJ(id){
		jackCache[id].Emit('cleanup',id);
		jackCache[id].Clear();
		jackCache[id].destroy();
		jackCache[id] = undefined;
		delete jackCache[id];
		jackCache.length--;		
	}
	
	function memDebug(_output){
		var detached = {};
		jackCache.KUBE().each(function(_id,_dj){
			if(KUBE.Is(_dj) === 'object' && _dj.GetNode().KUBEDJ.save && _dj.IsDetached()){
				if(_output){
					KUBE.console.log(_id,_dj.GetRoot().GetInner());
				}
				else{
					detached[_id] = _dj;
				}
			}
		});
		return detached;
	}
	
	function cacheCount(){
		return jackCache.KUBE().count();
	}
	
	function handleClean(){
		if(!scheduleClean){
			scheduleClean = true;
			requestAnimationFrame(cleanCache);
		}
	}

	DomJack.prototype.toString = function(){ return '[object '+this.constructor.name+']' };
	// Primary class declaration
	function DomJack(initNode){
		var FeatureDetect,SJ,DJ,Events,Node,nodeType,StyleJack,domListeners,id,mapId,rawAPI,$DomJackAPI = {},animationAPI,prefix,keyListener,keyStore,keyComboTriggered,keyF;

        DJ = KUBE.Class('/Library/DOM/DomJack');
        SJ = KUBE.Class('/Library/DOM/StyleJack');
        FeatureDetect = KUBE.Class('/Library/DOM/FeatureDetect');

		if(!nodeInsertedStyleInit){
			nodeInsertedStyleInit = true;
			(function(){
				var S = SJ('@keyframes nodeInserted');
				S.From().Text().Indent(1);
				S.To().Text().Indent(0);
			}());

		}

		domListeners = {};
		keyListener = false;
		keyStore = {};
		keyComboTriggered = false;
		keyF = {};
		rawAPI = {
			'dumpCache': function(){ return jackCache; },
			'memDebug': memDebug,
			'cacheCount': cacheCount,
			'destroy':destroy,
			
			//Special
			'UpdateAPI':UpdateAPI,				//Utility: Mutates the contextAPI, with additional methods appropriate to Node type
			
			//Attribute handling
			'GetAttribute':GetAttribute,		//Get: returns key val, or if empty, returns object with all attributes
			'SetAttribute':SetAttribute,		//Set: accepts key val for setting attribute, or object for multi set
			
			//Node Management
			'GetNode':GetNode,					//Get: the raw dom node
			'GetParent':GetParent,				//Get: the nodes parent DomJack
			'GetChildren':GetChildren,			//Get: an array of Node children wrapped with DomJack
			'GetChild':GetChild,				//Get: the Node at _index wrapped with DomJack
			'GetFirstChild':GetFirstChild,
			'GetLastChild':GetLastChild,
			'GetType':GetType,					//Get: type of Node (tag type)
			'GetIndex':GetIndex,				//Get: index of placement within parent
			'GetRoot':GetRoot,

			//Children management
			'Insert':Insert,					//Utility: Inject a node at a specific index within the children list
			'Prepend':Prepend,					//Utility: Inject a node at the beginning of the children list
			'Append':Append,					//Utility: Inject a node at the end of the children list
			'InsertBefore':InsertBefore,		//Utility: Inject a node before another node/index
			'InsertAfter':InsertAfter,			//Utility: Inject a node after another node/index
			'Prune':Prune,						//Utility: Prune children past an Index
			
			//Event management
			'On':On,							//Set: Set a callback to fire on a certain event queue
            'OnState': OnState,                  //Set: Bind a callback to be fired on state event
			'Once':Once,						//Set: Set a callback that will only fire once on a certain event queue
			'Emit':Emit,						//Utility: Trigger an event queue
            'EmitState': EmitState,              //Utility: Trigger a state event on the DomJack
			'Clear':Clear,						//Set: Clear an individual event, or event queue
			'RemoveListener':RemoveListener,	//Set: **not sure what this does
			'Ready':Ready,						//Utility: A special function that can fire callbacks when the Node is reinjected into the DOM
			
			//Content management
			'GetInner':GetInner,				//Get: Get the raw innerHtml contents
			'SetInner':SetInner,				//Set: Set the innerHtml contents
            'BuildInner':BuildInner,            //Utility: Take an HTML string with buildKeys, build it into a DOM structure, return an Object reference to the keyed DomJacks
			'Dump':Dump,						//Utility: Perform a special operation to properly empty the innerHtml contents (clear from memory,etc)
			
			//General Information			
			'GetId':GetId,						//Get: **Currently gets the id, not sure about this
			'SetId':SetId,						//Set: **Currently sets the id, not sure about this
			'CacheId':CacheId,					//Get: Get the KUBE ID (read only)
			'IsDetached':IsDetached,			//Utility: Is the Node attached/detached within the DOM?
			
			//Utilities
			'Copy':Copy,						//Utility: Create a copy of the Node (detached) and return.
			'Delete':Delete,					//Utility: Detach the Node and set it to be cleaned from the Cache. Cleans internal state, external references still need to be released
			'Detach':Detach,
			'DetachChildren':DetachChildren,
			'GetDrawDimensions':GetDrawDimensions,	//Utility: This is probably going to replace CalcHeight and CalcWidth entirely
			'Rect': Rect,							//This is a shortcut to getBoundingRect
            'HasScrollBar': HasScrollBar,
            'GetScrollBarDimensions': GetScrollBarDimensions,
			
			//Style
			'Style':Style,						//Get: the StyleJack of the Node
			'GetClasses':GetClasses,			//Get: an array of classes (StyleJack wrapped) that are applied to the Node
			'AddClass':AddClass,				//Utility: Adds a class to Node (accepts string, or StyleJack object)
			'RemoveClass':RemoveClass,			//Utility: Removes a class from Node (accepts string, or StyleJack object)
			'ToggleClass':ToggleClass,			//Utility: Adds or Removes class from Node depending on current state
			'HasClass':HasClass,				//Utility: Quick check for whether a class is currently applied to Node
			
			//Mapping
			'SetMapId':SetMapId,
			'MapData':MapData
		};

		//Init and return our API
		init(initNode);
		return $DomJackAPI;
		
		//Init
		//Do I need to handle something that's already a DomJack?
		function destroy(){
			//Do I need this?
			Events = Node = nodeType = StyleJack = domListeners = id = rawAPI = $DomJackAPI = animationAPI = prefix = keyListener = keyStore = keyComboTriggered = keyF = undefined;
		}		
		
		function init(_initVar){
			initWinDocJack(_initVar) || initOldDJ(_initVar) || initNewDJ(_initVar);
		}
		
		function initWinDocJack(_initVar){
			var $return = false;
			if(!_initVar || _initVar === window || _initVar === document){
				$DomJackAPI = KUBE.Class('/Library/DOM/WinDocJack')();
				$return = true;
			}
			return $return;
		}
		
		function initOldDJ(_dj){
			var $return = false;
			if(isDJ(_dj)){
				$DomJackAPI = _dj;
				$return = true;
			}
			return $return;
		}

		function isDJ(_dj){
			return  (_dj instanceof DomJack ? true : false); //(rawAPI.KUBE().duckType(_dj) === true ? true : false);
		}
		
		function initNewDJ(_initVar){
			return initCache(validateAndInitNode(_initVar));
		}
				
		function validateAndInitNode(_initVar){
			var type = KUBE.Is(_initVar),$return;
			$return = initByString(_initVar,type) || initByObject(_initVar,type);
            if($return === false) {
                throw new Error('Attempted to initialize a DomJack on an object that is not an HTMLElement');
            }
            return $return;
		}
		
		function initByString(_initVar,_type){
			return (_type === 'string' ? (initNewIdNode(_initVar) || checkTypeAndCreate(_initVar)) : false);
		}
		
		function initNewIdNode(_id){
			var $return = false;
			if(isID(_id)){
				if(!idNodeExists(_id)){
					$return = document.createElement('div');
					$return.id = prepID(_id);
				}
			}
			return $return;
		}
		
		function initByObject(_initVar,_type){
			return (_type === 'object' && KUBE.Is(_initVar.ownerDocument) === 'object' ? _initVar : false);
		}
				
		function initCache(_obj){
			if(KUBE.Is(_obj) === 'object'){
				if(KUBE.Is(_obj.KUBEDJ) === 'object' && _obj.KUBEDJ.id){
					$DomJackAPI = jackCache[_obj.KUBEDJ.id] || null;
				}
				else{
					Node = _obj;
					Node.KUBEDJ = {};
					Node.KUBEDJ.id = id = ++KUBENodeId;
					Node.KUBEDJ.save = true;
					
					nodeType = (Node.nodeName || Node.tagName).toLowerCase();
					$DomJackAPI = rawAPI.KUBE().copy().KUBE().create(DomJack.prototype);
					UpdateAPI();
					
					jackCache[id] = {};
					jackCache[id] = $DomJackAPI;
					jackCache.length++;
					cleanupCache.push(id);
				}
			}
		}
		
		
		function checkTypeAndCreate(_type){
			var $return = false;
			//KUBE.console.log(_type+': '+KUBENodeId+1);
			try{
				$return = document.createElement(''+_type.toLowerCase());
			}
			catch(e){
				//No node for you
				throw new Error('Dom Jack could not create element. Invalid type: '+_type);
			}
			return $return;
		}
				
		// Special
		function UpdateAPI(){
			$DomJackAPI = BindCustomAPI($DomJackAPI,nodeType,rawAPI);
			jackCache[id] = $DomJackAPI;
			return $DomJackAPI;
		}
		
		// Attribute handling
		function GetAttribute(_key){
			return domGetAttribute(_key) || propertyGetAttribute(_key) || listGetAttribute(_key);
		}
		
		function domGetAttribute(_key){
			return (_key && KUBE.Is(Node.getAttribute) === 'function' ? Node.getAttribute(_key) : undefined);
		}
		
		function propertyGetAttribute(_key){
			return (_key && KUBE.Is(Node[_key]) !== 'undefined' ? Node[_key] : undefined);
		}
		
		function listGetAttribute(_key){
			return (_key ? findAttribute(_key) : parseAttributesIntoObject());
		}
		
		function parseAttributesIntoObject(){
			var $return;
            //TODO: this is probably broken, and should do what findAttribute does (check for NamedNodeMap)
			if(KUBE.Is(Node['attributes']) === 'object'){
				$return = {};
				Node.attributes.KUBE().each(function(_keyIndex,_attr){
					if(_attr.name !== undefined){
						$return[_attr.name] = _attr.value;
					}
				});
			}
			return $return;
		}
		
		function findAttribute(_key){
			var $return;
            if(KUBE.Is(Node['attributes'],true) === 'NamedNodeMap'){
                var length = Node['attributes'].length;
                for(var i=0;i<length;i++){
                    var tempAttr = Node['attributes'][i];
                    if(tempAttr.name.toLowerCase() === _key.toLowerCase()){
                        $return = tempAttr.value || tempAttr.nodeValue || tempAttr.textContent;
                        break;
                    }
                }
            }
			return $return;
		}
		
		function SetAttribute(_key,_val){
			return setMultiAttributes(_key) || setSingleAttribute(_key,_val);
		}
						
		function setMultiAttributes(_key){
			var $return;
			if(KUBE.Is(_key) === 'object'){
				_key.KUBE().each(function(_objKey,_objVal){
					setSingleAttribute(_objKey,_objVal);
				});
				$return = $DomJackAPI;
			}
			return $return;
		}
		
		function setSingleAttribute(_key,_val){
			(_val === false ? removeAttribute(_key) : trySetAttribute(_key,_val));
			return $DomJackAPI;
		}
		
		function removeAttribute(_key){
			var $return = false;
			try{
				Node.removeAttribute(_key);
				$return = true;
			}
			catch(e){
				throw new Error(e.message);
			}
			return $return;
		}
		
		function trySetAttribute(_key,_val){
			var $return = false;
			try{
				Node.setAttribute(_key,_val);
				$return = true;
			}
			catch(E){
				if(KUBE.Is(Node[_key]) !== 'function' && KUBE.Is(Node[_key]) !== 'object'){
					Node[_key] = _val;
					$return = false;
				}
				throw E;
			}
			return $return;
		}
		
		// Node Management
		function GetNode(){
			return Node;
		}
		
		function GetRoot(){
			return rGetRoot(Node);
			function rGetRoot(_N){
				return (_N.parentNode === null ? DJ(_N) : rGetRoot(_N.parentNode));
			}
		}
		
		function GetParent(){
			return (Node.parentNode !== null ? DJ(Node.parentNode) : false);
		}
		
		function GetChildren(){
			var i,$return = [];
			if(Node.children.length){
				for(i=0;i<Node.children.length;i++){
					$return.push(DJ(Node.children[i]));
				}
			}
			return $return;
		}
		
		function GetChild(_index){
			return (Node.children[_index] ? DJ(Node.children[_index]) : undefined);
		}
		
		function GetFirstChild(){
			return GetChild(0);
		}
		
		function GetLastChild(){
			return GetChild(Node.children.length-1);
		}
		
		function GetType(){
			return nodeType;
		}
		
		function GetIndex(){
			var $return;
			GetParent().GetChildren().KUBE().each(function(_child,_index){
				if(_child.GetNode() === Node){
					$return = _index;
					this.break;
				}
			});
			return $return;
		}
				
		// Children Management
		function Insert(_Node, _index){
			var Inject,$return = false;
			
			Inject = DJ(_Node);
			
			if(isDJ(Inject)){
				$return = insertAtNumber(Inject,_index) || insertAtObject(Inject,_index) || Append(Inject);
			}
			
			if($return){
				Inject.UpdateAPI();
				Inject.Ready();
				$return = (KUBE.Is(_Node) === 'string' ? Inject : $DomJackAPI);
			}
			return $return;
		}
		
		function insertAtNumber(_Inject,_index){
			var children,$return = false;
			
			if(KUBE.Is(_index) === 'number'){
				children = GetChildren();
				if(children[_index]){
					Node.insertBefore(_Inject.GetNode(),children[_index].GetNode());
					$return = true;
				}
			}
			return $return;
		}
		
		function insertAtObject(_Inject,_index){
			var target,$return = false;
			
			if(KUBE.Is(_index) === 'object'){
				target = DJ(_index);
				if(isDJ(target) && target.GetParent().GetNode() === Node){
					Node.insertBefore(_Inject.GetNode(),target.GetNode());
					$return = true;
				}
			}
			return $return;
		}
		
		function Prepend(_Node){
			return Insert(_Node, 0);
		}
				
		function Append(_Node){
			if(KUBE.Is(_Node) === 'array'){
				_Node.KUBE().each(Append);
			}
			else{
				var Inject,$return = false;
				Inject = (!isDJ(_Node) ? DJ(_Node) : _Node);
				if(Inject){
					Node.appendChild(Inject.GetNode());
					Inject.UpdateAPI();
					Inject.Ready();
					$return = (KUBE.Is(_Node) === 'string' ? Inject : $DomJackAPI);
				}
				return $return;
			}
		}
		
		//Not sure about Before/After yet, will have to use them first
		function InsertBefore(_Target){
			var Parent;
			_Target = (!isDJ(_Target) ? DJ(_Target) : _Target);
			if(_Target.GetParent()){
				Parent = _Target.GetParent();
				Parent.Insert($DomJackAPI,_Target.GetIndex());
			}
		}
		
		function InsertAfter(_Target){
			var Parent;
			_Target = (!isDJ(_Target) ? DJ(_Target) : _Target);
			if(_Target.GetParent()){
				Parent = _Target.GetParent();
				Parent.Insert($DomJackAPI,_Target.GetIndex()+1);
			}
		}
		
		function Prune(_index){
			GetChildren().KUBE().each(function(_Child,_childIndex){
				if(_childIndex >= _index){
					_Child.Delete();
				}
			});
			return $DomJackAPI;
		}
		
		
		//Event Management
		function On(_event,_callback){
			var $return;
			if(!multiOn(_event,_callback)){
				_event = translateEvent(_event);
				catchReady(_event,_callback) || catchKeys(_event,_callback) || bindOn(_event,_callback);
			}
			return $return;
		}
		
		function multiOn(_array,_callback){
			var $return = false;
			if(KUBE.Is(_array) === 'array'){
				$return = true;
				_array.KUBE().each(function(_e){
					On(_e,_callback);
				});
			}
			return $return;
		}
		
		function catchReady(_event,_callback){
			//Do not ever allow ready events to be bound to on.
			//Ready events are intended to only fire a single time when a Node is inserted into the DOM.
			//As those events may start manipulating the DOM, this results in runaway ready events being fired incorrectly, 
			//creating a loop of infinite madness.
			var $return = false;
			if(_event === 'ready'){
				Once(_event,_callback);
				$return = true;
			}
			return $return;
		}
		
		function catchKeys(_event,_callback){
			var $return = false;
			if(_event.substr(0,4) === 'key:'){
				initKeyListener();
				addKeyListenerEvent(_event,_callback);
				$return = true;
			}
			return $return;
		}
		
		function initKeyListener(){
			if(!keyListener){
				keyListener = true;
				On('keydown',pressKey);
				On('keyup',releaseKey);
			}
		}
		
		function pressKey(_e){
			//debugger;
			var $return,codeString,tempArray = [];
			$return = true;
			keyStore[_e.keyCode] = true;
			if(!keyComboTriggered){
				keyStore.KUBE().each(function(_k,_v){
					tempArray.push(_k);
				});
				tempArray.sort();
				codeString = tempArray.join('+');
				if(keyF[codeString] && keyComboTriggered === false){
					keyComboTriggered = true;
					for(var i=0;i<keyF[codeString].length;i++){
						if(KUBE.Is(keyF[codeString][i]) === 'function'){
							if(keyF[codeString][i](_e) === false){
								$return = false;
								break;
							}
						}
					}
					keyComboTriggered = false;
				}
			}
			return $return;
		}
		
		function releaseKey(_e){
			delete keyStore[_e.keyCode];
			if(keyStore.KUBE().isEmpty()){
				keyComboTriggered = false;
			}
		}
		
		function addKeyListenerEvent(_event,_callback){
			var tempArray,keyArray,codeString;
			tempArray = [];
			keyArray = /[+|:]([^+]*)/.KUBE().matchAll(_event);
			keyArray.KUBE().each(function(_matchArray){
				if(_matchArray[1].length === 1){
					tempArray.push(_matchArray[1].toUpperCase().charCodeAt(0));
				}
				else{
					tempArray.push(wordToKeyCodeMap[_matchArray[1].toLowerCase()]);
				}
			});
			
			tempArray.sort();
			codeString = tempArray.join('+');
			if(!keyF[codeString]){
				keyF[codeString] = [];
			}
			keyF[codeString].push(_callback);
		}
		
		function bindOn(_event,_callback){
			Events.On(_event,_callback,$DomJackAPI);
		}

        function OnState(_event,_callback){
            initEventObj(); //This is done this way as OnState/EmitState can't use the DOM.
            Events.OnState(_event,_callback);
        }
		
		function Once(_event,_callback){
			_event = translateEvent(_event);
			Events.Once(_event,_callback, $DomJackAPI);
		}
		
		function Emit(_event){
			var args,i;
			args = [];
			args[0] = translateEvent(_event);
			if(arguments.length > 1){
				for (i = 1; i < arguments.length; i++){
					args[i] = arguments[i];
				}			
			}
			Events.Emit.apply($DomJackAPI,args);
		}

        function EmitState(_event){
            var args,i;
            initEventObj();
            args = [];
            args[0] = _event;
            if(arguments.length > 1){
                for (i = 1; i < arguments.length; i++){
                    args[i] = arguments[i];
                }
            }
            Events.EmitState.apply($DomJackAPI,args);
        }
		
		function Clear(_event,_recursive){
			_event = (_event === undefined ? undefined : translateEvent(_event));
            if(Events !== undefined){
                Events.Clear(_event);
            }
			cleanListeners(_event);
            if(_recursive === true){
                GetChildren().KUBE().each(function(_Child){
                    _Child.Clear(_event,true);
                });
            }
		}
		//TODO: I feel like we're leaking events. I think we could unbind the event from the DOM node when nothing else is bound.
		function RemoveListener(_event,_callback){
			_event = translateEvent(_event);
			Events.RemoveListener(_event,_callback);
			if(domListeners[_event] && !Events.ListenerCount(_event)){
				domListeners[_event] = false;
			}
		}
		
		//special event to allow structures to be created detached, and then trigger animations once inserted, etc
		function Ready(_readyVar){
			attachEvent(_readyVar) || fireReady(_readyVar);
		}
		
		function attachEvent(_callback){
			var $return = false;
			if(KUBE.Is(_callback) === 'function'){
				(IsDetached() ? Once('ready',_callback) : _callback.call($DomJackAPI));
				$return = true;
			}
			return $return;
		}

		function fireReady(_forceReady){
			if(_forceReady === true){
				GetChildren().KUBE().each(function(_Child){
					_Child.Ready(true);
				});
			}
			else{
                if(Node.nodeType === Node.ELEMENT_NODE){
                    On('animationstart',animationTriggerReady);
                    Style().Animation().Name('nodeInserted').Duration(0.001);
                }
                else{
                    //Things that aren't elements have to do something different.
                }

			}
		}

		function animationTriggerReady(event){
			if(event.animationName == "nodeInserted"){
				RemoveListener('animationstart',animationTriggerReady);
				Style().Animation().Name('').Duration('');
				Emit('ready', Node);
			}

		}

		function translateEvent(_event){
			//I intended toLowerCase this for friendliness. But webkit hates nice things. So yeah....
			_event = (''+_event.toLowerCase().substr(0,2) === 'on' ? ''+_event.substr(2) : ''+_event).toLowerCase();
			
			/************************* KILL THIS WITH FIRE AS SOON AS WEBKIT IS LESS STUPID ***********************/
			//Now I need to handle webkit stupidness for animations at least until it's properly implemented.
			//Apparently at one point firefox did use mozAnimationEnd, and I don't know if IE9 ever supported animations. But regardless
			//as of current testing, webkit is the only one who hasn't implemented it to spec
			if(_event.substr(0,9) === 'animation' || _event.substr(0,10) === 'transition'){
				prefix = prefix || FeatureDetect().Prefix();
				if(prefix === 'webkit'){
					switch(_event){
						case 'animationstart':
							if(!domListeners[_event]){
								initDomListener(_event);
								domListeners['webkitAnimationStart'] = domListeners['animationstart'];
								useAddEventListener('webkitAnimationStart',domListeners['animationstart']); 
							}
							break;
						case 'animationend': 
							if(!domListeners[_event]){
								initDomListener(_event);
								domListeners['webkitAnimationEnd'] = domListeners['animationend'];
								useAddEventListener('webkitAnimationEnd',domListeners['animationend']); 
							}
							break;
						case 'animationiteration': 
							if(!domListeners[_event]){
								initDomListener(_event);
								domListeners['webkitAnimationIteration'] = domListeners['animationiteration'];
								useAddEventListener('webkitAnimationIteration',domListeners['animationiteration']);
							}
							break;
							
						case 'transitionend':
							if(!domListeners[_event]){
								initDomListener(_event);
								domListeners['webkitTransitionEnd'] = domListeners['transitionend'];
								useAddEventListener('webkitTransitionEnd',domListeners['transitionend']);
							}							
							break;
							
						default:
							initDomListener(_event);
							break;
					}
				}
				else{
					initDomListener(_event);
				}
			}
			/*******************************************************************************************************/
			else{
				initDomListener(_event);
			}
			return _event;
		}
		
		function initDomListener(_event){
			var f;
			initEventObj();
			if(!domListeners[_event]){
				f = function(e){ domTrigger(_event,e); };
				domListeners[_event] = f;
				useAddEventListener(_event,f) || useAttachEvent(_event,f);
			}
		}
		
		function useAddEventListener(_event,_f){
			var $return = false;
			if(Node.addEventListener){
				$return = true;
				Node.addEventListener(_event,_f,true);
			}
			return $return;
		}
		
		function useAttachEvent(_event,_f){
			_event = (Node['on'+_event] !== undefined ? 'on'+_event : _event);
			Node.attachEvent(_event,_f);
		}
		
		
		function initEventObj(){
			if(KUBE.Is(Events) !== 'object'){
				Events = KUBE.Events();
				domListeners = {};
			}
		}
		
		function cleanListeners(_event){
			cleanAllListeners(_event) || cleanSingleListener(_event);
		}
		
		function cleanAllListeners(_event){
			var $return = false;
			if(!_event){
				domListeners.KUBE().each(function(_key){
					cleanSingleListener(_key);
				});
				$return = true;
			}
			return $return;
		}
		
		function cleanSingleListener(_event){
			if(domListeners[_event]){
				(Node.removeEventListener ? Node.removeEventListener(_event, domListeners[_event],true) : Node.detachEvent('on'+_event, domListeners[_event]));
				delete domListeners[_event];
			}
		}
		
		
		function domTrigger(_event, e){
			if(Events.Emit(_event, e) === false){
				if(e.preventDefault){
					e.preventDefault();
				}
				else{
					e.returnValue = false;
				}
			}
		}
	
		//Content Management
		function GetInner(){
			return Node.innerHTML;
			//Removed old implementation because I don't know if it's relevant. Look at github for past hackiness
		}
		
		function SetInner(_content,_asText){
            _asText = (_asText === false ? false : true);
			cleanChildren(Node);

            if(_asText){
                Node.textContent = _content;
            }
            else{
                Node.innerHTML = _content;
            }

			return $DomJackAPI;
			//Removed old hacky implementation of this as well
		}
<commit_msg>random<commit_after>/*
 * This file is part of the KUBEjs package
 *
 * (c) Red Scotch Software Inc <<EMAIL>>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
(function(KUBE){
	"use strict";

    //Remove Animate from this, will be removing animations from it entirely as they don't belong here
    var dependancyArray = [
        '/Library/DOM/WinDocJack',
        '/Library/DOM/StyleJack',
        '/Library/DOM/FeatureDetect',
        '/Library/Tools/Convert',
        '/Library/Extend/Array',
        '/Library/Extend/Object',
        '/Library/Extend/RegExp'
    ];

	KUBE.LoadFactory('/Library/DOM/DomJack', DomJack,dependancyArray);
	
	//These are global cache variables
	var KUBENodeId,jackCache,cleanPointer,wordToKeyCodeMap,scheduleClean,cleanupCache,nodeInsertedStyleInit;
	
	KUBENodeId = 0;			//Current KUBE Node ID
	jackCache = {};			//Our cache
	jackCache.length = 0;	//Start our cache length
	cleanupCache = [];
	scheduleClean = false;
	nodeInsertedStyleInit = false;
	wordToKeyCodeMap = {
		'backspace':8,
		'tab':9,
		'enter':13,
		'shift':16,
		'ctrl':17,
		'alt':18,
        'left':37,
        'up':38,
        'right':39,
        'down':40
	};
	
	function cleanCache(){
		KUBE.console.log('clean cache fired');
		var offset,id,i,slice,slices = [];
		offset = 0;
		slice = [undefined,0];
		for(i=0;i<cleanupCache.length;i++){
			id = cleanupCache[i];
			if(jackCache[id] && !jackCache[id].GetNode().KUBEDJ.save){
				deleteDJ(id);
				if(slice[0] === undefined){
					slice[0] = i;
					slice[1]++;
				}
			}
			else{
				if(slice[0] !== undefined){
					slices.push(slice);
					slice = [undefined,0];
				}
			}
		}
		
		for(i=0;i<slices.length;i++){
			cleanupCache.splice(slices[0]-offset,slices[1]);
			offset = offset+slices[1];
		}
		scheduleClean = false;
	}
	
	function deleteDJ(id){
		jackCache[id].Emit('cleanup',id);
		jackCache[id].Clear();
		jackCache[id].destroy();
		jackCache[id] = undefined;
		delete jackCache[id];
		jackCache.length--;		
	}
	
	function memDebug(_output){
		var detached = {};
		jackCache.KUBE().each(function(_id,_dj){
			if(KUBE.Is(_dj) === 'object' && _dj.GetNode().KUBEDJ.save && _dj.IsDetached()){
				if(_output){
					KUBE.console.log(_id,_dj.GetRoot().GetInner());
				}
				else{
					detached[_id] = _dj;
				}
			}
		});
		return detached;
	}
	
	function cacheCount(){
		return jackCache.KUBE().count();
	}
	
	function handleClean(){
		if(!scheduleClean){
			scheduleClean = true;
			requestAnimationFrame(cleanCache);
		}
	}

	DomJack.prototype.toString = function(){ return '[object '+this.constructor.name+']' };
	// Primary class declaration
	function DomJack(initNode){
		var FeatureDetect,SJ,DJ,Events,Node,nodeType,StyleJack,domListeners,id,mapId,rawAPI,$DomJackAPI = {},animationAPI,prefix,keyListener,keyStore,keyComboTriggered,keyF;

        DJ = KUBE.Class('/Library/DOM/DomJack');
        SJ = KUBE.Class('/Library/DOM/StyleJack');
        FeatureDetect = KUBE.Class('/Library/DOM/FeatureDetect');

		if(!nodeInsertedStyleInit){
			nodeInsertedStyleInit = true;
			(function(){
				var S = SJ('@keyframes nodeInserted');
				S.From().Text().Indent(1);
				S.To().Text().Indent(0);
			}());

		}

		domListeners = {};
		keyListener = false;
		keyStore = {};
		keyComboTriggered = false;
		keyF = {};
		rawAPI = {
			'dumpCache': function(){ return jackCache; },
			'memDebug': memDebug,
			'cacheCount': cacheCount,
			'destroy':destroy,
			
			//Special
			'UpdateAPI':UpdateAPI,				//Utility: Mutates the contextAPI, with additional methods appropriate to Node type
			
			//Attribute handling
			'GetAttribute':GetAttribute,		//Get: returns key val, or if empty, returns object with all attributes
			'SetAttribute':SetAttribute,		//Set: accepts key val for setting attribute, or object for multi set
			
			//Node Management
			'GetNode':GetNode,					//Get: the raw dom node
			'GetParent':GetParent,				//Get: the nodes parent DomJack
			'GetChildren':GetChildren,			//Get: an array of Node children wrapped with DomJack
			'GetChild':GetChild,				//Get: the Node at _index wrapped with DomJack
			'GetFirstChild':GetFirstChild,
			'GetLastChild':GetLastChild,
			'GetType':GetType,					//Get: type of Node (tag type)
			'GetIndex':GetIndex,				//Get: index of placement within parent
			'GetRoot':GetRoot,

			//Children management
			'Insert':Insert,					//Utility: Inject a node at a specific index within the children list
			'Prepend':Prepend,					//Utility: Inject a node at the beginning of the children list
			'Append':Append,					//Utility: Inject a node at the end of the children list
			'InsertBefore':InsertBefore,		//Utility: Inject a node before another node/index
			'InsertAfter':InsertAfter,			//Utility: Inject a node after another node/index
			'Prune':Prune,						//Utility: Prune children past an Index
			
			//Event management
			'On':On,							//Set: Set a callback to fire on a certain event queue
            'OnState': OnState,                  //Set: Bind a callback to be fired on state event
			'Once':Once,						//Set: Set a callback that will only fire once on a certain event queue
			'Emit':Emit,						//Utility: Trigger an event queue
            'EmitState': EmitState,              //Utility: Trigger a state event on the DomJack
			'Clear':Clear,						//Set: Clear an individual event, or event queue
			'RemoveListener':RemoveListener,	//Set: **not sure what this does
			'Ready':Ready,						//Utility: A special function that can fire callbacks when the Node is reinjected into the DOM
			
			//Content management
			'GetInner':GetInner,				//Get: Get the raw innerHtml contents
			'SetInner':SetInner,				//Set: Set the innerHtml contents
            'BuildInner':BuildInner,            //Utility: Take an HTML string with buildKeys, build it into a DOM structure, return an Object reference to the keyed DomJacks
			'Dump':Dump,						//Utility: Perform a special operation to properly empty the innerHtml contents (clear from memory,etc)
			
			//General Information			
			'GetId':GetId,						//Get: **Currently gets the id, not sure about this
			'SetId':SetId,						//Set: **Currently sets the id, not sure about this
			'CacheId':CacheId,					//Get: Get the KUBE ID (read only)
			'IsDetached':IsDetached,			//Utility: Is the Node attached/detached within the DOM?
			
			//Utilities
			'Copy':Copy,						//Utility: Create a copy of the Node (detached) and return.
			'Delete':Delete,					//Utility: Detach the Node and set it to be cleaned from the Cache. Cleans internal state, external references still need to be released
			'Detach':Detach,
			'DetachChildren':DetachChildren,
			'GetDrawDimensions':GetDrawDimensions,	//Utility: This is probably going to replace CalcHeight and CalcWidth entirely
			'Rect': Rect,							//This is a shortcut to getBoundingRect
            'HasScrollBar': HasScrollBar,
            'GetScrollBarDimensions': GetScrollBarDimensions,
			
			//Style
			'Style':Style,						//Get: the StyleJack of the Node
			'GetClasses':GetClasses,			//Get: an array of classes (StyleJack wrapped) that are applied to the Node
			'AddClass':AddClass,				//Utility: Adds a class to Node (accepts string, or StyleJack object)
			'RemoveClass':RemoveClass,			//Utility: Removes a class from Node (accepts string, or StyleJack object)
			'ToggleClass':ToggleClass,			//Utility: Adds or Removes class from Node depending on current state
			'HasClass':HasClass,				//Utility: Quick check for whether a class is currently applied to Node
			
			//Mapping
			'SetMapId':SetMapId,
			'MapData':MapData
		};

		//Init and return our API
		init(initNode);
		return $DomJackAPI;
		
		//Init
		//Do I need to handle something that's already a DomJack?
		function destroy(){
			//Do I need this?
			Events = Node = nodeType = StyleJack = domListeners = id = rawAPI = $DomJackAPI = animationAPI = prefix = keyListener = keyStore = keyComboTriggered = keyF = undefined;
		}		
		
		function init(_initVar){
			initWinDocJack(_initVar) || initOldDJ(_initVar) || initNewDJ(_initVar);
		}
		
		function initWinDocJack(_initVar){
			var $return = false;
			if(!_initVar || _initVar === window || _initVar === document){
				$DomJackAPI = KUBE.Class('/Library/DOM/WinDocJack')();
				$return = true;
			}
			return $return;
		}
		
		function initOldDJ(_dj){
			var $return = false;
			if(isDJ(_dj)){
				$DomJackAPI = _dj;
				$return = true;
			}
			return $return;
		}

		function isDJ(_dj){
			return  (_dj instanceof DomJack ? true : false); //(rawAPI.KUBE().duckType(_dj) === true ? true : false);
		}
		
		function initNewDJ(_initVar){
			return initCache(validateAndInitNode(_initVar));
		}
				
		function validateAndInitNode(_initVar){
			var type = KUBE.Is(_initVar),$return;
			$return = initByString(_initVar,type) || initByObject(_initVar,type);
            if($return === false) {
                throw new Error('Attempted to initialize a DomJack on an object that is not an HTMLElement');
            }
            return $return;
		}
		
		function initByString(_initVar,_type){
			return (_type === 'string' ? (initNewIdNode(_initVar) || checkTypeAndCreate(_initVar)) : false);
		}
		
		function initNewIdNode(_id){
			var $return = false;
			if(isID(_id)){
				if(!idNodeExists(_id)){
					$return = document.createElement('div');
					$return.id = prepID(_id);
				}
			}
			return $return;
		}
		
		function initByObject(_initVar,_type){
			return (_type === 'object' && KUBE.Is(_initVar.ownerDocument) === 'object' ? _initVar : false);
		}
				
		function initCache(_obj){
			if(KUBE.Is(_obj) === 'object'){
				if(KUBE.Is(_obj.KUBEDJ) === 'object' && _obj.KUBEDJ.id){
					$DomJackAPI = jackCache[_obj.KUBEDJ.id] || null;
				}
				else{
					Node = _obj;
					Node.KUBEDJ = {};
					Node.KUBEDJ.id = id = ++KUBENodeId;
					Node.KUBEDJ.save = true;
					
					nodeType = (Node.nodeName || Node.tagName).toLowerCase();
					$DomJackAPI = rawAPI.KUBE().copy().KUBE().create(DomJack.prototype);
					UpdateAPI();
					
					jackCache[id] = {};
					jackCache[id] = $DomJackAPI;
					jackCache.length++;
					cleanupCache.push(id);
				}
			}
		}
		
		
		function checkTypeAndCreate(_type){
			var $return = false;
			//KUBE.console.log(_type+': '+KUBENodeId+1);
			try{
				$return = document.createElement(''+_type.toLowerCase());
			}
			catch(e){
				//No node for you
				throw new Error('Dom Jack could not create element. Invalid type: '+_type);
			}
			return $return;
		}
				
		// Special
		function UpdateAPI(){
			$DomJackAPI = BindCustomAPI($DomJackAPI,nodeType,rawAPI);
			jackCache[id] = $DomJackAPI;
			return $DomJackAPI;
		}
		
		// Attribute handling
		function GetAttribute(_key){
			return domGetAttribute(_key) || propertyGetAttribute(_key) || listGetAttribute(_key);
		}
		
		function domGetAttribute(_key){
			return (_key && KUBE.Is(Node.getAttribute) === 'function' ? Node.getAttribute(_key) : undefined);
		}
		
		function propertyGetAttribute(_key){
			return (_key && KUBE.Is(Node[_key]) !== 'undefined' ? Node[_key] : undefined);
		}
		
		function listGetAttribute(_key){
			return (_key ? findAttribute(_key) : parseAttributesIntoObject());
		}
		
		function parseAttributesIntoObject(){
			var $return;
            //TODO: this is probably broken, and should do what findAttribute does (check for NamedNodeMap)
			if(KUBE.Is(Node['attributes']) === 'object'){
				$return = {};
				Node.attributes.KUBE().each(function(_keyIndex,_attr){
					if(_attr.name !== undefined){
						$return[_attr.name] = _attr.value;
					}
				});
			}
			return $return;
		}
		
		function findAttribute(_key){
			var $return;
            if(KUBE.Is(Node['attributes'],true) === 'NamedNodeMap'){
                var length = Node['attributes'].length;
                for(var i=0;i<length;i++){
                    var tempAttr = Node['attributes'][i];
                    if(tempAttr.name.toLowerCase() === _key.toLowerCase()){
                        $return = tempAttr.value || tempAttr.nodeValue || tempAttr.textContent;
                        break;
                    }
                }
            }
			return $return;
		}
		
		function SetAttribute(_key,_val){
			return setMultiAttributes(_key) || setSingleAttribute(_key,_val);
		}
						
		function setMultiAttributes(_key){
			var $return;
			if(KUBE.Is(_key) === 'object'){
				_key.KUBE().each(function(_objKey,_objVal){
					setSingleAttribute(_objKey,_objVal);
				});
				$return = $DomJackAPI;
			}
			return $return;
		}
		
		function setSingleAttribute(_key,_val){
			(_val === false ? removeAttribute(_key) : trySetAttribute(_key,_val));
			return $DomJackAPI;
		}
		
		function removeAttribute(_key){
			var $return = false;
			try{
				Node.removeAttribute(_key);
				$return = true;
			}
			catch(e){
				throw new Error(e.message);
			}
			return $return;
		}
		
		function trySetAttribute(_key,_val){
			var $return = false;
			try{
				Node.setAttribute(_key,_val);
				$return = true;
			}
			catch(E){
				if(KUBE.Is(Node[_key]) !== 'function' && KUBE.Is(Node[_key]) !== 'object'){
					Node[_key] = _val;
					$return = false;
				}
				throw E;
			}
			return $return;
		}
		
		// Node Management
		function GetNode(){
			return Node;
		}
		
		function GetRoot(){
			return rGetRoot(Node);
			function rGetRoot(_N){
				return (_N.parentNode === null ? DJ(_N) : rGetRoot(_N.parentNode));
			}
		}
		
		function GetParent(){
			return (Node.parentNode !== null ? DJ(Node.parentNode) : false);
		}
		
		function GetChildren(){
			var i,$return = [];
			if(Node.children.length){
				for(i=0;i<Node.children.length;i++){
					$return.push(DJ(Node.children[i]));
				}
			}
			return $return;
		}
		
		function GetChild(_index){
			return (Node.children[_index] ? DJ(Node.children[_index]) : undefined);
		}
		
		function GetFirstChild(){
			return GetChild(0);
		}
		
		function GetLastChild(){
			return GetChild(Node.children.length-1);
		}
		
		function GetType(){
			return nodeType;
		}
		
		function GetIndex(){
			var $return;
			GetParent().GetChildren().KUBE().each(function(_child,_index){
				if(_child.GetNode() === Node){
					$return = _index;
					this.break;
				}
			});
			return $return;
		}
				
		// Children Management
		function Insert(_Node, _index){
			var Inject,$return = false;
			
			Inject = DJ(_Node);
			
			if(isDJ(Inject)){
				$return = insertAtNumber(Inject,_index) || insertAtObject(Inject,_index) || Append(Inject);
			}
			
			if($return){
				Inject.UpdateAPI();
				Inject.Ready();
				$return = (KUBE.Is(_Node) === 'string' ? Inject : $DomJackAPI);
			}
			return $return;
		}
		
		function insertAtNumber(_Inject,_index){
			var children,$return = false;
			
			if(KUBE.Is(_index) === 'number'){
				children = GetChildren();
				if(children[_index]){
					Node.insertBefore(_Inject.GetNode(),children[_index].GetNode());
					$return = true;
				}
			}
			return $return;
		}
		
		function insertAtObject(_Inject,_index){
			var target,$return = false;
			
			if(KUBE.Is(_index) === 'object'){
				target = DJ(_index);
				if(isDJ(target) && target.GetParent().GetNode() === Node){
					Node.insertBefore(_Inject.GetNode(),target.GetNode());
					$return = true;
				}
			}
			return $return;
		}
		
		function Prepend(_Node){
			return Insert(_Node, 0);
		}
				
		function Append(_Node){
			if(KUBE.Is(_Node) === 'array'){
				_Node.KUBE().each(Append);
			}
			else{
				var Inject,$return = false;
				Inject = (!isDJ(_Node) ? DJ(_Node) : _Node);
				if(Inject){
					Node.appendChild(Inject.GetNode());
					Inject.UpdateAPI();
					Inject.Ready();
					$return = (KUBE.Is(_Node) === 'string' ? Inject : $DomJackAPI);
				}
				return $return;
			}
		}
		
		//Not sure about Before/After yet, will have to use them first
		function InsertBefore(_Target){
			var Parent;
			_Target = (!isDJ(_Target) ? DJ(_Target) : _Target);
			if(_Target.GetParent()){
				Parent = _Target.GetParent();
				Parent.Insert($DomJackAPI,_Target.GetIndex());
			}
		}
		
		function InsertAfter(_Target){
			var Parent;
			_Target = (!isDJ(_Target) ? DJ(_Target) : _Target);
			if(_Target.GetParent()){
				Parent = _Target.GetParent();
				Parent.Insert($DomJackAPI,_Target.GetIndex()+1);
			}
		}
		
		function Prune(_index){
			GetChildren().KUBE().each(function(_Child,_childIndex){
				if(_childIndex >= _index){
					_Child.Delete();
				}
			});
			return $DomJackAPI;
		}
		
		
		//Event Management
		function On(_event,_callback){
			var $return;
			if(!multiOn(_event,_callback)){
				_event = translateEvent(_event);
				catchReady(_event,_callback) || catchKeys(_event,_callback) || bindOn(_event,_callback);
			}
			return $return;
		}
		
		function multiOn(_array,_callback){
			var $return = false;
			if(KUBE.Is(_array) === 'array'){
				$return = true;
				_array.KUBE().each(function(_e){
					On(_e,_callback);
				});
			}
			return $return;
		}
		
		function catchReady(_event,_callback){
			//Do not ever allow ready events to be bound to on.
			//Ready events are intended to only fire a single time when a Node is inserted into the DOM.
			//As those events may start manipulating the DOM, this results in runaway ready events being fired incorrectly, 
			//creating a loop of infinite madness.
			var $return = false;
			if(_event === 'ready'){
				Once(_event,_callback);
				$return = true;
			}
			return $return;
		}
		
		function catchKeys(_event,_callback){
			var $return = false;
			if(_event.substr(0,4) === 'key:'){
				initKeyListener();
				addKeyListenerEvent(_event,_callback);
				$return = true;
			}
			return $return;
		}
		
		function initKeyListener(){
			if(!keyListener){
				keyListener = true;
				On('keydown',pressKey);
				On('keyup',releaseKey);
			}
		}
		
		function pressKey(_e){
			//debugger;
			var $return,codeString,tempArray = [];
			$return = true;
			keyStore[_e.keyCode] = true;
			if(!keyComboTriggered){
				keyStore.KUBE().each(function(_k,_v){
					tempArray.push(_k);
				});
				tempArray.sort();
				codeString = tempArray.join('+');
				if(keyF[codeString] && keyComboTriggered === false){
					keyComboTriggered = true;
					for(var i=0;i<keyF[codeString].length;i++){
						if(KUBE.Is(keyF[codeString][i]) === 'function'){
							if(keyF[codeString][i](_e) === false){
								$return = false;
								break;
							}
						}
					}
					keyComboTriggered = false;
				}
			}
			return $return;
		}
		
		function releaseKey(_e){
			delete keyStore[_e.keyCode];
			if(keyStore.KUBE().isEmpty()){
				keyComboTriggered = false;
			}
		}
		
		function addKeyListenerEvent(_event,_callback){
			var tempArray,keyArray,codeString;
			tempArray = [];
			keyArray = /[+|:]([^+]*)/.KUBE().matchAll(_event);
			keyArray.KUBE().each(function(_matchArray){
				if(_matchArray[1].length === 1){
					tempArray.push(_matchArray[1].toUpperCase().charCodeAt(0));
				}
				else{
					tempArray.push(wordToKeyCodeMap[_matchArray[1].toLowerCase()]);
				}
			});
			
			tempArray.sort();
			codeString = tempArray.join('+');
			if(!keyF[codeString]){
				keyF[codeString] = [];
			}
			keyF[codeString].push(_callback);
		}
		
		function bindOn(_event,_callback){
			Events.On(_event,_callback,$DomJackAPI);
		}

        function OnState(_event,_callback){
            initEventObj(); //This is done this way as OnState/EmitState can't use the DOM.
            Events.OnState(_event,_callback);
        }
		
		function Once(_event,_callback){
			_event = translateEvent(_event);
			Events.Once(_event,_callback, $DomJackAPI);
		}
		
		function Emit(_event){
			var args,i;
			args = [];
			args[0] = translateEvent(_event);
			if(arguments.length > 1){
				for (i = 1; i < arguments.length; i++){
					args[i] = arguments[i];
				}			
			}
			Events.Emit.apply($DomJackAPI,args);
		}

        function EmitState(_event){
            var args,i;
            initEventObj();
            args = [];
            args[0] = _event;
            if(arguments.length > 1){
                for (i = 1; i < arguments.length; i++){
                    args[i] = arguments[i];
                }
            }
            Events.EmitState.apply($DomJackAPI,args);
        }
		
		function Clear(_event,_recursive){
			_event = (_event === undefined ? undefined : translateEvent(_event));
            if(Events !== undefined){
                Events.Clear(_event);
            }
			cleanListeners(_event);
            if(_recursive === true){
                GetChildren().KUBE().each(function(_Child){
                    _Child.Clear(_event,true);
                });
            }
		}
		//TODO: I feel like we're leaking events. I think we could unbind the event from the DOM node when nothing else is bound.
		function RemoveListener(_event,_callback){
			_event = translateEvent(_event);
			Events.RemoveListener(_event,_callback);
			if(domListeners[_event] && !Events.ListenerCount(_event)){
				domListeners[_event] = false;
			}
		}
		
		//special event to allow structures to be created detached, and then trigger animations once inserted, etc
		function Ready(_readyVar){
			attachEvent(_readyVar) || fireReady(_readyVar);
		}
		
		function attachEvent(_callback){
			var $return = false;
			if(KUBE.Is(_callback) === 'function'){
				(IsDetached() ? Once('ready',_callback) : _callback.call($DomJackAPI));
				$return = true;
			}
			return $return;
		}

		function fireReady(_forceReady){
			if(_forceReady === true){
				GetChildren().KUBE().each(function(_Child){
					_Child.Ready(true);
				});
			}
			else{
                if(Node.nodeType === Node.ELEMENT_NODE){
                    On('animationstart',animationTriggerReady);
                    Style().Animation().Name('nodeInserted').Duration(0.001);
                }
                else{
                    //Things that aren't elements have to do something different.
                }

			}
		}

		function animationTriggerReady(event){
			if(event.animationName == "nodeInserted"){
				RemoveListener('animationstart',animationTriggerReady);
				Style().Animation().Name('').Duration('');
				Emit('ready', Node);
			}

		}

		function translateEvent(_event){
			//I intended toLowerCase this for friendliness. But webkit hates nice things. So yeah....
			_event = (''+_event.toLowerCase().substr(0,2) === 'on' ? ''+_event.substr(2) : ''+_event).toLowerCase();
			
			/************************* KILL THIS WITH FIRE AS SOON AS WEBKIT IS LESS STUPID ***********************/
			//Now I need to handle webkit stupidness for animations at least until it's properly implemented.
			//Apparently at one point firefox did use mozAnimationEnd, and I don't know if IE9 ever supported animations. But regardless
			//as of current testing, webkit is the only one who hasn't implemented it to spec
			if(_event.substr(0,9) === 'animation' || _event.substr(0,10) === 'transition'){
				prefix = prefix || FeatureDetect().Prefix();
				if(prefix === 'webkit'){
					switch(_event){
						case 'animationstart':
							if(!domListeners[_event]){
								initDomListener(_event);
								domListeners['webkitAnimationStart'] = domListeners['animationstart'];
								useAddEventListener('webkitAnimationStart',domListeners['animationstart']); 
							}
							break;
						case 'animationend': 
							if(!domListeners[_event]){
								initDomListener(_event);
								domListeners['webkitAnimationEnd'] = domListeners['animationend'];
								useAddEventListener('webkitAnimationEnd',domListeners['animationend']); 
							}
							break;
						case 'animationiteration': 
							if(!domListeners[_event]){
								initDomListener(_event);
								domListeners['webkitAnimationIteration'] = domListeners['animationiteration'];
								useAddEventListener('webkitAnimationIteration',domListeners['animationiteration']);
							}
							break;
							
						case 'transitionend':
							if(!domListeners[_event]){
								initDomListener(_event);
								domListeners['webkitTransitionEnd'] = domListeners['transitionend'];
								useAddEventListener('webkitTransitionEnd',domListeners['transitionend']);
							}							
							break;
							
						default:
							initDomListener(_event);
							break;
					}
				}
				else{
					initDomListener(_event);
				}
			}
			/*******************************************************************************************************/
			else{
				initDomListener(_event);
			}
			return _event;
		}
		
		function initDomListener(_event){
			var f;
			initEventObj();
			if(!domListeners[_event]){
				f = function(e){ domTrigger(_event,e); };
				domListeners[_event] = f;
				useAddEventListener(_event,f) || useAttachEvent(_event,f);
			}
		}
		
		function useAddEventListener(_event,_f){
			var $return = false;
			if(Node.addEventListener){
				$return = true;
				Node.addEventListener(_event,_f,true);
			}
			return $return;
		}
		
		function useAttachEvent(_event,_f){
			_event = (Node['on'+_event] !== undefined ? 'on'+_event : _event);
			Node.attachEvent(_event,_f);
		}
		
		
		function initEventObj(){
			if(KUBE.Is(Events) !== 'object'){
				Events = KUBE.Events();
				domListeners = {};
			}
		}
		
		function cleanListeners(_event){
			cleanAllListeners(_event) || cleanSingleListener(_event);
		}
		
		function cleanAllListeners(_event){
			var $return = false;
			if(!_event){
				domListeners.KUBE().each(function(_key){
					cleanSingleListener(_key);
				});
				$return = true;
			}
			return $return;
		}
		
		function cleanSingleListener(_event){
			if(domListeners[_event]){
				(Node.removeEventListener ? Node.removeEventListener(_event, domListeners[_event],true) : Node.detachEvent('on'+_event, domListeners[_event]));
				delete domListeners[_event];
			}
		}
		
		
		function domTrigger(_event, e){
			if(Events.Emit(_event, e) === false){
				if(e.preventDefault){
					e.preventDefault();
				}
				else{
					e.returnValue = false;
				}
			}
		}
	
		//Content Management
		function GetInner(){
			return Node.innerHTML;
			//Removed old implementation because I don't know if it's relevant. Look at github for past hackiness
		}
		
		function SetInner(_content,_asText){
            _asText = (_asText === false ? false : true);
			cleanChildren(Node);
			if(!Node){
				console.log('Attempted to operate on a destroyed Node');
				return;
			}
            if(_asText){
                Node.textContent = _content;
            }
            else{
                Node.innerHTML = _content;
            }

			return $DomJackAPI;
			//Removed old hacky implementation of this as well
		}

{'fraction_non_alphanumeric': 0.1438495809106698, 'fraction_numerical': 0.0025673940949935813, 'mean_word_length': 2.9868292315797396, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4862232', 'n_tokens_mistral': 20483, 'n_tokens_neox': 16442, 'n_words': 3669}