EventPage =
{
	initialize: function (nodes, eventsDB)
	{
		var view = this.view,
			model = this.model,
			controller = this.controller
		
		model.owner = this
		view.owner = this
		controller.owner = this
		
		view.initialize(nodes)
		model.initialize(eventsDB)
		controller.initialize()
		
		view.readEvent()
	}
}

$.onready(function ()
{
	var de = document.documentElement
	de.removeClassName('loading')
	de.addClassName(guessBrowser(navigator.userAgent).join(' '))
	de.addClassName(guessOS(navigator.userAgent).join(' '))
})

function guessOS (ua)
{
	var m = /Windows/.exec(ua)
	if (m)
		return ['win']
	
	var m = /Macintosh|Mac OS/.exec(ua)
	if (m)
		return ['mac']
}

function guessBrowser (ua)
{
	function classes (n, a, b, c)
	{
		return [n, n + '-' + a, n + '-' + a + '-' + b, n + '-' + a + '-' + b + '-' + c]
	}
	
	var m = /Firefox\/(\d+)\.(\d+)\.(\d+)/.exec(ua)
	if (m)
		return classes('firefox', m[1], m[2], m[3])
	
	var m = /Opera\/\d+.+Version\/(\d+)\.(\d)(\d)/.exec(ua)
	if (m)
		return classes('opera', m[1], m[2], m[3])
	
	var m = /Opera\/(\d+)\.(\d)(\d)/.exec(ua)
	if (m)
		return classes('opera', m[1], m[2], m[3])
	
	var m = /MSIE (\d+)\./.exec(ua)
	if (m)
		return ['msie', 'msie-' + m[1]]
	
	var m = /Version\/(\d+)\.(\d+)\.(\d+) Safari\/\d+/.exec(ua)
	if (m)
		return classes('safari', m[1], m[2], m[3])
	
	var m = /Chrome\/(\d+)\.(\d+)\.(\d+)/.exec(ua)
	if (m)
		return classes('chrome', m[1], m[2], m[3])
	
	return []
}

$.onready
(
	function ()
	{
		var nodes =
		{
			name: $('event-name'),
			promoBack: $('promo-back'),
			mark: $('mark'),
			previews: $$('.previews')[0],
			previewSurface: $$('.previews .viewport .surface')[0],
			illustration: $('illustration'),
			illustrationPopups: $$('#illustration img')[0],
			rating: $$('#comming .rating')[0],
			ratingHead: $$('#comming h2')[0],
			ratingShowAll: $$('#comming .list-all')[0],
			ratingFrom: $$('#comming .from')[0],
			sidebar: $('sidebar'),
			sponsorsLow: $('low-sponsors'),
			sponsorsLowContent: $$('#low-sponsors .b-content')[0],
			sponsorsMedium: $('medium-sponsors'),
			sponsorsHighBlock: $$('#main-sponsors')[0],
			sponsorsHighTitle: $$('#main-sponsors .b-title h4')[0],
			sponsorsHigh: $$('#main-sponsors .banner')[0],
			form: $$('#form-popup form')[0],
			formPopup: $('form-popup'),
			formPopupOverlay: $$('#form-popup #overlay')[0],
			formPopupContent: $$('#form-popup .content')[0],
			formPopupMenu: $$('#form-popup .menu')[0],
			formPopupFields: $$('#form-popup .fields')[0],
			formPopupThanks: $$('#form-popup .thanks')[0],
			formPopupHolding: $$('#form-popup .holding')[0],
			formPopupNameInput: $$('#form-popup input[name=event]')[0],
			formPopupHrefInput: $$('#form-popup input[name=href]')[0],
			formPopupSubmit: $$('#form-popup input[type=submit]')[0],
			variableInputs: $$('#form-popup .variable')[0],
			getInvitation: [$('invitations-only'), $$('.about .sign-on')[0]]
		}
		
		// log(document.documentElement.appendChild($('form-popup')))
		
		// document.addEventListener('click', function () { alert(document.body.parentNode.scrollHeight + document.body.parentNode.scrollTop) })
		
		EventPage.initialize(nodes, Event)
	}
)


;(function(){

var myName = 'InputTip'

function Me () {}

Me.prototype =
{
	filledClassName: 'filled',
	focusedClassName: 'focused',
	
	bind: function (nodes)
	{
		this.nodes = nodes
		
		var me = this
		function onvalue (e) { setTimeout(function () { me.checkValue(e.target) }, 0) }
		function onfocus (e) { me.focus(e.target) }
		function onblur (e) { me.blur(e.target) }
		
		for (var i = 0, il = nodes.length; i < il; i++)
		{
			var node = nodes[i]
			
			node.addEventListener('keypress', onvalue, false)
			node.addEventListener('focus', onfocus, false)
			node.addEventListener('blur', onblur, false)
		}
		
		return this
	},
	
	checkValue: function (node)
	{
		var parent = node.parentNode
		if (node.value === '')
			parent.removeClassName(this.filledClassName)
		else
			parent.addClassName(this.filledClassName)
		
	},
	
	focus: function (node)
	{
		node.parentNode.addClassName(this.focusedClassName)
	},
	
	blur: function (node)
	{
		node.parentNode.removeClassName(this.focusedClassName)
	}
}

Me.className = myName
self[myName] = Me

})();


;(function(){

var myName = 'FormHelper', Me =
{
	toHash: function (form, fa)
	{
		var hash = {}, els = form.elements
		
		for (var i = 0, l = els.length; i < l; i++)
		{
			var node = els[i], name = node.name, val
			
			// skip nodes with the exact empty names
			if (name === '')
				continue
			
			switch (node.type)
			{
				case 'checkbox':
				case 'radio':
					if (node.checked)
						val = [node.value]
					else
						continue
					break
				
				case 'select-one':
					if (node.selectedIndex < 0)
						continue
					val = [node.options[node.selectedIndex].value]
					break
				
				case 'select-multiple':
					if (node.selectedIndex < 0)
						continue
					var options = node.options
					val = []
					for (var j = 0, jl = options.length; j < jl; j++)
					{
						var option = options[j]
						if (option.selected)
							val.push(option.value)
					}
					break
				
				default:
					val = [node.value]
			}
			
			if (name in hash)
				hash[name] = hash[name].concat(val)
			else
				hash[name] = val
		}
		
		if (!fa)
			for (var k in hash)
			{
				val = hash[k]
				if (val.length == 1)
					hash[k] = val[0]
			}
		
		return hash;
	}
}

self[myName] = Me

})();
;(function(){

var Me =
{
	decode: decodeURIComponent,
	encode: encodeURIComponent,
	paramDelimiter: '&',
	
	parse: function (string, forceArray)
	{
		var decode = this.decode
		var res = {}
		
		var parts = String(string).split(this.paramDelimiter)
		for (var i = 0; i < parts.length; i++)
		{
			var pair = parts[i].split('='),
				k = pair[0],
				v = pair[1]
			
			if (v === undefined)
			{
				if (k == '')
					continue
			}
			else
				v = decode(v)
			
			k = decode(k)
			
			if (forceArray)
			{
				if (res[k])
					res[k].push(v)
				else
					res[k] = [v]
			}
			else
			{
				var a = res[k]
				if (a)
				{
					if (typeof res[k] == 'object')
						res[k].push(v)
					else
						res[k] = [res[k], v]
				}
				else
					res[k] = v
			}
		}
		
		return res
	},
	
	stringify: function (data)
	{
		var encode = this.encode, A = Array,
			pairs = []
		
		for (var k in data)
		{
			var v = data[k]
			
			k = encode(k)
			if (v && v.constructor == A)
			{
				for (var i = 0, il = v.length; i < il; i++)
					pairs.push(k + '=' + encode(v[i]))
			}
			else
				pairs.push(k + '=' + encode(v))
		}
		
		return pairs.join(this.paramDelimiter)
	}
}

Me.className = 'UrlEncode'
self[Me.className] = Me

})();
;(function(){

var myName = 'Request',
	XHR = XMLHttpRequest,
	types = ['success', 'information', 'success', 'redirect', 'error', 'error']

function onreadystatechange ()
{
	if (this.readyState == 4)
	{
		this.statusType = types[Math.floor(this.status / 100)]
		if (this.callback)
			this.callback()
	}
}

XHR.UNSENT = 0
XHR.OPENED = 1
XHR.HEADERS_RECEIVED = 2
XHR.LOADING = 3
XHR.DONE = 4

var Me = self[myName] =
{
	onreadystatechange: onreadystatechange,
	charset: 'utf-8',
	post: function (url, params, callback, sync)
	{
		var r = new XHR()
		
		r.open('POST', url, !sync)
		r.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=' + this.charset)
		if (!sync)
			r.onreadystatechange = function () { onreadystatechange.call(r) } // wrapped for FF 2.0
		if (callback)
			r.callback = callback
		r.send(typeof params == 'string' ? params : UrlEncode.stringify(params))
		if (sync)
			onreadystatechange.call(r)
		
		return r
	},
	
	get: function (url, params, callback, sync)
	{
		var r = new XHR()
		
		if (params)
			url += (url.indexOf('?') != -1 ? UrlEncode.paramDelimiter : '?') + UrlEncode.stringify(params)
		
		r.open('GET', url, !sync)
		if (!sync)
			r.onreadystatechange = function () { onreadystatechange.call(r) } // wrapped for FF 2.0
		if (callback)
			r.callback = callback
		r.send(null)
		if (sync)
			onreadystatechange.call(r) // called for FF 3.5, 3.6
		
		return r
	}
}


})();

;(function () {

var M = Math, myName = 'Motion', globalTimer = GlobalTimer
function Me (begin, end, duration, motion, onstep, onstop)
{
	var me = this, frame = 0, total = M.ceil(duration * globalTimer.fps), delta = end - begin
	
	this.onstop = onstop
	this.onstep = onstep
	this.step = function ()
	{
		me.onstep(motion(frame, begin, delta, total))
		if (frame++ >= total)
			me.stop(true)
	}
	
}

Me.prototype =
{
	start: function ()
	{
		if (!this.running)
		{
			this.running = true
			globalTimer.remove(this.timer)
			this.timer = globalTimer.add(this.step) // step is already a prepared callback
		}
		return this
	},
	
	stop: function (comleted)
	{
		if (this.running)
		{
			this.running = false
			globalTimer.remove(this.timer)
			if (this.onstop)
				this.onstop(comleted)
		}
		return this
	}
}

self[myName] = Me

})();

// ============================================================================================
// Easing Equations v2.0
// September 1, 2003
// (c) 2003 Robert Penner, all rights reserved. 
// This work is subject to the terms in http://www.robertpenner.com/easing_terms_of_use.html.
// Ported to JavaScript by Peter Leonov for Liby.js motion ability
// ============================================================================================
Motion.types=
(function(){var M=Math,e=M.cos,f=M.sin,g=M.sqrt,h=M.PI,i=M.pow,j=M.abs,k=M.asin,l={directJump:function(t,b,c,d){return c},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 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 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 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}};return l})();
;(function () {

var M = Motion, myName = 'Animation'

function Me (node, motion, duration, trans, unit)
{
	this.node = node
	switch (typeof motion)
	{
		case 'string':
			var name = motion
			if (!(motion = M.types[name]))
				throw new Error('Unknown motion type name "' + name + '"')
			break
		case 'function':
			break
		default:
			throw new Error('Motion type must be a string or a function, got "' + typeof motion + '"')
	}
	this.motion = motion
	this.duration = duration
	this.trans = trans
	this.unit = unit
	this.running = false
	this.completed = 0
	this.motions = []
	
	var me = this
	function complete () { me.complete() }
	for (var i = 0; i < trans.length; i++)
	{
		var tr = trans[i]
		function bakeStep (tr)
		{
			return function (value)
			{
				Me.setStyleProperty(node, tr.property, value, unit)
			}
		}
		this.motions[i] = new M(tr.begin, tr.end, duration, motion, bakeStep(tr), complete)
	}
	
}

Me.defaults = {unit: 'px', motion: 'linearTween', duration: 1}

Element.prototype.animate = function (motion, props, duration, unit)
{
	var defaults = Me.defaults
	if (!motion)
		motion = defaults.motion
	if (!duration)
		duration = defaults.duration
	if (!unit)
		unit = defaults.unit
	
	var trans = []
	for (var k in props)
	{
		var prop = props[k]
		if (prop.length == 2)
			trans.push({property: k, begin: prop[0], end: prop[1]})
		else
			trans.push({property: k, begin: Me.getStyleProperty(this, k), end: prop[0] || prop})
	}
	return new Me(this, motion, duration, trans, unit).start()
}


Me.prototype =
{
	oncomplete: function () {},
	start: function ()
	{
		if (!this.running)
		{
			this.running = true
			
			var motions = this.motions
			for (var i = 0; i < motions.length; i++)
				motions[i].start()
		}
		return this
	},
	
	stop: function ()
	{
		if (this.running)
		{
			this.running = false
			
			var motions = this.motions
			for (var i = 0; i < motions.length; i++)
				motions[i].stop()
		}
		return this
	},
	
	complete: function ()
	{
		if (++this.completed >= this.motions.length)
			this.oncomplete()
	}
}

Me.getStyleProperty = function (node, p)
{
	if (p == "top" && !node.style[p])
		return node.offsetTop
	
	if (p == "left" && !node.style[p])
		return node.offsetLeft
	
	
	if (p == "opacity" && isNaN(parseFloat(node.style[p])))
		return 1
	
	
	if (/scroll/.test(p))
		return node[p]
	
	return parseFloat(node.style[p]) || 0
},

Me.setStyleProperty = function (node, p, value, unit)
{
	try
	{
		if (/color/.test(p))
			return node.style[p] = 'rgb(' + parseInt(value) + ',' + parseInt(value) + ',' + parseInt(value) + ')'
		
		// for SVG elements
		if (p == 'r')
			return node.r.baseVal.value = value
		
		
		if (/scroll/.test(p))
			return node[p] = Math.round(value)
		
		if (p == 'opacity')
			return node.style[p] = value
		
		if ((p == 'width' || p == 'height') && value < 0)
			value = 0
		
		if (unit == 'em')
			return node.style[p] = Math.round(value * 100) / 100 + unit
		
		return node.style[p] = Math.round(value) + unit
	}
	catch (ex)
	{
		log('setStyleProperty(' + arguments +'): ' + ex)
		return value
	}
}

self[myName] = Me

})();

function RollingImagesLite (node, conf)
{
	this.conf = {duration: 1, animationType: 'easeOutBack'}
	Object.extend(this.conf, conf)
	this.current = null
	this.mainNode = node
	this.mainNode.RollingImagesLite = this
	
	
	var t = this
	function mouseup (e)
	{
		e.preventDefault()
		clearInterval(t.svInt)
		document.removeEventListener('mouseup', mouseup, false)
	}
	
	this.prevmousedown = function (e)
	{
		e.preventDefault()
		clearInterval(t.svInt)
		t.goPrev()
		t.svInt = setInterval(function () { t.goPrev() }, t.conf.duration * 1000 * 0.5 + 150)
		document.addEventListener('mouseup', mouseup, false)
	}
	
	this.nextmousedown = function (e)
	{
		e.preventDefault()
		clearInterval(t.svInt)
		t.goNext()
		t.svInt = setInterval(function () { t.goNext() }, t.conf.duration * 1000 * 0.5 + 150)
		document.addEventListener('mouseup', mouseup, false)
	}
	
	this.sync()
	if (this.conf.goInit !== false)
		this.goInit()
}

RollingImagesLite.prototype =
{
	sync: function ()
	{
		this.viewport = this.my('viewport')[0]
		if (!this.viewport)
			throw new Error('Can`t find viewport for ' + this.mainNode)
		if (!this.viewport.animate)
			throw new Error('Viewport can`t be animated!')
		
		this.points = this.my('point')
		this.buttons = this.my('button')
		this.aPrev = this.my('prev')[0]
		this.aNext = this.my('next')[0]
		
		// if syncing when pushed
		clearInterval(this.svInt)
		
		var t = this
		if (this.aPrev)
			this.aPrev.addEventListener('mousedown', this.prevmousedown, false)
		
		if (this.aNext)
			this.aNext.addEventListener('mousedown', this.nextmousedown, false)
		
		for (var i = 0, il = this.buttons.length; i < il; i++)
			this.buttons[i].onmousedown = function (fi) { return function () { t.goToFrame(fi) } } (i)
		
		this.updateNavigation()
	},
	
	goPrev: function () { if (this.current > 0) this.goToFrame(this.current - 1) },
	goNext: function () { if (this.current < this.points.length - 1) this.goToFrame(this.current + 1) },
	my: function (cn) { return this.mainNode.getElementsByClassName(cn) },
	
	goInit: function ()
	{
		return this.goToFrame(0, 'directJump')
	},
	
	goToFrame: function (n, anim, dur) { return this.points ? this.goToNode(this.points[n || 0], anim, dur) : null },
	
	goToNode: function (node, anim, dur)
	{
		if (!node)
			return null
		
		if (this.mainNode.onjump)
			if (this.mainNode.onjump(node) === false)
				return null
		
		// change number of current node
		for (var i = 0, il = this.points.length; i < il; i++)
			if (this.points[i] == node)
				this.setCurrent(i)
		
		return this.animateTo(node.offsetLeft, node.offsetTop, anim, dur)
	},
	
	animateTo: function (left, top, anim, dur)
	{
		if (this.animation)
			this.animation.stop()
		return this.animation = this.viewport.animate(anim || this.conf.animationType, {scrollLeft: left, scrollTop: top}, dur || this.conf.duration)
	},
	
	jumpTo: function (left, top) { this.viewport.scrollLeft = left; this.viewport.scrollTop = top },
	jumpToFrame: function (n)
	{
		var node = this.points[n]
		if (node)
		{
			this.setCurrent(n)
			this.jumpTo(node.offsetLeft, node.offsetTop)
		}
	},
	
	updateNavigation: function ()
	{
		for (var i = 0, il = this.buttons.length; i < il; i++)
			this.buttons[i].removeClassName('selected-button')
		
		var button = this.buttons[this.current]
		if (button)
			button.addClassName('selected-button')
		
		if (this.aPrev)
		{
			if (this.current > 0)
				this.aPrev.removeClassName('disabled')
			else
				this.aPrev.addClassName('disabled')
		}
		
		if (this.aNext)
		{
			if (this.current < this.points.length - 1)
				this.aNext.removeClassName('disabled')
			else
				this.aNext.addClassName('disabled')
		}
	},
	
	setCurrent: function (num)
	{
    // if (num == this.current)
		//	return
		
		this.current = num
		this.updateNavigation()
		
		var cp = this.points[this.current]
		if (this.onselect)
			this.onselect(cp, this.current)
		
		if (cp && cp.onselect)
			cp.onselect()
	}
}

;(function(){

var Me =
{
	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($$(selector, root)) }, 1)
	}
}

Me.className = 'Humanize'
self[Me.className] = Me

})();

Switcher =
{
	bind: function (main, buttons, tabs, names)
	{
		if (!main || !buttons || !tabs)
			throw new Error('main, buttons or tabs are not defined: ' + [!!main, !!buttons, !!tabs].join(', '))
		main.nodes = {buttons: Array.copy(buttons), tabs: Array.copy(tabs)}
		main.names = names || []
		
		main.onselect = function () {}
		main.setTabs = function (tabs) { this.nodes.tabs = tabs }
		main.setNames = function (names) { this.names = names }
		main.select = function (num)
		{
			if (typeof num != 'number')
				num = this.names.indexOf(num)
			
			if (num < 0 || this.onselect(num) === false)
				return
			
			this.drawSelected(num)
		}
		main.drawSelected = function (num)
		{
			if (typeof num != 'number')
				num = this.names.indexOf(num)
			
			if (num < 0)
				return
			
			var buttons = this.nodes.buttons
			for (var i = 0; i < buttons.length; i++)
				if (buttons[i])
					num == i ? buttons[i].addClassName('selected') : buttons[i].removeClassName('selected')
			
			var tabs = this.nodes.tabs
			if (tabs && tabs[num])
				for (var i = 0; i < tabs.length; i++)
					if (tabs[i])
						num == i ? tabs[i].show() : tabs[i].hide()
		}
		
		function isParent (node, parent, root)
		{
			do
			{
				// log(node)
				if (node == parent)
					return true
				if (node == root)
					return false
			}
			while (node = node.parentNode)
			
			return false
		}
		
		function mouseSelect (e)
		{
			var buttons = this.nodes.buttons
			var num = -1
			for (var i = 0; i < buttons.length; i++)
				if (isParent(e.target, buttons[i], this))
					num = i
			
			return this.select(num)
		}
		main.addEventListener('mousedown', mouseSelect, false)
		
		return main
	}
}



EventPage.model =
{
	owner: null, // must be defined before initialize
	
	initialize: function (eventsDB)
	{
		this.eventsDB = eventsDB
	},
	
	setState: function (state)
	{
		var db = this.eventsDB,
			event = db.getByName(state.name)[0],
			all = db.getByType(event.type)
		
		this.owner.view.modelChanged(event, all)
	}
}
EventPage.controller =
{
	owner: null, // must be defined before initialize
	
	initialize: function () {},
	
	setEventName: function (name)
	{
		this.owner.model.setState({name: name})
	},
	
	formPopupCloseClicked: function ()
	{
		this.owner.view.hideFormPopup()
	},
	
	formPopupOpenClicked: function ()
	{
		this.owner.view.showFormPopup()
	},
	
	checkTheForm: function (hash, fields)
	{
		for (var i = 0, il = fields.length; i < il; i++)
			if (!/\S/.test(hash[fields[i]]))
				return false
		
		return true
	},
	
	formOnCheck: function (hash, fields)
	{
		return this.checkTheForm(hash, fields)
	},
	
	formTimeCheck: function (hash, fields)
	{
		this.owner.view.setFormLock(!this.checkTheForm(hash, fields))
	},
	
	formSend: function ()
	{
		this.owner.view.stopFormChecker()
		this.owner.view.setFormLock(true)
	},
	
	formLoad: function ()
	{
		this.owner.view.setFormLock(false)
	},
	
	formSuccess: function ()
	{
		var view = this.owner.view
		view.showFormPopupThanks()
		// setTimeout(function () { view.hideFormPopup() }, 10000)
	},
	
	formError: function (message)
	{
		reportError(message)
	},
	
	ratingShowAllClicked: function ()
	{
		this.owner.view.showAllRating()
	}
}

{(function(){

var doc = document
function N (name, classN) { var res = doc.createElement(name); if(classN) res.className = classN; return res; }
function T (text) { return doc.createTextNode(text) }

Number.prototype.toTime = function ()
{
	var m = /([+\-]?\d+)(?:\.(\d+))?/.exec(this) //.oString var mins = this & -1,
	return m[1] + ':' + (m[2] === undefined ? '00' : (m[2].length <= 1 ? '0' + m[2] : m[2]))
}

Date.prototype.getFormatted = function(withYear){
	var weekdays = ["воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"]
	var months = ["января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"] 
	return this.getDate() + " " + months[this.getMonth()] + (withYear ? " " + this.getFullYear() : ", " + weekdays[this.getDay()])
}


EventPage.view =
{
	owner: null, // must be defined before initialize
	
	initialize: function (nodes)
	{
		this.nodes = nodes
		var me = this,
			controller = this.owner.controller
	},
	
	bindRaiting: function ()
	{
		var me = this, nodes = this.nodes,
			controller = this.owner.controller
		
		nodes.ratingShowAll.addEventListener('click', function () { controller.ratingShowAllClicked() }, false)
		
		nodes.ratingShowAll.hide = nodes.formPopupFields.hide = nodes.formPopupThanks.hide = function () { this.style.visibility = 'hidden' }
		nodes.ratingShowAll.show = nodes.formPopupFields.show = nodes.formPopupThanks.show = function () { this.style.visibility = 'visible' }
	},
	
	bindFormPopup: function ()
	{
		var me = this, nodes = this.nodes,
			controller = this.owner.controller
		
		function formPopupCloseClicked () { controller.formPopupCloseClicked() }
		nodes.formPopupOverlay.addEventListener('click', formPopupCloseClicked, false)
		nodes.formPopupMenu.addEventListener('click', formPopupCloseClicked, false)
		
		function formPopupOpenClicked () { controller.formPopupOpenClicked() }
		nodes.getInvitation.forEach(function (v) { if (v) v.addEventListener('click', formPopupOpenClicked, false) })
		
		
		function sendListener (e)
		{
			e.preventDefault()
			
			controller.formSend()
			
			function sent ()
			{
				if (this.statusType == 'success')
				{
					controller.formSuccess()
				}
				else
				{
					alert('Произошла ошибка! Пожалуйста, сообщите о ней по адресу support@inshaker.ru')
				}
				
				controller.formLoad()
			}
			
			Request.post(this.action, FormHelper.toHash(this), sent)
		}
		nodes.form.addEventListener('submit', sendListener,  false)
	},
	
	readEvent: function ()
	{
		var name = this.nodes.name.getAttribute('data-name') || this.nodes.name.firstChild.nodeValue
		this.owner.controller.setEventName(name)
	},
	
	modelChanged: function (event, previewSet)
	{
		this.event = event
		this.root = event.pageHref()
		
		this.bindRaiting()
		
		if (event.status == 'preparing')
			this.bindFormPopup()
		
		this.renderPreviews(previewSet, event)
		
		this.renderDialogue(event.dialogue)
		this.renderRating(event.rating)
		this.renderRatingHead(event.rating)
		this.renderLowSponsors(event.low)
		this.renderMediumSponsors(event.medium)
		this.renderHighSponsors(event.high)
		this.setupForm(event)
		// this.showFormPopup()
		// this.showFormPopupThanks()
		this.setFormLock(true)
	},
	
	renderPreviews: function(events, selectedEvent)
	{
		var surface = this.nodes.previewSurface, previews = this.nodes.previews
		
		events = events.sort(Event.dateSort)
		
		// find nearest
		var now = new Date(),
			past = [], future = []
		for (var i = 0; i < events.length; i++)
		{
			var event = events[i]
			if (event.date < now)
				past.push(event)
			else
				future.push(event)
		}
		
		surface.empty()
		var pointNum = -1, pastDiff = 4 - past.length % 4
		
		for (var i = 0; i < past.length; i++)
		{
			var event = past[i],
				selected = selectedEvent == event
			
			if (!point || (i + pastDiff) % 4 == 0)
			{
				pointNum++
				var point = N('li', 'point past')
				surface.appendChild(point)
			}
			if (selected)
				var selectedPoint = pointNum
				
			point.appendChild(this.createPreviewElement(event, selected))
		}
		
		
		for (var i = 0; i < future.length; i++)
		{
			var event = future[i],
				selected = selectedEvent == event
			
			if (i % 4 == 0)
			{
				pointNum++
				var point = N('li', 'point future')
				surface.appendChild(point)
			}
			if (selected)
				var selectedPoint = pointNum
				
			point.appendChild(this.createPreviewElement(event, selected))
		}
		
		new RollingImagesLite(previews, {animationType: 'easeOutQuad', goInit: false}).jumpToFrame(selectedPoint)
	},
	
	createPreviewElement: function(event, selected)
	{   
		var main
		
		if (selected)
		{
			main = N('span', 'event selected')
			main.appendChild(N('span', 'mark'))
		}
		else
		{
			main = N('a', 'event')
			main.href = event.pageHref()
		}
		
		var mini = N('span', 'mini')
		mini.style.backgroundImage = 'url(' + event.pageHref() + '/preview.jpg)'
		
		var mask = N('span', 'mask')
		mini.appendChild(mask)
		main.appendChild(mini)
		
		var date = N('span', 'date')
		date.appendChild(T(event.adate || event.date.getFormatted()))
		main.appendChild(date)
		
		var desc = N('span', 'desc')
		desc.appendChild(T(event.name))
		main.appendChild(desc)
		
		return main
	},
	
	renderLowSponsors: function (sponsorsSet)
	{
		if (sponsorsSet.length == 0)
		{
			return
		}
		
		var root = this.nodes.sponsorsLowContent
		root.empty()
		
		var main = N('dl')
		root.appendChild(main)
		
		var tabs = []
		var buttons = []
		
		var all = []
		for (var i = 0; i < sponsorsSet.length; i++)
		{
			var logos = sponsorsSet[i].logos
			for (var j = 0; j < logos.length; j++)
				all.push(logos[j])
		}
		sponsorsSet.unshift({name: '*', logos: all})
		
		for (var i = 0; i < sponsorsSet.length; i++)
		{
			var part = sponsorsSet[i]
			var dt = N('dt')
			var a = N('a')
			a.appendChild(T(part.name.replace(/\s+/g, ' ')))
			dt.appendChild(a)
			
			var dd = N('dd')
			
			var node = this.createLowSponsorNode(part.logos)
			dd.appendChild(node)
			
			main.appendChild(dt)
			main.appendChild(T(' '))
			main.appendChild(dd)
			main.appendChild(T(' '))
			
			buttons.push(dt)
			tabs.push(dd)
			
			new RollingImagesLite(node, {animationType: 'easeOutQuad'})
			dd.hide = function () { this.style.visibility = 'hidden' }
			dd.show = function () { this.style.visibility = 'visible' }
		}
		
		var spacer = N('dt')
		spacer.className = 'spacer'
		spacer.appendChild(T(' '))
		main.appendChild(spacer)
		
		Switcher.bind(main, buttons, tabs)
		main.select(0)
		
		this.nodes.sponsorsLow.addClassName('visible')
	},
	
	createLowSponsorNode: function (logosSet)
	{
		var root = N('div')
		root.className = 'programica-rolling-images'
		
		var prev = N('a')
		prev.className = 'prev'
		var next = N('a')
		next.className = 'next'
		
		var viewport = N('div')
		viewport.className = 'viewport'
		
		var surface = N('ul')
		surface.className = 'surface'
		viewport.appendChild(surface)
		
		for (var i = 0; i < logosSet.length; i++)
		{
			var logo = logosSet[i]
			
			if (i % 5 == 0)
			{
				var point = N('li')
				point.className = 'point'
			}
			
			if (logo)
			{
				var a = N('a')
				a.href = logo.href
				point.appendChild(a)
				point.appendChild(T(' '))
				
				var img = N('img')
				a.appendChild(img)
				img.src = this.root + '/logos/' + logo.src
			}
			else
			{
				point.appendChild(N('b')).appendChild(T(' '))
			}
			
			
			
			if (i % 5 == 4)
				point.appendChild(N('b')).appendChild(T(' '))
			
			surface.appendChild(point)
		}
		
		root.appendChild(prev)
		root.appendChild(next)
		root.appendChild(viewport)
		
		return root
	},
	
	renderRating: function (rating)
	{
		if (!rating || !rating.data)
			return
		
		var nodes = this.nodes,
			root = nodes.rating
		
		
		var data = rating.data, type = rating.type, sorted = Object.keys(data), max, min
		
		if (rating.reverse)
		{
			// a - b
			sorted = sorted.sort(function (a, b) { return data[a] - data[b] || a.localeCompare(b) })
			max = data[sorted[sorted.length-1]],
			min = data[sorted[0]]
		}
		else
		{
			// b - a
			sorted = sorted.sort(function (a, b) { return data[b] - data[a] || a.localeCompare(b) }),
			max = data[sorted[0]],
			min = data[sorted[sorted.length-1]]
		}
		
		var k = max > min ? 100 / (max - min) : 0 // 100 means 100%
		
		root.empty()
		
		var labels = N('div', 'labels'),
			rates = N('div', 'rates')
		
		root.appendChild(labels)
		root.appendChild(rates)
		
		for (var i = 0; i < sorted.length; i++)
		{
			var name = sorted[i],
				count = data[name],
				text = type === 'comp' ? count.toTime() : count.toString(),
				start = text.length * 3.4, // means 3.4% for a digit
				width = Math.floor((count - min) * k)
			
			if (width < start)
				width = start
			
			var label = N('div', 'label')
			label.appendChild(T(name))
			labels.appendChild(label)
			
			var rate = N('div', 'rate')
			rate.style.width = start + '%'
			rate.animate('easeInOutQuad', {width: [start, width]}, 1, '%')
			rate.appendChild(T(text))
			rates.appendChild(rate)
		}
		
		Humanize.adjustTextSizeOfNodes(labels, 'div')
		
		if (sorted.length > rating.max)
		{
			root.style.height = rating.max * 18 + 'px'
			this.nodes.ratingShowAll.show()
		}
		
		nodes.sidebar.addClassName('visible')
	},
	
	showAllRating: function ()
	{
		var root = this.nodes.rating
		root.animate('easeOutQuad', {height:root.scrollHeight}, 0.5)
		this.nodes.ratingShowAll.hide()
	},
	
	renderRatingHead: function (rating)
	{
		var totalPeople = 0, totalFrom = 0, data = rating.data
		
		if (rating.type == 'comp')
		{
			for (var k in data)
				totalPeople++
		}
		else
			for (var k in data)
				totalPeople += data[k],
				totalFrom++
		
		var ratingHead = this.nodes.ratingHead
		if (rating.phrase)
			ratingHead.innerHTML = rating.phrase.interpolate({people: totalPeople, from: totalFrom})
	},
	
	renderMediumSponsors: function (sponsorsSet)
	{
		var root = this.nodes.sponsorsMedium
		root.empty()
		
		for (var i = 0; i < sponsorsSet.length; i++)
		{
			var sponsor = sponsorsSet[i]
			
			var a = N('a')
			a.className = 'sponsor'
			a.href = sponsor.href
			
			var img = N('img')
			img.src = this.root + '/logos/' + sponsor.src
			img.alt = sponsor.name
			a.appendChild(img)
			
			root.appendChild(a)
		}
	},
	
	renderHighSponsors: function (sponsorsSet)
	{
		var nodes = this.nodes
		
		var sponsor = sponsorsSet[0]
		if (sponsor)
		{
			if (nodes.sponsorsHighTitle)
				nodes.sponsorsHighTitle.innerHTML = sponsor.name
			nodes.sponsorsHigh.style.backgroundImage = 'url(' + this.root + '/logos/' + sponsor.src + ')'
			nodes.sponsorsHigh.href = sponsor.href
			nodes.sidebar.addClassName('visible')
		}
		else
		{
			nodes.sponsorsHighBlock.hide()
		}
	},
	
	renderDialogue: function (dialogue)
	{
		dialogue = dialogue ? dialogue[Math.floor(dialogue.length * Math.random())] || dialogue[0] : null
			
		var nodes = this.nodes,
			illustration = nodes.illustration,
			illustrationPopups = nodes.illustrationPopups
		
		if (!dialogue)
			return illustration.remove()
		
		illustration.style.backgroundImage = 'url(' + this.root + '/dialogues/' + dialogue.back + ')'
		
		if (dialogue.popups)
		{
			illustrationPopups.src = this.root + '/dialogues/' + dialogue.popups
			
			function animatePopups ()
			{
				illustrationPopups.addClassName('hidden')
				setTimeout
				(
					function ()
					{ 
						if (illustration.scrollTop + 300 >= illustration.scrollHeight)
							illustration.scrollTop = 0
						else
							illustration.scrollTop += 300
						
						illustrationPopups.removeClassName('hidden')
					},
					500
				)
			}
			
			setInterval(animatePopups, 3200)
		}
		else
			illustrationPopups.remove()
	},
	
	setupForm: function (event)
	{
		var fieldsSet = event.fields
		if (!fieldsSet)
			return
		
		var nodes = this.nodes
		
		var root = nodes.variableInputs
		
		var variableFields = nodes.form.variableFields = []
		
		var inputs = []
		for (var i = 0; i < fieldsSet.length; i++)
		{
			var field = fieldsSet[i]
			
			var label = N('label')
			label.appendChild(N('span', 'label')).appendChild(T(field.label + ':'))
			
			var input
			if (field.type == 'textarea')
			{
				input = N('textarea')
				label.addClassName('big')
			}
			else
			{
				input = N('input')
				input.type = 'text'
			}
			
			inputs[i] = input
			
			input.name = field.name || field.label
			variableFields.push(input.name)
			label.appendChild(input)
			
			var t = field.tip
			if (t)
			{
				// input.value = t
				var tip = N('span', 'tip')
				tip.appendChild(T(t))
				label.appendChild(tip)
			}
			
			root.appendChild(label)
		}
		
		new InputTip().bind(inputs)
		
		var thanks = nodes.formPopupThanks
		if (!thanks.hasClassName('default'))
		{
			var input = inputs[i] = N('input')
			input.type = 'hidden'
			input.name = 'sent-message'
			input.value = thanks.innerHTML
			
			root.appendChild(input)
		}
		
		nodes.formPopupNameInput.value = event.name
		nodes.formPopupHrefInput.value = event.href
	},
	
	showFormPopup: function ()
	{
		this.startFormChecker()
		this.nodes.formPopup.show()
		this.nodes.formPopupContent.style.top = (document.documentElement.scrollTop || document.body.scrollTop) + 'px'
		// this.hideFormPopupThanks()
	},
	
	hideFormPopup: function ()
	{
		this.stopFormChecker()
		this.nodes.formPopup.hide()
	},
	
	showFormPopupThanks: function ()
	{
		this.stopFormChecker()
		this.nodes.formPopupFields.hide()
		this.nodes.formPopupThanks.show()
	},
	
	hideFormPopupThanks: function ()
	{
		this.startFormChecker()
		this.nodes.formPopupThanks.hide()
		this.nodes.formPopupFields.show()
	},
	
	setFormLock: function (status)
	{
		var button = this.nodes.formPopupSubmit
		if (status)
		{
			button.addClassName('disabled')
			button.setAttribute('disabled', true)
		}
		else
		{
			button.removeAttribute('disabled')
			button.removeClassName('disabled')
		}
	},
	
	startFormChecker: function ()
	{
		var me = this
		clearInterval(this.formCheckTimer)
		this.formCheckTimer = setInterval(function () { me.owner.controller.formTimeCheck(FormHelper.toHash(me.nodes.form), me.nodes.form.variableFields) }, 200)
	},
	
	stopFormChecker: function ()
	{
		clearInterval(this.formCheckTimer)
	}
}

})()}


;(function(){

// fast and easy but looks terrible
function parseOutJS (src)
{
	// this trick is for IE only
	/*@cc_on src = src.split('') @*/
	var i = 0, len = src.length,
		res = [], buf = ''
	
	// string
	while (i < len)
	{
		var c = src[i++]
		// backslash
		if (c === '\\')
		{
			buf += src[i++]
			continue
		}
		
		// maybe code
		if (c === '$')
		{
			c = src[i++]
			// block
			if (c === '{')
			{
				res.push(buf)
				buf = ''
				var open = 1
				while (i < len)
				{
					c = src[i++]
					// string ""
					if (c === '"')
					{
						buf += c
						while (i < len)
						{
							c = src[i++]
							// backslash
							if (c === '\\') { buf += src[i++]; continue }
							// end of string ""
							if (c === '"') { break }
							buf += c
						}
					}
					// string ''
					if (c === "'")
					{
						buf += c
						while (i < len)
						{
							c = src[i++]
							// backslash
							if (c === '\\') { buf += src[i++]; continue }
							// end of string ""
							if (c === "'") { break }
							buf += c
						}
					}
					// block
					if (c === '{') { open++ }
					// end of block
					else if (c === '}' && !--open) { res.push(buf); buf = ''; break}
					 
					// if (c === '\\') { i++; continue }
					buf += c
				}
			}
			// just one dollar ;)
			else
				buf += '$' + c
		}
		else
			buf += c
	}
	
	res.push(buf)
	
	return res
}

// bake() is used to increase compression level by placing nomungeble variables in one closure
function bake ($_$h, $_$s, $_$o)
{
	"$_$h:nomunge, $_$s:nomunge, $_$o:nomunge"
	return eval('(0, function($_$h){with($_$h){return ' + $_$o.join('+') + '}})')
}

var cache = {}
function interpolate (h)
{
	var i, b, s, o, f = cache[this]
	if (!f)
	{
		b = parseOutJS(this), s = [], o = []
		
		s[0] = b[0]
		o[0] = '($_$s[0])'
		
		for (i = 1; i < b.length; i+=2)
		{
			s.push(b[i+1])
		
			o.push('(' + b[i] + ')')
			o.push('$_$s[' + (i + 1)/2 + ']')
		}
		
		if (!b[b.length-1])
			o.length--
		try { f = cache[this] = bake(h, s, o) }
		catch (ex)
		{
			log('Error while compiling string: "' + this + '"')
			throw ex
		}
	}
	try { return f.apply(this, arguments) }
	catch (ex)
	{
		log('Error while executing string: "' + this + '"')
		return null
	}
}

interpolate.cache = cache
String.prototype.interpolate = interpolate
String.parseOutJS = parseOutJS

// log('1${a}2${b}3${c}4'.interpolate({a:'A',b:'B',c:'C'}) === '1A2B3C4')
// log('${a/*{"a{\\\'a}\\"}a"b}c*/} wor$ld ${n/*{{{"\'"}}}n\'n\'*/}'.interpolate({a:111,n:222}) === '111 wor$ld 222')
// log('${a} text ${a} ${a} text ${b} ${b}'.interpolate({a:2222, b: 333}) === '2222 text 2222 2222 text 333 333')
// log('${a} text ${a} ${a} text ${b} ${b} text'.interpolate({a:2222, b: 333}) === '2222 text 2222 2222 text 333 333 text')

// // test for parseOutJS
// var blocks = parseOutJS('"hello" \\${x} ${a{"a{\\\'a}\\"}a"b}c} wor$ld ${n{{{"\'"}}}n\'n\'}')
// log(blocks[0] === '"hello" ${x} ')
// log(blocks[1] === 'a{"a{\'a}"}a"b}c')
// log(blocks[2] === ' wor$ld ')
// log(blocks[3] === 'n{{{"\'"}}}n\'n\'')
// log(blocks[4] === '')
// log(blocks[5] === undefined)

})();


