﻿// contains Aris.js, Aris.DOM.js, Aris.Event.js
var Aris = new function() {
	var _overwrites = {};
	var _guid = 0;
	return {
		guid: function(id) {
			return ( (id || '')  + (_guid++));
		},

		register: function(obj, name) {
			if (obj) {
				if ( name ) {
					var old = window[name];
					window[name] = obj;
					if (typeof old != 'undefined') _overwrites[name] = old;
				}
				if (typeof obj.init == 'function') { obj.init(); obj.init = function(){} };
			}
			return this;
		},

		restore: function(name/*,...*/) {
			Array.forEach(arguments, function(n) {
				if (n in _overwrites) {
					window[n] = _overwrites[n];
					delete _overwrites[n];
				}
			});
			return this;
		}
	};
};


Aris.Utils = {
	override: function(obj, src/*, val*/) {
		if ( arguments.length==3 ) {
			var val = arguments[2];
			var ancestor = obj[src];
			if ( ancestor && typeof val == 'function' && src != 'base' ) {
				var method = val;
				val = function() {
					var old = this.base;
					this.base = ancestor;
					var rv = method.apply(this, arguments);
					this.base = old;
					return rv;
				};
				val.ancestor = ancestor;
			}
			obj[src] = val;
		} else {
			for (var key in src) if ( !Object.prototype[key] ) {
				$override(obj, key, src[key]);
			}
		}
		return obj;
	},

	extract: function(obj, prop/*,...*/) {
		var rv = {};
		if ( obj ) {
			for(var i=1,p; p = arguments[i]; i++) {
				if ( p in obj ) {
					rv[p] = obj[p];
				}
			}
		}
		return arguments.length == 2 ? rv[prop] : rv;
	},
	
	merge: function(dest, src) {
		dest = dest || {};
		if ( src ) {
			for (var prop in src) {
				dest[prop] = src[prop];
			}	
		}
		return dest;
	},
	
	remove: function(obj, prop/*,...*/) {
		var rv = {};
		if ( obj ) {
			for(var i=1,p; p = arguments[i]; i++) {
				if ( p in obj ) {
					rv[p] = obj[p];
					delete obj[p];
				}
			}
		}
		return arguments.length == 2 ? rv[prop] :  rv;
	},
	
	serialize: function(obj, s1, s2) {
		s1 = s1 || '=';
		s2 = s2 || '&';
		var rv = [];
		forEach(obj, function(val,name) {
			rv.push(name+s1+(val.join ? val.join(',') : val));
		});
		return rv.join(s2);
	},

	defined: function(obj) {
		var dummy; return obj !== dummy;
	}
};

for( var key in Aris.Utils ) {
	Aris.register(Aris.Utils[key], '$'+key);
}

// array-like enumeration
if (!Array.forEach) { // mozilla already supports this
    Array.forEach = function(obj, callback, context) {
        for (var i=0,len=obj.length; i < len; i++) {
            if ($defined(obj[i])) callback.call(context, obj[i], i, obj);
        }
    };
}
Aris.Utils.forEach = Array.forEach;

Function.prototype.method = function (n, fn) {
    $override(this.prototype, n, fn);
	return this;
};
Function.method('forEach', function(obj, callback, context) {
	for (var key in obj) {
		if (typeof this.prototype[key] == "undefined") {
			callback.call(context, obj[key], key, obj);
		}
	}
	return obj;
}).
method('extend', function(_inst, _static) {
	_inst = _inst || {};
	//var proto = new this;
	var members = ['constructor','toString','valueOf'];
	var proto = $extract.apply(this, [this.prototype].concat(members) );
	var key, i = 0;
	while(key = members[i++]) if (_inst[key] != Object.prototype[key]) {
		$override(proto, key, _inst[key]);
	}
	$override(proto, this.prototype);
	$override(proto, _inst);
	
	var constructor = proto.constructor;
	var klass = function() {
		return constructor.apply(this, arguments);
	};
	
	klass.extend = this.extend;
	klass.ancestor = this;
	klass.prototype = proto;
	klass.toString = this.toString;
	$override(klass, _static);
	if (typeof klass.init == 'function') klass.init();
	return klass;
}).
method('bind', function(/* context, arguments... */) {
	var __method = this, args = $A(arguments),  obj = args.shift();
	return function() { return __method.apply(obj, args); };
}).
method('delay', function(/* ms, context, arguments...*/) {
	var args = $A(arguments), ms = args.shift();
	return setTimeout(this.bind.apply(this, args),ms);
}).
method('inherits', function(src) {
	var dest = this;
	var i = 0;
	while(src = arguments[i]) {
		forEach(src, function(obj, name) {
			var over = true;
			if ( name.charAt(0) == '@' ) {
				name = name.slice(1);
				over = !(name in dest.prototype);
			}
			if ( over) { dest.method(name, obj); }
		});
		i++;
	}
	return dest;
});

// globally resolve forEach enumeration
var forEach = function(obj, callback, context) {
    if (obj) {
        var resolve = Object; // default
        if (obj instanceof Function) {
            // functions have a "length" property
            resolve = Function;
        } else if (obj.forEach instanceof Function) {
            // the object implements a custom forEach method so use that
			obj.forEach(callback, context);
            return;
        } else if (typeof obj.length == "number") {
            // the object is array-like
            resolve = Aris.Utils;
        }
        resolve.forEach(obj, callback, context);
		return;
    }
};

var $A = Array.from = function(enumerable) {
	return [].map.call(enumerable, function(v) { return v; });
};


Array.inherits({
	//javascript 1.6 array functions forEach, every, some, filter
	'@forEach': function(callback, context) {
		for(var i=0,len=this.length;i < len; i++) {
			if(i in this)
				callback.call(context, this[i], i, this );
		}
	},

	'@filter': function(callback, context) {
		var rv = [];
		for (var i=0,len=this.length;i < len; i++) {
			if (i in this){
				if ( callback.call(context,this[i],i,this) )
					rv.push( this[i] );
			}
		}
		return rv;
	},

	'@every': function(callback,context) {
		var rv = true;
		for (var i=0,len=this.length;i < len; i++) {
			if (i in this && !callback.call(context,this[i],i,this) )
				return false;
		}
		return true;
	},

	'@some': function(callback,context) {
		for (var i=0,len=this.length;i < len; i++) {
			
			if( i in this && callback.call(context,this[i],i,this) )
				return true;
		}
		return false;
	},

	'@map': function(callback,context) {
		var len = this.length;
		var rv = new Array(len);
		for (var i=0; i < len; i++) {
			if(i in this)
				rv[i] = callback.call(context,this[i],i,this);
		}
		return rv;
	},

	'@indexOf': function(obj, start) {
		var len = this.length;
		var i = start || 0;
		if (i < 0) i += len;
		while(start<len) {
			if (i in this && this[i]===obj) 
				return i;
			i++;
		}
		return -1;
	}
});

Aris.BOM = new function() {
	var ua = navigator.userAgent.toLowerCase();
	this.xpath = !!(document.evaluate);
	this.xml = !!(document.implementation && document.implementation.hasFeature('XML','1.0'));
	if (window.ActiveXObject) { this.ie = this[window.XMLHttpRequest ? 'ie7' : 'ie6'] = true; }
	else if (document.childNodes && !document.all && !navigator.taintEnabled){ this.khtml = this.webkit = this[this.xpath ? 'webkit420' : 'webkit419'] = true; }
	else if (document.getBoxObjectFor !== null){ this.gecko = true; }
	this.mac = /mac/.test(ua);
	this.winCE = /windows ce/.test(ua);
	
	this.opera = /opera/.test(ua);
	
	this.is = function() {
		return [].every.call(arguments, function(condition) {
				return this[condition] === true;
		},this);
	};
	this.has = function(name) {
		var n = name.replace('window.','').split('.');
		var root = window;
		var i=0, l= n.length;
		var rv = true;
		while(i < l) {
			if (!(n[i] in root)){
				rv = false;
				break;
			}
			root = root[n[i++]];
		}
		return rv;
	};
	if ( this.is('ie6') ) try {document.execCommand("BackgroundImageCache", false, true);} catch(e){};
};

Aris.Observer = function(owner) {
	this.fns = [];
	this.locked = false;
	this.expired = false;
	this.owner = owner;
}
Aris.Observer.inherits({
	add: function(fn, thisObj) {
		if (typeof fn != 'function') return;
		else if ( !this.fns.some( function (obj) { return obj.fn === fn; }) )
			this.fns.push({fn: fn, context:thisObj});
		if ( this.expired ) this.fire(this.owner, {});
		return this;
	},
	remove: function(fn) {
		this.fns = this.fns.filter( function(el) {
			if ( typeof fn == 'undefined' || (el.fn && el.fn !== fn) ) {
				delete el;
				return false;
			}
			return true;
		});
		return this;
	},
	fire: function(sender, args) {
		if ( this.locked ) this.fire.delay(this, 10, sender, args);
		var i = this.fns.length, el;
		if ( i == 0 ) return this;
		this.locked = true;
		while(el = this.fns[--i]) {
			if ( el.fn.call(el.context, sender, args) === false )
				break;
		}
		this.locked = false;
		return this;
	},
	expire: function () {
		this.expired = true;
		this.remove();
		return this;
	}
});
Aris.Event = new function() {
	var _cache = [],
		_timer = null;
	var Observer = Aris.Observer;
	var _add = function() {
		if (document.addEventListener) {
			return function(element, type, handler) {
				element.addEventListener(type, handler, false);
			};
		} else if (document.attachEvent) {
			return function(element, type, handler) {
				element.attachEvent("on" + type, handler);
			};
		} else {
			return function(){}; // not supported
		}
	}();

	var _remove = function () {
		if (document.removeEventListener) {
			return function(element, type, handler) {
				element.removeEventListener(type, handler, false);
			};
		} else if (document.detachEvent) {
			return function(element, type, handler) {
				element.detachEvent("on"+type, handler);
			};
		} else {
			return function(){};
		}
	}();

	function _getCacheIndex(el, eType, fn) {
		for (var i=0,len=_cache.length; i<len; i++) {
			var li = _cache[i];
			if ( li && li[2] == fn && li[0] == el && li[1] == eType ) return i;
		}
		return -1;
	};

	function _ready() {
		var AE = Aris.Event;
		if (!AE.pageIsReady) {
			AE.pageIsReady = true;
			AE.onDOMLoaded.fire(Aris.Event).expire();
			_ready = function(){};
		}
	};

return {
	onDOMLoaded: new Observer(this),
	onPageUnloading: new Observer(this),
	onPageLoad: new Observer(this),
	pageIsReady: false,
	
	add: function(el, eType, fn, scope) {
		if ( !el || !fn || !fn.call ) return this;

		if ( typeof el == "string" ) { 
			if ( this.pageIsReady ) {
				return this.add( document.getElementById(el), eType, fn, scope );
			}
			else {
				this.onDOMReady.add( this.add.bind(this, el, eType, fn, scope) );
				return this;
			}
		}

		var wFn = (scope) ? this.bind(fn, scope, el) : this.bind(fn,el);
		var li = [el,eType,fn,wFn,scope];
		_cache.push(li);
		_add(el,eType,wFn);
		return this;
	}, //end add
	
	bind: function(fn, obj, orig) {
		var __method = fn;
		return function(e) {
			return __method.call(obj,e||window.event,orig);
		};
	},
	
	init: function() {
		var d = document, ie = /*@cc_on !@*/false;
		// for Mozilla/Opera 9
		if (d.addEventListener) {
		    d.addEventListener("DOMContentLoaded", _ready, false);
		}

		// polling for no erros on ie
		if (ie)  (function () {
			try {
				// throws errors until after ondocumentready
				d.documentElement.doScroll('left');
			} catch (e) {
				setTimeout(arguments.callee, 100);
				return;
			}
			// no errors, fire
			_ready();
		})();

		//for Safari/KTHML based browsers
		if (/KHTML/i.test(navigator.userAgent)) { // sniff
		    _timer = setInterval(function() {
		        if (/loaded|complete/.test(document.readyState)) {
		           _ready(); // call the onload handler
	        	}
		    }, 100);
		}		
		
		if (ie) this.add(window, 'unload', this.unLoad, this);
		this.add(window, 'load', this.load, this);
		return this;
	},

	load: function() {
		_ready();
		this.onPageLoad.fire(this).expire();
	},
	
	preventDefault: function(ev) {
		if ( ev.preventDefault) { ev.preventDefault(); }
		else { ev.returnValue = false; }
	},
	
	remove: function(el,eType,fn,idx) {
		if (!el || !fn || !fn.call) return false;
		var cacheItem = null;
		if (typeof idx == "undefined") idx = _getCacheIndex(el, eType, fn);
		if (idx >= 0) cacheItem = _cache[idx];
		if (cacheItem) {
			_remove(el, eType, cacheItem[3]);
			delete cacheItem[3];
			delete cacheItem[2];
			delete cacheItem;
		}
		return this;
	},

	stopEvent: function(ev) {
		this.stopPropagation(ev);
		this.preventDefault(ev);
	},
	
	stopPropagation: function(ev) {
		if (ev.stopPropagation)
			ev.stopPropagation();
		else
			ev.cancelBubble = true;
	},
	
	unLoad: function(e) {
		this.onPageUnloading.fire(this);
		_cache.forEach( function(l,idx) {
			this.remove(l[0],l[1],l[2],idx);
		},this);
	}
  };
};
Aris.register(Aris.Event); 

Aris.DOM = new function() {
	var BOM = Aris.BOM;
	var ie = BOM.is('ie');
	var css = {
		float: ie ? 'styleFloat' : 'cssFloat'
	}
	var getCSS = function(prop) { return css[prop] || prop; };
return {
	
	addClass: function(o, c1) {
		if(!this.hasClass(o, c1)) {
			o.className += o.className ?' ' + c1 : c1;
		}
		return o;
	},
	
	adopt: function(o, a) {
		o.appendChild( a.parentNode.removeChild(a) );
		return o;
	},

	clear: function(el) {
		while(el && el.hasChildNodes()) {
			var r = el.removeChild(el.firstChild);
		}
		return el;
	},

	create: function(htmlJson) {
		var elName = typeof htmlJson == 'string' ? htmlJson : htmlJson.nodeName;
		var props = arguments.length > 1 ? arguments[1] : htmlJson.props;
		if (BOM.is('ie') && props) {
			var el = [elName];
			['name','type','checked'].forEach(function(val) {
				if (props[val]){ el.push(val+'="'+$remove(props[val])+'"');  }
			});
			if ( el.length > 1 ){ elName = '<'+el.join(' ')+'>'; }
		}
		var el = document.createElement(elName);
		if ( props ) {
			var children = $remove(props, 'children');
			forEach(props, function(val, a) {
				if (a=='style'){ DOM.setStyle(el,val); }
				else { el[a] = val; }
			});
			if (typeof children =='string') children = [children];
			if ( children ) Array.from(children).forEach(function(c) {
				if ( typeof c == 'string' ) { el.appendChild(document.createTextNode(c)); }
				else if ( c.nodeType == 3 ) { el.appendChild(document.createTextNode(c.nodeValue)); }
				else if ( c.nodeType == 1 ) { el.appendChild(c); }
				else { el.appendChild( DOM.create(c) ); }
			});
		}
		return el;
	},

	get: function(el) {
		if (typeof el == 'string')
			el = document.getElementById(el);
		return el;
	},
	
	getElementsByTagName: function(root, tag) {
		var node = root||document;
		tag = tag || '*';
		var els = node.getElementsByTagName(tag);
		if( !els.length && (tag == '*' && node.all) ) els = node.all;
		return els;
	},
	
	getElementsByClass: function (searchClass,node,tag) {
		var cEls = [];
		var els = this.getElementsByTagName(node, tag);
		var elsLen = els.length;
		for (var i=0,j=0; i < elsLen; i++) {
			if ( this.hasClass(els[i], searchClass) ) { cEls[j++] = els[i]; }
		}
		return cEls;
	},
	
	getStyle: function() {
		if (document.defaultView && document.defaultView.getComputedStyle) {
			return function(o,prop) {
				if (!o) return;
				prop = getCSS(prop);
				var s = document.defaultView.getComputedStyle(o,'');
				return s ? s[prop] : o.style[prop];
			};
		}
		else if (BOM.is('ie')) {
			return  function(o, prop) {
				if (!o) return;
				if ( prop == 'opacity') {
					var val = 100;
                    try { // will error if no DXImageTransform
                        val = o.filters['DXImageTransform.Microsoft.Alpha'].opacity;
                    } catch(e) {
                        try { // make sure its in the document
                            val = o.filters('alpha').opacity;
                        } catch(e) { }
                    }
                    return val / 100;
				}
				else {
					prop = getCSS(prop);
					return o.style[prop] || o.currentStyle[prop];
				}
			};
		}
		else {
			return function(o, prop) { if(!o) return; return o.style[prop] };
		}
	}(),
	
	getStyles: function(o) {
		var rv = {};
		for(var i=1, prop;prop = arguments[i]; i++ ) {
			rv[prop] = DOM.getStyle(o, prop);
			if (!rv[prop]) delete rv[prop];
		}
		return rv;
	},

	hasClass: function(o, c1) {
		return new RegExp('(^|\\s)'+c1+'(\\s|$)').test(o.className)
	},
	
	removeClass: function(o, c1) {
		var rep = o.className.match(' '+c1) ? ' ' + c1 : c1;
		o.className=o.className.replace(rep,'');
		return o;
	},
	
	setOpacity: function() {
		if (window.ActiveXObject) {
			return function(o, val) {
				o.style.filter = "alpha(opacity:"+val*100+")";
				if (!o.hasLayout) o.style.zoom = 1; 
			}
		}
		else {
			return function(o,val) {
				o.style.opacity = val<1 ? val : .999;
			};
		}
	}(),
	
	setStyle: function(el, style, val) {
		if ( !el ) return;
		if (arguments.length ==3)
			style == "opacity" ?  DOM.setOpacity(el, val) :  el.style[getCSS(style)] = val;
		else {
			if (typeof style == 'string') { el.style.cssText = style; }
			else for( var s in style) {
				s == "opacity" ? DOM.setOpacity(el, style[s]) :  el.style[getCSS(s)] = style[s];
			}
		}
		return el;
	},

	swapClass: function(o,c1,c2) {
		o.className=!this.hasClass(o,c1) ? o.className.replace(c2,c1) : o.className.replace(c1,c2);
		return o;
	},
	
	getPos: function(tEl, asArray) {
		var el = DOM.get(tEl);
		var pos = { top: 0, left: 0 };
		do {
			pos.top += el.offsetTop;
			pos.left += el.offsetLeft;
		} while( el = el.offsetParent );
		return asArray? [pos.top, pos.left] : pos;
	},
	
	getDim: function(tEl,asArray) {
		var tEl = DOM.get(tEl);
		var dim = {};
		dim.width = tEl.offsetWidth;
		dim.height = tEl.offsetHeight;
		return asArray ? [dim.width,dim.height] : dim;
	},
	
	getLayout: function(el) {
		return $merge( this.getPos(el), this.getDim(el) );
	}
	
  };
};
