if (!window.apt){
	var apt={
		version: 'apt 1.15 AL 20081018',
		bootPath: 'undefined',
		currentPath: '', //html file path
		browser: {
			ie: false,
			gecko: false,
			opera: false,
			webkit: false,
			adobeAIR: false,
			w3c: false,
			name: 'unknown',
			quirks: document.compatMode ? document.compatMode.indexOf('CSS1Compat') < 0 : true,
			version: 0
		},
		aptIf: false,
		behaviors: [],
		content: {},
		aContent: [], //content array
		core: {},
		data: [],
		debug: false,
		debugFiles: [], //scripts to load
		domain: '',
		element: {
			name: '',
			value: '',  //_value
			current: {}, //node _current //type=element or behavior
			label: null,
			//nameAttribute: '',
			icon: null,
			type: ''
			},
		elements: [],
		engine: {},
		error: {
			ERROR: 0,
			WARNING: 1,
			LOG: 2,
			startTime: new Date(),
			console: {}
		},
		filename: '', //last load 
		filepath: '',
		iconPath: '',
		icons: [],
		ids: [],
		handlerStart: 'apt.core.event.getTarget(event);',
		namespaces: ['a','o','p','s'],
		nodeReplace: {},
		scrollBarWidth: null,
		skin: 'system',
		skinFile: [], //skin file to load
		stack: [],
		aptStack: [],
		startTime: new Date(),
		xmlnsIntern: 'http://www.aptivoconsulting.com/a',
		xmlnsExtern: 'http://www.aptivoconsulting.com/o',
		xmlnsApplication: 'http://www.aptivoconsulting.com/p',
		xmlnsSVG: 'http://www.w3.org/2000/svg',
		xmlnsVML: 'urn:schemas-microsoft-com:vml',
		xmlns: 'xmlns:a="http://www.aptivoconsulting.com/a" xmlns:o="http://www.aptivoconsulting.com/o" xmlns:p="http://www.aptivoconsulting.com/p"',
		viewport: document.documentElement,
		viewportBox: {//see setup
			width: window.screen.availWidth,
			height: window.screen.availHeight
		},
		viewportHead: document.getElementsByTagName('head')[0],
		viewportHtml: document.getElementsByTagName('html')[0],
		viewportBody: null,
		boot:function(){
			apt.engine.start();
		}
	};
window.onload = apt.boot;
var _current = {},
	_value = '';
};

//Initial load (after load the function must return only)
apt.cursorInit = function(){
	apt.engine.loadJSFile('cursor');
};
apt.dataInit = function(){
	apt.engine.loadJSFile('data');
};

//global control for events
if (!_event){
	var _event = {
		debug: [], //for debugging
		enable: true,
		modal: [], //only accept events for node modal[0]
		redirect: null, //node to send the event
		observers: [], //global
		timeStamp: new Date(), //for last event 
		eventName: '', //for last event
		bus: [], //[event].[] <- nodeList
		observeBus: function(eventName, node){ //suscription
			if(!_event.bus[eventName]){_event.bus[eventName]=[];}
			_event.bus[eventName].push(node);
		},
		
		dispatchBus: function(eventName, $event){//propagation - TODO
			var e = _event.bus[eventName];
			if(!e)return;
			for(var i=0; i<e.length;i++){
				$event.currentTarget = e[i];
				e[i].handlers.execute(eventName, $event);
			}
		},
		
		removeBus: function(node, eventName){ //removes a node from bus
			var count = _event.bus[eventName].length -1;
			while (count > -1){
				if(_event.bus[eventName][count] == node){_event.bus[eventName].splice(count, 1);}
				count --;
			}
		},
		
		dispatchEvent: function(node, eventName, $event){
			var e = node.handlers.events[eventName];
			if(!e)return;
			if(!$event) $event = _event.create$event();
			$event.currentTarget = node;
			node.handlers.execute(eventName, $event);
		},
		
		observeEvent: function(fromNode, toNode, eventName, functionTo){
			if(!(fromNode.handlers && fromNode.handlers.events[eventName])){
				apt.core.nodeSetHandler(fromNode, eventName,null);
			}
			fromNode.handlers.events[eventName].observers.push(toNode);
			if(functionTo != null){
				apt.core.nodeSetHandler(toNode, eventName, functionTo,{register:false});
			}
		},
		
		redirectEvent: function(fromNode, toNode, eventName, functionTo){
			if (!(fromNode.handlers && fromNode.handlers.events[eventName])) {
				apt.core.nodeSetHandler(fromNode, eventName, null);
			}
			var fEvent = fromNode.handlers.events[eventName];
			fEvent.enable = true;
			
			var isNode = false;
			for(var i=0; i<fEvent.redirect.length; i++){
				if(fEvent.redirect[i] == toNode)isNode = true;
			}
			
			if(!isNode) fEvent.redirect.push(toNode);
			if (toNode.handlers == null || toNode.handlers.events[eventName] == null) {
				if(functionTo != null) apt.core.nodeSetHandler(toNode, eventName, functionTo,{register: false});
			}
			toNode.handlers.events[eventName].enable = true;
		},
		
		cancelRedirectEvent: function(fromNode, toNode, eventName){
			var count = fromNode.handlers.events[eventName].redirect.length - 1;
			while (count > -1) {
				if (fromNode.handlers.events[eventName].redirect[count] == toNode) {
					fromNode.handlers.events[eventName].redirect.splice(count, 1); //delete
				}
				count --;
			}
		},
		
		create$event: function(){
			var r = {};
			r.stopHorizontalPropagation = false;
			return r;
		},
		
		disableEvent: function(node, event){
			node.handlers.events[event].enable = false;
		},
		
		nodeDelete: function(node){
			if (node.handlers && node.handlers.events.nodeDelete){_event.dispatchEvent(node, 'nodeDelete');}
			if (node.apt && node.apt.childNodes){
				for (var i=0; i<node.apt.childNodes.length; i++){
					_event.nodeDelete(node.apt.childNodes[i]);
				}
			}
		}
	};
};
//Attribute definition for element
if(!_attribute){
	var _attribute = {q:[]};
	
	_attribute.check = function(names){
		var result = true;
		var n = names.splitSpace();
		for(var i=0; i<n.length;i++){
			if(_attribute.q[n[i]]=='')result = false;
		}
		return result;
	}
};

//Window Manager support
if(!_window){
	var _window = {
		manager: null,
		queue: []
	};
	_window.checkItem = function(id){//ckeck if id is in queue
		for(var i=0; i<_window.queue.length; i++){
			if(_window.queue[i].id == id)return true;
		}
		return false;
	};
	_window.addItem = function(item){
		var q = {
			id: item.getAttribute('id'),
			title: item.showTitle,
			obj: item
		};
		if(!_window.checkItem(q.id))_window.queue.push(q);
	};
	_window.add = function(item){
		_window.addItem(item);
		_window.refresh();
	};
	_window.addActiveChilds = function(p){//clean visible windows
		var item;
		for(var i=0; i<p.childNodes.length; i++){
			item = p.childNodes[i];
			if((item.nodeType==1) && (item.getAttribute('type')== 'o:window') && (item.openStatus)){
				_window.addItem(item);
				item.close();
			}
		}		
	};
	_window.refresh = function(){
		if (_window.manager)_window.manager.refresh();
	};
	_window.restore = function(index){//makes visible a window
		var q = _window.queue[index];
		var p = q.obj.parentNode;
		_window.addActiveChilds(p);
		q.obj.open();
		_window.queue.splice(index,1);
		_window.refresh();
	};
	_window.removeByID = function(id){
		for(var i=0; i<_window.queue.length; i++){
			if (_window.queue[i].id == id) {
				_window.queue.splice(i,1);
				return;
			}
		}
	};
};
//smil support
if(!_smil){
	var _smil = {
		queue: [],//t=time type o=object p=parameter v=value a=adjust parameter
		timeSlice: 50, //ms
		timer: null
	};
	_smil.sort = function(a,b){
		return (a.t - b.t);
	};
	/**
	 * Convert to miliseconds. t = '5s' -> 5000
	 * @param {Number} t
	 */
	_smil.toMs = function(t){
		var v = parseIntDefault(t,0);
		if(t.getDimension() == 's') v = v * 1000;
		return v;
	};
	//Process the queue
	_smil.run = function(){
		if(!_smil.timer){_smil.timer = setInterval(_smil.run, _smil.timeSlice);}
		_history.check();
		var dt, q, v;
		while((_smil.queue.length>0)&& (dt = new Date() - apt.startTime - _smil.queue[0].t)> -10){
			q = _smil.queue.shift();
			if(q.disable){}
			else{
				switch (q.type){
				case 'px':
					v = q.v + q.a * dt;
					if(q.max){
						if (v>q.max && q.a>0)v=q.max;
						if (v<q.max && q.a<0)v=q.max;
					}
					q.o[q.p]= Math.floor(v) + 'px';
					break;
				case '%':
					v = q.v + q.a * dt;
					if(q.max){
						if (v>q.max && q.a>0)v=q.max;
						if (v<q.max && q.a<0)v=q.max;
					}
					q.o[q.p]= Math.floor(v) + '%';
					break;				
				case 'custom':
					q.func(q.o, q.t - q.timeBase , q);
					break;
				case 'set':
					q.o[q.p]= q.v;
					break;
				}
			}
		}
		/*
		if(_smil.queue.length == 0){
			_smil.timer = clearInterval(_smil.timer); //sets null
			}
		*/
	};
	/**
	 * Stop the name events
	 * @param {String} name
	 */
	_smil.disable = function(name){
		_smil.timer = clearInterval(_smil.timer); //stop
		for(var i=0; i<_smil.queue.length; i++){
			if(_smil.queue[i].name && _smil.queue[i].name == name) _smil.queue[i].disable = true;
		}
		_smil.run();
	};
	/**
	 * Stop the timer
	 */
	_smil.stop = function(){
		_smil.timer = clearInterval(_smil.timer);
	};
	_smil.start = function(){
		if(!_smil.timer) _smil.run();
	};
	/**
	 * Remove de name events
	 * @param {String} name
	 */
	_smil.remove = function(name){
		_smil.timer = clearInterval(_smil.timer); //stop
		var q = _smil.queue;
		_smil.queue = [];
		for(var i=0; i<q.length; i++){
			if(q[i].name && q[i].name == name){
			} else {
				_smil.queue.push(q[i]);
			}
		}
		_smil.run();
	};
	
	/**
	 * Set a time event
	 * @param {Node} item
	 * @param {Object} param
	 */
	_smil.set = function(item, param){
		if(!param.attributeName) return;
		if(!param.to )return;
		if(!param.delay) param.delay = 0; //ms
		var delay = _smil.toMs(param.delay);
		
		var timeBase = new Date() - apt.startTime + delay;
	
		_smil.queue.push({t: timeBase, type: 'set', o: item, p: param.attributeName, v: param.to});
		_smil.queue.sort(_smil.sort);
		if(!_smil.timer) _smil.run();
	};
	/**
	 * Set a custom time event
	 * @param {Node} item
	 * @param {Object} param
	 */
	_smil.setCustom = function(item, param){
		if(!param.delay) param.delay = 0; //ms
		var delay = _smil.toMs(param.delay);
		var timeBase = new Date() - apt.startTime + delay;
		if(!param.name)param.name='';
		
		_smil.queue.push({t: timeBase, timeBase: timeBase, type: 'custom', name: param.name, o: item, func: param.func});
	
		_smil.queue.sort(_smil.sort);
		if(!_smil.timer) _smil.run();
	};
	
	/**
	 * Fill the queue with the time events
	 * @param {Node} item
	 * @param {Object} param
	 */
	_smil.animate = function(item, param){
		//if(!param.type)param.type = 'px';
		if(!param.attributeName) return;
		if(!param.name)param.name='';
		
		if(!param.duration) param.duration = '500ms';
		var duration = _smil.toMs(param.duration);
		
		if(!param.delay) param.delay ='0ms';
		var delay = _smil.toMs(param.delay);
		
		if(!param.from || param.from == '') {param.from = item[param.attributeName];} //*** test pending
		var from = parseInt(param.from);
		if(!param.to )return;
		var to = parseInt(param.to);
		if(!param.type) param.type = param.to.getDimension();
		
		//if(param.repeatCount == null)param.repeatCount = '1';
		
		var timeBase = new Date() - apt.startTime + delay;
		var t, a, v;
		var p = (to - from)/ duration;
		for(var i=0; i<duration+_smil.timeSlice; i = i + _smil.timeSlice){
			t = timeBase + i;
			v = from + p * i;
			a = p;
			if (i >= duration) {
				t = timeBase + duration;
				v = to;
				a = 0;
			}
			_smil.queue.push({t: t, type: param.type, o: item, name: param.name, p: param.attributeName, v: v, a: a, max: to});
		}
		_smil.queue.sort(_smil.sort);
		if(!_smil.timer) _smil.run();		
	};
	/**
	 * Fill the queue with color time events
	 * @param {Node} item
	 * @param {Object} param
	 */
	_smil.animateColor = function(item, param){
		if(!param.attributeName) return;
		if(!param.from ) return;
		if(!param.to)return;
		if(!param.duration) param.duration = '500ms';
		var duration = _smil.toMs(param.duration);
		
		if(!param.delay) param.delay ='0ms';
		var delay = _smil.toMs(param.delay);

		var timeBase = new Date() - apt.startTime + delay;
		var from = _color.toNumber(param.from);
		var to = _color.toNumber(param.to);

		var p = {
			r: (to.r - from.r)/duration,
			g: (to.g - from.g)/duration,
			b: (to.b - from.b)/duration
		};
		
		var t, v;
		for(var i=0; i<duration+_smil.timeSlice; i = i + _smil.timeSlice){
			t = timeBase + i;
			v = _color.toString({
				r: from.r + p.r * i,
				g: from.g + p.g * i,
				b: from.b + p.b * i
			});
			if (i >= duration) {
				t = timeBase + duration;
				v = _color.toString(to);
			}
			_smil.queue.push({t: t, type: 'set', o: item, p: param.attributeName, v: v});
		}
		_smil.queue.sort(_smil.sort);
		if(!_smil.timer) _smil.run();
	};
}
if(!_color){
	var _color = {};
	
	_color.split = function(a,s){
		var result = [];
		var colors = a.split(s);
		var i = 0;
		while(i<colors.length){
			result[colors[i++]] = s + colors[i++];
		}
		return result;
	};	
	_color.color = _color.split('aliceblue#f0f8ff#antiquewhite#faebd7#aqua#00ffff#aquamarine#7fffd4#azure#f0ffff#beige#f5f5dc#bisque#ffe4c4#black#000000#blanchedalmond#ffebcd#blue#0000ff#blueviolet#8a2be2#brown#a52a2a#burleywood#deb887#cadetblue#5f9ea0#chartreuse#7fff00#chocolate#d2691e#coral#ff7f50#cornflowerblue#6495ed#cornsilk#fff8dc#crimson#dc143c#cyan#00ffff#darkblue#00008b#darkcyan#008b8b#darkgoldenrod#b8b60b#darkgray#a9a9a9#darkgreen#006400#darkkhaki#bdb76b#darkmagenta#8b008b#darkolivegreen#556b2f#darkorange#ff8c00#darkorchid#9932cc#darkred#8b0000#darksalmon#e9967a#darkseagreen#8fbc8f#darkslateblue#483d8b#darkslategray#2f4f4f#darkturquoise#00ced1#darkviolet#9400d3#deeppink#ff1493#deepskyblue#00bfff#dimgray#696969#dodgerblue#1e90ff#firebrick#b22222#floralwhite#fffaf0#forestgreen#228b22#fuchsia#ff00ff#gainsboro#dcdcdc#ghostwhite#f8f8ff#gold#ffd700#goldenrod#daa520#grey#808080#green#008000#greenyellow#adff2f#honeydew#f0fff0#hotpink#ff69b4#indianred#cd5c5c#indigo#4b0082#ivory#fffff0#khaki#f0e68c#lavender#e6e6fa#lavenderblush#fff0f5#lawngreen#7cfc00#lemonchiffon#fffacd#lightblue#add8e6#lightcoral#f08080#lightcyan#e0ffff#lightgoldenrodyellow#fafad2#lightgreen#90ee90#lightgray#d3d3d3#lightpink#ffb6c1#lightsalmon#ffa07a#lightseagreen#20b2aa#lightskyblue#b7cefa#lightslategray#778899#lightsteelblue#b0c4de#lightyellow#ffffe0#lime#00ff00#limegreen#32cd32#linen#faf0e6#magenta#ff00ff#maroon#800000#mediumaquamarine#66cdaa#mediumblue#0000cd#mediumorchid#ba55d3#mediumpurple#9370db#mediumseagreen#3cb371#mediumslateblue#7b68ee#mediumspringgreen#00fa9a#mediumturquoise#48d1cc#mediumvioletred#c71585#midnightblue#191970#mintcream#f5fffa#mistyrose#ffe4e1#moccasin#ffe4b5#navajowhite#ffdead#navy#000080#oldface#fdf5e6#olive#808000#olivedrab#6b8e23#orange#ffa500#orangered#ff4500#orchid#da70d6#palegoldenrod#eee8aa#palegreen#98fb98#paleturquoise#afeeee#palevioletred#db7093#papayawhip#ffefd5#peachpuff#ffdab9#peru#cd853f#pink#ffc0cb#plum#dda0dd#powderblue#b0e0e6#purple#800080#red#ff0000#rosybrown#bc8f8f#royalblue#4169e1#saddlebrown#b84513#salmon#fa8072#sandybrown#f4a460#seagreen#2e8b57#seashell#fff5ee#sienna#a0522d#silver#c0c0c0#skyblue#87ceeb#slateblue#6a5acd#slategray#708090#snow#fffafa#springgreen#00ff7f#steelblue#46b2b4#tan#d2b48c#teal#008080#thistle#d8bfd8#tomato#ff6347#turquoise#40e0d0#violet#ee82ee#wheat#f5deb3#white#ffffff#whitesmoke#f5f5f5#yellow#ffff00#yellowgreen#9acd32', '#');
	/**
	 * returns color in format # or rgb()
	 * @param {String} c (color, rgb(c1, c2, c3), #c1c2c3
	 */
	_color.translate = function(c){
		if((c.indexOf('rgb(')=== 0) ||(c.indexOf('#')=== 0)) {
			return c;
		} else {
			if(c in _color.color){return(_color.color[c]);} else {return (_color.color['white']);}
		}
	};
	/**
	 * returns objecto rgb
	 * @param {String} c
	 */
	_color.toNumber = function(c){
		if(c.indexOf('rgb(') == 0){
			var r = c.substring(4, c.indexOf(')')).split(',');
			return {
				r: parseInt(r[0]),
				g: parseInt(r[1]),
				b: parseInt(r[2])
			};
		}
		if(c.indexOf('#') == 0){
			if(c.length == 4){
				return{
					r: parseInt(c.charAt(1),16)*17,
					g: parseInt(c.charAt(2),16)*17,
					b: parseInt(c.charAt(3),16)*17};
			} else {//7
				return{
					r: parseInt(c.substring(1,3),16),
					g: parseInt(c.substring(3,5),16),
					b: parseInt(c.substring(5,7),16)};
			}
		}
		return _color.toNumber(_color.translate(c));		
	};
	/**
	 * returns #c1c2c3
	 * @param {Object} c
	 */
	_color.toString = function(c){
		if(!c)return null;
		var r = Math.round(c.r).toString(16);
		if(r.length<2) r = '0' + r;
		if(r.length>2) r ='ff';
		
		var g = Math.round(c.g).toString(16);
		if(g.length<2)g = '0' + g;
		if(g.length>2) g ='ff';
		
		var b = Math.round(c.b).toString(16);
		if(b.length<2) b = '0' + b;
		if(b.length>2) b ='ff';
		
		return '#' + r + g + b;
	};
}

if (!_history){
	var _history = {
		current: '',
		doc: null, //ie
		cont: null //ie
	};
	_history.add = function(name, title){
		if(_history.current == name)return;
		
		name = (name+'').replace(/\?/g,'%3F');
		if(apt.browser.w3c){
			var t = document.title;
			if(title)document.title = title;
			location.hash = name;
			if(title)document.title = t;
		} else { //ie
			if(!_history.doc){
				_history.doc = document.createElement('<iframe src=javascript:false; style=display:none;>');
				document.body.appendChild(_history.doc);
				_history.cont = _history.doc.contentWindow;
			}
			var c = '<html><head><title>'+title+'</title></head><body onload="parent.document.location.hash=\''+name + '\';"></body></html>';
			var d = _history.cont.document;
			if(d){
				d.open();
				d.write(c);
				d.close();
			}
		}
		
		_history.current = _history.getHash();
	};
	_history.getHash = function(){
		var h = document.location.hash;
		h = h.replace(/%3F/g, '?').substr(1);
		return decodeURI(h)+'';
	};
	_history.check = function(){
		var h = _history.getHash();
		if(_history.current != h){
			_history.current = h;
			var e = _event.create$event();
			e.history = h;
			_event.dispatchBus('busHistory',e);
		}
		//if(!_smil.timer) setTimeout(_smil.run,5000);
		if(!_smil.timer) _smil.run();
	};
}

//Browser detect
if (document.all && !navigator.userAgent.match(/opera/i) && !navigator.appVersion.match(/mac/i)){
	apt.browser.ie = true;
	apt.browser.name = 'ie';
	apt.browser.version = parseFloat(navigator.appVersion.substr(navigator.appVersion.indexOf('MSIE ') + 5, 3));
} else {
	if (window.controllers && parseInt(navigator.productSub, 10) > 20031001){
		apt.browser.gecko = true;
		apt.browser.w3c = true;
		apt.browser.name = 'gecko';
		apt.browser.version = parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf('rv:') + 3, 4));
	}	else {
			if (navigator.userAgent.match(/opera/i) && typeof XSLTProcessor != 'undefined'){
				apt.browser.opera = true;
				apt.browser.w3c = true;
				apt.browser.name = 'opera';
				apt.browser.version = parseFloat(navigator.userAgent.substr(navigator.userAgent.indexOf(/opera[ \/]/i) + 7, 5));
			} else {
				if (navigator.userAgent.match(/applewebkit/i) && (parseInt(navigator.userAgent.substr(navigator.userAgent.search(/applewebkit\//i)+12, 3), 10) > 418)){
					apt.browser.webkit = true;
					apt.browser.w3c = true;
					apt.browser.name = 'webkit';
					apt.browser.version = parseFloat(navigator.userAgent.match(/applewebkit\/([0-9\.]+)/i)[1]);
					apt.browser.adobeAir = navigator.userAgent.match(/AdobeAir/)&& !!window.runtime;
				}
			}
	}
};

apt.engine.clearNode = function(node){
	var newNode = document.createElement('div');
	node.parentNode.insertBefore(newNode,node);
	node.parentNode.removeChild(node);
	return newNode;
};

apt.engine.loadElement = function(name, space){
	switch (space){
		case 'p':
			apt.stack.push('File-p: ' + name+'.xml');
			var fileName = apt.currentPath + name + '/' + name + '.xml';
			apt.filename = fileName;
			apt.filepath = apt.currentPath + name + '/';
			break;
		case 'skinSettings':
			apt.stack.push('File-skinSettings: ' + name+'.xml');
			var fileName = apt.bootPath + 'skinSettings' + '/' + name + '.xml';
			apt.filename = fileName;
			apt.filepath = apt.bootPath + 'skinSettings' + '/';
			break;
		default:
			apt.stack.push('File-o: ' + name+'.xml');
			var fileName = apt.bootPath + name + '/' + name + '.xml';
			apt.filename = fileName;
			apt.filepath = apt.bootPath + name + '/';
			break;
	}
	/*
	if (space=='p') {
		apt.stack.push('File-p: ' + name+'.xml');
		var fileName = apt.currentPath + name + '/' + name + '.xml';
		apt.filename = fileName;
		apt.filepath = apt.currentPath + name + '/';
	} else { //'o:'
		apt.stack.push('File-o: ' + name+'.xml');
		var fileName = apt.bootPath + name + '/' + name + '.xml';
		apt.filename = fileName;
		apt.filepath = apt.bootPath + name + '/';
	}
	*/
	var fileContent = apt.engine.loadXMLFile(fileName);
	var content, node;
	content = apt.browser.w3c ? fileContent.firstChild : fileContent.lastChild;
	apt.skinFile.push('');
	for (var i=0; i<content.childNodes.length; i++) {
		node = content.childNodes[i];
		if(node.nodeType == 1) {apt.engine.processNode(node);}
	}
	//skin file load
	var skinFile = apt.skinFile.pop();
	if (skinFile != ''){
		fileName = apt.bootPath + name + '/' + skinFile + '.xml';
		apt.filename = fileName;
		apt.filepath = apt.bootPath + name + '/';
		fileContent = apt.engine.loadXMLFile(fileName);
		content = apt.browser.w3c ? fileContent.firstChild : fileContent.lastChild;
		for (var i=0; i<content.childNodes.length; i++) {
			node = content.childNodes[i];
			if(node.nodeType == 1) {apt.engine.processNode(node);}
		}
	}
	apt.stack.pop();
};

//Load and execute a js file in boot directory
apt.engine.loadJSFile = function(name){
	var fileName = apt.bootPath + name + '.js';
	var rq = new XMLHttpRequest;
	var fileContent;
	rq.open('GET', fileName, false);
	rq.send(null);
	
	if (rq.readyState != 4){return;}
	
	if (rq.status == 200 || rq.status == 0) {
		fileContent =  rq.responseText;
	}else{
		alert ('error file: ' + fileName);
	}
	
	apt.core.execute(fileContent);
};

apt.engine.loadXMLFile = function(fileName) {
	var rq = new XMLHttpRequest;
	rq.open('GET', fileName, false);
	rq.send(null);
	
	if (rq.readyState != 4){return}
	
	if (rq.status == 200 || rq.status == 0) {
		
		if (apt.browser.w3c){
			return rq.responseXML;
		} else {
			return apt.engine.parser.parseFromString(rq.responseText,'text/xml');
		}
	}else{
		alert ('error file: ' + fileName);
	}
};

apt.engine.saveFile = function(fileName, data){
	var rq = new XMLHttpRequest;
	rq.open('POST', fileName, false);
	rq.send(data);
};

if (!window.DOMParser) {//ie
	DOMParser = new Function;
	DOMParser.prototype.baseURI = null;
	DOMParser.prototype.parseFromString = function(sObj, sType) {
		var pObj = new ActiveXObject('Microsoft.XMLDOM');
		try {
			pObj.setProperty('SelectionLanguage', 'XPath');
		} catch (result) {}
		
		//pObj.async = false;
		pObj.preserveWhiteSpace = true;
		//pObj.validateOnParse = false;
		//pObj.resolveExternals = false;
		
		pObj.loadXML(sObj);
		return pObj;
	};
};

if (!window.XMLHttpRequest){//ie
	XMLHttpRequest = function(){
		return new ActiveXObject('Microsoft.XMLHTTP');
	};
};

apt.engine.parser = new DOMParser();

apt.startSVG = function(){
	apt.xmlns += ' xmlns:s="'+apt.xmlnsSVG+'"';
	apt.viewportHtml.setAttribute('xmlns:s',apt.xmlnsSVG);
	apt.startSVG = function(){};  //function disable
};

apt.startVML = function(){
	var space = 'v';
	if (!apt.namespaces.join().match(space)) {
		apt.namespaces.push(space);
		document.namespaces.add(space, apt.xmlnsVML);
	}
	apt.xmlns += ' xmlns:v="'+apt.xmlnsVML+'"';
	apt.viewportHtml.setAttribute('xmlns:v',apt.xmlnsVML);
	//document.namespaces.add('v',apt.xmlnsVML);
	
	//document.createStyleSheet().addRule('v\\:*', 'behavior:url(#default#VML)');
	apt.startVML = function(){};  //function disable
};

apt.engine.processNamespaceApplication = function(node, parentResult) {
	var name = apt.core.localName(node);

	if (!apt.elements[name]){apt.engine.loadElement(name,'p');}
	apt.engine.processNamespaceExtern(node, parentResult);
};

apt.engine.processNamespaceExtern = function(node, parentResult) {
	var name = apt.core.localName(node);
	if (!apt.elements[name])apt.engine.loadElement(name);
	apt.content = node.childNodes;
	apt.aContent.push(apt.content);
	
	var template = apt.elements[name].template;
	var item, code, value;
	var load, loaded; //handle to execute
	
	if (template) {
		item = null; //get the principal node
		for(var i=0; i<template.childNodes.length; i++) {
			if (!item && template.childNodes[i].nodeType == 1) item = template.childNodes[i].cloneNode(true);
		}
		if (!item) {return;}
		var nodeResult;
		switch (item.namespaceURI){
			case apt.xmlnsSVG:
				nodeResult = document.createElementNS(apt.xmlnsSVG, item.nodeName);
				break;
			case apt.xmlnsVML:
				apt.startVML();
				nodeResult = document.createElement(item.nodeName);
				break;
			default:
				nodeResult = document.createElement(item.nodeName);
		}
		_current = nodeResult;
		//parentResult.appendChild(nodeResult); //in this point vml problems
		
		//apt Obj
		nodeResult.apt = new AptElement(nodeResult);
		
		//o: internal tree
		apt.aptStack.push(nodeResult);
		nodeResult.apt.childNodes = [];
		if (apt.aptStack.length > 1) {
			nodeResult.apt.parentNode = apt.aptStack[apt.aptStack.length-2];
			apt.aptStack[apt.aptStack.length-2].apt.childNodes.push(nodeResult);
			nodeResult.apt.indexNode = apt.aptStack[apt.aptStack.length-2].apt.childNodes.length -1;
		}
		
		//behaviors - load handlers
		var aBehaviors;
		if (apt.elements[name].behavior){
			aBehaviors = apt.elements[name].behavior.splitSpace();
			for(var i=0;i<aBehaviors.length;i++){
				for (var attr in apt.behaviors[aBehaviors[i]].handlers) {
					apt.core.nodeSetHandler(nodeResult, attr, apt.behaviors[aBehaviors[i]].handlers[attr]);
				}
			}
		}		
		
		//properties - load first for default values
		for(var attr in apt.elements[name].properties) {
			apt.element.name = attr;
			_value = apt.elements[name].properties[attr].defaultValue;
			apt.core.execute(apt.elements[name].properties[attr].code);
			apt.core.nodeSetProperty(nodeResult, attr, _value);
		}
		//methods
		for (var attr in apt.elements[name].methods) {
			apt.core.nodeSetMethod(nodeResult, attr, apt.elements[name].methods[attr]);
		}
		//attributes
		nodeResult.setAttribute('type',node.nodeName); //standard mark
		nodeResult.apt.tagName = node.nodeName;
		//if (node.getAttribute('name')) {apt.element.nameAttribute = node.getAttribute('name');}
		apt.element.label = null;
		if (node.getAttribute('label')) {apt.element.label = node.getAttribute('label');}
		apt.element.icon = null;
		if (node.getAttribute('icon')) {apt.element.icon = node.getAttribute('icon');}
		if (item.getAttribute('class')) {nodeResult.className = item.getAttribute('class');}
		_attribute.q = [];
		for (var attr in apt.elements[name].attributes) {
			apt.element.name = attr;
			_value = apt.elements[name].attributes[attr].defaultValue;
			value = node.getAttribute(attr);
			if (value){
				if (value.indexOf('^') == 0) {
					value = window.eval(value.substring(1, value.length));
				}
				_value = value;
				}
			if (_value != null){apt.core.execute(apt.elements[name].attributes[attr].code);}
			_attribute.q[attr] = _value;
		}
		
		//handlers
		for (var attr in apt.elements[name].handlers) {
			switch (attr) {
				case 'load':
					load = apt.elements[name].handlers[attr];
					break;
				case 'loaded':
					loaded = apt.elements[name].handlers[attr];
					break;
				case 'nodeDelete':  //dont register
					apt.core.nodeSetHandler(nodeResult, attr, apt.elements[name].handlers[attr],{register: false});
					break;
				default:
				apt.core.nodeSetHandler(nodeResult, attr, apt.elements[name].handlers[attr]);
			}
		}		
		parentResult.appendChild(nodeResult);
		//template
		if (load) {apt.core.execute(load);}
		apt.engine.processNodes(item.childNodes, nodeResult);
		_current = nodeResult;
		if (loaded) {apt.core.execute(loaded);}
		apt.aptStack.pop();
		apt.aContent.pop();
	}

};

apt.engine.processNamespaceIntern = function(nodeIn, parentResult){
	var node = nodeIn.cloneNode(true); //to avoid back propagation
	var name = apt.core.localName(node);
	var nodeResult;
	switch (name){
		case 'attribute':
			if (apt.element.type != 'element') return;
			var attr = node.getAttribute('name');
			apt.elements[apt.element.name].attributes[attr] = {
				defaultValue: null,
				code: null
				};
			apt.elements[apt.element.name].attributes[attr].defaultValue = node.getAttribute('default');
			if (node.firstChild) {apt.elements[apt.element.name].attributes[attr].code = node.firstChild.nodeValue;}
			break;
		case 'attributeInsert':
			var attr = node.getAttribute('name');
			parentResult.appendChild(document.createTextNode(_attribute.q[attr]));
			break;
		case 'attributeSet':
			var attr = node.getAttribute('name');
			parentResult.setAttribute(attr, _attribute.q[attr]);
			break;
		case 'attributeSetStyle':
			var attr = node.getAttribute('name');
			parentResult.style[attr] = _attribute.q[attr];
			break;
		case 'behavior':
			apt.element.type = 'behavior';
			var localName = node.getAttribute('name');
			apt.element.name = localName;
			apt.stack.push('behavior: ' + localName);
			apt.behaviors[localName] = {
				handlers: []
			};
			apt.engine.processNodes(node.childNodes);
			apt.stack.pop();
			break;
		case 'create':
			var target = node.getAttribute('target');
			if (target == null) {
				target = parentResult;
			} else {
				target = eval(target);
			}
			var mode = node.getAttribute('mode');
			if (mode == null){mode = 'replaceChildren';}
			apt.stack.push('create');
			
			switch (mode) {
				case 'appendChild':
					apt.engine.processNodes(node.childNodes, target);
					break;
				case 'replaceChildren':
					while (target.firstChild) {target.removeChild(target.lastChild);}
					apt.engine.processNodes(node.childNodes, target);
				break;
			}
	
			apt.stack.pop();
			break;
		case 'data':
			apt.dataInit();
			var dataName = node.getAttribute('name');
			var dataType = node.getAttribute('type');
			var dataIndex = node.getAttribute('index');
			var v;
			if (!apt.data[dataName]) {
				switch (dataType){
					case 'json':
						apt.data[dataName]= new AptDataJSON();
						if (apt.browser.w3c) {
							v = eval('(' + node.firstChild.textContent + ')');
						}else{
							v = eval('(' + node.firstChild.text + ')');
						};
						apt.data[dataName].val = v.val;
						break;
					default:
						//error
				}
			}
			apt.data[dataName].init({index: dataIndex});
			break;
		case 'element':
			apt.element.type = 'element';
			apt.element.name = node.getAttribute('name');
			var localName = apt.element.name;
			apt.stack.push(localName);
			if(apt.elements[apt.element.name]){apt.core.error(0,'Elemento previamente definido: '+ apt.element.name, 'node');}
			apt.elements[apt.element.name] = {
				template: null,
				attributes:[] ,
				properties:[],
				methods:[],
				handlers:[],
				behavior: ''
			};
				
			//extends
			var attr = node.getAttribute('extends');
			var aExtends, extend;
			if (attr) {
				aExtends = attr.split(' ');
				for(var i=0; i<aExtends.length; i++) {
					extend = aExtends[i].substring(aExtends[i].indexOf(':')+1,aExtends[i].length);
					if (!apt.elements[extend]) {apt.engine.loadElement(extend);}
					
					for (var it in apt.elements[extend].attributes) {
						apt.elements[localName].attributes[it] = apt.elements[extend].attributes[it];
					}
					for (var it in apt.elements[extend].properties) {
						apt.elements[localName].properties[it] = apt.elements[extend].properties[it];
					}
					for (var it in apt.elements[extend].methods) {
						apt.elements[localName].methods[it] = apt.elements[extend].methods[it];
					}
					for (var it in apt.elements[extend].handlers) {
						apt.elements[localName].handlers[it] = apt.elements[extend].handlers[it];
					}
					if(apt.elements[extend].behavior) apt.elements[localName].behavior += apt.elements[extend].behavior;	
				}
			}
			apt.element.name = localName;
			apt.element.type = 'element';
			
			var attr = node.getAttribute('behavior');
			if (attr)apt.elements[localName].behavior += attr; //TODO soportar + para conservar behavior anterior (?)
			
			//behaviors - load
            var aBehaviors;
            if (apt.elements[localName].behavior != '') {
                aBehaviors = apt.elements[localName].behavior.splitSpace();
                for (var i = 0; i < aBehaviors.length; i++) {
                    if (!apt.behaviors[aBehaviors[i]]) {
                        apt.engine.loadElement(aBehaviors[i]);
                    }
                }
            }
			apt.element.name = localName;
			apt.element.type = 'element';
				
			//load explict element components
			apt.engine.processNodes(node.childNodes);
			apt.stack.pop();
			break;
		case 'execute':
			if(node.getAttribute('src')){apt.debugFiles[apt.debugFiles.length] = apt.core.absoluteToRelativePath(apt.currentPath, apt.filepath)+ node.getAttribute('src');}
			try {
				if (apt.browser.w3c) {window.eval(node.firstChild.textContent)}
					else {window.execScript(node.firstChild.text)};
			} catch (desc){
				apt.core.error(0,'Error de Script', 'script', desc);
			}
			break;
		case 'expand':
			apt.element.type = 'element';
			apt.element.name = node.getAttribute('name');
			var localName = apt.element.name;
			apt.stack.push('expand: '+localName);
			apt.engine.processNodes(node.childNodes);
			apt.stack.pop();
			break;
		case 'handler':
			var attr = node.getAttribute('name');
			apt.stack.push(attr);
			if(apt.element.type == 'element'){
				apt.elements[apt.element.name].handlers[attr] = null;
				if (node.firstChild) {apt.elements[apt.element.name].handlers[attr] = node.firstChild.nodeValue;}
			}else{
				apt.behaviors[apt.element.name].handlers[attr] = null;
				if (node.firstChild) {apt.behaviors[apt.element.name].handlers[attr] = node.firstChild.nodeValue;}
			}
			apt.stack.pop();
			break;
		case 'icon':
			if (apt.element.icon) {
				var icon = apt.icons[apt.element.icon];
				if(icon) {
					icon = apt.iconPath + icon;
				} else {
					icon = apt.iconPath + apt.element.icon;
				}
				parentResult.style.backgroundImage = 'url(' + icon + ')';
				parentResult.className += ' apt-icon';
				apt.element.icon = null;
			}
			break;
		case 'if':
			var test = node.getAttribute('test');
			apt.aptIf = window.eval(test);
			apt.engine.processNodes(node.childNodes, parentResult);
			break;
		case 'ifthen':
			if (apt.aptIf) {
				apt.engine.processNodes(node.childNodes, parentResult);
			}
			break;
		case 'ifelse':
			if (!apt.aptIf) {
				apt.engine.processNodes(node.childNodes, parentResult);
			}
			break;
		case 'while':
			var test = node.getAttribute('test');
			while (window.eval(test)){
				apt.engine.processNodes(node.childNodes, parentResult);
			}
			break;
		case 'include':
			var href = node.getAttribute('href');
			apt.stack.push('include: ' + href);
			var content = apt.engine.loadXMLFile(href);
			nodeResult = apt.browser.w3c ? content.firstChild : content.lastChild;
			//apt.engine.processNode(nodeResult, parentResult);
			apt.engine.processNodes(nodeResult.childNodes, parentResult);
			apt.stack.pop();
			break;
		case 'label':
			if (apt.element.label) {
				var item = apt.core.createElementFromString('<label>'+apt.element.label+'</label>');
				var attr = node.getAttribute('class');
				if(attr) item.className = attr;
				parentResult.appendChild(item);
				parentResult.className += ' apt-label';
				apt.element.label = null;
			}
			break;
		case 'load':
			var href = node.getAttribute('href');
			apt.stack.push('load: ' + href);
			var target = node.getAttribute('target');
			if (target == null) {
				target = parentResult;
			} else {
				target = eval(target);
			}
			var mode = node.getAttribute('mode');
			apt.core.load(target, href, mode);
			apt.stack.pop();
			break;
		case 'method':
			if(apt.element.type != 'element')return;
			var attr = node.getAttribute('name');
			apt.stack.push(attr);
			apt.elements[apt.element.name].methods[attr] = {};
			apt.elements[apt.element.name].methods[attr].code = null;
			apt.elements[apt.element.name].methods[attr].args = null;
			
			if (node.firstChild) {apt.elements[apt.element.name].methods[attr].code = node.firstChild.nodeValue;}
			apt.elements[apt.element.name].methods[attr].args = node.getAttribute('arguments');
			
			apt.stack.pop();
			break;
		case 'modify':
			var target = node.getAttribute('target');
			var attrs = node.attributes;
			var attr;
			for (var i=0; i<attrs.length; i++) {
				attr = attrs[i];
				if (attr.nodeName != 'target') {
					destination = eval(target);
					if (destination['modify-'+attr.nodeName]) {
						destination['modify-'+attr.nodeName](attr.nodeValue);
					}
				}
			}
			break;
		case 'property':
			if(apt.element.type != 'element')return;
			var attr = node.getAttribute('name');
			apt.stack.push(attr);
			apt.elements[apt.element.name].properties[attr] = {
				defaultValue: null,
				code: null
			};
			apt.elements[apt.element.name].properties[attr].defaultValue = node.getAttribute('default');
			if (node.firstChild) {apt.elements[apt.element.name].properties[attr].code = node.firstChild.nodeValue;}
			apt.stack.pop();
			break;
		case 'skin':
			var attr = node.getAttribute('name');
			if (attr == apt.skin) {
				apt.skinFile[apt.skinFile.length - 1] = node.getAttribute('src');
			}
			break;
		case 'style':
			//replace in value url(file) with url(path+file)
			var imagePath = node.getAttribute('imagePath');
			if(!imagePath){imagePath = apt.filepath;}
			var value = node.firstChild.nodeValue;
			//firefox limit 4096 in node size
			for(var i=1; i<node.childNodes.length;i++){
				value = value + node.childNodes[i].nodeValue;
			}
			var aValue = value.match(/url *\([^\)]+\)/gi);
			var frag;
			if (aValue) {
				for(var i=0; i<aValue.length;i++) {
					frag = aValue[i].match(/(url *\([\'\"]?)([^\)]+)([\'\"]?\))/i);
					if (frag[2].match('#')) {
						//exception like url(#default#VML)
					} else {
						value = value.replace(frag[0], frag[1] + imagePath + frag[2]+frag[3]);
					}
				}
			}
			//Create style node
			var item = apt.core.createStyleSheet(value);
			//name
			var newSheetName = node.getAttribute('name');
			if(newSheetName){item.setAttribute("Name",newSheetName);}

			var styleSheets = [];
			var nameStyleSheets = [];
			var sheet;
			var styleReplaced = false;
			for (var i=0; i<apt.viewportHead.childNodes.length; i++){
				if (apt.viewportHead.childNodes[i].tagName == 'STYLE') {
					sheet = apt.viewportHead.childNodes[i];
					if (newSheetName && sheet.getAttribute('name')== newSheetName){
						//replace
						apt.viewportHead.replaceChild(item,sheet);
						styleReplaced = true;
						sheet = item;
					}
					styleSheets[styleSheets.length] = sheet;
					if (sheet.getAttribute('name')) nameStyleSheets[sheet.getAttribute('name')]= sheet;
				}
			}
			
			if(styleSheets.length==0)styleSheets[0]=apt.viewportHead.firstChild;
			if (!styleReplaced) { //insert new sheet
				var attr = node.getAttribute('default');
                var def = [];
                def[0] = '';
                if (attr) def = attr.splitSpace();

                switch (def[0]) {
                    case 'true': //first
                        apt.viewportHead.insertBefore(item, styleSheets[0]);
                        break;
                    case 'after': //default="after name"
						if (nameStyleSheets[def[1]]) {
							sheet = nameStyleSheets[def[1]].nextSibling;
							if(!sheet){sheet = styleSheets[styleSheets.length-1]}
						}else{
							//Not such sheet - insert as firts
							sheet = apt.viewportHead.firstChild;
						}
						apt.viewportHead.insertBefore(item,sheet);
                        break;
					case 'before': //default="before name"
						if (nameStyleSheets[def[1]]) {
							sheet = nameStyleSheets[def[1]];
						}else{
							//Not such sheet
							sheet = apt.viewportHead.firstChild;
						}
						apt.viewportHead.insertBefore(item,sheet);
						break;
					case 'append': //default="append name" Dont use attribute name
						if(nameStyleSheets[def[1]]){
							sheet = nameStyleSheets[def[1]];
							//var sheetContent = sheet.firstChild.nodeValue; //onfly ff
							var sheetContent = sheet.innerHTML;
							sheetContent += value;
							item = apt.core.createStyleSheet(sheetContent);
							item.setAttribute("Name",def[1]);
							apt.viewportHead.replaceChild(item,sheet);
						}else {
							//error no name
							apt.viewportHead.appendChild(item);
						}
						break;
                    default:
                        apt.viewportHead.appendChild(item);
                        break;
                }
			}
			break;
		case 'template':
			if(apt.element.type != 'element')return;
			apt.elements[apt.element.name].template = node;
			break;
		/*
		case 'variable':
			apt.variableInit();
			var varName = node.getAttribute('name');
			var varType = node.getAttribute('type');
			var v;
			if (!apt.vars[varName]) {
				switch (varType){
					case 'xml':
						apt.vars[varName]= new AptVariableXML();
						apt.core.clearNodes(node);
						apt.vars[varName].data = node.childNodes[0].cloneNode(true); //TODO add values
						break;
					case 'json':
						apt.vars[varName]= new AptVariableJSON();
						if (apt.browser.w3c) {
							v = eval('(' + node.firstChild.textContent + ')');
						}else{
							v = eval('(' + node.firstChild.text + ')');
						};
						apt.vars[varName].data = v.data;
						break;
					default:
						//error
				}
			}
			apt.vars[varName].init();
			break;
		*/
		case 'content':
			for(var i=apt.aContent.length-1; i>=0; i--){ //En templates que incluyen componentes (o:..) revisar el a:content
				apt.content = apt.aContent[i];
				if (apt.content[0] && apt.content[0].nodeName != 'a:content')break;
				
			}
			var item;
			var childsBefore = node.childNodes.length;
			while (apt.content.length > 0) {
				node.appendChild(apt.content[0]);
			}
			
			for (var i=childsBefore; i<node.childNodes.length; i++) {
				item = node.childNodes[i];
				switch (item.nodeType) {
					case 1:
						apt.engine.processNode(item, parentResult);
						break;
					case 3: //text
						parentResult.appendChild(document.createTextNode(item.nodeValue));
						break;
				}
			}
			break;
	}
};

apt.engine.processNamespaceSVG = function(nodeSource, parentResult){
	//parentResult.appendChild(nodeSource); //simple version

	var attrs = nodeSource.attributes;
	var attr, loaded;
    var nodeResult = document.createElementNS(apt.xmlnsSVG, nodeSource.nodeName);
    _current = nodeResult;          

	if (parentResult) {
		parentResult.appendChild(nodeResult);
	}
	else {
		apt.core.error(0, 'Nodo fuera de contexto SVG', 'node');
	}
	var loaded = null;
	for (var i = 0; i < attrs.length; i++) {
		attr = attrs[i];
		if(attr.nodeValue.indexOf('^')== 0) attr.nodeValue = window.eval(attr.nodeValue.substring(1,attr.nodeValue.length));
		switch (attr.nodeName) {
			case 'apt':
				nodeResult.apt = new AptElement(nodeResult);
				nodeResult.apt.parentNode = apt.aptStack[apt.aptStack.length - 1];
				break;
			case 'onload':
				try {
					window.eval(attr.nodeValue); //both ie and gecko
				} 
				catch (desc) {
					apt.core.error(0, 'Error de Script', 'script', desc)
				}
				break;
			case 'onloaded':
				loaded = attr.nodeValue;
				break;
			case 'class':
				nodeResult.className = attr.nodeValue;
				break;
			default:
				if (attr.nodeName.indexOf('on') == 0) {
					apt.core.nodeSetHandler(nodeResult, attr.nodeName.substring(2, attr.nodeName.length), attr.nodeValue);
				} else {
					nodeResult.setAttribute(attr.nodeName, attr.nodeValue);
				}	
		}
	}
	
	for (var i = 0; i < nodeSource.childNodes.length; i++) {
		var node = nodeSource.childNodes[i];
		if (node.namespaceURI && node.namespaceURI != 'http://www.w3.org/1999/xhtml') {
			apt.engine.processNode(node, nodeResult);
		}
		else {
			if (node.childNodes.length == 0) { //final node
				if (node.nodeType == 3) {
					nodeResult.appendChild(document.createTextNode(node.nodeValue));
				}
				else {//support empty tag
					apt.engine.processNode(node, nodeResult);
				}
			}
			else {
				apt.engine.processNode(node, nodeResult);
			}
		}
	}
	if (loaded) {
		apt.core.execute(loaded);
	}
};
apt.engine.processNamespaceVML = function(node, parentResult){
	var space = node.prefix;
	var name = node.namespaceURI;
	if (!apt.namespaces.join().match(space)) {
		apt.namespaces.push(space);
		document.namespaces.add(space, name);
	}
	
	nodeResult = document.createElement(node.tagName);
	var attrs = node.attributes;
	var attr;
	for(var i=0; i<attrs.length; i++) {
		attr = attrs[i];
		switch(attr.nodeName){
			case 'class':
				nodeResult.className = attr.nodeValue;
				break;
			case 'height':
				nodeResult.style.height = attr.nodeValue;
				break;
			case 'style':
				nodeResult.style.cssText = attr.nodeValue;
				break;
			case 'visibility':
				nodeResult.style.visibility = attr.nodeValue;
				break;
			case 'width':
				nodeResult.style.width = attr.nodeValue;
				break;			
			default:
				nodeResult.setAttribute(attr.nodeName, attr.nodeValue);
				break;
		}
	}
	parentResult.appendChild(nodeResult);
	apt.engine.processNodes(node.childNodes,nodeResult);
};

apt.engine.processNamespaceOther = function(node, parentResult){
	var space = node.prefix;
	var name = node.namespaceURI;
	if (!apt.namespaces.join().match(space)) {
		apt.namespaces.push(space);
		document.namespaces.add(space, name);
	}
	var nodeResult = document.createElement(node.tagName);
	parentResult.appendChild(nodeResult);
	
	//copy attributes
	var attrs = node.attributes;
	var attr;
	for(var i=0; i<attrs.length; i++) {
		attr = attrs[i];
		if(attr.nodeValue.indexOf('^')== 0) attr.nodeValue = window.eval(attr.nodeValue.substring(1,attr.nodeValue.length));
		if (attr.nodeName == 'style') {
			nodeResult.style.cssText = attr.nodeValue
		} else {
			nodeResult.setAttribute(attr.nodeName, attr.nodeValue);
		}
	}
	apt.engine.processNodes(node.childNodes,nodeResult);
	//not supported yet descendents - creo que si
};

//nodes array of node
apt.engine.processNodes = function(nodes, parentResult){
	var item;
	for (var i=0; i<nodes.length;i++) {
		item = nodes[i];
		if (item.nodeType == 1){apt.engine.processNode(item, parentResult);}
	}
};

apt.engine.processNode = function(nodeSource, parentResult){
	if(nodeSource.nodeType == 8)return; //comment
    var nodeResult, attrs, attr;
    var loaded;
    apt.stack.push(nodeSource.nodeName);
    
    //if (nodeSource.prefix){
    if (nodeSource.namespaceURI && nodeSource.namespaceURI != 'http://www.w3.org/1999/xhtml') {
        switch (nodeSource.namespaceURI) {
            case apt.xmlnsIntern:
                apt.engine.processNamespaceIntern(nodeSource, parentResult);
                break;
            case apt.xmlnsExtern:
                apt.engine.processNamespaceExtern(nodeSource, parentResult);
                break;
            case apt.xmlnsApplication:
                apt.engine.processNamespaceApplication(nodeSource, parentResult);
                break;
			case apt.xmlnsSVG:
                apt.engine.processNamespaceSVG(nodeSource, parentResult);
				break;
			case apt.xmlnsVML:
                apt.engine.processNamespaceVML(nodeSource, parentResult);
				break;
            default: //copy
                apt.engine.processNamespaceOther(nodeSource, parentResult);
                break;
        }
    }
    else {
        switch (nodeSource.tagName) {
            case 'script':			
				if (nodeSource.type == 'text/javascript') {apt.core.insertScript(nodeSource.src);}
                break;
            default:
                attrs = nodeSource.attributes;
                nodeResult = document.createElement(nodeSource.nodeName);
                _current = nodeResult;
                
                if (parentResult) {
                    parentResult.appendChild(nodeResult);
                }
                else {
                    apt.core.error(0, 'Nodo fuera de contexto', 'node');
                }
                loaded = null;
                for (var i = 0; i < attrs.length; i++) {
                    attr = attrs[i];
					if(attr.nodeValue.indexOf('^')== 0) attr.nodeValue = window.eval(attr.nodeValue.substring(1,attr.nodeValue.length));
                    switch (attr.nodeName) {
						case 'apt':
							nodeResult.apt = new AptElement(nodeResult);
							nodeResult.apt.parentNode = apt.aptStack[apt.aptStack.length-1];
							break;
                        case 'onload':
                            try {
                                window.eval(attr.nodeValue); //both ie and gecko
                            } 
                            catch (desc) {
                                apt.core.error(0, 'Error de Script', 'script', desc)
                            }
                            break;
                        case 'onloaded':
                            loaded = attr.nodeValue;
                            break;
                        case 'class':
                            nodeResult.className = attr.nodeValue;
                            break;
                        default:
                            if (apt.browser.w3c) {
                                if (attr.nodeName.indexOf('on') == 0) {
									apt.core.nodeSetHandler(nodeResult, attr.nodeName.substring(2, attr.nodeName.length), attr.nodeValue);
                                }
                                else {
                                    nodeResult.setAttribute(attr.nodeName, attr.nodeValue);
                                }
                            }
                            else { //ie
                                if (attr.nodeName == 'style') {
                                    nodeResult.style.cssText = attr.nodeValue;
                                }
                                else {
                                    if (attr.nodeName.indexOf('on') == 0) {
                                        apt.core.nodeSetHandler(nodeResult, attr.nodeName.substring(2, attr.nodeName.length), attr.nodeValue);
                                    }
                                    else {//default
                                        nodeResult.setAttribute(attr.nodeName, attr.nodeValue);
                                    }
                                }
                            }
                    }
                }
                
                for (var i = 0; i < nodeSource.childNodes.length; i++) {
                    var node = nodeSource.childNodes[i];
                    if (node.namespaceURI && node.namespaceURI != 'http://www.w3.org/1999/xhtml') {
                        apt.engine.processNode(node, nodeResult);
                    } else {
                        if (node.childNodes.length == 0) { //final node
                            if (node.nodeType == 3) {
                                nodeResult.appendChild(document.createTextNode(node.nodeValue));
                            }
                            else {//support empty tag
                                apt.engine.processNode(node, nodeResult);
                            }
                        }
                        else {
                            apt.engine.processNode(node, nodeResult);
                        }
                    }
                }
                if (loaded) {apt.core.execute(loaded);}
                break;
        }
    }
    apt.stack.pop();
};

//process scripts in first document level
apt.engine.processScript = function(script,node) {
	var process, item, tag;	
	var nodeResult;
	
	process = apt.browser.w3c ? script.firstChild.childNodes : script.lastChild.childNodes;
	apt.nodeReplace = apt.engine.clearNode(node); //convert to empty div
	_current = apt.nodeReplace;
	
	if(node.getAttribute('skin'))apt.skin = node.getAttribute('skin');
	apt.engine.loadElement(apt.skin,'skinSettings');
	
	for (var i=0; i<process.length;i++) {
		item = process[i];
		switch (item.nodeType){
			case 1:
				if (item.prefix){
					switch (item.namespaceURI){
					case apt.xmlnsIntern:
						apt.engine.processNamespaceIntern(item, apt.nodeReplace);
						break;
					case apt.xmlnsExtern:
						apt.engine.processNamespaceExtern(item, apt.nodeReplace);
						break;
					case apt.xmlnsApplication:
						apt.engine.processNamespaceApplication(item, apt.nodeReplace);
						break;
					default: //copy
						apt.engine.processNamespaceOther(item, apt.nodeReplace);
						break;
					}
				}else{ //html elements
					apt.engine.processNode(item, apt.nodeReplace);
				}
				break;
		}
	}
};
/**
 * Create nodes manually, from string s to be append to parentResult
 * @param {String} s
 * @param {Node} parentResult
 */
apt.engine.processNodeFromString = function(s, parentResult){
	var r = {text: s};
	var node = apt.engine.transformToXMLNodes(r);
	
	if(apt.browser.w3c){
		apt.engine.processNode(node.firstChild.firstChild, parentResult);
	} else {
		apt.engine.processNode(node.lastChild.firstChild, parentResult);
	}
};

//calculate screen dimensions
apt.engine.setup = function() {
	apt.viewport = apt.browser.quirks ? document.body : document.documentElement;
	 if (apt.browser.w3c) {
	 	apt.viewportBox.width = window.innerWidth;
	 	apt.viewportBox.height = window.innerHeight;
	 } else {
	 	/*
	 	apt.viewportBox.width = document.documentElement.clientWidth;
	 	apt.viewportBox.height = document.documentElement.clientHeight;
		
		if(apt.viewportBox.width == 0){//ie quirks mode
			apt.viewportBox.width = document.body.clientWidth;
			apt.viewportBox.height = document.body.clientHeight;
		}
		*/
	 	apt.viewportBox.width = apt.viewport.clientWidth;
	 	apt.viewportBox.height = apt.viewport.clientHeight;
	 }
	 //apt.viewportBody = apt.viewport.childNodes[1];
	 //apt.viewportBody = apt.viewportHead.nextSibling;
	 for(var i=0;i<apt.viewportHtml.childNodes.length;i++){if(apt.viewportHtml.childNodes[i].tagName=='BODY')apt.viewportBody = apt.viewportHtml.childNodes[i];} //firebug bug
	//apt.viewportBody = document.getElementsByTagName('body')[0];
};

apt.engine.start = function() {
	apt.engine.setup();
	apt.core.nodeSetHandler(window, 'resize','apt.engine.setup()');
	
	//currentPath
	//var path = location.href;
	var path = location.href.split('#')[0]; //clear hash
	//var pathFind = path.match(/(\/\w+.html)/gi);
	var pathFind = path.match(/(\/\w*[-]*\w*\.html)/gi);
	apt.currentPath = path.replace(pathFind, '/');
	//domain
	var aDomain = path.split('/');
	if(aDomain[0]=='file:'){
		apt.domain = 'file';
	}else{
		apt.domain = aDomain[2];
	}
	
	var aScripts = document.getElementsByTagName('script');
	var script;
	
	var i = 0;
	while (i<aScripts.length)	{
	//for (var i=0; i<aScripts.length;i++){
		script = aScripts[i];
		switch (script.type) {
			case 'text/javascript':
				var src = script.src;
				if (src.match('boot.js')){
					apt.bootPath = src.replace('boot.js',"");					
					apt.iconPath = apt.bootPath + 'icons/';
				} else {
					if (src.match('boot-compact.js')){
					apt.bootPath = src.replace('boot-compact.js',"");				
					apt.iconPath = apt.bootPath + 'icons/';
					}
				}
				i++;
				break;
				
			case 'text/aptivo+xml':
				apt.engine.processScript(apt.engine.transformToXMLNodes(script),script);
				//one element minus in aScripts, no i++
				break
		}		
	}
	
	//addClass apt.browser.name
	var className = document.body.className;
	className += ' '+ apt.browser.name;
	document.body.className = className.trim();
	
	
	if (apt.console){apt.console.addMessage(2, new Date(), 'Final','Final de la carga');}
	if(apt.debug){
		for (var i=0; i < apt.debugFiles.length; i++){
			apt.core.insertScript(apt.debugFiles[i]);
			if (apt.console){apt.console.addMessage(2, new Date(), 'Debug',apt.debugFiles[i]);}
		}
	}
	_event.dispatchBus('busInit',_event.create$event());
	_history.check();
};
apt.engine.transformToXMLNodes = function(sCode) {
	var oCode=['<?xml version="1.0" ?>'];
	
	oCode.push('<item ' + apt.xmlns + '>');
	oCode.push(sCode.text);
	oCode.push('</item>');
	
	oCode = oCode.join('');
	oCode = apt.engine.parser.parseFromString(oCode,'text/xml');
	if (oCode.firstChild.tagName=='parsererror') {
		apt.core.error(0, oCode.firstChild.textContent, 'xmlParsing');
	}
	
	return oCode;
};

/*
 * core
 */

/**
 * Converts newPath to relative to basePath
 * @param {String} basePath
 * @param {String} newPath
 * @return {String} relativePath
 */
apt.core.absoluteToRelativePath = function(basePath, newPath){
	var relativePath = '';
	var same = true;
	var i= 0;
	var aBasePath = basePath.split('/');
	var aNewPath = newPath.split('/');
	
	while (same){
		if (aBasePath[i] == aNewPath[i]){
			i++;
			if(!(i<aBasePath.length && i<aNewPath.length))same = false; //exit
		}else{
			same = false;
		}
	}
	for(var j=i; j<aBasePath.length-1; j++){relativePath += '../';}
	for(var j=i; j<aNewPath.length-1; j++){relativePath += aNewPath[j]+ '/';}
	
	return relativePath;
};
/**
 * add two paths. basePath Asolute. path relative, supports ..
 * @param {String} basePath
 * @param {String} path
 * @return {String} newPath
 */
apt.core.addPath = function(basePath, path){
	var b = basePath.split('/');
	var p = path.split('/');
	if(b[b.length-1]=='')b.pop();
	if(p[p.length-1]=='')p.pop();
	
	for(var i=0; i<p.length; i++){
		if(p[i]== '..'){
			b.pop();
		} else {
			b.push(p[i]);
		}
	}
	return b.join('/')+'/';
};

/**
 * Prune nodes no type 1 or leafs. The text remains only in the leafs.
 * @param {Node} node
 */
apt.core.clearNodes = function(node){
	if (node.childNodes.length == 0) return;
	var i = 0;
	while(i<node.childNodes.length) {
		if (node.childNodes[i].nodeType == 1) {
			apt.core.clearNodes(node.childNodes[i]);
			i++;
		} else {
			if(node.childNodes.length>1){
				node.removeChild(node.childNodes[i]);
			} else {
				i++;
			}
		}
	}
};

/**
 * Returns a node to be inserted in the DOM. Ej. element='&lt;DIV&gt;nuevo nodo&lt;/DIV&gt;'
 * @param {String} element. 
 * @return {Node}
 */
apt.core.createElementFromString = function(element) {
 	if (!element) {return null}
 	var nodeResult = apt.core.spanNode.cloneNode(false);
 	nodeResult.innerHTML = '&nbsp;' + element;
 	
 	nodeResult = nodeResult.childNodes[1];
 	nodeResult = nodeResult.parentNode.removeChild(nodeResult);
 	
 	return nodeResult; 
};

/**
 * Disable text selection in the node
 * @param {Node} node
 */
apt.core.disableUserSelect = function (node) {
	node.style.MozUserSelect = 'none';
	node.onselectstart = apt.core.ffalse;
	node.ondragstart = apt.core.ffalse;
};

/**
 * Enable text selection in the node. Default behavior.
 * @param {Node} node
 */
apt.core.enableUserSelect = function (node) {
	node.style.MozUserSelect = '';
	node.onselectstart = null;
	node.ondragstart = null
};

/**
 * Send a message to the console. Level: 0=Error, 1=Warning, 2=LOg. Type: node, script, xmlParsing
 * @param {String} level
 * @param {String} message
 * @param {String} type
 * @param {Object} desc
 */

apt.core.error = function(level, message, type, desc) {
	var extended;
	if (apt.console){
		switch (type) {
			case 'node':
				extended = apt.stack.join(' | ');
				break;
			case 'script':
				if(apt.browser.webkit){
					extended = desc;
				} else {
					extended = desc.message + desc.stack.split('@')[1];
				}
				break;
			case 'xmlPargising':
				extended = message;
				message = 'XML Parsing Error';
				break;
		}
		apt.console.addMessage(level, new Date(), message, extended);
	} else {
		alert('ERROR: ' + message);
	}
};

/**
 * Execute the js code
 * @param {String} code
 */
apt.core.execute = function(code) {
	if (!code || code == '') {return}
	try {
		if (apt.browser.w3c) {
			return window.eval(code);
		} else {
			return window.execScript(code);
		}
	} catch (desc){
		apt.core.error(0,'Error de Script', 'script', desc)
	}
	return false;
};

/**
 * Returns false. For use in handlers.
 */
apt.core.ffalse = function(){
	return false;
};

/**
 * Returs true. For use in handlers.
 */
apt.core.ftrue = function () {
	return true;
};

/**
 * a:load command. Loads the destination node with the file hred in mode: appendChild o replaceChildren.
 * @param {Node} destination
 * @param {String} href
 * @param {String} mode
 */
apt.core.load = function(destination, href, mode){
	var content = apt.engine.loadXMLFile(href);
	var	nodeResult = apt.browser.w3c ? content.firstChild : content.lastChild;
	if (mode == null){mode = 'replaceChildren';}
	
	switch (mode) {
		case 'appendChild':
			apt.engine.processNodes(nodeResult.childNodes, destination);
		break;
		case 'replaceChildren':
			while (destination.firstChild) {destination.removeChild(destination.lastChild);}
			apt.engine.processNodes(nodeResult.childNodes, destination);
		break;
	}
};

/**
 * Returns the node that trigger the event
 * @param {Object} node
 * @param {Object} eventName
 * @return {Object} nodeEventFirer
 */
apt.core.nodeGetEventFirer = function(node, eventName){
	if(!node)return null;
    var item = node;
    while (item != apt.viewport) {
        if (!item.handlers) {
			item = item.parentNode;
			if (item == null)return null;
		}
		else {
			if (!item.handlers.events[eventName]) {
				item = item.parentNode;
			} else {
				return item;
			}
		}
    }
    return null;
};

/**
 * True if node has a child in html space
 * @param {Node} parent
 * @param {Node} child
 * @return {Boolean}
 */
apt.core.nodeIsAChildOf = function(parent, child) {
	if (parent === child) {return false;}
	var item = child;
	while (item && item !== parent) {
		item = item.parentNode;
	}
	return item === parent;
};

/**
 * Inserts a handler in the node
 * @param {Node} node
 * @param {String} name //if starts with bus dont register
 * @param {String} value - function
 * @param {Object} param - multiparameters
 */
apt.core.nodeSetHandler = function(node, name, value, param){
	if(param == null){param = {};}
	if(param.enable == null) param.enable = true;
	if(param.register == null)param.register = true; //register the event
	if(name.indexOf('bus')==0){
		param.register = false;
		_event.observeBus(name, node);
	}
	
    if (!node.handlers) {node.handlers = new AptHandlers();}
    if (!node.handlers.events[name]) {
        node.handlers.events[name] = {};
		node.handlers.events[name].enable = param.enable;
		node.handlers.events[name].skip = 0; //number events to skip
		node.handlers.events[name].code = [];
		node.handlers.events[name].observers = []; //after procesing send event
		node.handlers.events[name].redirect = []; //where to process the event
		
		//set node handler only the first time
		var fn = function(evt){apt.core.handler(evt, fn.node);};
		fn.node = node;
		if (param.register) {
			if (apt.browser.w3c) {
				switch (name) {
					case 'mouseenter':
						node.addEventListener('mouseover', fn, false);
						break;
					case 'mouseleave':
						node.addEventListener('mouseout', fn, false);
						break;
					default:
						node.addEventListener(name, fn, false);
				}
			}
			else {
				node.attachEvent('on' + name, fn);
			}
		}
    }
    var code = node.handlers.events[name].code;
    code[code.length] = new Function('$event', value);
};

/**
 * Set the node position relative to the reference node. Mode-> at-pos: leftPos and topPos. 
 * lAfterItem-tSameParent: left after the reference, top same as reference.
 * lSameItem-tAfterItem: left same as reference, top below the reference
 * @param {Node} node
 * @param {Node} reference
 * @param {String} mode
 * @param {Number} leftPos
 * @param {Number} topPos
 */
apt.core.position = function (node, reference, mode, leftPos, topPos) {
	var left, top;
	var nodePos, refPos, refParentPos;
	
	switch (mode) {
		case 'at-pos':
			left = 0; //see the final
			top = 0;
			break;
		case 'lAfterItem-tSameParent':
			nodePos = node.apt.getElementLayout();
			refPos = reference.apt.getElementLayout();
			var refParent = reference.apt.parentNode;
			var dh = 0; //top distance parent and first child
			if (refParent){
				refParentPos = refParent.apt.getElementLayout();
				var refParentChildPos = refParent.apt.childNodes[0].apt.getElementLayout();
				dh = refParentChildPos.y - refParentPos.y;
			}
			left = refPos.x + refPos.w;
			top = refPos.y - dh;
			break;
		case 'lSameItem-tAfterItem':
			refPos = reference.apt.getElementLayout();
			left = refPos.x;
			top = refPos.y + refPos.h;
			break;		
	}

	if (leftPos) {left += leftPos}
	if (topPos) {top += topPos}
	
	node.style.left = left + 'px';
	node.style.top = top + 'px';	
};

apt.core.spanNode = document.createElement('span');

/**
 * Inserts a method in the node
 * @param {Node} node
 * @param {String} name
 * @param {String} value
 */
apt.core.nodeSetMethod = function(node, name, value) {
	if(value.args) {
		node[name] = new Function(value.args, value.code);
	} else
	{
		node[name] = new Function(value.code);
	}
};

/**
 * Inserts a property in the node
 * @param {Node} node
 * @param {String} name
 * @param {String} value
 */
apt.core.nodeSetProperty = function(node, name, value) {
	node[name] = value;
};

/**
 * Return the scrollBar width in pixels. After first use apt.scrollBarWidth has this value.
 * @return {Number}
 */
apt.core.getScrollBarWidth = function(){
	if(!apt.scrollBarWidth){
		var node = document.createElement('div');
		node.style.position = 'absolute';
		node.style.overflow = 'scroll';
		node.style.width = '100px';
		node.style.left = '-100px';
		node.style.top = '-100px';
		node.style.visibility = 'hidden';
		document.body.appendChild(node);
		apt.scrollBarWidth = node.offsetWidth - node.clientWidth;
		document.body.removeChild(node);
		node = null;
	}
	return apt.scrollBarWidth;
};

/**
 * All events are processed by this handler
 * @param {Event} evt - event
 * @param {Node} nodeFirer - node that triggers the event
 */
apt.core.handler = function(evt,nodeFirer){
	var $event = new AptEvent(evt);
	$event.evt.cancelBubble = true;
	if(evt.stopPropagation) evt.stopPropagation();
	$event.target = nodeFirer;
	if(_event.redirect){$event.target = _event.redirect;}
	if(_event.modal.length>0 && !apt.core.nodeIsAChildOf(_event.modal[0], $event.target)){return;}
	
	for (var t = 0; t < $event.type.length; t++) {
		$event.currentTarget = $event.target;
		$event.effectiveTarget = $event.currentTarget;
		while ($event.currentTarget) {
			if ($event.isValid($event.type[t]) && _event.enable) {
				$event.currentTarget.handlers.execute($event.type[t],$event);
			}
			if(!$event.bubble){
				$event.currentTarget = null;
			} else {
				$event.currentTarget = apt.core.nodeGetEventFirer($event.currentTarget.parentNode, $event.type[t]);
			}
		}
	}
	return $event.returnValue;
};

/**
 * Create a script node in the html head
 * @param {String} src - js file
 */
apt.core.insertScript = function(src){
	var node = document.createElement('script');
	node.src = src;
	node.type = 'text/javascript';
	apt.viewportHead.appendChild(node);
};

//core browser dependent
if (apt.browser.w3c) {
	
/**
 * Returns a styleSheet node to be inserted in the DOM
 * @param {String} content
 */
	apt.core.createStyleSheet=function(content){
		var node = document.createElement('style');
        node.setAttribute('type', 'text/css');
        node.appendChild(document.createTextNode(content));
		return node;
	};

/**
 * Returns th node localName property 
 * @param {Node} node
 * @return {String}
 */	
	apt.core.localName = function(node) {return node.localName};

/**
 * Normalize mozila property names
 * @param {String} name
 * @return {String} propertyName
 */
	apt.core.mozPropertySwitch = function(name){
		switch(name){ 
        case 'opacity':
            return '-moz-opacity';
        case 'box-sizing':
            return '-moz-box-sizing';
        case 'border-color':
            return 'border-top-color';
        case 'border-width':
            return 'border-top-width';
        case 'border-style':
            return 'border-top-style';
        default:
            return name;
		}
	};
	
} else {//ie

/**
 * Returns a styleSheet node to be inserted in the DOM
 * @param {String} content
 */
	apt.core.createStyleSheet=function(content){
		var dummy = document.createElement('div');
        dummy.innerHTML = '&nbsp;<style type=\'text/css\'>' + content + '</style>';
        var node = dummy.childNodes[1];
        dummy.removeChild(node);
		return node;
	};
	
/**
 * Returns the node localName property 
 * @param {Node} node
 * @return {String}
 */	
 	apt.core.localName = function(node) {return node.baseName};

}

/*
 * prototypes
 */

//Date
(function() {
/**
 * Date to String with format hh:mm:ss
 * @return {String}
 */
	Date.prototype.timeToString = function() {
		return this.getHours().toDigits(2) + ':' + this.getMinutes().toDigits(2) + ':' + this.getSeconds().toDigits(2)
 	};
/**
 * Date to String with miliseconds. Format hh:mm:ss.msg
 */
	Date.prototype.timemToString = function() {
		return this.getHours().toDigits(2) + ':' + this.getMinutes().toDigits(2) + ':' + this.getSeconds().toDigits(2)+ '.' + this.getMilliseconds().toDigits(3)
 	};
/**
 * add 1 to the day. Adjust month if necessary
 */	
	Date.prototype.nextDay = function(){
		this.setDate(this.getDate()+1);
	};
/**
 * returns week day base Monday=0 Sun=6
 */	
	Date.prototype.getWeekDay = function(){
		var d = this.getDay()-1;
		if (d<0)d=6;
		return d;
	};
})();

//Number
(function(){
/**
 * Limits the Number between min and max
 * @param {Number} min 
 * @param {Number} max 
 * @return {Number} result
 */	
	Number.prototype.limitRange = function(min, max){
		var result = this;
		if (this < min) result = min;
		if (this > max)result = max;
		return result;
	};
	
/**
 * Convert int to string with d digits. Ej n=2; n.toDigits(3)-> '002'
 * @param {Number} d - precision
 * @return {String}
 */
	Number.prototype.toDigits = function(d) {
 		var result = '' + parseInt(this);
 		while (result.length < d) {
			result = '0' + result;
		}
		return result;
 	};
	/**
	 * Add 1 to the number until max, then 0
	 * @param {Number} max
	 */
	Number.prototype.upCyclic = function(max){
		var result = this + 1;
		//if(result >= max)result = 0;
		if(result > max)result = 0;
		return result;
	};
})();

// String
(function() {
/**
 * add a integer to a number string. Return string.
 * @param {Integer} value
 * @return {String}
 */
	String.prototype.addInt = function(value){
		return '' + (parseInt(this)+ value);
	};
/**
 * Dimension = Text after the number. Ej. '58px' -> 'px'
 * @return {String}
 */
	String.prototype.getDimension = function() {
		return this.replace(/^[0-9\.]*/i, '');
 	};
/**
 * True if the string has the value at the begin, end or wrapped by spaces
 * @param {String} value
 * @return {Logical}
 */
	String.prototype.matchWord = function(value) {
 		return this.match(new RegExp('(\\s|^)' + value + '(\\s|$)'))? true : false
 	};
/**
 * Replace (and trim) the word from with to. 
 * Word is a string at the begin, end or wrapped by spaces
 * @param {String} from
 * @param {String} to
 * @return {String}
 */
	String.prototype.replaceWord = function(from, to) {
 		//return this.replace(new RegExp('(\\s|^)' + from + '(\\s|$)'), to);
		var s = this.replace(new RegExp('(\\s|^)' + from + '(\\s|$)'), ' '+to+' ');
		return s.replace(/^\s+|\s+$/g, '');
 	};
/**
 * Split the string by spaces. Varios spaces same as one
 * @return {ArrayString}
 */
	String.prototype.splitSpace = function() {
		return this.split(/\s+/);
 	};
/**
 * true if string = 'true'
 * @return {Logical}
 */	
	String.prototype.toLogical = function(){
		return this == 'true' ? true : false;
	};
/**
 * Erase spaces before and after
 * @return {String}
 */	
	String.prototype.trim = function() {
		return this.replace(/^\s+|\s+$/g, '');
 	};
})();

/**
 * Convert a String to Number. If not possible use defaultValue.
 * Ej. parseIntDefault("texto", 10) returns 10. 
 *     parseIntDefault("27", 10) returns 27.
 * @param {String} value to convert
 * @param {Number} defaultValue
 * @return {Number}
 */
parseIntDefault = function(value, defaultValue)	{
	return isNaN(parseInt(value, 10)) ? (!defaultValue ? 0 : defaultValue) : parseInt(value, 10)
};

//Objects

//$event Object definition
function AptEvent(evt){
	this.evt = evt;
	this.timeStamp = new Date();
	this.timeLastEvent = _event.timeStamp;
	_event.timeStamp = this.timeStamp;
	this.lastEvent = _event.eventName;
	_event.eventName = evt.type;
	this.bubble = evt.cancelBubble ? false : true;
	this.button = evt.button;
	this.keyCode = evt.keyCode;
	this.type = [evt.type];
	this.target = null; //node that triggers the event
	this.currentTarget = null; //node processing now
	this.relatedTarget = null; //from node
	this.redirectTarget = null; //Node to process the redirect event
	this.effectiveTarget = null; //real node. redirectTarget o currentTarget
	this.returnValue = true;
	this.sendEvent = evt.sendEvent; //apt internal event
	this.stopHorizontalPropagation = false; //stop other handlers for same node and event
	this.x = evt.clientX;
	this.y = evt.clientY;
	
	if (apt.browser.w3c) {
		this.relatedTarget = evt.relatedTarget;
		switch(evt.type){
			case 'mouseover':
				this.type.push('mouseenter');
				break;
			case 'mouseout':
				this.type.push('mouseleave');
				break;
		}
	}else {
		this.relatedTarget = evt.fromElement;
		this.button = evt.button == 0 ? 0 : evt.button - 1;
	};
	
	//return if is a valid event
	this.isValid = function(type){
		var valid = true;
		if(apt.browser.w3c){
			switch (type){
				case 'mouseenter':
				case 'mouseleave':
					if(this.currentTarget === this.relatedTarget || apt.core.nodeIsAChildOf(this.currentTarget,this.relatedTarget)){valid = false;}
					break;
			}
		} else {
			switch (type){
				case 'mouseenter':
				case 'mouseleave':
					if (this.currentTarget != this.target) {//no bubble
						valid = false;
					} 
					break;
			}
		}
		return valid;
	};
	
	this.preventDefaultAndStop = function(){
		this.preventDefault();
		this.stopInmediatePropagation();
		this.returnValue= false;
	};
	
	this.stopInmediatePropagation = function(){
		this.bubble = false;
		this.stopHorizontalPropagation = true;
	};
	
	if(apt.browser.w3c){
		this.preventDefault = function(){
			this.evt.preventDefault();
		};
	} else {
		this.preventDefault = function(){
			this.evt.returnValue = false;
		};
	}
};

//node handlers object
function AptHandlers(){
	this.enable = true;
	this.events = []; //events[name].enable events[name].code[]
	//this.observers = []; //after procesing send event
	//this.redirect = []; //where to process the event
	
	this.execute = function(eventName, $event){
		var handler = this.events[eventName];
		if(!handler){return;}
		
		var code;
		if(handler.skip >0){
			handler.skip--;
			return;
		}
		if (this.enable && handler && handler.enable) {
			if (handler.redirect.length > 0) {
				this.doRedirect(eventName, $event);
			}
			else { //execute
				//if(apt.debug) _event.debug.push(new Date() + '[execute event: '+ eventName + '] [class: ' + $event.currentTarget.className+']');
				for (var i = 0; i < handler.code.length; i++) {
					code = handler.code[i];
					if(!$event.stopHorizontalPropagation) code($event);
				}
			}
			if (handler.observers.length > 0) {
				this.doObservers(eventName, $event);
			}
		}
	};
	
	this.doRedirect = function(eventName, $event){
		//if(apt.debug) _event.debug.push(new Date() + '[redirect event: '+ eventName + '] [class: ' + $event.currentTarget.className+'][x: '+$event.x + ' y: ' + $event.y +']');
		var handler = this.events[eventName];
		for(i=0; i<handler.redirect.length; i++){
			$event.redirectTarget = handler.redirect[i];
			$event.effectiveTarget = $event.redirectTarget;
			$event.redirectTarget.handlers.execute(eventName, $event);
		}
	};
	
	this.doObservers = function(eventName, $event){
		//if(apt.debug) _event.debug.push(new Date() + '[observe event: '+ eventName + '] [class: ' + $event.currentTarget.className+']');
		var handler = this.events[eventName];
		for(i=0; i<handler.observers.length; i++){
			$event.redirectTarget = handler.observers[i];
			$event.effectiveTarget = $event.redirectTarget;
			$event.redirectTarget.handlers.execute(eventName, $event);
		}
	};
	
	//this.redirectSet = function(node){this.redirect.unshift(node);}; //DEPRECATED
	
	//this.redirectReset = function(){this.redirect.shift();}; //DEPRECATED
};


function AptElement(node) {
	this.current = node;
};

/**
 * Add one or more class to the node if condition true, else remove.
 * @param {String} className - Ej. 'class1 class2'
 * @param {String} condition - false or null, remove
 * @return {Boolean} isChange
 */
AptElement.prototype.classAddRemove = function(className, condition){
	var isChange = false;
	var isRemove = (condition == false);
 	var aClass = className.splitSpace(); 	
 	var previous = this.current.className;
	
 	for (var i=0; i<aClass.length; i++) {
		if(isRemove){
			if(previous.matchWord(aClass[i])){
				isChange = true;
				previous = previous.replaceWord(aClass[i], ' ');
			}
		}else{
			if (!previous.matchWord(aClass[i])) {
				isChange = true;
				previous += ' ' + aClass[i];
			}
		}
 	}	
 	previous = previous.trim();
 	if (isChange){this.current.className = previous;}
 	return isChange;
};
/**
 * Search a Descendent with the given className
 * @param {String} className
 * @return {Node} resultNode
 */
AptElement.prototype.classGetDescendant = function(className){
	var nodes = [];
	var node, item;
	nodes.push(this.current);
	while(nodes.length > 0){
		node = nodes.shift();
		for (var i=0; i<node.childNodes.length; i++){
			item = node.childNodes[i];
			if(item.nodeType == 1){
				if(item.className.matchWord(className))return item;
				nodes.push(item);
			}
		}
	}	
	return null;
};
/**
 * Return true if node has the class
 * @param {String} className
 * @return {Boolean}
 */
AptElement.prototype.classHas = function(className) {
 	return this.current.className.matchWord(className);
};

/**
 * Remove fromClass and adds toClass.
 * @param {String} fromClass - to remove
 * @param {String} toClass - to add
 */
AptElement.prototype.classReplace = function(fromClass, toClass){
	var aFrom = fromClass.splitSpace();
	var aTo = toClass.splitSpace();
	var previous = this.current.className;
	
	for(var i=0; i<aFrom.length; i++){
		if(previous.matchWord(aFrom[i])){previous = previous.replaceWord(aFrom[i], ' ');}
	}
	for(var i=0; i<aTo.length; i++){
		if(!previous.matchWord(aTo[i])){previous += ' ' + aTo[i];}
	}
	previous = previous.trim();
	this.current.className = previous;
};

/**
 * True if node has a direct child of type tagName, in apt space
 * @param {String} tagName
 * @return {Boolean}
 */
AptElement.prototype.hasAptDirectChild = function(tagName) {
	var find = false;
	for(var i=0; i<this.childNodes.length; i++){
		if (this.childNodes[i].apt.tagName == tagName) find = true;
	}
	return find;
};

/**
 * True if node has scroll Bars. a = axis to check (x, y, xy default:some of them)
 * @param {String} a
 */
AptElement.prototype.hasScroll = function(a){
	var result = false;
	var node = this.current;
	switch (a){
		case 'x':
			node.scrollLeft = 1;
			if (node.scrollLeft > 1){
				node.scrollLeft = 0;
				result = true;
			}
			break;
		case 'y':
			node.scrollTop = 1;
			if (node.scrollTop > 0){
				node.scrollTop = 0;
				result = true;
			}
			break;
		case 'xy':
			node.scrollLeft = 1;
			node.scrollTop = 1;
			if(node.scrollLeft > 0 & node.scrollTop > 0)result = true;
			node.scrollLeft = 0;
			node.scroll.Top = 0;
			break;
		default:
			node.scrollLeft = 1;
			node.scrollTop = 1;
			if(node.scrollLeft > 0 | node.scrollTop > 0)result = true;
			node.scrollLeft = 0;
			node.scroll.Top = 0;
			break;
	}
	return result;
};

/**
 * Prune all node's childs
 */
AptElement.prototype.removeChilds = function (){
	var node = this.current;
	while (node.firstChild){
		_event.nodeDelete(node.lastChild);
		node.removeChild(node.lastChild);
	}
};

//Browser specific
if (apt.browser.w3c) {
	
	AptElement.prototype.getElementLayout = function(){
		var left, top;
		var node = this.current;
		var st = document.defaultView.getComputedStyle(node, null);
		var n = node;
		var stn;
		var box;
		var pos = {};
		if(node.getBoundingClientRect){//ff3
			box = node.getBoundingClientRect();
			pos.x = box.left;
			pos.y = box.top;
			pos.w = box.right - box.left;
			pos.h = box.bottom - box.top;	
		} else { //pte WEBKIT
			if(node.ownerDocument.getBoxObjectFor){
				box = node.ownerDocument.getBoxObjectFor(node);
				pos.x = box.x;
				pos.y = box.y;
				pos.w = box.width;
				pos.h = box.height;
			}
		}
		
		while(n.offsetParent && n.offsetParent != apt.viewport){
			n = n.offsetParent;
			if(n.style){
				stn = document.defaultView.getComputedStyle(n, null);
				if(stn.getPropertyValue('overflow')!= 'visible'){
					pos.x += parseIntDefault(stn.getPropertyValue('border-left-width'));
					pos.y += parseIntDefault(stn.getPropertyValue('border-top-width'));
				}
			}
		}
		pos.x -= parseIntDefault(st.getPropertyValue('border-left-width'));
		pos.y -= parseIntDefault(st.getPropertyValue('border-top-width'));
		
		n = node.parentNode;
		while(n && n.nodeType == 1 && n != apt.viewport){
			if(n.tagName != 'TR' && document.defaultView.getComputedStyle(n, null).getPropertyValue('display')== 'inline'){
				pos.x -= n.scrollLeft;
				pos.y -= n.scrollTop;
			}
			n = n.parentNode;
		}
		
		if(node == apt.viewport){
			pos.w = self.innerWidth;
			pos.h = self.innerHeight;
		}

		pos.border = {};
		pos.border.x = pos.x;
		pos.border.y = pos.y;
		pos.border.w = pos.w;
		pos.border.h = pos.h;
		pos.border.r = pos.x + pos.w;
		pos.border.b = pos.y + pos.h;
		
		pos.margin = {};
		left = parseIntDefault(st.getPropertyValue('margin-left'));
		pos.margin.x = pos.x - left;
		pos.margin.w = left + pos.w + parseIntDefault(st.getPropertyValue('margin-right'));
		top = parseIntDefault(st.getPropertyValue('margin-top'));
		pos.margin.y = pos.y - top;
		pos.margin.h = top + pos.h + parseIntDefault(st.getPropertyValue('margin-bottom'));
		pos.margin.r = pos.margin.x + pos.margin.w;
		pos.margin.b = pos.margin.y + pos.margin.h;
		
		pos.padding = {};
		left = st.getPropertyValue('border-left-style')== 'none' ? 0 : parseIntDefault(st.getPropertyValue('border-left-width'));
		pos.padding.x = pos.x + left;
		pos.padding.w = -left + pos.w - (st.getPropertyValue('border-right-style')== 'none' ? 0 : parseIntDefault(st.getPropertyValue('border-right-width')));
		top = st.getPropertyValue('border-top-style')== 'none' ? 0 : parseIntDefault(st.getPropertyValue('border-top-width'));
		pos.padding.y = top + pos.y;
		pos.padding.h = -top + pos.h - (st.getPropertyValue('border-bottom-style')== 'none' ? 0 : parseIntDefault(st.getPropertyValue('border-bottom-width')));
		pos.padding.r = pos.padding.x + pos.padding.w;
		pos.padding.b = pos.padding.y + pos.padding.h;
		
		pos.content = {};
		left = parseIntDefault(st.getPropertyValue('padding-left'));
		pos.content.x = pos.padding.x + left;
		pos.content.w = -left + pos.padding.w - parseIntDefault(st.getPropertyValue('padding-right'));
		top = parseIntDefault(st.getPropertyValue('padding-top'));
		pos.content.y = top + pos.padding.y;
		pos.content.h = -top + pos.padding.h - parseIntDefault(st.getPropertyValue('padding-bottom'));
		pos.content.r = pos.content.x + pos.content.w;
		pos.content.b = pos.content.y + pos.content.h;
		
		pos.style = {};
		pos.style.x = parseInt(this.getElementStyle('left'));
		pos.style.y = parseInt(this.getElementStyle('top'));
		pos.style.w = parseInt(this.getElementStyle('width'));
		pos.style.h = parseInt(this.getElementStyle('height'));
		pos.style.r = pos.style.x + pos.style.w;
		pos.style.b = pos.style.y + pos.style.h;
		
		return pos;
	};
	
	AptElement.prototype.getElementStyle = function(name){
		var st = document.defaultView.getComputedStyle(this.current, null);
		return  st.getPropertyValue(apt.core.mozPropertySwitch(name));
	};
	
	//xpath selection
	AptElement.prototype.selectSingleNode = function(val){
		return this.current.ownerDocument.evaluate(val, this.current, null, XPathResult.ANY_UNORDERED_NODE_TYPE, null).singleNodeValue;
	};
	
/**
 * 
 * @param {String} name - startResize
 * @param {String} type - Events
 * @param {Object} obj - properties for the event object
 * @param {Logical} bubble
 * @param {Logical} noCancel
 * @return {Object} event Object
 */
	AptElement.prototype.sendEvent = function(name, type, obj, bubble, noCancel){
		var oNewEvent = document.createEvent(type);
		oNewEvent.initEvent(name, (bubble? true: false), (noCancel? false:true));
		oNewEvent.sendEvent = true;
		if(obj){for(var p in obj){oNewEvent[p]=obj[p];}}
		//this.current.dispatchEvent(oNewEvent);//better the apt event engine
		apt.core.handler(oNewEvent,this.current);
		return oNewEvent;
	};
} else {//ie

	AptElement.prototype.getElementLayout = function(){
		var left, top;
		var node = this.current;
		var box = node.getBoundingClientRect();
		var pos = {};
		
		pos.x = box.left - 2;
		pos.y = box.top - 2;
		pos.w = box.right - box.left;
		pos.h = box.bottom - box.top;
		
		if (node != apt.viewport){
			pos.x += apt.viewport.scrollLeft;
			pos.y += apt.viewport.scrollTop;
		}
		
		pos.border = {};
		pos.border.x = pos.x;
		pos.border.y = pos.y;
		pos.border.w = pos.w;
		pos.border.h = pos.h;		
		pos.border.r = pos.x + pos.w;
		pos.border.b = pos.y + pos.h;
		//apt.console.addMessage(0, new Date(), 'Border',pos.border.x+' '+pos.border.y+' '+pos.border.w+' '+pos.border.h);
		
		pos.margin = {};
		left = parseIntDefault(node.currentStyle.marginLeft);		
		pos.margin.x = pos.x - left;
		pos.margin.w = left + pos.w + parseIntDefault(node.currentStyle.marginRight);
		top = parseIntDefault(node.currentStyle.marginTop);
		pos.margin.y = pos.y - top;
		pos.margin.h = top + pos.h + parseIntDefault(node.currentStyle.marginBottom);
		pos.margin.r = pos.margin.x + pos.margin.w;
		pos.margin.b = pos.margin.y + pos.margin.h;
		//apt.console.addMessage(0, new Date(), 'Margin',pos.margin.x+' '+pos.margin.y+' '+pos.margin.w+' '+pos.margin.h);

		pos.padding = {};
		left = node.currentStyle.borderLeftStyle == 'none' ? 0 : parseIntDefault(node.currentStyle.borderLeftWidth);
		pos.padding.x = pos.x + left;
		pos.padding.w = -left + pos.w - (node.currentStyle.borderRightStyle== 'none' ? 0 : parseIntDefault(node.currentStyle.borderRightWidth));
		top = node.currentStyle.borderTopStyle == 'none' ? 0 : parseIntDefault(node.currentStyle.borderTopWidth);
		pos.padding.y = top + pos.y;
		pos.padding.h = -top + pos.h - (node.currentStyle.borderBottomStyle == 'none' ? 0 : parseIntDefault(node.currentStyle.borderBottomWidth));
		pos.padding.r = pos.padding.x + pos.padding.w;
		pos.padding.b = pos.padding.y + pos.padding.h;
		//apt.console.addMessage(0, new Date(), 'Padding',pos.padding.x+' '+pos.padding.y+' '+pos.padding.w+' '+pos.padding.h);

		pos.content = {};
		left = parseIntDefault(node.currentStyle.padding-left);
		pos.content.x = pos.padding.x + left;
		pos.content.w = -left + pos.padding.w - parseIntDefault(node.currentStyle.paddingRight);
		top = parseIntDefault(node.currentStyle.paddingTop);
		pos.content.y = top + pos.padding.y;
		pos.content.h = -top + pos.padding.h - parseIntDefault(node.currentStyle.paddingBottom);
		pos.content.r = pos.content.x + pos.content.w;
		pos.content.b = pos.content.y + pos.content.h;
		//apt.console.addMessage(0, new Date(), 'Content',pos.content.x+' '+pos.content.y+' '+pos.content.w+' '+pos.content.h);

		pos.style = {};
		pos.style.x = parseInt(this.getElementStyle('left'));
		pos.style.y = parseInt(this.getElementStyle('top'));
		pos.style.w = parseInt(this.getElementStyle('width'));
		pos.style.h = parseInt(this.getElementStyle('height'));
		pos.style.r = pos.style.x + pos.style.w;
		pos.style.b = pos.style.y + pos.style.h;
		return pos;
	};
	
	AptElement.prototype.getElementStyle = function(pName){
		var name;
		switch (pName){
			case 'box-sizing':
				//name = pName;
				return apt.browser.quirks ? 'border-box' : 'content-box';
				break;
			default:
				var a = pName.split('-');
				name = a[0];
				for(var i=1;i<a.length;i++){name += a[i].substr(0,1).toUpperCase() + a[i].substr(1)}
				return this.current.currentStyle[name];
				break;
		}
		
	};
	
	AptElement.prototype.selectSingleNode = function(val){
		return null; //TODO
	};
	
/**
 * 
 * @param {String} name - startResize
 * @param {String} type - Events
 * @param {Object} obj - properties for the event object
 * @param {Logical} bubble
 * @param {Logical} noCancel
 * @return {Object} event Object
 */
	AptElement.prototype.sendEvent = function(name, type, obj, bubble, noCancel){
		var oNewEvent = document.createEventObject();
		oNewEvent.type = name;
		oNewEvent.sendEvent = true;
		if(obj){for(var p in obj){oNewEvent[p]=obj[p];}}
		oNewEvent.cancelBubble = bubble? true: false;
		//this.current.fireEvent('on'+name,oNewEvent); //better the apt event engine
		apt.core.handler(oNewEvent, this.current);
		return oNewEvent;
	};
}