/*! https://www.litejs.com/MIT-LICENSE.txt */



/**
 * @version    0.1.7
 * @date       2015-02-25
 * @stability  1 - Experimental
 * @author     Lauri Rooden <lauri@rooden.ee>
 * @license    MIT License
 */



!function(exports) {
	var P = "prototype"
	, A = Array[P], F = Function[P], S = String[P]
	, O = Object
	, hasOwn = O[P].hasOwnProperty
	, slice = F.call.bind(A.slice)
	, constructFns = []
	, fnRe = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|this|arguments|\.\w+|\w+:/g



	// Function extensions
	// -------------------


	F.construct = function(a) {
		// bind version have bad performance and memory consumption
		// return new(F.bind.apply(this, A.concat.apply([null], a)))
		var len = a.length
		return len ?
			(constructFns[len] || (constructFns[len] = Fn("t a->new t(a["+O.keys(slice(a)).join("],a[")+"])")))(this, a) :
			new this
	}

	F.partial = function() {
		var self = this
		, a = slice(arguments)
		return function() {
			return self.apply(this, a.concat.apply(a, arguments))
		}
	}

	/*
	 * **argi** - index of argument, which will be split
	 * **re**   - optional RegExp for matching words
	 */

	F.byWords = function(argi, re) {
		var self = this
		return function() {
			var s = this
			, r = s
			, a = arguments
			;(a[argi |= 0]||"").replace(re || /\S+/g, function(w) {
				a[argi] = w
				r = self.apply(s, a)
			})
			return r
		}
	}

	F.byKeyVal = function() {
		var self = this
		return function(o) {
			var r
			, s = this
			, a = slice(arguments)
			if (typeof o == "object") for (r in o) {
				a[0] = r
				a[1] = o[r]
				r = self.apply(s, a)
			} else r = self.apply(s, a)
			return r
		}
	}

	// Run function once and return cached value or cached instance
	F.cache = function(instance, keyFn, cache) {
		var self = this
		, c = cache || {}
		, f = function() {
			var a = arguments
			, i = !!instance || this instanceof f
			, k = keyFn ? keyFn.apply(self, a) : i + ":" + a.length + ":" + slice(a)

			return k in c ? c[k] : (c[k] = i ? self.construct(a) : self.apply(this, a))
		}

		f.origin = self
		f.cached = c
		f.extend = function() {
			return self.extend.apply(self, arguments).cache(instance, keyFn, cache)
		}
		f[P] = self[P] // prototype for better access on extending 
		return f
	}

	F.extend = function() {
		var a
		, self = this
		, i = 0

		function f() {
			return self.apply(this, arguments)
		}

		for (f[P] = O.create(self[P]); a = arguments[i++];) O.merge(f[P], a)
		f[P].constructor = f
		return f
	}


	// Non-standard
	O.each = function(obj, fn, scope, key) {
		if (obj) for (key in obj) hasOwn.call(obj, key) && fn.call(scope, obj[key], key, obj)
	}

	// Object.assign ( target, source ) in ECMAScript 6

	O.merge = function(target, source) {
		for (var key, i = 1; source = arguments[i++];)
			for (key in source) if (hasOwn.call(source, key)) target[key] = source[key]
		return target
	}

	// Note: use for Object literals only,
	// as it returns false for custom objects,
	// like new Date or new YourObject.

	function isObject(obj) {
		return obj && obj.constructor === O
	}

	O.clone = function(source, temp, key) {
		if (isObject(source)) {
			temp = {}
			for (key in source) if (hasOwn.call(source, key))
				temp[key] = O.clone(source[key])
			source = temp
		}
		return source
	}

	O.zip = function(keys, vals) {
		return keys.fold(function(_, key, i) {
			_[key] = vals[i]
			return _
		}, {})
	}

	/*
	Array.flatten = function(arr) {
	for(var i=arr.length;i--;)
	0 in arr[i] && A.splice.apply(arr, [i, 1].concat(Array.flatten(arr[i])))
	return arr
	}
	flat([1,2,[3,4,[5,6]],7])
	*/
	// Non-standard
	// // IE < 9 bug: [1,2].splice(0).join("") == "" but should be "12"
	A.remove = function() {
		var arr = this
		, len = arr.length
		, o = slice(arguments)
		, lastId = -1

		for (;len--;) if (~o.indexOf(arr[len])) arr.splice(lastId = len, 1)
		return lastId
	}

	A.each = A.forEach
	A.fold = A.reduce
	A.foldr = A.reduceRight
	// uniq
	// first item preserved
	A.uniq = A.filter.partial(function(elem, i, arr) {
		return i == arr.indexOf(elem)
	})

	A.pushUniq = function(item) {
		return this.indexOf(item) == -1 ? this.push(item) : false
	}

	!function(n) {
		F[n] = S[n] = function() {
			var a = arguments, l = a[0]
			a[0] = this.fn()
			return A[n].apply(l, a)
		}
	}.byWords()("every filter each map fold foldr some")


	F.fn = function() {
		return this
	}

	S.fn = function(scope) {
		return Fn(this, scope)
	}

	// THANKS: Oliver Steele http://www.osteele.com/sources/javascript/functional/
	function Fn(expr, scope) {
		for (var args = "_", body = expr, arr = expr.split("->"); arr.length > 1; ) {
			body = arr.pop()
			args = arr.pop().match(/\w+/g) || ""
			if (arr.length) arr.push("(function("+args+"){return("+body+")})")
		}
		// `replace` removes symbols that follow '.',
		//  precede ':', are 'this' or 'arguments'; and also the insides of
		//  strings (by a crude test).  `match` extracts the remaining
		//  symbols.
		return new Function(args, (scope && (expr = expr.replace(fnRe, "").match(/\b[a-z]\w*|\b_\w+/g)) ?
			"var " + expr.uniq().join("='',") + "='';with(" + scope + "||{})" : "") + "return(" + body + ")")
	}

	exports.Fn = Fn.cache()

}(this)







/*
 * @version    0.1.7
 * @date       2015-02-25
 * @stability  1 - Experimental
 * @author     Lauri Rooden <lauri@rooden.ee>
 * @license    MIT License
 */



!function(window, document) {
	// The addEventListener is supported in Internet Explorer from version 9.
	// https://developer.mozilla.org/en-US/docs/Web/Reference/Events/wheel
	// - IE8 always prevents the default of the mousewheel event.

	var removed
	, Event = window.Event || (window.Event = {})
	, wheelDiff = 120
	, addEv = "addEventListener"
	, remEv = "removeEventListener"
	, prefix = window[addEv] ? "" : (addEv = "attachEvent", remEv = "detachEvent", "on")
	, translateEvent = {
		wheel:
			"onwheel" in document      ? "wheel" :      // Modern browsers
			"onmousewheel" in document ? "mousewheel" : // Webkit and IE
			"DOMMouseScroll"                            // older Firefox
	}

	Event.Emitter = {
		on: on,
		non: non,
		off: non,
		once: once,
		one: once,
		emit: emit
	}

	function on(type, fn, scope, _origin) {
		var emitter = this
		, events = emitter._e || (emitter._e = {})
		;(events[type] || (events[type] = [])).unshift(scope, _origin, fn)
		return emitter
	}

	function non(type, fn, scope) {
		var i
		, emitter = this
		, events = emitter._e && emitter._e[type]
		if (events) {
			if (fn) for (i = events.length; i--; i--) {
				if ((events[i--] === fn || events[i] === fn) && events[i - 1] == scope) {
					removed = events.splice(i - 1, 3)
					break
				}
			}
			else events.length = 0
		}
		return emitter
	}

	function once(type, fn, scope) {
		var emitter = this
		function remove() {
			emitter.non(type, fn, scope).non(type, remove, scope)
		}
		return emitter.on(type, remove, scope).on(type, fn, scope)
	}

	function emit(type) {
		var args, i
		, emitter = this
		if (type = (emitter._e && emitter._e[type])) {
			type = type.slice()
			for (i = type.length, args = type.slice.call(arguments, 1); i--; i--) {
				type[i--].apply(type[i - 1] || emitter, args)
			}
		}
		return emitter
	}

	Event.add = function(el, ev, _fn) {
		var fn = ev == "wheel" ?
			function(e) {
				if (!e) e = window.event
				var delta = (e.wheelDelta || -e.detail || -e.deltaY)/wheelDiff
				if (delta != 0) {
					if (delta < 1 && delta > -1) {
						var diff = (delta < 0 ? -1 : 1)/delta
						delta *= diff
						wheelDiff /= diff
					}
					//TODO: fix event
					// e.deltaY =
					// e.deltaX = - 1/40 * e.wheelDeltaX|0
					// e.target = e.target || e.srcElement
					_fn.call(el, e, delta)
				}
			} :
			prefix ? function() {
				_fn.call(el, window.event)
			} : _fn

		on.call(el, ev, fn, el, _fn)

		el[addEv](prefix + (translateEvent[ev] || ev), fn, false)
		return Event
	}

	Event.remove = function(el, ev, fn) {
		// HACK:2015-01-23:lauri:capturing removed this way is ugly but let it be till better idea will come
		removed = null
		non.call(el, ev, fn, el)
		if (removed) {
			el[remEv](prefix + (translateEvent[ev] || ev), removed[2])
		}
		return Event
	}

	Event.stop = function(e) {
		e.stopPropagation && e.stopPropagation()
		e.preventDefault && e.preventDefault()
		e.cancelBubble = e.cancel = true
		return e.returnValue = false
	}

	Event.removeAll = function(el, ev, key, arr, i) {
		if (el._e) for (key in el._e) {
			if (!ev || key === ev) Event.remove(el, key)
		}
	}

}(this, document)







/**
 * @version    0.1.7
 * @date       2015-02-25
 * @stability  1 - Experimental
 * @author     Lauri Rooden <lauri@rooden.ee>
 * @license    MIT License
 */



!function(window, document, protoStr) {
	var currentLang
	, body = document.body
	, createElement = document.createElement
	, txtAttr = "textContent" in body ? "textContent" : "innerText"
	, elCache = {}
	, fnCache = {}
	, proto = (window.HTMLElement || window.Element || El)[protoStr]
	, selectorCache = {}
	, selectorRe = /([.#:[])([-\w]+)(?:([~^$*|]?)=((["'\/])(?:\\?.)*?\5|[-\w]+)])?]?/g
	, lastSelectorRe = /(\s*[>+]?\s*)((["'\/])(?:\\?.)*?\2|[^\s+>])+$/
	, pseudoClasses = {
		"empty": "!_.hasChildNodes()",
		"first-child": "_.parentNode&&_.parentNode.firstChild==_",
		"last-child" : "_.parentNode&&_.parentNode.lastChild==_",
		"link": "_.nodeName=='A'&&_.getAttribute('href')"
	}
	, templateRe = /^([ \t]*)(@?)((?:(["'\/])(?:\\?.)*?\4|[-\w\:.#\[\]=])+)[ \t]*(.*)$/gm
	, renderRe = /[;\s]*(\w+)(?:\s*\:((?:(["'\/])(?:\\?.)*?\3|[-,\s\w])*))?/g
	, bindings = El.bindings = {
		"class": function(node, data, name, fn) {
			toggleClass.call(node, name, fn.fn("_")(data))
		},
		"html": function(node, data, html) {
			node.innerHTML = html.format(data)
		},
		"txt": function(node, data, text) {
			node.txt(text.format(data))
		},
		"val": function(node, data, text) {
			node.val(text.format(data))
		}
	}


	/**
	 * Turns CSS selector like syntax to DOM Node
	 * @returns {Node}
	 *
	 * @example
	 * El("input#12.nice[type=checkbox]:checked:disabled[data-lang=en].class")
	 * <input id="12" class="nice class" type="checkbox" checked="checked" disabled="disabled" data-lang="en">
	 */

	function El(name, args, silence) {
		var el
		, pre = {}
		name = name.replace(selectorRe, function(_, op, key, fn, val, quotation) {
			pre[
				op == "." ?
				((pre[op = "class"] && (key = pre[op] + " " + key)), op) :
				op == "#" ?
				"id" :
				key
			] = quotation ? val.slice(1, -1) : val || key
			return ""
		}) || "div"

		// NOTE: IE-s cloneNode consolidates the two text nodes together as one
		// http://brooknovak.wordpress.com/2009/08/23/ies-clonenode-doesnt-actually-clone/
		el = (elCache[name] || (elCache[name] = document.createElement(name))).cloneNode(true).set(pre)

		return silence ?
		(fnCache[name] && el.setAttribute("data-call", name), el) :
		fnCache[name] && fnCache[name].call(el, args) || el.set(args)
	}
	window.El = El

	function append(child, before) {
		var fragment
		, el = this
		, i = 0
		, tmp = typeof child
		if (child) {
			if (tmp == "string" || tmp == "number") child = El.text(child)
			else if ( !("nodeType" in child) && "length" in child ) {
				// document.createDocumentFragment is unsupported in IE5.5
				// fragment = "createDocumentFragment" in document ? document.createDocumentFragment() : El("div")
				for (
					tmp = child.length
					, fragment = document.createDocumentFragment();
					i < tmp; ) append.call(fragment, child[i++])
				child = fragment
			}

			if (child.nodeType) el.insertBefore(child,
				(before === true ? el.firstChild :
				typeof before == "number" ? el.childNodes[
					before < 0 ? el.childNodes.length - before - 2 : before
				] : before) || null
			)
			if (child.appendHook) child.appendHook()
			//"childHook" in el && el.childHook()
		}
		return el
	}
	proto.append = append

	proto.after = function(silbing, before) {
		// call append so it works with DocumentFragment
		append.call(silbing.parentNode, this, before ? silbing : silbing.nextSibling)
		return this
	}

	proto.to = function(parent, before) {
		append.call(parent, this, before)
		return this
	}

	function hasClass(name) {
		// http://jsperf.com/regexp-indexof-perf/32
		return (" " + this.className + " ").indexOf(" " + name + " ") > -1
	}
	proto.hasClass = hasClass

	function addClass(name) {
		var el = this
		el.className += !el.className ? name : hasClass.call(el, name) ? "" : " " + name
		return el
	}
	proto.addClass = addClass

	function rmClass(name) {
		var el = this
		el.className = (" "+el.className+" ").replace(" "+name+" ", " ").trim()
		return el
	}
	proto.rmClass = rmClass

	function toggleClass(name, force) {
		if (arguments.length == 1) force = !hasClass.call(this, name)
		return ( force ? addClass : rmClass ).call(this, name), force
	}
	proto.toggleClass = toggleClass

	proto.empty = function() {
		for (var node, el = this; node = el.firstChild; ) kill.call(node)
		return el
	}

	function kill() {
		var el = this
		if (el.parentNode) el.parentNode.removeChild(el)
		if (Event.removeAll) Event.removeAll(el)
		if (el.killHook) el.killHook()
		if (el.empty) el.empty()
		return el
	}
	proto.kill = kill

	proto.on = function(ev, fn) {
		Event.add(this, ev, fn)
		return this
	}

	proto.non = function(ev, fn) {
		Event.remove(this, ev, fn)
		return this
	}

	proto.attr = attr
	function attr(key, val) {
		var el = this
		if (arguments.length == 1) {
			if (key && key.constructor === Object) {
				Object.each(key, function(val, key) {
					attr.call(el, key, val)
				})
				return
			}
			return el.getAttribute(key)
		}
		// Note: IE5-7 doesn't set styles and removes events when you try to set them.
		//
		// in IE6, a label with a for attribute linked to a select list
		// will cause a re-selection of the first option instead of just giving focus.
		// http://webbugtrack.blogspot.com/2007/09/bug-116-for-attribute-woes-in-ie6.html

		// there are bug in IE<9 where changed 'name' param not accepted on form submit
		// The JScript engine used in IE doesn't recognize vertical tabulation character
		// oldIE = "\v" == "v"
		//
		// IE8 and below also support document.createElement('<P>')
		//
		// http://www.matts411.com/post/setting_the_name_attribute_in_ie_dom/
		// http://msdn.microsoft.com/en-us/library/ms536614(VS.85).aspx

		//** modernBrowser
		if ((key == "id" || key == "name" || key == "type") && "\v" == "v") {
			el.mergeAttributes(createElement('<INPUT '+key+'="' + val + '">'), false)
		} else {
			el.setAttribute(key, val)
		}
		/*/
		el.setAttribute(key, val)
		//*/
	}

	proto.set = set
	function set(args, key, val) {
		var el = this
		if (args) {
			if (typeof args == "object") for (key in args) {
				val = args[key]
				// El uses class
				if (key == "class") addClass.call(el, val)
				else if (typeof val == "string") {
					attr.call(el, key, val)
				}
				else if (!val) el.removeAttribute(key)
			}
			else append.call(el, args)
		}
		return el
	}

	function render(data, skipSelf) {
		var bind, fn, lang
		, node = this

		if (bind = !skipSelf && node.getAttribute("data-call")) {
			node.removeAttribute("data-call")
			fnCache[bind].call(node)
		}
		if (bind = !skipSelf && node.getAttribute("data-bind")) {
			lang = node.getAttribute("lang") || lang
			// HACK: allow .el ={name} short syntax
			if (bind.charAt(0) == "{") bind='txt:"' + bind.replace(/"/g, '\\"') + '"'
			// i18n(bind, lang).format(data)
			// document.documentElement.lang
			// document.getElementsByTagName('html')[0].getAttribute('lang')

			fn = "n d p r->d&&(" + bind.replace(renderRe, "(p['$1']?(r=p['$1'](n,d,$2)||r):(n['$1']=$2.format(d))),") + "r)"

			if (Fn(fn)(node, data, bindings)) return node
		}

		for (node = node.firstChild; node; node = node.nextSibling) {
			if (node.nodeType == 1) {
				render.call(node, data)
			}
		}
		return this
	}

	proto.render = render

	// In Safari 2.x, innerText functions properly only
	// if an element is neither hidden (via style.display == "none")
	// nor orphaned from the document.
	// Otherwise, innerText results in an empty string.
	//
	// textContent is suported from IE9
	// Opera 9-10 have Node.text so we use Node.txt

	proto.txt = function(newText) {
		return arguments.length ? (this[txtAttr] = newText) : this[txtAttr]
	}

	proto.val = function(val) {
		var el = this
		, type = el.type
		, opts = el.options

		if (arguments.length) {
			return el.value = val
		}

		if (type == "select-multiple") {
			for (val = [], type = 0; el = opts[type++];) {
				if (el.selected && !el.disabled && !el.parentNode.disabled) val.push(el.value)
			}
			return val
		}

		return (type == "checkbox" || type == "radio") && !el.checked ? "" :
			el.value ||
			options && options[el.selectedIndex].value ||
			"on"
	}

	function findEl(node, sel, first) {
		var el
		, i = 0
		, out = []
		, els = node.getElementsByTagName("*")
		, fn = selectorFn(sel.split(/\s*,\s*/).map(function(sel) {
			return "_.matches('" + sel + "')"
		}).join("||"))

		for (; (el = els[i++]); ) if (fn(el)) {
			if (first) return el
			out.push(el)
		}
		return first ? null : out
	}

	function selectorFnStr(sel) {
		var rules = ["_"]
		, tag = sel.replace(selectorRe, function(_, op, key, fn, val, quotation, len) {
			if (quotation) val = val.slice(1, -1)
			if (val) {
				len = val.length
				val = val.replace(/'/g, "\\'")
			}
			rules.push(
				op == "." ? "(' '+_.className+' ').indexOf(' " + key + " ')>-1" :
				op == "#" ? "_.id=='" + key + "'" :
				op == ":" && pseudoClasses[key] ||
				"(a=_.getAttribute('" + key + "'))" + (!fn && val ? "=='" + val + "'" : "")
			)
			if (fn) rules.push(
				fn == "^" ? "a.slice(0," + len + ")=='" + val + "'" :
				fn == "|" ? "a.split('-')[0]=='" + val + "'" :
				fn == "$" ? "a.slice(-" + len + ")=='" + val + "'" :
				fn == "~" ? "(' '+a+' ').indexOf(' " + val + " ')>-1" :
				"a.indexOf('" + val + "')>-1" // fn == "*"
			)
			return ""
		})

		if (tag && tag != "*") rules.unshift("_.nodeName=='" + tag.toUpperCase() + "'")
		return rules.join("&&")
	}

	function selectorFn(str) {
		// jshint evil:true
		return selectorCache[str] ||
		(selectorCache[str] = Function("_,a", "return " + str))
	}

	proto.matches = proto.matches || function(sel) {
		var relation, from
		, parentSel = sel.replace(lastSelectorRe, function(_, _rel, a, b, start) {
			from = start + _rel.length
			relation = _rel.trim()
			return ""
		})
		, next = relation == "+" ? this.previousSibling : this.parentNode
		, fn = selectorFn(selectorFnStr(sel.slice(from)))

		if (!fn(this)) return false

		if (parentSel) {
			if (!relation) return !!(next && next.closest && next.closest(parentSel))
			return next && next.matches && next.matches(parentSel) || false
		}
		return true
	}

	proto.closest = proto.closest || function(sel) {
		for (var el = this; el; el = el.parentNode) if (el.matches && el.matches(sel)) return el
		return null
	}

	//** modernBrowser
	// Note: IE8 don't support :disabled
	proto.find = "\v" !== "v" && proto.querySelector || function(sel) {
		return findEl(this, sel, true)
	}

	proto.findAll = proto.querySelectorAll ?
		function(sel) {
			return new ElWrap(this.querySelectorAll(sel))
		} :
		function(sel) {
			return new ElWrap(findEl(this, sel))
		}
	/*/
	proto.find = proto.querySelector
	proto.findAll = function(sel) {
		return new ElWrap(this.querySelectorAll(sel))
	}
	//*/


	function ElWrap(nodes) {
		this._nodes = nodes
	}
	El.wrap = ElWrap

	ElWrap.prototype = Object.keys(proto).reduce(function(memo, key) {
		memo[key] = function() {
			var nodes = this._nodes
			, i = 0
			, len = nodes.length
			for (; i < len; ) {
				proto[key].apply(nodes[i++], arguments)
			}
			return this
		}
		return memo
	}, {})

	//** modernBrowser
	function extend(node, key) {
		if (node) for (key in proto) node[key] = proto[key]
		return node
	}

	// IE 6-7
	if (proto === El[protoStr]) {
		document.createElement = function(name) {
			return extend(createElement(name))
		}

		// NOTE: document.body will not get extended with later added extensions
		extend(body)
	}
	//*/

	El[protoStr] = proto

	El.get = function(id) {
		if (typeof id == "string") id = document.getElementById(id)
		//** modernBrowser
		return id && id.to ? id : extend(id)
		/*/
		return id
		//*/
	}

	function elCacheFn(name, el, custom) {
		elCache[name] = typeof el == "string" ? El(el) : el
		if (custom) {
			fnCache[name] = custom
		}
	}

	elCacheFn._el = elCache
	elCacheFn._fn = fnCache
	El.cache = elCacheFn

	El.text = function(str) {
		return document.createTextNode(str)
	}

	//** templates

	function tpl(str) {
		var root = document.createDocumentFragment()
		, parent = root
		, stack = [-1]

		function work(all, indent, plugin, name, q, text) {
			for (var i = indent.length; i <= stack[0]; ) {
				stack.shift()
				parent = (parent.plugin) ? parent.plugin.done() : parent.parentNode
			}

			if (plugin) {
				if (tpl.plugins[name]) {
					parent = (new tpl.plugins[name](parent, text)).el
					stack.unshift(i)
				} else {
					parent.append(El.text( name == "text" ? text : all ))
				}
			} else {
				if (name) {
					parent = El(name, 0, 1).to(parent)
					stack.unshift(i)
				}
				if (text) {
					q = text.charAt(0)
					if (q == ">") {
						(indent +" "+ text.slice(1)).replace(templateRe, work)
					} else if (q == "=") {
						parent.set({"data-bind": text.slice(1)})
					} else {
						parent.append(text.replace(/\\([=>:])/g, "$1"))
					}
				}
			}
		}
		str.replace(templateRe, work)
		root = root.childNodes
		if (root.length == 1) return root[0]

		for (stack = [], parent = root.length; parent--;) stack[parent] = root[parent]
		return stack
	}

	function template(parent, name) {
		var t = this
		t.name = name
		t.parent = parent
		t.el = El("div")
		t.el.plugin = t
		return t
	}

	template.prototype.done = function() {
		var t = this
		El.cache(t.name, t.el.removeChild(t.el.firstChild), render)
		t.el.plugin = null
		return t.parent
	}

	tpl.plugins = {
		"template": template
	}

	El.create = El.tpl = function(str) {
		return tpl(str).render()
	}
	El.include = function(id, data, parent) {
		var src = El.get(id)
		new template(null, id).el.append( El.tpl(src.innerHTML) ).plugin.done()
		src.kill()
	}
	//*/


	//** i18n
	function i18n(text, lang) {
		return i18n[ lang ? getLang(lang) : currentLang ][text] || text
	}
	El.i18n = i18n

	function getLang(lang) {
		if (!lang) return currentLang
		lang = (lang || "").toLowerCase()
		return i18n[lang] ? lang : ((lang = lang.split("-")[0]), i18n[lang]) ? lang : currentLang
	}

	function setLang(lang) {
		lang = getLang(lang)
		if (currentLang != (currentLang = lang)) {
			i18n[lang] = i18n[lang] || {}
		}
		// Use setAttribute
		document.documentElement.lang = lang
		return lang
	}

	function addLang(lang, texts) {
		Object.merge(i18n[lang] || (i18n[lang] = {}), texts)
	}

	i18n.get = getLang
	i18n.use = setLang
	i18n.add = addLang
	i18n.def = function(map) {
		Object.each(map, function(name, tag) {
			addLang(tag, map)
		})
	}
	// navigator.userLanguage for IE, navigator.language for others
	// var lang = navigator.language || navigator.userLanguage;
	// setLang("en")
	//*/

}(window, document, "prototype")



/*! litejs.com/MIT-LICENSE.txt */



/**
 * @version    0.1.7
 * @date       2015-02-25
 * @stability  1 - Experimental
 * @author     Lauri Rooden <lauri@rooden.ee>
 * @license    MIT License
 */



// https://github.com/browserstate/history.js/wiki/The-State-of-the-HTML5-History-API

!function(window, document, history) {
	var cb, base, last_route, iframe, tick, last
	, loc = location
	, clean_route = /^[#\/\!]+|[\s\/]+$/g

	// The JScript engine used in IE doesn't recognize vertical tabulation character
	// http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
	// oldIE = "\v" == "v"
	//
	// The documentMode is an IE only property, supported in IE8+.
	//
	// Starting in Internet Explorer 9 standards mode, Internet Explorer 10 standards mode,
	// and win8_appname_long apps, you cannot identify the browser as Internet Explorer
	// by testing for the equivalence of the vertical tab (\v) and the "v".
	// In earlier versions, the expression "\v" === "v" returns true.
	// In Internet Explorer 9 standards mode, Internet Explorer 10 standards mode,
	// and win8_appname_long apps, the expression returns false.
	, ie6_7 = !+"\v1" && (document.documentMode||1) < 8

	function getUrl(_loc) {
		var url
		/*** PUSH
		if (base) {
			url = loc.pathname.slice(base.length)
		} else {
		//*/
			// bug in Firefox where location.hash is decoded
			// bug in Safari where location.pathname is decoded

			// var hash = loc.href.split('#')[1] || '';
			// https://bugs.webkit.org/show_bug.cgi?id=30225
			// https://github.com/documentcloud/backbone/pull/967
			url = (_loc || loc).href.split("#")[1] || ""
		/*** PUSH
		}
		//*/
		// Fix decode
		// http://unixpapa.com/js/querystring.html
		url = decodeURIComponent(url.replace(/\+/g, " "))
		return url.replace(clean_route, "")
	}

	function setUrl(url, replace) {
		url = encodeURIComponent(url).replace(/%20/g, "+").replace(/%2F/gi, "/")
		/*** PUSH
		if (base) {
			history[replace ? "replaceState" : "pushState"](null, null, base + url)
		} else {
		//*/
			loc[replace ? "replace" : "assign"]("#" + url)
			// Opening and closing the iframe tricks IE7 and earlier
			// to push a history entry on hash-tag change.
			if (iframe && getUrl() !== getUrl(iframe.location) ) {
				iframe.location[replace ? "replace" : iframe.document.open().close(), "assign"]("#" + url)
			}
		/*** PUSH
		}
		//*/
		checkUrl()
	}

	function checkUrl() {
		if (last_route != (last_route = getUrl()) && cb) cb(last_route)
	}

	history.getUrl = getUrl
	history.setUrl = setUrl

	history.start = function(_cb, _base) {
		if (!cb) {
			/*** PUSH
			if (_base && history.pushState) {
				base = _base
				// Chrome and Safari emit a popstate event on page load, Firefox doesn't.
				// Firing popstate after onload is as designed.
				//
				// See the discussion on https://bugs.webkit.org/show_bug.cgi?id=41372,
				// https://code.google.com/p/chromium/issues/detail?id=63040
				// and the change to the HTML5 spec that was made:
				// http://html5.org/tools/web-apps-tracker?from=5345&to=5346.
				window.onpopstate = checkUrl
			} else
			//*/
				if ("onhashchange" in window && !ie6_7) {
				// There are onhashchange in IE7 but its not get emitted
				//
				// Basic support:
				// Chrome 5.0, Firefox (Gecko) 3.6 (1.9.2), IE 8.0, Opera 10.6, Safari 5.0
				window.onhashchange = checkUrl
			} else {
				if (ie6_7) {
					// IE<9 encounters the Mixed Content warning when the URI javascript: is used.
					// IE5/6 additionally encounters the Mixed Content warning when the URI about:blank is used.
					// src="//:"
					iframe = document.body.appendChild(document.createElement('<iframe class="hide" tabindex="-1">')).contentWindow
				}
				last = getUrl()
				tick = setInterval(function(){
					var cur = getUrl()
					if (iframe && last === cur) cur = getUrl(iframe.location)
					if (last !== cur) {
						last = cur
						iframe ? setUrl(cur) : checkUrl()
					}
				}, 50)
			}
		}
		cb = _cb
		checkUrl()
	}
}(this, document, history)







/*
 * @version    0.1.7
 * @date       2015-02-25
 * @stability  1 - Experimental
 * @author     Lauri Rooden <lauri@rooden.ee>
 * @license    MIT License
 */



!function(exports) {
	var fn, re
	, fnStr = ""
	, reStr = ""
	, views = {}
	, loadedFiles = {}
	, groupsCount = 1
	, escapeRe = /[.*+?^=!:${}()|\[\]\/\\]/g
	, parseRe = /\{([\w%.]+?)\}|.[^{\\]*?/g

	function escapeRegExp(string) {
		return string.replace(escapeRe, "\\$&")
	}

	function View(route, el, parent, selector) {
		var view = this
		if (views[route]) {
			if (el) views[route].init(el, parent, selector)
			return views[route]
		}
		if (!(view instanceof View)) return new View(route, el, parent, selector)
		views[view.route = route] = view
		view.init(el, parent, selector)

		if (route.charAt(0) != "#") {
			var startLen = groupsCount++
			, a = ["o._r='" + route + "'"]
			, _re = route.replace(/:(\w+)/g, "{$1}").replace(parseRe, function(_, key) {
				return key ?
					a.push("o['" + key + "']=a[" + (groupsCount++) + "]") && "([^/]+?)" :
					escapeRegExp(_)
			})

			fnStr += (fnStr ? "||" : "") + "a[" + startLen + "]&&(" + a + ")"
			reStr += (reStr ? "|" : "") + "(" + _re + ")"

			fn = new Function("a", "var o={};return " + fnStr + ",o")
			re = new RegExp("^\\/?(?:" + reStr + ")[\\/\\s]*$")
		}

		if (View.active == route) view.show()
	}

	View.prototype = {
		init: function(el, parent, selector) {
			var view = this
			view.el = el
			view.parent = typeof parent == "string" ? View(parent) : parent
			view.selector = selector
		},
		getEl: function() {
			var view = this
			, type = typeof view.el
			if (type == "function") view.el = view.el()
			else if (type == "string") view.el = El(view.el)
			return view.el
		},
		getContentEl: function() {
			var view = this
			return view.selector && view.getEl().find(view.selector) || view.getEl()
		},
		load: function(opts) {
			var view = this
			, file = (View.base || "") + (view.file || view.route + ".js")

			if (loadedFiles[file]) show()
			else xhr.load(file, loadedFiles[file] = show)

			function show() {
				if (view.el) view.show(opts)
				else if (view.route !== View["404"]) {
					View(View["404"]).show({})
				}
			}
		},
		show: function(opts) {
			var view = this
			View.active = view.route
			if (view.el) {
				if (view.active) view.close()
				view.ping(opts)
				if (View.active == view.route) view.emit("show", opts)
			} else {
				view.load(opts)
			}
		},
		close: function(opts) {
			var view = this
			, parent = view.parent

			view.active = false

			if (parent) {
				parent.getContentEl().removeChild(view.getEl())
				parent.child = null
			}
			view.emit("close", opts)
			if (view.child) view.child.close()
		},
		ping: function(opts) {
			var view = this
			, parent = view.parent

			view.active = true

			if (parent) {
				if (parent.child != view) {
					if (parent.child) parent.child.close()
					parent.child = view
					parent.getContentEl().appendChild(view.getEl())
				}
				parent.ping(opts)
			}
			view.emit("ping", opts)
		}
	}

	Object.merge(View.prototype, Event.Emitter)

	View["404"] = "404"
	View.main = "main"

	View.show = function(route) {
		var match = re.exec(route)
		View(
			match && (match = fn(match))._r ||
			route && View["404"] ||
			View.main
		).show(match || {})
	}

	View.define = function(str) {
		for (var match, re = /(\S+)\?(\S+)/g; match = re.exec(str);) {
			match[2].split(",").map(function(route) {
				View(route).file = match[1]
			})
		}
	}

	exports.View = View
}(this)

