/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Do not use without permission.							20110310 *
 *																	 *
 * jquery-page 0.36, Copyright 2010-2011 davidenke,post@davidenke.de *
 * This script runs with jQuery (http://jquery.com) and some plugins *
 *																	 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

var Options = function() {
	var self = this;

	this.refreshBase = function() {
		this.elementBody = $('body');
		this.elementBase = $('base');
		this.urlSuffix = '.html';
		this.urlBase = (this.elementBase.attr('href') + '/').replace(/\/\/$/, '/');
	}

	this.refreshBase();
}

var Environment = function() {
	var self = this,
		urlBase = arguments[0],
		urlSuffix = arguments[1],
		referer = {},
		current = {};

	this.refreshBase = function() {
		base = arguments[0];
	}
	this.hashToObject = function(h) {
		return {
			'hash': h.slice(),
			'link': h.shift() || 0,
			'deep': h.pop() || 0
		}
	}
	this.setCurrent = function(s) {
		referer = current;
		current = self.hashToObject(self.getRequest(self.relativize(s)))
	}
	this.getCurrent = function() {
		return current;
	}
	this.getReferer = function() {
		return referer;
	}

	this.getCurrentLink = function(absolute) {
		return !current.link ? current.link : (absolute ? urlBase : '') + current.link + urlSuffix;
	}
	this.getCurrentDeep = function(absolute) {
		return !current.deep ? current.link : (absolute ? urlBase : '') + current.deep + urlSuffix;
	}
	this.getRefererLink = function(absolute) {
		return !referer.link ? current.link : (absolute ? urlBase : '') + referer.link + urlSuffix;
	}
	this.getRefererDeep = function(absolute) {
		return !referer.deep ? current.link : (absolute ? urlBase : '') + referer.deep + urlSuffix;
	}

	this.relativize = function(s) {
		return s.replace(urlBase, '').replace(/^\/*\#*\/+/g, '');
	}
	this.absolutize = function(s) {
		return urlBase + self.relativize(s);
	}

	this.setRequest = function(piece, last, absolute) {
		var link = last ? self.getRequest() : [];
			link.pop();
			link.push(piece);
		return (absolute ? urlBase : '') + link.join('/') + urlSuffix;
	}
	this.getRequest = function(link, last) {
		return (link || location.href)
			.replace(urlBase, '')
			.replace(/^\/*#+\/*/, '')
			.replace(urlSuffix, '')
			.split('/')[last ? 'pop' : 'concat']();
	}
	this.addToLink = function(link, piece, absolute) {
		var pieces = self.getRequest(link);
		if (absolute)
			pieces.unshift(urlBase);
		if (pieces.push(piece))
			return pieces.join('/') + urlSuffix;
	}
	this.addToUrl = function(piece, absolute) {
		return self.addToLink(self.getRequest().join('/'), piece, absolute);
	}
}

var Page = function() {
	var self = this,
		idle = false;

	// !options instance
	this.options = new Options();

	// !environment instance
	this.environment = new Environment(this.options.urlBase, this.options.urlSuffix);

	this.idle = function() {
		idle = true;
		$('body').addClass('idle');
	}
	this.unidle = function() {
		idle = false;
		$('body').removeClass('idle');
	}
	this.isIdle = function() {
		return idle;
	}

	this.ajaxLinking = function() {
		$('*:not(.nolink) a').filter(':not(.nolink,[href$="pdf"],[href$="PDF"],.invisible,[href^="mailto:"],[href^="http:"])[href$=".html"]').each(function(i, el) {
			var link = $(el),
				href = link.attr('rev') || link.attr('href');
			if (link.is('a[title]:not(a[title^="deep:"])'))
				link.data('title', link.attr('title')).removeAttr('title');
			if (href[0] != '#') {
				link.attr({ 'rev': href.replace(self.options.urlBase, '') });
				link.attr({ 'href': '/#/' + self.environment.relativize(href) });
			}
				link.click(function(e) {
					e.preventDefault();

					if (!self.isIdle()) {
						self.idle(e);
						location.href = link.attr('href');
						self.prepareLinks(link);
						self.loadContents(link.attr('rev'), function() {
							self.unidle();
						}, { ajax: true });
					};
				});
		});
	}
	this.prepareLinks = function() {}
	this.onloadbegin = function(callback) {
		callback();
	}
	this.onloadfinish = function(response, xhr, callback) {
		callback(response, xhr);
	}
	this.checkResponse = function(url, response, xhr, callback) {
		var res = self.environment.relativize(xhr.getResponseHeader('Referer'));
		if (url != self.options.urlBase + res) {
			location.href = self.options.urlBase + '/#/' + res;
			self.loadContents(self.environment.absolutize(res), callback, { ajax: true });
		} else self.prepareContents(response, xhr, callback);
	}
	this.loadContents = function(url, callback, data) {
		var url = self.options.urlBase + url.replace(self.options.urlBase, '');
		var prepared = false, loaded = false, response = false, status = false, xhr = false;
		var checkready = function() {
			if (loaded && prepared) self.checkResponse(url, response, xhr, callback);
		}

		self.onloadbegin(function() {
			prepared = true;
			checkready();
		});

		$.ajax({
			'url':	url,
			'type':	'POST',
			'cache': false,
			'data':	data,
			'dataType': 'html',
			'error': function(x) {
				if (x.status == 404 || x.status == 403 || x.status == 303) {
					loaded = true;
					response = x.responseText;
					xhr = x;
					checkready();
				} else if (x.status == 303 && x.getResponseHeader('Location'))
					self.loadContents(x.getResponseHeader('Location'), callback, data);
			},
			'success': function(r, s, x) {
				loaded = true;
				response = r;
				xhr = x;
				checkready();
			}
		});
	}
	this.prepareContents = function(response, xhr, callback) {
		var images = $.unique($(response).find('img[src]:not(.noload), *[style~="url"]'));
		var cached = new Array();
		if (images.length)
			images.each(function() {
				var img = new Image();
					img.onload = function() {
						cached.push(true);
						if (cached.length == images.length)
							self.placeContents(response, xhr, callback);
					}
					img.onerror = function() {
						cached.push(false);
						$(this).hide();
						if (cached.length == images.length)
							self.placeContents(response, xhr, callback);
					}
					img.src = $(this).is('img')
							? $(this).attr('src')
							: $(this).css('background-image').replace(/((url\()+(\'|\")*)|((\'|\")*\)+\;*)/g, '');
			});
		else self.placeContents(response, xhr, callback);
	}
	this.placeContents = function(response, xhr, callback) {
		self.options.elementBody.attr('className', xhr.getResponseHeader('Content-Class'));
		document.title = unescape(xhr.getResponseHeader('Content-Title') + ' - ' + xhr.getResponseHeader('Content-Name'));
		$('html').attr('lang', xhr.getResponseHeader('Content-Language'));
		$('meta[property="og:title"]').attr('content', unescape(xhr.getResponseHeader('Content-Title')));
		$('meta[property="og:site_name"]').attr('content', unescape(xhr.getResponseHeader('Content-Name')));

		self.options.refreshBase();
		self.environment.refreshBase(this.options.urlBase);
		self.onloadfinish(response, xhr, callback);
		self._setup(response, xhr, callback);
	}
	this._setup = function(response, xhr, callback) {
		self.ajaxLinking();
		self.setup(self, response, xhr, callback);
	}
	this.setup = function(self, response, xhr, callback) {}
	this.initialize = function() {
		$.history.init(function(hash) {
			self.environment.setCurrent(hash);
			if (!self.isIdle() && self.environment.getCurrentLink()) {
				var url = self.environment.getCurrentLink();
				self.idle();
				self.loadContents(url, function(response, xhr) {
					$(response).filter('script').each(function() { $.globalEval($(this).html()); });
					self.environment.setCurrent(url);
					self.unidle();
				}, { ajax: true });
			} else self._setup();
		});
	}
};
