(function(document) {
	
	window.Routes = window.routes || function() {
		this.root = "#!/";
		this.currentHash = null;
		this.paths = [];
		this.block = false;
	};
	
	Routes.prototype.listen = function() {
	    if(this.listening === true) {
	        if (console !== undefined)
	        	console.log('Already listening for Route events.');
	        return;
	    }
	    
	    if(/#!\/.*/.test(location.hash) === false){
	    	this.setLocation(this.root);
	        this.dispatch.call(this);
	    } else{
	        this.dispatch.call(this);
	    }
	    
	    var that = this;
	    if("onhashchange" in window){
	        window.onhashchange = function() {
	        	that.dispatch.call(that);
	        };
	    } else{
	        setInterval(function() {
	        	if (that.currentHash !== location.hash)
	        		that.dispatch.call(that);
	        }, 500);
	    }
	};
	
	Routes.prototype.dispatch = function(hash) {
	    if (hash === null || hash === undefined)
	    	hash = location.hash;
	    
	    for (var i = 0; i < this.paths.length; i++) {
	        if (this.paths[i].path === hash) {
	        	var args = undefined;
	        	if (arguments[1] !== undefined)
	        		args = Array.prototype.slice.call(arguments).slice(1);
	        	else if (this.paths[i].callback_args !== undefined)
	        		args = this.paths[i].callback_args;
	        		
	        	this.paths[i].callback.apply(this.paths[i], args);
	        	this.currentHash = this.paths[i].path;
	        	break;
	        }
	    }
	};
	
	Routes.prototype.setLocation = function(location) {
		window.location = location;
	}
	
	Routes.prototype.addPath = function(path, callback) {
	    var that = this;
	    var callback_args = Array.prototype.slice.call(arguments).slice(2); 
	    var path = new Path(path, callback, callback_args, function() {
	    	that.updateRoot(this.path);
	    });
	    
	    this.paths.push(path);
	    return path;
	};
	
	Routes.prototype.updateRoot = function() {
	    var path = arguments[0];
	    if (path !== undefined) this.root = path;
	};
	
	Path = function(path, callback, callback_args) {
	    this.path = path;
	    this.callback = callback;
	    this.callback_args = callback_args;
	    this.onsetroot = arguments[3];
	    
	    this.setRoot = function() {
	        this.root = true;
	        this.onsetroot.call(this);
	    }
	};
	
	Path.prototype.setRoot = function() {
	    this.root = true;
	    this.onsetroot.call(this);
	};
	
})(document);


(function(document) {
	
	window.Sections = window.Sections || function() {
		this.onenter = Function;
    	this.onleave = Function;
    	this.active = Object;
	};
	
	Sections.prototype.reposition = function() {
	    var element = this.detect($('#sections > *'), 50);
	    if (element === undefined || element.id === this.active.name) {
	        return false;
	    } else {
	        if (this.active.element !== null && this.active.element !== undefined)
	        	if (this.onleave !== undefined && typeof this.onleave === 'function')
	        		this.onleave.call(this.active);
	        
	        this.active = new Section;
	        this.active.name = element.id;
	        this.active.element = element;
	        
	        if (this.onenter !== undefined && typeof this.onenter === 'function')
	        	this.onenter.call(this.active);
	    }
	};
	
	Sections.prototype.detect = function(els, offset) {
	    var sections, pos, element;
	    sections = $(els);
	    pos = $(window).scrollTop();
	    offset = offset || 0;
	    
	    jQuery.each(sections, function(index, el) {
	        if (pos > ($(el).offset().top - offset) && pos <= (sections[++index] ? ($(sections[index]).offset().top - offset) : $(document).height())) {
	        	element = el; return false;
	        }
	    });
	    return element;
	};
	
	Section = function() {
		this.name = null;
    	this.element = null;
	};
	
})(document);
