;(function(){

var myName = 'IngredientsPage',
	Me = self[myName] = MVC.create(myName)

// Me.mixIn(EventDriven)

var myProto =
{
	initialize: function ()
	{
		this.model.initialize()
		this.view.initialize()
		this.controller.initialize()
	},

	bind: function (nodes, sources, state)
	{
		this.model.bind(sources)
		this.view.bind(nodes)
		this.controller.bind(state)
		
		return this
	}
}

Object.extend(Me.prototype, myProto)

})();

;(function(){

var myProto =
{
	childIndexedPath: function (node)
	{
		var path = []
		path:
		for (;;)
		{
			if (node == this)
				break
			
			var parent = node.parentNode
			if (!parent)
				return null
			
			var childs = parent.childNodes
			for (var i = 0, num = 0, il = childs.length; i < il; i++)
			{
				var child = childs[i]
				if (child == node)
				{
					path.push(num)
					node = parent
					continue path
				}
				// to provide a path useful cross browser lets count only the elements
				else if (child.nodeType == 1)
					num++
			}
			// we'r here if child node lied to us about its parentNode
			return null
		}
		
		return path.reverse()
	},
	
	getChildByIndexedPath: function (path)
	{
		var node = this
		path:
		for (var i = 0, il = path.length; node && i < il; i++)
		{
			var n = path[i],
				childs = node.childNodes
			// if there is no children this loop will be skipped
			// so we can not go too deep
			for (var j = 0, jl = childs.length; j < jl; j++)
			{
				var child = childs[j]
				if (child.nodeType == 1)
					if (n == 0)
					{
						node = child
						continue path
					}
					else
						n--
			}
			// we are here only if child num goes out of bounds
			return null
		}
		return node
	}
}

Object.add(Element.prototype, myProto)
Object.add(document, myProto)


})();
;(function(){

var myName = 'Cloner'

function Me ()
{
	this.nodes = {}
	this.constructor = Me
}

Me.prototype =
{
	bind: function (root, nodes)
	{
		this.root = root
		this.nodes = nodes
		
		this.sync()
		
		return this
	},
	
	sync: function ()
	{
		this.paths = this.getPaths(this.root, this.nodes)
	},
	
	getPaths: function (root, nodes)
	{
		var paths = {}
		for (var k in nodes)
			paths[k] = root.childIndexedPath(nodes[k])
		return paths
	},
	
	getNodes: function (root, paths)
	{
		var nodes = {}
		for (var k in paths)
			nodes[k] = root.getChildByIndexedPath(paths[k])
		return nodes
	},
	
	create: function ()
	{
		var root = this.root.cloneNode(true),
			nodes = this.getNodes(root, this.paths)
		
		return {root: root, nodes: nodes}
	}
}

// Me.mixIn(EventDriven)
Me.className = myName
self[myName] = Me

})();

;(function(){

var myName = 'TabSwitcher'

function Me ()
{
	this.nodes = {tabs:[], sections:[]}
}

Me.prototype =
{
	eventType: 'mousedown',
	
	bind: function (nodes)
	{
		var me = this
		this.mouseListener = function (e) { me.onMouse(e, this) }
		
		this.setTabs(nodes.tabs)
		this.setSections(nodes.sections)
		
		return this
	},
	
	unbind: function ()
	{
		this.setTabs([])
	},
	
	onMouse: function (e, node)
	{
		// if (e.target != node)
		// 	return
		
		var tabs = this.nodes.tabs,
			i, num = -1, value
		
		for (i = 0; i < tabs.length; i++)
			if (node === tabs[i])
			{
				value = node.getAttribute('data-tab-name')
				num = i
			}
		
		this.select(value, num)
	},
	
	select: function (value, num)
	{
		var ok = this.dispatchEventData('select', {value: value, num: num})
		if (ok)
			this.renderSelected(num)
		return ok
	},
	
	setTabs: function (tabs)
	{
		var listener = this.mouseListener, node, eventType = this.eventType
		
		var old = this.nodes.tabs
		for (var i = 0; i < old.length; i++)
			if ((node = old[i]))
				node.removeEventListener(eventType, listener, false)
		
		var numByName = this.numByName = {},
			names = this.names = []
		this.nodes.tabs = tabs
		for (var i = 0; i < tabs.length; i++)
			if ((node = tabs[i]))
			{
				node.addEventListener(eventType, listener, false)
				var name = node.getAttribute('data-tab-name')
				if (name !== undefined)
				{
					numByName[name] = i
					names.push(name)
				}
			}
	},
	
	getNames: function () { return this.names },
	
	setSections: function (sections)
	{
		this.nodes.sections = sections
	},
	
	renderSelected: function (num)
	{
		var nodes = this.nodes, node
		
		if (typeof num !== 'number')
			num = this.numByName[num]
		
		var tabs = nodes.tabs
		for (var i = 0; i < tabs.length; i++)
			if ((node = tabs[i]))
				node.toggleClassName('selected', num === i)
		
		var sections = nodes.sections
		for (var i = 0; i < sections.length; i++)
			if ((node = sections[i]))
				node.toggleClassName('hidden', num !== i)
	}
	
}

Me.mixIn(EventDriven)

self[myName] = Me
Me.className = myName

})();


;(function(){

var myName = 'Popup'

function Me ()
{
	this.nodes = {}
	this.listeners = {}
	this.constructor = Me
}

// eval(NodesShortcut())

Me.prototype =
{
	visible: false,
	
	bind: function (nodes)
	{
		this.nodes = nodes
		
		var me = this
		this.listeners.click = function (e) { me.hide() }
		this.listeners.key = function (e) { e.keyCode == 27 && me.hide() }
		
		nodes.window.addEventListener('click', function (e) { e.stopPropagation() }, false)
		
		return this
	},
	
	hide: function ()
	{
		if (!this.visible)
			return
		
		this.nodes.root.hide()
		this.visible = false
		
		var me = this
		setTimeout(function () { me.unbindListeners() }, 0)
	},
	
	show: function ()
	{
		if (this.visible)
			return
		
		var nodes = this.nodes
		nodes.root.show()
		nodes.front.style.top = (document.documentElement.scrollTop || document.body.scrollTop) + 'px'
		this.visible = true
		
		var me = this
		setTimeout(function () { me.bindListeners() }, 0)
	},
	
	bindListeners: function ()
	{
		document.addEventListener('click', this.listeners.click, false)
		document.addEventListener('keydown', this.listeners.key, false)
	},
	
	unbindListeners: function ()
	{
		document.removeEventListener('click', this.listeners.click, false)
		document.removeEventListener('keydown', this.listeners.key, false)
	}
}

// Me.mixIn(EventDriven)
Me.className = myName
self[myName] = Me

})();
;(function(){

var Papa = IngredientsPage, Me = Papa.Model

var myProto =
{
	initialize: function ()
	{
		this.sources = {}
		this.state = {}
	},
	
	bind: function (ds)
	{
		this.ds = ds
		// we will mess this.all, so better make a copy
		this.all = Array.copy(ds.ingredient.getAll())
		this.groups = this.ds.ingredient.getGroups()
		Ingredient.calculateEachIngredientUsage()
	},
	
	setState: function (state)
	{
		this.state = state
		
		var data, all = this.all
		
		if (state.groupBy == 'group')
			data = this.groupByGroup(all)
		else
			data = [{list: all}]
		
		
		var func
		if (state.sortBy == 'usage')
			func = this.sortByUsage
		else if (state.sortBy == 'alphabet')
			func = this.sortByAlphabet
		
		if (func)
			this.sortBy(data, func)
		
		
		this.data = data
		this.view.groupByChanged(state.groupBy)
		this.view.sortByChanged(state.sortBy)
		this.view.drawByChanged(state.drawBy)
		this.view.listChanged(data)
	},
	
	selectIngredient: function (ingredient)
	{
		this.view.showIngredient(ingredient)
	},
	
	setGroupBy: function (type)
	{
		this.state.groupBy = type
		this.setState(this.state)
	},
	
	setSortBy: function (type)
	{
		this.state.sortBy = type
		this.setState(this.state)
	},
	
	setDrawBy: function (type)
	{
		this.state.drawBy = type
		this.setState(this.state)
	},
	
	groupByGroup: function (all)
	{
		var data = []
		{
			var slices = {}, groups = this.groups
			for (var i = 0; i < groups.length; i++)
			{
				var list = [], name = groups[i]
				slices[name] = list
				data.push({name: name, list: list})
			}
			
			for (var i = 0; i < all.length; i++)
			{
				var ingred = all[i]
				slices[ingred.group].push(ingred)
			}
		}
		return data
	},
	
	sortBy: function (data, func)
	{
		for (var i = 0; i < data.length; i++)
			data[i].list.sort(func)
	},
	
	sortByAlphabet: function (a, b) { return a.name.localeCompare(b.name) },
	
	// this is possible only after Ingredient.calculateEachIngredientUsage()
	sortByUsage: function (a, b) { return b.cocktails.length - a.cocktails.length }
}

Object.extend(Me.prototype, myProto)

})();
;(function(){

var Papa = IngredientsPage, Me = Papa.View

eval(NodesShortcut.include())

var myProto =
{
	initialize: function ()
	{
		this.nodes = {}
		this.popupCache = {}
		this.ingredientCache = {}
		this.itemCache = [] // just a plain list
	},
	
	bind: function (nodes)
	{
		this.nodes = nodes
		
		var controller = this.controller
		
		var groupBy = this.groupBySwitcher = new TabSwitcher()
		groupBy.bind({tabs: nodes.groupByItems, sections:[]})
		groupBy.addEventListener('select', function (e) { e.preventDefault(); controller.groupBySelected(e.data.value) }, false)
		
		var sortBy = this.sortBySwitcher = new TabSwitcher()
		sortBy.bind({tabs: nodes.sortByItems, sections:[]})
		sortBy.addEventListener('select', function (e) { e.preventDefault(); controller.sortBySelected(e.data.value) }, false)
		
		var drawBy = this.drawBySwitcher = new TabSwitcher()
		drawBy.bind({tabs: nodes.drawByItems, sections:[]})
		drawBy.addEventListener('select', function (e) { e.preventDefault(); controller.drawBySelected(e.data.value) }, false)
		
		var me = this
		nodes.output.addEventListener('click', function (e) { me.mayBeIngredintClicked(e.target) }, false)
		
		var popupCloner = this.popupCloner = new Cloner()
		popupCloner.bind(this.nodes.ingredientPopupRoot, this.nodes.ingredientPopup)
		
		return this
	},
	
	mayBeIngredintClicked: function (target)
	{
		var output = this.nodes.output, ingredient
		
		for (var node = target; node != output; node = node.parentNode)
			if (node.ingredient)
			{
				ingredient = node.ingredient
				break
			}
		
		if (ingredient)
			this.controller.ingredientSelected(ingredient)
	},
	
	showIngredient: function (ingredient)
	{
		var popup = this.popupCache[ingredient.name]
		
		if (popup)
			return popup.show()
		
		var popupClone = this.popupCloner.create()
		document.body.appendChild(popupClone.root)
		
		var nodes = popupClone.nodes
		var popup = new Popup()
		this.popupCache[ingredient.name] = popup
		popup.bind({root: popupClone.root, window: nodes.window, front: nodes.front})
		popup.show()
		
		var brand = ingredient.brand
		if (brand)
		{
			nodes.mark.appendChild(T(ingredient.brand))
			nodes.ingredientWindow.addClassName('branded')
			nodes.brand.appendChild(T(ingredient.mark))
			nodes.brand.href = Ingredient.ingredientsLinkByMark(ingredient.mark)
		}
		
		nodes.name.appendChild(T(ingredient.name))
		
		var len = ingredient.cocktails.length
		if (len)
			nodes.allLink.appendChild(T(' ' + len + ' ' + len.plural('коктейль', 'коктейля', 'коктейлей')))
		
		if (ingredient.decls)
			nodes.allLink.appendChild(T(' ' + ingredient.decls.t))
		nodes.allLink.href = ingredient.cocktailsLink()
		
		nodes.text.innerHTML = ingredient.about
		
		nodes.image.src = ingredient.getMainImageSrc()
		
		var me = this
		setTimeout(function () { me.renderCocktails(nodes, ingredient.cocktails) }, 0)
		require('Good', function () { me.renderWhereToBuy(nodes, ingredient) })
		
		Statistics.ingredientPopupOpened(ingredient)
	},
	
	renderWhereToBuy: function (popupNodes, ingredient)
	{
		var good = Good.getBySellName(ingredient.name)[0]
		if (good)
		{
			popupNodes.ingredientWindow.addClassName('can-buy')
			popupNodes.buy.appendChild(T(good.name))
			popupNodes.buy.href = good.getHref()
		}
	},
	
	renderCocktails: function (popupNodes, cocktails)
	{
		cocktails = cocktails.slice().randomize()
		
		var cl = new CocktailList()
		var nodes =
		{
			root: popupNodes.cocktails,
			viewport: popupNodes.cocktailsViewport,
			surface: popupNodes.cocktailsSurface,
			prev: popupNodes.cocktailsPrev,
			next: popupNodes.cocktailsNext
		}
		cl.bind(nodes)
		cl.configure({pageLength: 5, pageVelocity: 38})
		cl.setCocktails(cocktails)
	},
	
	listChanged: function (data)
	{
		var output = this.nodes.output
		output.empty()
		
		this.itemCache = []
		
		// console.time('render')
		for (var i = 0; i < data.length; i++)
		{
			var group = this.renderGroup(data[i])
			output.appendChild(group)
		}
		// console.timeEnd('render')
		
		this.setupVisibilityFrame(this.itemCache)
	},
	
	setupVisibilityFrame: function (nodes)
	{
		if (!nodes.length)
			return
		
		var boxes = Boxer.sameNodesToBoxes(nodes)
		
		var frame = new VisibilityFrame()
		frame.setFrame(4000, 1500) // hardcoded for now
		frame.setStep(500, 500)
		frame.setBoxes(boxes)
		
		frame.onmove = function (show, hide)
		{
			for (var i = 0; i < show.length; i++)
			{
				var box = show[i]
				if (!box.loaded)
				{
					var node = box.node,
						image = node.ingredientNode.ingredientImage
					
					image.src = image.lazySrc
					node.removeClassName('lazy')
					
					box.loaded = true
				}
			}
		}
		
		function onscroll ()
		{
			frame.moveTo(window.pageXOffset, window.pageYOffset)
		}
		var timer
		window.addEventListener('scroll', function () { clearTimeout(timer); timer = setTimeout(onscroll, 200) }, false)
		onscroll()
	},
	
	groupByChanged: function (type)
	{
		this.groupBySwitcher.renderSelected(type)
	},
	
	sortByChanged: function (type)
	{
		this.sortBySwitcher.renderSelected(type)
	},
	
	drawByChanged: function (type)
	{
		var output = this.nodes.output,
			names = this.drawBySwitcher.getNames()
		for (var i = 0; i < names.length; i++)
			output.toggleClassName(names[i], names[i] === type)
		
		this.drawBySwitcher.renderSelected(type)
	},
	
	renderGroup: function (group)
	{
		var root = Nc('dl', 'group')
		
		if ('name' in group)
			root.appendChild(Nct('dt', 'head', group.name))
		
		var body = Nc('dt', 'body')
		root.appendChild(body)
		
		var list = Nc('ul', 'list')
		var ingreds = group.list, itemCache = this.itemCache
		for (var i = 0; i < ingreds.length; i++)
		{
			var item = Nc('li', 'item lazy')
			itemCache.push(item)
			var ingredientNode = this.getIngredientNode(ingreds[i])
			item.appendChild(ingredientNode)
			item.ingredientNode = ingredientNode
			list.appendChild(item)
		}
		body.appendChild(list)
		
		return root
	},
	
	getIngredientNode: function (ingredient)
	{
		if ((node = this.ingredientCache[ingredient.name]))
			return node
		
		var node = Nc('a', 'ingredient')
		var image = Nc('img', 'image')
		image.lazySrc = ingredient.getMiniImageSrc()
		node.appendChild(image)
		
		var name = Nct('span', 'name', ingredient.name)
		node.appendChild(name)
		
		node.ingredient = ingredient
		node.ingredientImage = image
		
		return this.ingredientCache[ingredient.name] = node
	}
}

Object.extend(Me.prototype, myProto)

})();

;(function(){

var Papa = IngredientsPage, Me = Papa.Controller

var myProto =
{
	initialize: function ()
	{
		this.state = {}
	},
	
	bind: function (state)
	{
		this.model.setState(state)
	},
	
	groupBySelected: function (type)
	{
		this.model.setGroupBy(type)
	},
	
	sortBySelected: function (type)
	{
		this.model.setSortBy(type)
	},
	
	drawBySelected: function (type)
	{
		this.model.setDrawBy(type)
	},
	
	ingredientSelected: function (ingredient)
	{
		this.model.selectIngredient(ingredient)
	}
}

Object.extend(Me.prototype, myProto)

})();




;(function(){

function onready ()
{
	var nodes =
	{
		main: $$('.b-content')[0],
		output: $('output'),
		
		groupBy: $('group-by'),
		groupByItems: $$('#group-by .item'),
		
		sortBy: $('sort-by'),
		sortByItems: $$('#sort-by .item'),
		
		drawBy: $('draw-by'),
		drawByItems: $$('#draw-by .item'),
		
		ingredientPopupRoot: $('ingredient-info-popup'),
		ingredientPopup:
		{
			window: $$('#ingredient-info-popup .popup-window')[0],
			front: $$('#ingredient-info-popup .popup-front')[0],
			ingredientWindow: $$('#ingredient-info-popup .popup-window .ingredient-window')[0],
			image: $$('#ingredient-info-popup .description .image')[0],
			mark: $$('#ingredient-info-popup .description .about .mark')[0],
			brand: $$('#ingredient-info-popup .description .about .brand .link')[0],
			buy: $$('#ingredient-info-popup .description .about .where-to-buy .link')[0],
			name: $$('#ingredient-info-popup .description .about .name')[0],
			text: $$('#ingredient-info-popup .description .about .text')[0],
			allLink: $$('#ingredient-info-popup .description .about .all-cocktails .link')[0],
			cocktails: $$('#ingredient-info-popup .cocktail-list')[0],
			cocktailsViewport: $$('#ingredient-info-popup .cocktail-list .viewport')[0],
			cocktailsSurface: $$('#ingredient-info-popup .cocktail-list .surface')[0],
			cocktailsPrev: $$('#ingredient-info-popup .cocktail-list .prev')[0],
			cocktailsNext: $$('#ingredient-info-popup .cocktail-list .next')[0]
		}
	}
	
	var widget = new IngredientsPage()
	widget.bind(nodes, {ingredient:Ingredient, cocktail:Cocktail}, {groupBy: 'group', sortBy: 'usage', drawBy: 'with-text'})
}

$.onready(onready)

})();