/**
 * Hash manager. Controls modes in window.location.hash.
 * Url format: '#/<mode>[/<arg1>[/arg2[...]]]'
 */
var hash = {
	_previousHash: '',
	_active: false,
	_handlers: [],
	_interval: null,
	_registered: {},
	_activeMode: null,
	check: function() {
		if (window.location.hash != hash._previousHash) {
			hash._previousHash = window.location.hash;
			hash.notify(
				window.location.href.substr(window.location.href.indexOf('#'))
			);
		}
	},
	onChange: function(handler) {
		this._handlers.push(handler);
	},
	notify: function(hash) {
		var _this = this;
		function shutdownActiveMode() {
			if (_this._activeMode) {
				_this._activeMode.shutdown();
				_this._activeMode = null;
			}
		}
		if (hash != '' && hash != '#' && hash != '#/' && hash.charAt(1) == '/') {
			var hashBits = hash.split('/').slice(1), modeName = hashBits[0], mode;
			var newMode = null;
			if (newMode = this._registered[modeName]) {
				if (this._activeMode != newMode) {
					shutdownActiveMode();
				}
				if (hashBits[hashBits.length - 1] == '') hashBits.splice(hashBits.length - 1, 1);
				var args = hashBits.slice(1);
				for (var i = 0, l = args.length; i < l; i++) {
					args[i] = decodeURIComponent(args[i]);
				}
				if (this._activeMode != newMode) {
					newMode.activate.apply(null, args);
					this._activeMode = newMode;
				} else {
					newMode.change.apply(null, args);
				}
			} else {
				shutdownActiveMode();
			}
		} else {
			shutdownActiveMode();
		}
		if (this._handlers.length) {
			for (var i = 0, l = this._handlers.length; i < l; i++) {
				this._handlers[i](hash);
			}
		}
	},
	activate: function() {
		if (this._active) return;
		this._active = true;
		this._interval = window.setInterval(this.check, 300);
	},
	register: function(name, activate, change, shutdown) {
		this._registered[name] = {activate: activate, change: change, shutdown: shutdown};
	},
	open: function(hash) {
		window.location.hash = '#' + hash;
		this.check();
	},
	toggle: function(hash) {
		if (window.location.hash == '#' + hash) {
			this.open('/');
		} else {
			this.open(hash);
		}
	},
	cancel: function() {
		window.location.hash = '#/';
		this.check();
	},
	init: function() {
		if (!hash.initialized) {
			hash.initializing = true;
			hash.activate();
			hash.check();
			hash.initializing = false;
			hash.initialized = true;
		}
	}
};
