// defining spaces
if (!self.Programica) self.Programica = {}

// preparing DOM prototypes
if (!self.Element)
	self.Element = {}

if (!self.Element.prototype)
	self.Element.prototype = document.createElement('div').__proto__ || {}

if (!self.HTMLFormElement)
	self.HTMLFormElement = {}

if (!self.HTMLFormElement.prototype)
	self.HTMLFormElement.prototype = document.createElement('form').__proto__ || {}


// base objects extensions
if (!Object.extend)
	Object.extend = function (d, s) { if (d) for (var p in s) d[p] = s[p]; return d }

if (!Object.copy)
	Object.copy = function (s) { var d = {}; for (var k in s) d[k] = s[k]; return d }

if (!Object.keys)
	Object.keys = function (s) { var r = []; for (var k in s) r.push(k); return r }

if (!Math.longRandom)
	Math.longRandom = function () { return (new Date()).getTime().toString() + Math.round(Math.random() * 1E+17) }

if (!String.localeCompare)
	String.localeCompare = function (a, b) { return a < b ? -1 : (a > b ? 1 : 0) }

if (!Array.prototype.forEach)
	Array.prototype.forEach = function (f, inv) { for (var i = 0, len = this.length; i < len; i++) f.call(inv, this[i], i, this) }

if (!Array.prototype.indexOf)
	Array.prototype.indexOf = function(v, i)
	{
		var len = this.length,
			i = Number(i) || 0
		i = (i < 0) ? (Math.ceil(i) + len) : Math.floor(i)
		
		for (; i < len; i++)
			if (i in this && this[i] === v)
				return i
		return -1
	}


if (!Array.prototype.map)
	Array.prototype.map = function(f, inv)
	{
		var len = this.length,
			res = new Array(len)
		for (var i = 0; i < len; i++)
			if (i in this)
				res[i] = f.call(inv, this[i], i, this)
		return res
	}

if (!Array.copy)
	Array.copy = function (src) { return Array.prototype.slice.call(src) }

if (!Function.prototype.delay)
	Function.prototype.delay = function (delay, args, inv) { var me = this; return setTimeout(function () { return me.apply(inv, args || arguments) }, delay || 10) }

if (!Function.prototype.bind)
	Function.prototype.bind = function (inv, args) { var me = this; return function () { me.apply(inv, args || arguments) } }


function $   (id)   { return document.getElementById(id) }
function $E  (type, props)
{
	var node = document.createElement(type)
	if (props)
		for (var i in props)
			node.setAttribute(i, props[i])
	return node
}

$.onload = function (fn) { return self.addEventListener('load', fn, false) }
$.include = function (src)
{
	var me = arguments.callee
	var cache = me.cache || (me.cache = {}) 
	if (me.cache[src])
		return me.cache[src]
	var node = document.createElement('script')
	node.type = 'text/javascript'
	node.src = src
	document.getElementsByTagName('head')[0].appendChild(node)
	cache[src] = node
	return node
}

if (!self.Programica)
	self.Programica = {}

// light infantile browser matching
Programica.userAgentRegExps =
{
	MSIE: /MSIE/,
	MSIE6: /MSIE 6/,
	MSIE7: /MSIE 7/,
	Gecko: /Gecko\//,
	Opera: /Opera/,
	Opera9: /Opera\/9/,
	Safari: /AppleWebKit/,
	Safari2: /AppleWebKit\/4/,
	Safari3: /AppleWebKit\/5/
}
        
Programica.htmlUserAgentSetter = function (doc, ua)
{
	doc = doc || document
	ua = ua || navigator.userAgent
	
	var htmlNode = doc.documentElement
	for (var p in this.userAgentRegExps)
		if (this.userAgentRegExps[p].test(ua))
			htmlNode.className = (htmlNode.className || '') + ' ' + p
}


// 1, 2, 5: банкир, банкира, банкиров
String.prototype.plural = Number.prototype.plural = function (a, b, c)
{
	if (this % 1)
		return b
	
	var v = Math.abs(this) % 100
	if (11 <= v && v <= 19)
		return c
	
	v = v % 10
	if (2 <= v && v <= 4)
		return b
	if (v == 1)
		return a
	
	return c
}

Date.rusMonths = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь']
Date.rusMonths2 = ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря']
Date.prototype.toRusDate = function () { return this.getDate() + ' ' + Date.rusMonths2[this.getMonth()] + ' ' + this.getFullYear() }

Humanize =
{
	adjustTextSize: function (nodes)
	{
		for (var i = 0; i < nodes.length; i++)
		{
			var node = nodes[i]
			
			if (node.scrollWidth > node.offsetWidth)
			{
				var text = node.firstChild,
					string = text.nodeValue
				node.realText = string
				text.nodeValue = string.substr(0, 16) + '…'
				node.title = string
			}
		}
	},
	
	adjustTextSizeOfNodes: function (root, selector)
	{
		var me = this
		setTimeout(function () { me.adjustTextSize(cssQuery(selector, root)) }, 1)
	}
}






// DOM для всех

;(function(){


var proto = Element.prototype

if (!document.getElementsByClassName)
{
	// from prototype 1.5.1.1
	proto.getElementsByClassName = document.getElementsByClassName = function (className, tagName)
	{
		// geting elems with native function
		var children = this.getElementsByTagName(tagName || '*')
		// predeclaring vars
		var elements = [], child, l = 0
		// precompile regexp
		var rex = new RegExp("(?:\\s+|^)" + className + "(?:\\s+|$)")
		// length is constant, so caching its value
		var len = children.length
		
		// memory for array of nodes will be allocated only once
		elements.length = len
		for (var i = 0; i < len; i++)
		{
			// even caching the reference for children[i] gives us some nanoseconds ;)
			child = children[i]
			// just rely on RegExp engine
			if (rex.test(child.className))
			{
				// simple assignment
				elements[l] = child
				// simple increment
				l++
			}
		}
		// truncating garbage length
		elements.length = l
		
		return elements
	}
}

proto.setClassName = function (cn)
{
	this.className = cn
	return cn
}

proto.addClassName = function (cn)
{
	this.remClassName(cn)
	this.className += ' ' + cn
	return cn
}

proto.remClassName = function (cn)
{
	if (this.className)
		this.className = this.className.replace(new RegExp('(?:\\s+|^)' + cn + '(?:\\s+|$)', 'g'), ' ').replace(/^\s+|\s+$/g, '')
	return cn
}

proto.hasClassName = function (cn)
{
	return (this.className == cn || (new RegExp('(?:\\s+|^)' + cn + '(?:\\s+|$)')).test(this.className))
}

proto.disable = function ()
{
	this.setAttribute('disabled', true)
	this.addClassName('disabled')
}

proto.enable = function ()
{
	this.removeAttribute('disabled')
	this.remClassName('disabled')
}

proto.empty = function ()
{
	var node
	while (node = this.firstChild)
		this.removeChild(node)
}

proto.show = function ()
{
	if (this.onshow)
	{
		if (typeof this.onshow == 'string')
			this.onshow = eval('function (event) { ' + this.onshow + ' }')
		
		if (this.onshow({}) === false)
			return false
	}
	
	this.style.visibility = 'visible'
	this.style.display = 'block'
	
	return true
}

proto.hide = function (t)
{
	if (this.onhide)
	{
		if (typeof this.onhide == 'string')
			this.onhide = eval('function (event) { ' + this.onhide + ' }')
		
		if (this.onhide({}) === false)
			return false
	}
	
	this.style.display = 'none'
	
	return true
}

proto.setVisible = function (show)
{
	if (show && !this.visible()) this.show()
	else if(!show) this.hide()
}

proto.visible = function ()
{
	return this.offsetWidth && this.style.display != 'none' && parseFloat(this.style.opacity) != 0
}

proto.toggle = function ()
{
	return this.visible() ? this.hide() : this.show()
}

proto.remove = function ()
{
	return this.parentNode ? this.parentNode.removeChild(this) : this
}

proto.getComputedStyle = function (prop)
{
	return document.defaultView.getComputedStyle(this, null)
}


})();
;(function () {

var PA = Programica.Animation = function (prms)
{
	prms = prms || {}

	// defaults
	this.trans	= []
	this.duration			= prms.duration || 1,
	this.unit				= prms.unit != null ? prms.unit : PA.defaults.unit
	this.motion				= prms.motion || PA.defaults.type
	this.running			= false
	this.complete			= false
	this.node				= prms.node || false
	this.animationTypes		= PA.Types
	
	this.onstart = this.oncomplete = this.onstep = function () {}
	
	switch (typeof this.motion)
	{
		case 'string':
			this.motion = this.animationTypes[this.motion] || null
			break
		case 'function':
			break
		default:
			this.motion = null
	}
	
	if (prms.trans)
		for (var i = 0; i < prms.trans.length; i++)
			if (prms.trans[i])
				this.trans.push(prms.trans[i])
}

Element.prototype.animate = function (motion, props, duration, unit)
{
	var trans = []
	for (var i in props)
	{
		var pi = props[i]
		if (pi.length == 2)
			trans.push({property: i, begin: pi[0], end: pi[1]})
		else
			trans.push({property: i, begin: null, end: pi[0] || pi})
	}
	return new PA({node: this, motion: motion, duration: duration, trans: trans, unit: unit}).start()
}


PA.fps = 60
PA.defaults = {unit: 'px', type: 'linearTween'}

PA.prototype =
{
	start: function ()
	{
		// if animation is already started
		if (this.running)
			return this
		
		// only one animation per node for now
		if (this.node.animation)
			this.node.animation.stop()
		this.node.animation = this
		
		this.running = true
		this.complete = false
		
		var t = this
		if (this.motion == this.animationTypes.directJump)
		{
			this.frame = this.totalFrames - 1
			setTimeout(function () { t.renderForceLast(); t.stop(); t.complete = true; t.oncomplete() }, 0)
		}
		else
		{
			this.frame = 0
			this.totalFrames = this.duration * PA.fps
			
			for (var i = 0; i < this.trans.length; i++)
			{
				var tr = this.trans[i]
				if (tr.begin == null)
					tr.begin = this.getStyleProperty(tr.property)
				tr.step = ( tr.end - tr.begin ) / this.totalFrames
			}
			
			this.timer = PA.addTimer( function () { t.step() } )
		}
		
		this.onstart()
		return this
	},
	
	stop: function ()
	{
		if (this.running)
		{
			this.node.animation = null
			this.running = false
			PA.removeTimer(this.timer)
		}
		
		return this
	},
	
	step: function ()
	{
		this.render()
		this.onstep()
		
		if (this.frame >= this.totalFrames - 1)
		{
			this.stop()
			this.complete = true
			this.oncomplete()
		}
		
		this.frame++
	},
	
	renderForceLast: function ()
	{
		for (var i = 0; i < this.trans.length; i++)
		{
			var t = this.trans[i]
			this.setStyleProperty(t.property, t.end)
		}
	},
	
	render: function ()
	{
		for (var i = 0; i < this.trans.length; i++)
		{
			var t = this.trans[i]
			if ( t.property != null && t.begin != null && t.end != null  )
				this.setStyleProperty(t.property, this.motion(this.frame + 1, t.begin, t.end - t.begin, this.totalFrames))
			else
				throw new Error("Corupted transformation: " + t)
		}
	},

	getStyleProperty: function (p)
	{
		if (p == "top" && !this.node.style[p])
			return this.node.offsetTop
		
		if (p == "left" && !this.node.style[p])
			return this.node.offsetLeft
		
		
		if (p == "opacity" && isNaN(parseFloat(this.node.style[p])))
			return 1
		
		
		if (/scroll/.test(p))
			return this.node[p]
		
		return parseFloat(this.node.style[p]) || 0
	},
	
	setStyleProperty: function (p, value)
	{
		try
		{
			if (/color/.test(p))
				return this.node.style[p] = 'rgb(' + parseInt(value) + ',' + parseInt(value) + ',' + parseInt(value) + ')'
			
			// for SVG elements
			if (p == 'r')
				return this.node.r.baseVal.value = value
			
			
			if (/scroll/.test(p))
				return this.node[p] = Math.round(value)
			
			if (p == "opacity")
				return this.node.style[p] = value
			
			if ((p == 'width' || p == 'height') && value < 0)
				value = 0
			
			if (this.unit == 'em')
				return this.node.style[p] = Math.round(value * 100) / 100 + this.unit
			
			return this.node.style[p] = Math.round(value) + this.unit
		}
		catch (ex)
		{
			log(ex + p + value)
			return value
		}
	}
}

PA.addTimer = function (func)
{
	if (!this.timer)
	{
		var t = this
		this.timer = setInterval(function (d) { t.time(d) }, 1000 / this.fps)
	}
	
	return this.timers.push(func)
}

PA.removeTimer = function (num)
{
	var ts = this.timers
	ts[num-1] = null
	
	while (ts[ts.length-1] === null)
		ts.length--
	
	if (!ts.length)
		clearInterval(this.timer), this.timer = null
}

PA.timers = []

PA.time = function (d)
{
	for (var i = 0; i < this.timers.length; i++)
		this.timers[i] && this.timers[i](d)
}


;(function(){var M=Math;var e=M.cos;var f=M.sin;var g=M.sqrt;var h=M.PI;var i=M.pow;var j=M.abs;var k=M.asin;var l=Programica.Animation.Types={directJump:function(t,b,c,d){},linearTween:function(t,b,c,d){return c*t/d+b},easeInQuad:function(t,b,c,d){return c*(t/=d)*t+b},easeOutQuad:function(t,b,c,d){return-c*(t/=d)*(t-2)+b},easeInOutQuad:function(t,b,c,d){return((t/=d/2)<1)?c/2*t*t+b:-c/2*((--t)*(t-2)-1)+b},easeInCubic:function(t,b,c,d){return c*(t/=d)*t*t+b},easeOutCubic:function(t,b,c,d){return c*((t=t/d-1)*t*t+1)+b},easeInOutCubic:function(t,b,c,d){return((t/=d/2)<1)?c/2*t*t*t+b:c/2*((t-=2)*t*t+2)+b},easeInQuart:function(t,b,c,d){return c*(t/=d)*t*t*t+b},easeOutQuart:function(t,b,c,d){return-c*((t=t/d-1)*t*t*t-1)+b},easeInOutQuart:function(t,b,c,d){return((t/=d/2)<1)?c/2*t*t*t*t+b:-c/2*((t-=2)*t*t*t-2)+b},easeInQuint:function(t,b,c,d){return c*(t/=d)*t*t*t*t+b},easeOutQuint:function(t,b,c,d){return c*((t=t/d-1)*t*t*t*t+1)+b},easeInOutQuint:function(t,b,c,d){return((t/=d/2)<1)?c/2*t*t*t*t*t+b:c/2*((t-=2)*t*t*t*t+2)+b},easeInSine:function(t,b,c,d){return-c*e(t/d*(h/2))+c+b},easeOutSine:function(t,b,c,d){return c*f(t/d*(h/2))+b},easeInOutSine:function(t,b,c,d){return-c/2*(e(h*t/d)-1)+b},easeInExpo:function(t,b,c,d){return(t==0)?b:c*i(2,10*(t/d-1))+b},easeOutExpo:function(t,b,c,d){return(t==d)?b+c:c*(-i(2,-10*t/d)+1)+b},easeInCirc:function(t,b,c,d){return-c*(g(1-(t/=d)*t)-1)+b},easeOutCirc:function(t,b,c,d){return c*g(1-(t=t/d-1)*t)+b},easeInOutCirc:function(t,b,c,d){return((t/=d/2)<1)?-c/2*(g(1-t*t)-1)+b:c/2*(g(1-(t-=2)*t)+1)+b},easeInBounce:function(t,b,c,d){return c-l.easeOutBounce(d-t,0,c,d)+b},easeInOutExpo:function(t,b,c,d){if(t==0)return b;if(t==d)return b+c;if((t/=d/2)<1)return c/2*i(2,10*(t-1))+b;return c/2*(-i(2,-10*--t)+2)+b},easeInElastic:function(t,b,c,d,a,p){if(t==0)return b;if((t/=d)==1)return b+c;if(!p)p=d*.3;if(a<j(c)){var s=p/4;a=c}else var s=p/(2*h)*k(c/a);return-(a*i(2,10*(t-=1))*f((t*d-s)*(2*h)/p))+b},easeOutElastic:function(t,b,c,d,a,p){if(t==0)return b;if((t/=d)==1)return b+c;if(!p)p=d*.3;if(a<j(c)){var s=p/4;a=c}else var s=p/(2*h)*k(c/a);return a*i(2,-10*t)*f((t*d-s)*(2*h)/p)+c+b},easeInOutElastic:function(t,b,c,d,a,p){if(t==0)return b;if((t/=d/2)==2)return b+c;if(!p)p=d*(.3*1.5);if(a<j(c)){a=c;var s=p/4}else var s=p/(2*h)*k(c/a);if(t<1)return-.5*(a*i(2,10*(t-=1))*f((t*d-s)*(2*h)/p))+b;else return a*i(2,-10*(t-=1))*f((t*d-s)*(2*h)/p)*.5+c+b},easeInBack:function(t,b,c,d,s){if(s==null)s=1.70158;return c*(t/=d)*t*((s+1)*t-s)+b},easeOutBack:function(t,b,c,d,s){if(s==null)s=1.70158;return c*((t=t/d-1)*t*((s+1)*t+s)+1)+b},easeInOutBack:function(t,b,c,d,s){if(s==null)s=1.70158;if((t/=d/2)<1)return c/2*(t*t*(((s*=(1.525))+1)*t-s))+b;else return c/2*((t-=2)*t*(((s*=(1.525))+1)*t+s)+2)+b},easeOutBounce:function(t,b,c,d){if((t/=d)<(1/2.75))return c*(7.5625*t*t)+b;else if(t<(2/2.75))return c*(7.5625*(t-=(1.5/2.75))*t+.75)+b;else if(t<(2.5/2.75))return c*(7.5625*(t-=(2.25/2.75))*t+.9375)+b;else return c*(7.5625*(t-=(2.625/2.75))*t+.984375)+b},easeInOutBounce:function(t,b,c,d){if(t<d/2)return l.easeInBounce(t*2,0,c,d)*.5+b;else return l.easeOutBounce(t*2-d,0,c,d)*.5+c*.5+b}}})();

})();

if (!self.Programica)
	self.Programica = {}

Programica.Request = function (prms)
{
	for (var p in prms)
		if (this[p] === undefined)
			this[p] = prms[p]
	
	// this.transport saves real request object
	
	if (self.XMLHttpRequest) // Gecko, WebKit, Presto...
	{
		try
		{
			this.transport = new XMLHttpRequest()
			if (this.transport.overrideMimeType)
				this.transport.overrideMimeType('application/xml')
		}
		catch (ex) {}
	}
	else if (self.ActiveXObject) // Trident
	{
		// microsux
		try { this.transport = new ActiveXObject("Msxml2.XMLHTTP") }
		catch (ex)
		{
			try { this.transport = new ActiveXObject("Microsoft.XMLHTTP") }
			catch (ex2) 
			{ 
				log("Can`t create neither Msxml2.XMLHTTP nor Microsoft.XMLHTTP: " + ex.messageText  + ", " + ex2.messageText ) 
			}
		}
	}
	
	if (this.transport)
	{
		var t = this
		this.transport.onreadystatechange = function ()
		{
			t.onreadystatechange()
		}
	}
	else
		log('Can`t create an instatce of the XMLHttpRequest')
}

Programica.Request.paramDelimiter = "&"

Programica.Request.urlEncode = function (data)
{
	if (!data) return ''
	
	// let object deside how to convert itself
	if (data.toUrlEncode)
		return data.toUrlEncode()
	
	switch (data.constructor)
	{
		case Array:
			return data.join(Programica.Request.paramDelimiter)
		
		case Object:
			var arr = []
			for (var i in data)
				if (i != undefined && data[i] != undefined)
					switch (data[i].constructor)
					{
						case Array:
							for (var j = 0, jl = data[i].length; j < jl; j++)
								arr.push(encodeURIComponent(i) + "=" + encodeURIComponent(data[i][j]))
							break
						default:
							arr.push(encodeURIComponent(i) + "=" + encodeURIComponent(data[i]))
							break
					}
			
			return arr.join(Programica.Request.paramDelimiter)
		
		default:
			return encodeURIComponent(data)
	}
}

Programica.Request.prototype =
{
	onLoad: function ()	{},
	
	// default error handler
	onError: function ()
	{
		log(this.errorMessage())
		return true
	},
	
	// this methods may return false to prevent calling onLoad()
	onInformation: function () {},
	onSuccess: function () {},
	onRedirect: function () {},
	
	// and this for onError()
	onClientError: function () {},
	onServerError: function () {},
	
	errorMessage: function () { return "Error while request " + this.lastRequest().uri + ": " + this.status() + " " + this.statusText() },
	
	//——————————————————————————————————————————————————————————————————————————
	// XMLHttpRequest methods wrappers
	
	open: function (method, uri, async, user, password)
	{
		this.lastRequestObject = {method:method,uri:uri,async:async,user:user,password:password}
		return this.transport.open(method, uri, async, user, password)
	},
	
	// transport.send() is wrapped in timer couse of #97
	send: function (data)
	{
		var t = this
		if (this.lastRequestObject.async)
			setTimeout(function () { t.transport.send(data) }, 0)
		else
			t.transport.send(data)
	},
	
	lastRequest: function () { return this.lastRequestObject },
	setRequestHeader: function (header, value) { return this.transport.setRequestHeader(header, value) },
	abort: function () { return this.transport.abort() },
	getAllResponseHeaders: function () { return this.transport.getAllResponseHeaders() },
	getResponseHeader: function (header) { return this.transport.getResponseHeader(header) },
	status: function () { return this.transport.status || 0 }, // 0 for direct file loading from filesystem
	statusText: function () { return this.transport.statusText },
	
	
	//——————————————————————————————————————————————————————————————————————————
	// XMLHttpRequest properties wrappers
	
	readyState: function () { return this.transport.readyState },
	responseText: function () { return this.transport.responseText },
	responseXML: function ()
	{
		var result = this.transport.responseXML
		
		// MSIE
		if(!result.documentElement && this.transport.responseStream)
			result.load(this.transport.responseStream)
		return result
	},
	
	onreadystatechange: function ()
	{
		if (this.readyState() == 4)
		{
			switch (Math.floor(this.status() / 100))
			{
				case 1:
					this.onInformation()
					break
				
				case 0:
				case 2:
					(this.onLoad() !== false) && this.onSuccess()
					break
				
				case 3:
					this.onRedirect()
					break
				
				case 4:
					(this.onError() !== false) && this.onClientError()
					break
				
				case 5:
					(this.onError() !== false) && this.onServerError()
					break
				
				default:
					log("Strange response status: " + this.status())
			}
		}
		
		return false
	}
}


//——————————————————————————————————————————————————————————————————————————————
// shortcuts

self.aPost = function (url, params)
{
	var r = new Programica.Request()
	if (!r) return null
	
	var data = Programica.Request.urlEncode(params)
	
	r.open('POST', url, true)
	r.setRequestHeader("Content-type", "application/x-www-form-urlencoded") // ; charset=utf-8
	r.setRequestHeader("Content-length", data.length)
	r.send(data)
	
	return r
}

self.sPost = function (url, params)
{
	var r = new Programica.Request()
	if (!r) return null
	
	var data = Programica.Request.urlEncode(params)
	
	r.open('POST', url, false)
	r.setRequestHeader("Content-type", "application/x-www-form-urlencoded") // ; charset=utf-8
	r.setRequestHeader("Content-length", data.length)
	r.send(data)
	
	return r
}



self.aGet = function (url, params)
{
	var r = new Programica.Request()
	if (!r) return null
	
	var data = Programica.Request.urlEncode(params)
	var delim = data ? url.indexOf('?') < 0 ? '?' : Programica.Request.paramDelimiter : ''
	
	r.open('GET', url + delim + data, true)
	r.send(null)
	
	return r
}

self.sGet = function (url, params, tail)
{
	var r = new Programica.Request()
	if (!r) return null
	
	var data = tail ? Programica.Request.urlEncode(params) + Programica.Request.paramDelimiter + Programica.Request.urlEncode(tail) : Programica.Request.urlEncode(params)
	var delim = data ? url.indexOf('?') < 0 ? '?' : Programica.Request.paramDelimiter : ''
	
	r.open('GET', url + (data ? delim + data : ''), false)
	r.send(null)
	
	return r
}


self.sPut = function (url, params, tail, data)
{
	var r = new Programica.Request()
	if (!r) return null
	if (!data) return null
	
	var params = tail ? Programica.Request.urlEncode(params) + Programica.Request.paramDelimiter + Programica.Request.urlEncode(tail) : Programica.Request.urlEncode(params)
	var delim = params ? url.indexOf('?') < 0 ? '?' : Programica.Request.paramDelimiter : ''
	
	r.open('POST', url + (params ? delim + params : ''), false)
	r.setRequestHeader("Content-type", "application/x-www-form-urlencoded") // ; charset=utf-8
	r.setRequestHeader("Content-length", data.length)
	r.send(data)
	
	return r
}


self.aHead = function (url, params)
{
	var r = new Programica.Request()
	if (!r) return null
	
	var data = Programica.Request.urlEncode(params)
	var delim = data ? url.indexOf('?') < 0 ? '?' : Programica.Request.paramDelimiter : ''
	
	r.open('HEAD', url + delim + data, true)
	r.send(null)
	
	return r
}

self.sHead = function (url, params)
{
	var r = new Programica.Request()
	if (!r) return false
	
	var data = Programica.Request.urlEncode(params)
	var delim = data ? url.indexOf('?') < 0 ? '?' : Programica.Request.paramDelimiter : ''
	
	r.open('HEAD', url + delim + data, false)
	r.send(null)
	
	return r
}

Programica.Request.transformFragment = function (xml, xsl, node, doc)
{
	doc = doc || document
	
	if (!node || !node.nodeName)
		node = doc.createElement(node)
	
	if (typeof xml.transformNode != 'undefined')
	{
		var markup = xml.transformNode(xsl)
		node.innerHTML = markup
	}
	else if (self.XSLTProcessor)
	{
		var processor = new XSLTProcessor()
		processor.importStylesheet(xsl)
		var fragment = processor.transformToFragment(xml, document)
		node.appendChild(fragment)
	}
	else
		throw new Error('Can`t find way to do XSL transformation')
	
	return node
}
// example: $('form').toHash()
HTMLFormElement.prototype.toHash = function (forse_array) { return Programica.Form.form2hash(this, forse_array) }
HTMLFormElement.prototype.fromHash = function (hash) { return Programica.Form.hash2form(hash, this) }

Programica.Form = {}

// Преобразование данных формы в хеш
Programica.Form =
{
	form2hash: function (f, forse_array)
	{
		var node, val, nn, hash = {}
		
		for (var i = 0; i < f.length; i++)
		{
			node = f.elements[i]
			nn = node.name
			
			// skip nodes with exect empty names
			if (nn === '')
				continue
			
			val = null
			switch (node.type)
			{
				case 'checkbox':
					val = node.checked ? node.value : null
					break
				
				case 'radio':
					val = node.checked ? node.value : null
					break
				
				case 'select-one':
					val = node.options[node.selectedIndex].value
					break
				
				case 'submit':
					break
				
				default:
					val = node.value
			}
			
			if (val != null)
			{
				if	(hash[nn] == null)
					hash[nn] = forse_array ? [val] : val
				else if (hash[nn].constructor == Array)
					hash[nn].push(val)
				else
					hash[nn] = [hash[nn], val]
			}
			else if (forse_array && !hash[nn])
				hash[nn] = []
		}
		
		return hash;
	},
	
	forceArrayFormHash: function (h)
	{
		var res = {}
		for (var k in h)
		{
			var v = h[k]
			if (typeof v === 'undefined')
				throw new Error('Can`t proceed undefined value for key "' + k + '"')
			else if (typeof v == 'string')
				res[k] = [String(v)]
			else if (v instanceof Array)
				res[k] = v.slice()
			else
				throw new Error('Can`t proceed key of constructor "' + v.constructor + '"')
		}
		return res
	},
	
	hash2form: function (h, f, forse_array)
	{
		if (!forse_array)
			h = this.forceArrayFormHash(h)
		
		for (var i = 0; i < f.length; i++)
		{
			var node = f.elements[i]
			var v
			
			if (v = h[node.name])
				switch (node.type)
				{
					case 'checkbox':
					case 'radio':
						if (v[0] == node.value)
							v.shift(), node.checked = 'checked'
						break
					
					case 'select-one':
						for (var j = 0; j < node.options.length; j++)
							if (v[0] == node.options[j].value)
							{
								node.selectedIndex = j
								v.shift()
								break
							}
						break
					
					case 'text':
					case 'textarea':
						node.value = v.shift()
						break
				}
		}
	}
}


self.addEventListener('load', function () { Programica.Widget.onLoader() }, false)

Programica.Widget = function () {}
Object.extend (Programica.Widget,
{
	registered: [],
	
	register: function (wgt)
	{
		this.registered.push(wgt)
	},
	
	sortby:
	{
		pri: function (a,b) { return a.pri - b.pri }
	},
	
	// search for widget nodes, sort it and call bind for each
	onLoader: function ()
	{
		if (this.thinkLoaded) return
		this.thinkLoaded = true
		
		// search notes into stack
		var stack = [];
		for (var wi in this.registered)
		{
			var w = this.registered[wi]
			
			var nodes = []
			if (w.mainNodeClassName)
				nodes = document.getElementsByClassName(w.mainNodeClassName)
			if (w.mainNodeTagName)
				nodes = document.getElementsByTagName(w.mainNodeTagName)
			
			for (var ni = 0; ni < nodes.length; ni++)
				stack.push
				(
					{
						w:w,
						node:nodes[ni],
						pri: (nodes[ni].getAttribute('pmc-widget-priority') || 0)
					}
				)
		}
		
		
		// sorting
		this.sorted = stack.sort(this.sortby.pri)
		
		for (var ni = 0; ni < this.sorted.length; ni++)
		{
			var n = this.sorted[ni]
			n.w.bind(n.node)
		}
	},
	
	// play in threads
	initJob: function ()
	{
		// bind
		for (var ni = 0; ni < this.sorted.length; ni++)
		{
			var n = this.sorted[ni]
			if (n.bint || n.error) continue
			
			try
			{
				n.w.bind(n.node)
			}
			catch (ex)
			{
				n.bint = false
				n.error = true
				
				throw ex
			}
			
			n.bint = true
			return
		}
		clearInterval(this.initInterval)
	}
})


// widgets base class
Programica.Widget.prototype =
{
	klass: 'Programica.Widget',
	bind: function (node) { (new this.Handler(node)).init() },
	toString: function () { return '[object ' + this.klass + ']' }
}

