BarPage =
{
	initialize: function (nodes, barsDB, cocktailsDB)
	{
		this.view.owner = this
		this.controller.owner = this
		this.model.owner = this
		
		this.view.initialize(nodes)
		this.model.initialize(barsDB, cocktailsDB)
		
		this.view.readBarCityNames()
		
		this.view.bindBrandingScroller()
	}
}

$.onready
(
	function ()
	{
		UserAgent.setupDocumentElementClassNames()
		
		var nodes =
		{
			page: $('bar-page'),
			brandedImageHolder: $('branded-image-holder'),
			photos:
			{
				root:            $$('.photos')[0],
				viewport:        $$('.photos .viewport')[0],
				surface:         $$('.photos .surface')[0],
				prev:            $$('.photos .prev')[0],
				next:            $$('.photos .next')[0],
				items:           $$('.photos .point')
			},
			shareBox:
			{
				root: $('share-box'),
				buttons: $$('#share-box .button')
			},
			hitBox: $$('#main-column .info .hit .body')[0],
			barName: $('bar-name'),
			cityName: $('city-name'),
			map: $('map'),
			positionControl: $$('.position-control')[0],
			barPrev: $$('#main-column .common-title .navigation .prev')[0],
			barNext: $$('#main-column .common-title .navigation .next')[0]
		}
		RoundedCorners.round(nodes.photos.root)
		BarPage.initialize(nodes, Bar, Cocktail)
	}
)

;(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(){

// 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, $_$code)
{
	"$_$h:nomunge, $_$s:nomunge, $_$code:nomunge"
	return eval('(0, function($_$h){with($_$h||{}){return ' + $_$code + '}})')
}

function compile (str, hash)
{
	var b = parseOutJS(str), bl = b.length
	
	// string has no js
	if (bl === 1)
		return b.join('')
	// string needs evaluation
	else
	{
		var s = [], o = []
		
		s[0] = b[0]
		o[0] = '($_$s[0])'
		
		for (var i = 1; i < bl; 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 { return bake(hash, s, o.join('+')) }
		catch (ex)
		{
			log('Error while compiling string: "' + str + '"')
			throw ex
		}
	}
}

var cache = {}
function interpolate ()
{
	var f = cache[this]
	if (!f)
		f = cache[this] = compile(this)
	
	if (typeof f === 'string')
		return f
	else
	{
		try { return f.apply(this, arguments) }
		catch (ex)
		{
			ex.message += ', while executing string: "' + this + '"'
			throw ex
		}
	}
}

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

})();
;(function(){

var myName = 'GoogleApiLoader'

function Me (keys, host, language)
{
	this.host = /[^.]+\.[^.]+$/i.exec(host || location.host)
	this.state = 'init'
	this.apis = []
	this.language = language || (self.LanguagePack ? LanguagePack.code : 'en')
	this.keys = keys
	this.prerequisites = {}
}

Me.prototype =
{
	load: function (name, version)
	{
		if (name)
			this.apis.push({name: name, version: version})
		
		var me = this
		
		if (this.state == 'init')
		{
			if (!this.keys)
				throw new Error(myName + ': keys is undefined')
			
			this.node = $.load('http://www.google.com/jsapi?key=' + this.keys[this.host])
			
			function wait ()
			{
				if (!window.google)
					return
				
				clearInterval(timer)
				me.apiLoaderLoaded()
			}
			
			var timer = setInterval(wait, 250)
			
			this.state = 'loading'
		}
		
		if (this.state == 'ready')
			setTimeout(function () { me.fireAll() }, 1)
			
		return this
	},
	
	apiLoaderLoaded: function ()
	{
		this.state = 'ready'
		this.apiLoaded({name: 'loader'}, window.google['loader'])
		
		this.fireAll()
	},
	
	fireAll: function ()
	{
		for (var i = 0; i < this.apis.length; i++)
			this.loadApi(this.apis[i])
		
		this.apis.length = 0
	},
	
	loadApi: function (api)
	{
		var me = this
		function callback (e)
		{
			me.apiLoaded(api)
		}
		
		var opts =
		{
			nocss: true,
			language: this.language,
			callback: callback
		}
		
		window.google.load(api.name, api.version, opts)
	},
	
	apiLoaded: function (api)
	{
		this.dispatchEvent({type: api.name, api: window.google[api.name]})
	}
}

Me.mixIn(EventDriven)

Me.className = myName
self[myName] = Me

})();


;(function(){

var Papa

;(function(){

var myName = 'Map',
	Me = Papa = MVC.create(myName)

var myProto = 
{
	bind: function (nodes)
	{
		this.view.bind(nodes)
		return this
	},
	
	setCenter: function (center, zoom) { this.model.setCenter(center, zoom) },
	setPoints: function (points) { this.model.setPoints(points) },
	apiLoaded: function () { this.dispatchEvent('ready') }
}

Object.extend(Me.prototype, myProto)
Me.mixIn(EventDriven)

self[myName] = Me

})();


;(function(){

function Me () {}
Papa.Overlay = Me

})();


;(function(){

var Me = Papa.View

eval(NodesShortcut.include())

var myProto =
{
	initialize: function ()
	{
		this.nodes = {}
		this.visibleMarkers = {}
	},
	
	bind: function (nodes)
	{
		this.nodes = nodes
		if (!nodes.wrapper)
			nodes.wrapper = nodes.main
		
		var me = this
		googleApiLoader.addEventListener('maps', function (e) { me.apiLoaded(e) }, false)
		googleApiLoader.load('maps', 2)
		nodes.wrapper.addClassName('loading')
	},
	
	apiLoaded: function (e)
	{
		var api = this.api = e.api
		this.updateOverlayProto()
		this.createMap()
		
		this.ready = true
		this.controller.apiLoaded()
	},
	
	createMap: function ()
	{
		var api = this.api
		
		var map = this.map = new api.Map2(this.nodes.main)
		var me = this
		api.Event.addListener(map, 'load', function () { me.mapLoaded(this) })
		map.enableContinuousZoom()
	},
	
	setCenter: function (center, zoom)
	{
		if (!this.ready)
			return
		
		this.map.setCenter(new this.api.LatLng(center.lat, center.lng), zoom)
	},
	
	mapLoaded: function ()
	{
		var me = this
		this.nodes.wrapper.removeClassName('loading')
		this.api.Event.addListener(this.map, 'moveend', function () { me.mapMoveEnd(this) })
		this.addControls()
	},
	
	addControls: function ()
	{
		var nodes = this.nodes,
			control = nodes.control
		
		if (!control)
		{
			var api = this.api, controlPosition = new api.ControlPosition(api.ANCHOR_TOP_LEFT, new api.Size(10, 15))
			this.map.addControl(new api.SmallMapControl(), controlPosition)
			return
		}
		
		var main = nodes.main,
			map = this.map
		
		main.appendChild(control)
		
		var actions =
		{
			top:    ['panDirection', 0, 1],
			right:  ['panDirection', -1, 0],
			bottom: ['panDirection', 0, -1],
			left:   ['panDirection', 1, 0],
			plus:   ['zoomIn'],
			minus:  ['zoomOut']
		}
		
		function move (e)
		{
			var action = actions[e.target.getAttribute('data-map-action')]
			if (action)
				map[action[0]].apply(map, action.slice(1))
		}
		
		control.addEventListener('click', move, false)
	},
	
	updateOverlayProto: function ()
	{
		var proto = Papa.Overlay.prototype,
			api = this.api
		
		Object.extend(proto, new api.Overlay())
		proto.api = api
	},
	
	mapMoveEnd: function (map)
	{
		var center = map.getCenter(),
			bounds = map.getBounds(),
			sw = bounds.getSouthWest(),
			ne = bounds.getNorthEast()
		
		this.controller.moved({lat:center.lat(), lng:center.lng()}, map.getZoom(), {lat:sw.lat(), lng:sw.lng()}, {lat:ne.lat(), lng:ne.lng()})
	},
	
	renderPoints: function (points)
	{
		if (!this.ready)
			return
		
		var map = this.map,
			visible = this.visibleMarkers,
			now = this.visibleMarkers = {}
		
		for (var i = 0; i < points.length; i++)
		{
			var point = points[i], pid = point.mapPointId
			
			// get the marker and insert it into the new visibleMarkers hash
			now[pid] = point
			
			// add marker (and delete its record) only if it isn't already shown
			if (!visible[pid])
				map.addOverlay(point)
			else
				delete visible[pid]
		}
		
		// remove all the markers that are still visible
		for (var k in visible)
			map.removeOverlay(visible[k])
	}
}

Object.extend(Me.prototype, myProto)

})();


;(function(){

var Me = Papa.Controller

var myProto = 
{
	moved: function (center, zoom, sw, ne)
	{
		this.parent.dispatchEvent({type:'moved', center:center, zoom:zoom, sw:sw, ne:ne})
	},
	
	apiLoaded: function () { this.model.apiLoaded(); this.parent.apiLoaded() }
}

Object.extend(Me.prototype, myProto)

})();


;(function(){

var Me = Papa.Model

var myProto =
{
	initialize: function ()
	{
		this.points = []
		this.count = 0
	},
	
	apiLoaded: function ()
	{
		this.view.setCenter(this.center, this.zoom)
		this.view.renderPoints(this.points)
	},
	
	setCenter: function (center, zoom)
	{
		this.center = center
		this.zoom = zoom
		this.view.setCenter(center, zoom)
	},
	
	setPoints: function (points)
	{
		if (!this.parent.dispatchEvent({type:'pointsSet', points: points}))
			return
		
		var count = this.count
		for (var i = 0, il = points.length; i < il; i++)
		{
			var point = points[i]
			if (!point.mapPointId)
				point.mapPointId = ++count
		}
		this.count = count
		
		this.points = points
		this.view.renderPoints(points)
	}
}

Object.extend(Me.prototype, myProto)

})();


})();
;(function(){

var myName = 'MapLightMarker'

function Me () {}

eval(NodesShortcut.include())

Me.prototype = new Map.Overlay()

var myProto =
{
	initialize: function (map)
	{
		var ll = this.ll
		this.latlng = new this.api.LatLng(ll.lat, ll.lng)
		
		var node = this.node || (this.node = this.createNode())
		map.getPane(G_MAP_MARKER_PANE).appendChild(node)
		this.map = map
	},
	
	createNode: function ()
	{
		return Nct('div', 'point', 'createNode() is not implemented')
	},
	
	redraw: function (force)
	{
		if (!force)
			return
		
		var sw = this.map.fromLatLngToDivPixel(this.latlng)
		
		var style = this.node.style
		style.left = sw.x + 'px'
		style.top = sw.y + 'px'
	},
	
	remove: function ()
	{
		this.node.remove()
	}
}

Object.extend(Me.prototype, myProto)

self[myName] = Me
Me.className = myName

})();


window.googleApiLoader = new GoogleApiLoader
({
	'inshaker.ru': 'ABQIAAAARQzYWu9IpurDSJW9DIJqrxRkBrAtdI-gKvPAeDorTK26GBT4DBTWsZV4qSbs-J27hgsqukt5Ef_TCg',
	'programica.ru':  'ABQIAAAARQzYWu9IpurDSJW9DIJqrxQVF992HTeapfH7j2YfASPwC0rg6BRLdvrEuTsIejJP0tsK0O0WOScMCw'
}, location.host, 'ru')

;(function(){

function Me () {}

Me.prototype =
{
	bind: function (nodes)
	{
		this.nodes = nodes
		
		this.fixedStartY = nodes.holder.offsetTop
		this.fixedEndY = nodes.page.scrollHeight
		
		if (nodes.holder.offsetHeight >= this.fixedEndY - this.fixedStartY)
			return
		
		var me = this
		window.addEventListener('scroll', function (e) { me.onscroll() }, false)
		this.onscroll()
	},
	
	onscroll: function ()
	{
		var nodes = this.nodes,
			holder = nodes.holder,
			page = nodes.page
		
		var pageYOffset = window.pageYOffset
		var stickTop = pageYOffset <= this.fixedStartY
		var stickBottom = pageYOffset + holder.offsetHeight >= this.fixedEndY
		
		var state
		if (stickBottom)
			state = 'stick-bottom'
		else if (stickTop)
			state = 'stick-top'
		else
			state = 'float-fixed'
		
		if (this.lastState == state)
			return
		this.lastState = state
		
		switch (state)
		{
			case 'stick-top':
			page.removeClassName('float-fixed')
			page.removeClassName('stick-bottom')
			break
			
			case 'float-fixed':
			page.removeClassName('stick-top')
			page.removeClassName('stick-bottom')
			break
			
			case 'stick-bottom':
			page.removeClassName('stick-top')
			page.removeClassName('float-fixed')
			break
		}
		
		// log(state)
		page.addClassName(state)
	}
}

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

})();

;(function(){

function Me () {}

Me.prototype =
{
	bind: function (nodes)
	{
		this.nodes = nodes
		
		var me = this
		nodes.root.addEventListener('click', function (e) { me.clicked(e) }, false)
	},
	
	render: function (url, text)
	{
		var buttons = this.nodes.buttons
		
		var values = {url: encodeURIComponent(url), text: encodeURIComponent(text)}
		for (var i = 0, il = buttons.length; i < il; i++)
			buttons[i].href = buttons[i].href.interpolate(values)
	},
	
	clicked: function (e)
	{
		var href = this.searchParentNodes(this.nodes.root, e.target, function (node) { return node.href })
		if (!href)
			return
		
		e.preventDefault()
		this.openWindow(href)
	},
	
	openWindow: function (href)
	{
		var w = 550, h = 450, l = 0, t = 0,
			sh = window.screen.height,
			sw = window.screen.width
		
		if (sw < w)
			w = sw
		else
			l = Math.round((sw - w) / 2)
		
		if (sh < h)
			h = sh
		else
			t = Math.round((sh - h) / 2)
		
		window.open(href, '', 'left=' + l + ',top=' + t + ',width=' + w + ',height=' + h + ',personalbar=0,toolbar=0,scrollbars=1,resizable=1')
	},
	
	searchParentNodes: function (root, node, f)
	{
		for (;;)
		{
			if (!node)
				break
			
			var v = f(node)
			if (v !== undefined)
				return v
			
			if (node == root)
				break
			
			node = node.parentNode
		}
	}
}

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

})();


;(function(){

var myName = 'BarPoint'

function Me (bar)
{
	this.bar = bar
	this.ll = {lat: bar.point[0], lng: bar.point[1]}
	this.nodes = {}
}

Me.prototype = new MapLightMarker()

eval(NodesShortcut.include())

function stopPropagation (e) { e.stopPropagation() }

var myProto =
{
	ll: {lat: 0, lng: 0},
	
	createNode: function ()
	{
		return this.node || (this.node = this.getNode())
	},
	
	getNode: function ()
	{
		var nodes = this.nodes,
			bar = this.bar
		
		var main = nodes.main = Nc('div', 'point')
		main.addEventListener('mousedown', stopPropagation, false)
		
		var icon = nodes.icon = main.appendChild(Nc('a', 'icon'))
		icon.href = this.bar.pageHref()
		
		var title = nodes.title = main.appendChild(Nc('dl', 'title'))
		
		var name = nodes.name = title.appendChild(Nct('dt', 'point-name', bar.name))
		
		var contacts = bar.contacts
		if (contacts)
		{
			nodes.address = title.appendChild(Nct('dd', 'address', contacts.address))
			nodes.tel = title.appendChild(Nct('dd', 'tel', contacts.tel))
		}
		
		return main
	}
}

Object.extend(Me.prototype, myProto)

Me.className = myName
self[myName] = Me

})();

BarPage.model =
{
	owner: null, // must be defined before initialize
	
	initialize: function () { },
	
	getCocktailsByNames: function (arr)
	{
		var res = []
		for (var i = 0; i < arr.length; i++)
			res[i] = Cocktail.getByName(arr[i])
		return res
	},
	
	getPrevNext: function (name, query)
	{
		query = query || {}
		
		var bars = Bar.getByQuery(query)
		if (!bars)
			return []
		
		for (var i = 0; i < bars.length; i++)
			if (bars[i].name == name)
				return [bars[i-1], bars[i+1]]
		
		return []
	},
	
	setQuery: function (query)
	{
		var bar = Bar.getByCityName(query.city, query.name)
		if (!bar)
			return
		
		var data =
		{
			bar: bar,
			carte: this.getCocktailsByNames(bar.carte),
			otherBarsSet: Bar.getAllByCity(query.city),
			prevNext: this.getPrevNext(query.name, {city: query.city, format: query.format, feel: query.feel})
		}
		
		this.owner.view.modelChanged(data)
	}
}
BarPage.controller =
{
	owner: null, // must be defined before initialize
	
	initialize: function ()
	{
		
	},
	
	barCityNamesLoaded: function (state)
	{
		this.cityName = state.city
		this.barName = state.name
		this.owner.model.setQuery(state)
	},
	
	moreIsMaximized: function () {  },
	moreIsMinimized: function () {  }
}
{(function(){

var doc = document
function N (name) { return doc.createElement(name) }
function T (text) { return doc.createTextNode(text) }

BarPage.view =
{
	owner: null, // must be defined before initialize
	
	initialize: function (nodes)
	{
		this.nodes = nodes
		
		var share = new ShareBox()
		share.bind(nodes.shareBox)
		share.render(window.location.href, 'Бар «' + nodes.barName.getAttribute('data-value') + '»')
		
		this.renderPhotos()
		
		nodes.barPrev.hide = nodes.barNext.hide = function () { this.addClassName('hidden') }
	},
	
	renderPhotos: function ()
	{
		var photos = this.nodes.photos,
			items = photos.items
		
		var total = items.length, last
		if (total > 1)
			last = photos.surface.appendChild(items[0].cloneNode(true))
		
		var list = new LazyList()
		list.bind(photos)
		list.configure({pageLength: 1, friction: 100, pageVelocity: 37.5, soft: Infinity, min: 75, max: 100})
		list.load = function (nodes)
		{
			for (var i = 0, il = nodes.length; i < il; i++)
			{
				// buggy in Firefox
				var image = nodes[i].firstChild
				if (!image.src)
					image.src = image.getAttribute('data-lazy-src')
			}
		}
		list.setNodes(items, total)
		list.load([last])
	},
	
	modelChanged: function (data)
	{
		var nodes = this.nodes
		
		// bar
		this.bar = data.bar
		
		// cocktails
		this.renderCocktails(data.carte)
		this.renderMap(data.bar, data.otherBarsSet)
		this.renderPrevNext(data.prevNext)
	},
	
	readBarCityNames: function ()
	{
		var nodes = this.nodes,
			barName = nodes.barName.getAttribute('data-value'),
			cityName = nodes.cityName.getAttribute('data-value')
		
		var state = {}
		state.name = barName
		state.city = cityName
		
		this.owner.controller.barCityNamesLoaded(state)
	},
	
	initMap: function (bar)
	{
		if (this.map)
			return
		
		var map = this.map = new Map(),
			nodes = this.nodes
		
		map.bind({main: this.nodes.map, control: nodes.positionControl})
	},
	
	renderMap: function (current, bars)
	{
		this.initMap()
		
		var map = this.map
		
		var points = []
		for (var i = 0; i < bars.length; i++)
		{
			var bar = bars[i]
			bar.mapPoint = points[i] = new BarPoint(bar)
		}
		map.setPoints(points)
		
		var node = current.mapPoint.createNode()
		node.addClassName('selected')
		map.setCenter({lat: current.point[0], lng: current.point[1]}, 15)
	},
	
	renderCocktails: function (cocktails)
	{
		this.nodes.hitBox.appendChild(cocktails[0].getPreviewNode(false, true))
	},
	
	renderPrevNext: function (prevNext)
	{
		if (prevNext[0])
		{
			this.nodes.barPrev.href = prevNext[0].pageHref()
			this.nodes.barPrev.title = prevNext[0].name
		}
		else
			this.nodes.barPrev.hide()
		
		if (prevNext[1])
		{
			this.nodes.barNext.href = prevNext[1].pageHref()
			this.nodes.barNext.title = prevNext[1].name
		}
		else
			this.nodes.barNext.hide()
	},
	
	bindBrandingScroller: function ()
	{
		var nodes = this.nodes
		
		var bs = new BrandingScroller()
		bs.bind({holder: nodes.brandedImageHolder, page: nodes.page})
	}
}

})()}


