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 () { document.documentElement.remClassName('loading') })

$.onready
(
	function ()
	{
		var nodes =
		{
			name: $('event-name'),
			promoBack: $('promo-back'),
			mark: $('mark'),
			previews: cssQuery('.previews')[0],
			previewSurface: cssQuery('.previews .viewport .surface')[0],
			illustration: $('illustration'),
			illustrationPopups: cssQuery('#illustration img')[0],
			rating: cssQuery('#comming .rating')[0],
			ratingHead: cssQuery('#comming h2')[0],
			ratingShowAll: cssQuery('#comming .list-all')[0],
			ratingFrom: cssQuery('#comming .from')[0],
			sponsorsLow: $('low-sponsors'),
			sponsorsLowContent: cssQuery('#low-sponsors .b-content')[0],
			sponsorsMedium: $('medium-sponsors'),
			sponsorsHighBlock: cssQuery('#main-sponsors')[0],
			sponsorsHighTitle: cssQuery('#main-sponsors .b-title h4')[0],
			sponsorsHigh: cssQuery('#main-sponsors .banner')[0],
			form: cssQuery('#form-popup form')[0],
			formPopup: $('form-popup'),
			formPopupOverlay: cssQuery('#form-popup #overlay')[0],
			formPopupContent: cssQuery('#form-popup .content')[0],
			formPopupMenu: cssQuery('#form-popup .menu')[0],
			formPopupFields: cssQuery('#form-popup .fields')[0],
			formPopupThanks: cssQuery('#form-popup .thanks')[0],
			formPopupHolding: cssQuery('#form-popup .holding')[0],
			formPopupNameInput: cssQuery('#form-popup input[name=event]')[0],
			formPopupHrefInput: cssQuery('#form-popup input[name=href]')[0],
			formPopupSubmit: cssQuery('#form-popup input[type=submit]')[0],
			variableInputs: cssQuery('#form-popup .variable')[0],
			getInvitation: [$('invitations-only'), cssQuery('.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)
	}
)


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 + ']' }
}

{(function () {
var P = Programica
var FormPoster = P.FormPoster = function () {}
var prototype = FormPoster.prototype = new P.Widget()

prototype.mainNodeTagName = 'form'
prototype.klass = 'Programica.FormPoster'
prototype.Handler = function (node)
{
	this.mainNode = node
	
	// where form.action is a node
	if (node.action && typeof node.action != 'string')
		throw new Error('Form element with name "action" masks form own attribute.')
	
	if (node.enctype && typeof node.enctype != 'string')
		throw new Error('Form element with name "enctype" masks form own attribute.')
	
	// for files upload
	if (/^multipart\/form\-data$/i.test(node.getAttribute('enctype')))
	{
		// check if this form needs to be proceeded
		if (!/^ajax$/i.test(node.target))
			return
		
		var frameName = 'id-' + Math.longRandom()
		
		var iframe = $E('iframe', {name: frameName, id: frameName})
		iframe.style.display = 'none'
		document.body.appendChild(iframe)
		iframe.src = 'about:blank'
		node.target = frameName
		
		FormPoster.bakeEvents(node)
		
		function firstOnLoad ()
		{
			this.removeEventListener('load', firstOnLoad, false)
			var me = this
			setTimeout(function () { me.addEventListener('load', function () { node.onsuccess({request:iframe}) }, false) }, 10)
		}
		
		iframe.addEventListener('load', firstOnLoad, false)
	}
	else
	{
		function sendListener (e)
		{
			// check if this form needs to be proceeded
			if (!/^ajax$/i.test(this.target))
				return
			
			e.preventDefault()
			
			// play in events
			var ev = {hash: this.toHash(), form: this}
			FormPoster.bakeEvents(node)
			if (this.oncheck(ev) === false)
				return
			
			this.addClassName('sending')
			this.onsend(ev)
			// acync sending data
			var met = /^post$/i.test(this.method) ? aPost : aGet,
				me = this,
				request
			with (request = met(this.action, ev.hash))
			{
				ev.request = request
				onLoad		= function () { me.remClassName('sending'); me.onload(ev) }
				onSuccess	= function () { me.onsuccess(ev) }
				onError		= function () { me.onerror(ev) }
			}
		}
		
		node.addEventListener('submit', sendListener,  false)
	}
}


prototype.Handler.prototype = { init: function () {} }


FormPoster.defaultEvents = ['onload', 'onsuccess', 'onerror', 'oncheck', 'onsend']
FormPoster.bakeEvents = function (node, events)
{
	events = events || this.defaultEvents
	for (var i = 0; i < events.length; i++)
		if (!node[events[i]] || node[events[i]].constructor == String)
			node[events[i]] = eval("[function (event) { " + node.getAttribute(events[i]) + " }]")[0]
}


P.Widget.register(new FormPoster())
})()}
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].remClassName('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)
	{
		for (var k in hash)
			if (!/\S/.test(hash[k]))
				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 (hash)
	{
		var view = this.owner.view
		view.showFormPopupThanks()
		view.resetForm()
		setTimeout(function () { view.hideFormPopup() }, 1200)
	},
	
	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) })
		
		var form = nodes.form
		form.oncheck = function (e) { return controller.formOnCheck(e.hash, e.form.variableFields) }
		form.onsuccess = function (e) { return controller.formSuccess(e.hash) }
		form.onsend = function (e) { return controller.formSend() }
		form.onload = function (e) { return controller.formLoad() }
		form.onerror = function (e) { return controller.formError(e.request.errorMessage()) }
	},
	
	readEvent: function ()
	{
		var 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.renderVariableFields(event.fields)
		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 Programica.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)
		{
			this.nodes.sponsorsLow.hide()
			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 Programica.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)
	},
	
	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()
		}
	},
	
	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 = 'column 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
		}
		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.remClassName('hidden')
					},
					500
				)
			}
			
			setInterval(animatePopups, 3200)
		}
		else
			illustrationPopups.remove()
	},
	
	renderVariableFields: function (fieldsSet)
	{
		if (!fieldsSet)
			return
		
		var root = this.nodes.variableInputs
		
		for (var i = 0; i < fieldsSet.length; i++)
		{
			var field = fieldsSet[i]
			var label = N('label')
			var input = N('input')
			input.type = 'text'
			input.name = field.name || field.label
			
			label.appendChild(T(field.label + ':'))
			label.appendChild(input)
			
			root.appendChild(label)
		}
		
		this.nodes.form.variableFields = fieldsSet
		this.resetForm()
	},
	
	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
		status ? button.disable() : button.enable()
	},
	
	resetForm: function ()
	{
		var nodes = this.nodes
		nodes.form.reset()
		nodes.formPopupNameInput.value = this.event.name
		nodes.formPopupHrefInput.value = this.event.href
		this.setFormLock(true)
	},
	
	startFormChecker: function ()
	{
		var me = this
		clearInterval(this.formCheckTimer)
		this.formCheckTimer = setInterval(function () { me.owner.controller.formTimeCheck(me.nodes.form.toHash(), 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)

})();


