// Extensions to Core JS 
// and jQuery methods and objects

/**
 * Easy access in Arrays to jQuerys $.each-function.
 */
Array.prototype.each = function (method) {
	$.each(this, method);
};

/**
 * Format-function to format a string.
 * Arguments: Unlimited, replaces {0} with first argument, {1} with second argument, and so forth
 * Ex: "Hello {0}, this is {1}".format("world", "nice");
 * Returns: "Hello world, this is nice"
 */
String.prototype.format = function() {
	var args = arguments;
	return this.replace(/{(\d+)}/g, function(o, m) {
		return args[m];
	});
};

String.prototype.capitalize = function() {
	return this.substring(0, 1).toUpperCase() + this.substring(1).toLowerCase();
};

String.prototype.isOneOf = function () {
	return $.inArray(this.toString(), arguments) > -1;
};
String.prototype.trim = function () {
	return this.replace(/^\s+(.*)\s+/, '$1');
};
String.prototype.supplant = function (o) {
	return this.replace(/{([^{}]*)}/g,
		function (a, b) {
			var r = o[b];
			return typeof r === 'string' || typeof r === 'number' ? r : a;
		}
	);
};
// Strip hash part of url
String.prototype.stripHash = function (string) {
	return this.replace(/#.+$/, '');
};




// Extend the document object
document.getLayout = function () {
	var _layout,
	    regex = /layout-\d+/i;
	function getLayout() {
		var result;
		if (!_layout) {
			result = regex.exec($('body').attr('class'));
			_layout = result ? result[0] : null;
		}
		return _layout;
	}
	return getLayout;
}();
document.hasLayout = function (layout) {
	return document.getLayout() === layout;
};
document.getPageType = function () {
	var _pagetype;
	function getPageType() {
		_pagetype = _pagetype || $('body').attr('id');
		return _pagetype;
	}
	return getPageType;
}();
document.hasPageType = function (type) {
	return document.getPageType() === type;
};
document.isStartPage = function () {
	return document.hasPageType('home');
};
document.isSubSectionPage = function () {
	return document.hasPageType('sub-section');
};

/**
 * Article functions
 * @requires jQuery
 * Add article functions, e.g., 'Print' or 'Tell a friend'.
 */
document.articleFunctions = function() {
	var ul;
	function getUl() {
		if (!ul) {
			var funcs = $('#article-functions');
			if (funcs.length) {
				ul = $('ul', funcs);
				if (!ul.length) {
					ul = $('<ul></ul>').prependTo(funcs);
				}
			}
		}
		return ul;
	}
	/**
 	 * Add an item
	 * @param  options  Object  An object with options
	 */
	function addItem(options) {
		var li, a;
		options = jQuery.extend({
			// Link html
			text: '',
			// Link (or li) class
			className: '',
			// Link (or li) id
			id: '',
			// Callback to execute on click
			callback: null,
			// An element to append the link to, bypassing the regular ul
			insertInto: null
		}, options || {});
		
		if (getUl() || options.insertInto) {
			// Create link
			a = $('<a href="#"></a>');
			a.html(options.text);
			a.click(options.callback);
			if (!options.insertInto) {
				// If no insertInto-element is specified,
				// append the link to an li and add it to the ul
				li = $('<li>');
				// Add class and id to the li
				li.attr({
					'class': options.className,
					'id': options.id
				});
				a.appendTo(li);
				li.appendTo(ul);
			} else {
				// Add class and id to the link
				a.attr({
					'class': options.className,
					'id': options.id
				});
				a.appendTo($(options.insertInto));
			}
			return a;
		}
	}
	return {
		addItem: addItem
	};
}();

// Extend jQuery
(function ($) {

	var defaultValueClassName = 'default-value-set';

	$.extend($.fn, {
		rememberDefaultValue: function() {
			$(this).each(function () {
				var t = $(this);
				t.data('defaultValue', t.attr('title'));
				if (t.data('defaultValue')) {
					t.resetDefaultValue();
					t.focus(function(e) {
						$(this).removeDefaultValue();
					});
					t.blur(function(e) {
						$(this).resetDefaultValue();
					});
					t.closest('form').submit(function () {
						if (t.valueIsDefault()) {
							t.removeDefaultValue();
						}
					});
				}
			});
			return this;
		},
		valueIsDefault: function () {
			return $(this).val() === $(this).data('defaultValue');
		},
		removeDefaultValue: function () {
			$(this).each(function () {
				var t = $(this);
				if (t.valueIsDefault()) {
					t.val('');
					t.removeClass(defaultValueClassName);
				}
			});
			return this;
		},
		resetDefaultValue: function () {
			$(this).each(function () {
				var t = $(this);
				if (t.val() === '' || t.valueIsDefault()) {
					t.addClass(defaultValueClassName);
					t.val(t.data('defaultValue'));
				}
			});
			return this;
		},
		isExternal: function () {
			var docHost, linkHost;
			// Is it a link to begin with?
			if (this.attr('tagName').toLowerCase() == "a") {
				// A mailto link is not external
				if (this.attr('protocol') != "mailto:") {
					// If the second-level domain matches the current one, it's not an external link
					docHost = document.location.hostname.match(/\w+\.\w+$/);
					linkHost = this.attr('hostname').match(/\w+\.\w+$/);
					if (docHost && linkHost && docHost[0] != linkHost[0]) {
						// If the hostname matches any of the specified internal domains, it's not an external link
						if ($.inArray(this.attr('hostname'), SolnaStad.InternalDomains) == -1) {
							return true;
						}
					}
				}
			}
			return false;
		},

		/**
		When clicked, supplied links should open i a new window
		*/
		triggerNewWindow: function (options) {
			options = $.extend({}, {
				widthRegEx: /\bw([0-9]+)\b/, // Regular expression for width (matches w400)
				heightRegEx: /\bh([0-9]+)\b/, // Regular expression for height (matches h400)
				warning: '' // HTML string that is appended to the link. E.g. '(will open in a new window)'
			}, options || {});

			return this.each(function () {
				var $this = $(this);
				if (options.warning) {
					$this.append(options.warning);
				}
				if (options.widthRegEx.test($this.attr('class')) && options.heightRegEx.test($this.attr('class'))) {
					$this.click(function (e) {
						e.preventDefault();
						window.open(this.href, '_blank', 'menubar=yes,toolbar=no,location=yes,resizable=yes,scrollbars=yes,status=yes,width=' + options.widthRegEx.exec($this.attr('class'))[1] + ',height=' + options.heightRegEx.exec($this.attr('class'))[1]);
					});
				}
				this.target = '_blank';
			});
		},

		getLabel: function() {
			var label;
			if (this.attr('tagName').toLowerCase().isOneOf('input', 'select', 'textarea') && this.attr('id')) {
				label = $('label[for=' + this.attr('id') + ']');
				return label;
			}
		},

		stickyTableHeaders: function () {
			// Save document reference
			this.each(function () {
				// Original element
				var thead = $(this);
				// Copy the original element and it's descendants, with events and data
				var theadCopy = thead.clone(true);
				// Table wrapper for thead copy
				var div = $('<div class="sticky-header">').append($('<table>').append(theadCopy).css({
					margin: 0,
					width: thead.width()
				}));
				// Find all original header cells
				var cells = thead.find('th');
				// Loop over cells in copy
				theadCopy.find('th').each(function (index) {
					// Set dimensions based on corresponding original cell
					$(this).css({
						width: cells.eq(index).width(),
						height: cells.eq(index).height()
					});
				});

				function handle(e) {
					var scrollTop = $(document).scrollTop();
					// If scroll is further down than the offset top
					// of the original element…
					if (scrollTop > thead.offset().top && scrollTop < thead.offset().top + thead.closest('table').height() - thead.height()) {
						// … insert copy
						thead.offsetParent().append(div);
						// Make the copy stick to the top of the viewport
						div.css({
							position: 'fixed',
							top: 0,
							zIndex: 100000
						});
					} else {
						// Detach copy from DOM, but don't delete it
						div.detach();
					}
				}
				// Observe document scroll event
				$(window).scroll(handle);
				// And make sure header sticks on page load
				$(handle);
			});
		},

		// Return or set the minimum height of an element.
		// @param  Number  size  New minimum height to set
		minHeight: function(size) {
			// Explorer versions prior to IE7 needs to have height instead of min-height
			var type = ($.browser.msie && parseInt($.browser.version, 10) < 7)	? "height" : "min-height";
			if (size == undefined) {
				// Get min-height for the first element
				return this.css(type);
			} else {
				// Set the min-height on all elements (default to pixels if value is unitless)
				this.css(type, size.toString().match(/^\d+$/) ? size + "px" : size);
				return this;
			}
		},

		// Justify element heights.
		// @param  minHeight  Number  Minimum height to set
		justify: function(minHeight) {
			var maxHeight = 0;
			// Get the height of the highest element
			this.each(function() {
				var el = $(this), height;
				el.minHeight(0);
				height = el.outerHeight();
				if (height > maxHeight) {
					maxHeight = height;
				}
			});
			maxHeight = maxHeight < minHeight ? minHeight : maxHeight;
			// Set min-height for all elements
			return this.each(function () {
				var el = $(this);
				el.minHeight(el.height() + maxHeight - el.outerHeight());
			});
		}
	});
})(jQuery);

/**
* A simple querystring parser.
* Example usage: var q = $.parseQuery(); q.foo returns  "bar" if query contains "?foo=bar"; multiple values are added to an array. 
* Values are unescaped by default and plus signs replaced with spaces, or an alternate processing function can be passed in the params object .
* http://actingthemaggot.com/jquery
*
* Copyright (c) 2008 Michael Manning (http://actingthemaggot.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
**/
jQuery.parseQuery = function(qs, options) {
	var q = (typeof qs === 'string' ? qs : window.location.search), o = { 'f': function(v) { return unescape(v).replace(/\+/g, ' '); } }, options = (typeof qs === 'object' && typeof options === 'undefined') ? qs : options, o = jQuery.extend({}, o, options), params = {};
	jQuery.each(q.match(/^\??(.*)$/)[1].split('&'), function(i, p) {
		p = p.split('=');
		p[1] = o.f(p[1]);
		params[p[0]] = params[p[0]] ? ((params[p[0]] instanceof Array) ? (params[p[0]].push(p[1]), params[p[0]]) : [params[p[0]], p[1]]) : p[1];
	});
	return params;
};
