if (window.ax && window.ax.author != 'Anton Matviichuk') throw ("AMAX: Namespace 'ax' already allocated!")

window.ax = {
	author: 'Anton Matviichuk',
	version: 2.1,
	
	lastUniq: 0,
	flag: [],
	
	uniq: function () {
		return this.lastUniq++
	},

	foreach: function (obj, func) {
		for(var key in obj) if (obj.hasOwnProperty(key)) func(obj[key], key)
	},
	
	first: function (obj) {
		for(var key in obj) if (obj.hasOwnProperty(key)) return obj[key]
	},
	
	firstKey: function (obj) {
		for(var key in obj) if (obj.hasOwnProperty(key)) return key
	},
	
	isEmptyObject: function (obj) {
		if (typeof obj !== 'object') return null
		for(var key in obj) {if (obj.hasOwnProperty(key)) return false}
		return true
	},
	
	extend: function (reciever, donor) {
		ax.foreach(donor, function(value, key) {reciever[key] = value})
	},
	
	now: function() {
		return ax.nowDate() + " " + ax.nowTime()
	},

	nowDate: function() {
		var d = new Date()
		var mSpace = d.getMonth()+1 < 10 ? '0' : ''
		var dSpace = d.getDate() < 10 ? '0' : ''
		return d.getFullYear()+"-"+mSpace+(d.getMonth()+1)+"-"+dSpace+d.getDate()
	},
	
	nowTime: function() {
		var d = new Date()
		var hSpace = d.getHours() < 10 ? '0' : ''
		var mSpace = d.getMinutes() < 10 ? '0' : ''
		var sSpace = d.getSeconds() < 10 ? '0' : ''
		return hSpace+d.getHours()+":"+mSpace+d.getMinutes()+":"+sSpace+d.getSeconds()
	},

	smooth: function(o) {
		o.fps = o.fps || 60

		var value = parseFloat(o.obj[o.param]) || o.from || 0
		var distance = o.to - value
		var framedelay = 1000 / o.fps
		var dropFlag = null
		
		if (o.fora) {
			o.speed = framedelay * distance / o.fora
		} else {
			if(distance < 0) o.speed = o.speed
		}
		var iterations = Math.abs(distance / o.speed)
		var time = 0
		
		if(o.flag) ax.flag[o.flag] = true;

		for(n=0; n<=iterations; n++) {
			time = n * framedelay
			if (o.transition) 
				value += o.speed * o.transition(n/iterations)
			else				
				value += o.speed

			if(n==iterations) {
				value = o.to
				if(o.flag) dropFlag = o.flag 
			}

			setTimeout(function(param, value, add, dropFlag) {
				this[param] = value + add;
				if (dropFlag) ax.flag[dropFlag] = null
			}.bind(o.obj, [o.param, value, o.add, dropFlag]), time)
		}
	}
}

ax.array = {
	$: function(arr) {
		ax.extend(arr, ax.array)
	},
	
	c: function(arr) {
		if(!(arr instanceof Array)) arr = new Array
		ax.array.$(arr)
		return arr
	},
	
	each: function(func, arr) {
		arr = arr || this
		for (var i = 0, l = arr.length; i < l; i++) func(arr[i], i)
	},
	
	merge: function (arr2, arr) {
		arr = arr || this
		for (var i = 0, l = arr2.length; i < l; i++) ax.array.include(arr2[i], arr)
		return arr
	},
	
	include: function (value, arr) {
		arr = arr || this
		if (!ax.array.contains(value, arr)) arr.push(value)
		return arr
	},
	
	remove: function (value, arr) {
		arr = arr || this
		for (var i = 0, l = arr.length; i < l; i++) if(arr[i] == value) arr.splice(i, 1);
		return arr
	},
	
	contains: function(value, arr){
		arr = arr || this
		for (var i = 0, l = arr.length; i < l; i++) if(arr[i] == value) return true
		return false
	}
}

ax.dom = {
	$: function(el) {
		if(typeof el == 'string') el = document.getElementById(el)
		if(!el) return false
		ax.extend(el, ax.dom)
		return el
	},
	
	c: function(name, attrs) {
		var el = document.createElement(name)
		ax.foreach(attrs, function(value, attr) {
//			el.setAttribute(attr, value)
			el[attr] = value
		})
		return ax.dom.$(el)
	},
	
	descendants: function(onlyTag) {
		var childs = ax.array.c()
		for(i in this.childNodes) {
			if (this.childNodes[i].tagName) {
				if(!onlyTag || this.childNodes[i].tagName == onlyTag) childs.push(this.childNodes[i])
				var subchilds = ax.dom.$(this.childNodes[i]).descendants(onlyTag)
				childs.merge(subchilds)
			}
		}
		return childs
	},
	
	setText: function(text) {
		if(typeof text == 'string') return this.setHTML(text.i18n())
		if(typeof text == 'number') return this.setHTML(text)
		else return this
	},
	
	setHTML: function(text) {
		this.innerHTML = text
		return this
	},
	
	empty: function() {
		return this.setHTML('')
	},

	remove: function() {
		if (this.parentNode) {
			this.parentNode.removeChild(this)
			return true
		}
		return false
	},
	
	inside: function(el) {
		if(!el) return false
		if(typeof el == 'string') el = document.getElementById(el)
		el.appendChild(this)
		return this
	},
	
	before: function(el) {
		if(!el) return false
		if(typeof el == 'string') el = document.getElementById(el)
		el.parentNode.insertBefore(this, el)
		return this
	},

	after: function(el) {
		el = ax.dom.$(el)
		if(!el) return false

		var sibling = el.findElement('nextSibling');
		if (!sibling) el.parentNode.appendChild(this);
		else el.parentNode.insertBefore(this, sibling);
		return this
	},

	findElement: function(el) {
		if (typeof el == 'string') el = this[el]
		while(el && el.nodeType != 1) {
			el = el.nextSibling			
		}
		return el
	},

	appendChilds: function(nodes) {
		for(i=0;i<nodes.length;i++) this.appendChild(nodes[i])
		return this
	},

	hasClass: function(className, el) {
		el = el || this
		return el.className.contains(className, ' ')
	},

	addClass: function(className, el) {
		el = el || this
		if (!ax.dom.hasClass(className, el)) el.className = (el.className + ' ' + className)
		return el
	},

	removeClass: function(className, el) {
		el = el || this
		el.className = el.className.replace(new RegExp('(^|\\s)' + className + '(?:\\s|$)'), '$1')
		return el
	},
	
	toggleClass: function(className, el) {	
		el = el || this
		ax.dom.hasClass(className, el) ? ax.dom.removeClass(className, el) : ax.dom.addClass(className, el)
		return el
	},

	listen: function(event, handle) {
		ax.event.listen(this, event, handle)
		return this
	}
}

ax.page = {
	X: function(el) {
		var x = 0
		do {
			x += el.offsetLeft || 0
			el = el.offsetParent
		} while (el)
		return x
	},
	
	Y: function(el) {
		var y = 0
		do {
			y += el.offsetTop || 0
			el = el.offsetParent
		} while (el)
		return y
	}
}

ax.event = {
	fix: function(event) {
		if (!event.pageX) { // assume that pageX doesn't come wo pageY
			try {
				event.pageX = event.clientX + document.body.scrollLeft
				event.pageY = event.clientY + document.body.scrollTop
			} catch(ex) {}
		}
	},

/**
 * Cross-browser event listener
 * @param {object} obj Listener DOM Element
 * @param {string} event Event (introducing MooTools-style 'mouseleave' event)
 * @param {function} handle Event handle
 * @return {object} Listener reference
 */
	listen: function (el, event, handle) {
		var func = handle;
		if (event == 'mouseleave') {
			func = function(event) {
				var timer = window.setTimeout(handle, 40)
				ax.event.listen(el, 'mouseover', function() {window.clearTimeout(timer)})
			}
			event = 'mouseout'
		}
		
		if (el.addEventListener) {
			el.addEventListener(event, func, false)
		} else if (el.attachEvent) {
			el.attachEvent(event, func)
			el.attachEvent('on'+event, func)
		}
		
		return {element:el, event:event, func:func}
	},
	
/**
 * Event listener remover
 * @param {object} listener Listener reference
 */
	unlisten: function (listener) {
		if (!listener) return
		if (listener.element.removeEventListener) {
			listener.element.removeEventListener(listener.event, listener.func, false)
		} else if (listener.element.detachEvent) {
			listener.element.detachEvent(listener.event, listener.func)
			listener.element.detachEvent('on'+listener.event, listener.func)
		}
	}
}

/**
 * observer class
 */
ax.observer = function() {
	this.events = {}
}

ax.observer.prototype = {
	subscribe: function (event, listener, triggerData) {
		if(triggerData) event = event + '(' + triggerData + ')'
		if(!this.events[event]) this.events[event] = []
		ax.array.include(listener, this.events[event])
	},
	
	unsubscribe: function (event, listener, triggerData) {
		if(triggerData) event = event + '(' + triggerData + ')'
		if(this.events[event] && !this.events[event].length) delete this.events[event]
		ax.array.remove(listener, this.events[event])
	},
	
	issue: function (event, triggerData, data) {
		if(this.events[event])	ax.foreach(this.events[event], function(listener) {listener(triggerData, data)})
		event = event + '(' + triggerData + ')'
		if(this.events[event])	ax.foreach(this.events[event], function(listener) {listener(triggerData, data)})
	}
}

/**
 * GLOBALS and PROTOTYPES
 */

document.head = document.getElementsByTagName('head')[0]

/**
 * Prototype to bind object to a function
 * @param {object} obj Object to bind
 * @param {array} args_pre Predefined arguments
 * @return {function} Function with object and predefined parameters binded
 */
Function.prototype.bind = function(obj, args_pre)  {
	var fn = this;
	if(args_pre)return function() {return fn.apply(obj, args_pre)}
	else		return function() {return fn.apply(obj, arguments)}
}

/**
 * Prototype to bind object to a event listener function
 * @param {object} obj Object to bind
 * @param {array} args_pre Predefined arguments
 * @return {function} Function with object, predefined parameters and current event binded
 */
Function.prototype.bindListener = function(obj, args_pre){
	var fn = this
	return function(event){
		event = event || window.event
		// prevent default
		if (event.preventDefault) {
			event.preventDefault()
		} else {
			event.returnValue = false
		}
		
		// add event to args
		var args = []
		if (args_pre) for (var i = 0, j = args_pre.length; i < j; i++) args.push(args_pre[i])
		args.push(event)
		
		var returns = function(){return fn.apply(obj, args)}
		return returns()
	}
}

/**
 * Tests string against needle
 * @param {string} string Test string
 * @param {string} s Separator
 * @return {boolean}
 */
String.prototype.contains = function(string, s) {
	return (s) ? (s + this + s).indexOf(s + string + s) > -1 : this.indexOf(string) > -1;
}

String.prototype.truncate = function(len, symbol) {
	str = this
	if (this.length > len) str = this.slice(0,len-symbol.length) + symbol
	return str
}

String.prototype.nl2br = function() {
	return this.replace(/[\n]/g,'<br/>')
}

/**
 * i18n
 */
String.prototype.i18n = function() {
	if(!String.I18N_TABLE) return this
	var tr = this.toLowerCase()
	var lastChar = tr.charAt(this.length-1)
	if (lastChar.match(/\W/)) 
		tr = tr.slice(0,-1)
	else
		lastChar = ''
	
	var tr = String.I18N_TABLE[tr]
	if (!tr) return this
	
	var c = this.charAt(0)
	if (c == c.toUpperCase()) tr = tr.charAt(0).toUpperCase() + tr.substr(1)
	return tr + lastChar
}
