/*

	PACWEB
	© Princeton Alliance Church

	Javascript Bootstrap

*/

// ============================================================================================= //
// Global
// ============================================================================================= //

/**
 * Prevent default action from being triggered
 */
function preventDefault(evt) {
	if (evt.preventDefault) {
		evt.preventDefault();
	}
	return false;
}

/**
 * Modify anchor tag behaviour:
 *
 * "rel" Attribute		Behaviour
 *
 * external				launch in new window, if allowed by global preference
 * external force		launch in new window, always
 * external die			launch in new window, closing referring window in process
 * 						note: referring window will only be closed if it was opened by spawn()
 * external force die	same as [external force] and [external die]
 */
function initAnchors() {

	// XHTML 1.1 deprecated the <a>'s target attribute (with fairly good reason);
	// but sometimes we still want to open new windows in new windows...
	var extLinks = $('a[rel="external"], a[rel="external die"], a[rel="external force"], a[rel="external force die"]').not('[href=""]');
	var extLinkHandler = function() {
		return !spawn(
			this.href,
			this.id,
			true,
			null,
			null,
			null,
			null,
			($(this).is('a[rel="external die"], a[rel="external force die"]')) ? window : null
		);
	};
	if (CONFIG.spawnOnExternalURL) {
		extLinks.bind('click', extLinkHandler);
	} else {
		extLinks.filter('a[rel="external force"], a[rel="external force die"]').bind('click', extLinkHandler);
	}
	extLinks.not('.implicit').addClass('extlink');

	// disable the disabled
	// note: these links will remain working when JS is disabled
	$('.disabled > a').bind('click', function() {
		return false;
	});

}

/**
 * Animate (shake) specified form element
 *
 * @requires jQuery.validator
 */
function formElementShaker() {
	var el, elLeft;
	for (name in this.invalid) {
		el = $('input[name="' + name +'"], textarea[name="' + name +'"], select[name="' + name +'"]').parent('div');
		elLeft = el.css('left') || '';					// this separation prevents validation from failing
		elLeft = elLeft.pxToInt();						// if no el is not found, or el has no 'left' value
		el
			.animate({left: elLeft + 5}, 40)
			.animate({left: elLeft - 5}, 40)
			.animate({left: elLeft + 5}, 40)
			.animate({left: elLeft}, 40);
	}
}

/**
 * Launch new embedded video window
 */
function spawnEmbed() {
	var type = this.className.replace(/.*embed-(\w+).*/g, "$1");
	var id = this.href.slice((this.href.lastIndexOf('=') + 1) || (this.href.lastIndexOf('/') + 1));
	spawn(
		'/worship/online/embed/' + type + '/' + id,
		'embed',
		true,
		'width=' + CONFIG.media.embed[type].swf.width + ',height=' + CONFIG.media.embed[type].swf.height + ',dependent=yes,dialog=no,scrollbars=no',
		null,
		null,
		true
	);
	return false;										// disable <a href>
}

/**
 * Launch new browser window, if approved by user
 *
 * @param {String} url URL for new window
 * @param {String} id DOM ID for new window  
 * @param {Boolean} silent Don't prompt user to open new window if true
 * @param {String} prompt Prompt message  
 * @param {String} failure Failure message  
 * @param {Boolean} respawn Always respawn open windows if true
 * @param {Object} die Window object to close
 * @return {Boolean} True if window was successfully opened; false otherwise 
 */
var spawned = [];
function spawn(url, id, silent, features, prompt, failure, respawn, die) {
	id = (typeof(id) != 'undefined' && id) ? id : '_blank';
	features = (typeof(features) != 'undefined' && features) ? features : '';
	prompt = (typeof(prompt) != 'undefined' && prompt) ? prompt : 'Would you like to open a new window?'; 
	failure = (typeof(failure) != 'undefined' && failure) ? failure : 'Your browser did not let the new window open.\nPlease disable your popup blocker, or add us to your safe list, then try again.';

	if (silent || confirm(prompt)) {
		if (respawn && spawned[id] != null && !spawned[id].closed) {
			spawned[id].close();
		} else if (typeof(die) == 'object' && die) {
			die.close();
		}
		if (respawn || spawned[id] == null || spawned[id].closed) {
			return (spawned[id] = window.open(url, id, features)) ? true : alert(failure);
		} else {
			spawned[id].location = url;
			spawned[id].focus();
			return true;
		}
	}

	return false;
}

// ============================================================================================= //
// Prototypes
// ============================================================================================= //

/**
 * Remove unnecessary whitespace from start/end of a string.. fast!
 *
 * @return {String} Trimmed string
 * @extends String
 */
if (!String.prototype.trim) {
	String.prototype.trim = function() {
		var	str = this.replace(/^\s\s*/, ''),
			ws = /\s/,
			i = this.length;
		while (ws.test(str.charAt(--i)));
		return str.slice(0, i + 1);
	};
}

/**
 * Remove 'px' suffix and convert string to integer
 *
 * @return {Number} Converted string
 * @extends String
 */
if (!String.prototype.pxToInt) {
	String.prototype.pxToInt = function() {
		return parseInt(this.substr(0, this.lastIndexOf('px'))) || 0;
	};
}

/**
 * Returns random integer between min and max;
 * Produces statistically better results compared to only using Math.round(), which results in a non-uniform distribution
 *
 * @param {Number} min Minimum number
 * @param {Number} max Maximum number 
 * @return {Number} Random number
 * @extends Math
 */
if (!Math.randomInt) {
	Math.randomInt = function(min, max) {
		return Math.floor(Math.random() * (max - min + 1)) + min;
	};
}

// ============================================================================================= //
// Extend jQuery
// ============================================================================================= //

$.fn.exists = function() {
	return this.is('*');
}

// ============================================================================================= //
// Extend jQuery Plugins
// ============================================================================================= //

// init jQuery Validation Plugin
$.validator.setDefaults({
	debug: false,
	focusInvalid: true,
	focusCleanup: false,
	errorElement: 'p',
	errorPlacement: function(error, element) {
		error.appendTo(element.parent('div').parent('div'));
	},
	invalidHandler: function() {
		formElementShaker.call(this);
	}
});

/**
 * Custom validation method: numberCurrency
 * Only allows non-numeric characters at the start/end of value  
 *
 * @requires jQuery.validator
 */
$.validator.addMethod('numberCurrency', function(value, element) {
	return this.optional(element) || /^[^\d]*\d*(\.+\d{1,2})?[^\d]*$/.test(value);
}, 'Please enter a valid value');

/**
 * Custom validation method: alphaNumeric
 * Only allows alphanumeric values, spaces and underscores  
 *
 * @requires jQuery.validator
 */
$.validator.addMethod('alphaNumeric', function(value, element) {
	return this.optional(element) || /^\w+$/i.test(value);
}, 'Please enter an alphanumeric value');  

/**
 * Custom validation method: notEqualTo
 * Only allows two different values  
 *
 * @requires jQuery.validator
 */
$.validator.addMethod('notEqualTo', function(value, element, param) {
	return this.optional(element) || value != jQuery(param).val();
}, 'Please enter a different value');  

// ============================================================================================= //
// Entry Point
// ============================================================================================= //

/**
 * Wait for DOM to load
 */
$(document).ready(function() {

	// init anchor tags
	initAnchors();

	// fix IE 6 bug that borks text selection when { position: absolute } is used
	// based on code from http://blog.tom.me.uk/2003/07/23/boie6selecta.php (use archive.org's copy of 2005-03-09)
	if ($.browser.msie && $.browser.version == 6 && document.compatMode && document.compatMode == 'CSS1Compat') {
		document.body.style.height = document.documentElement.scrollHeight + 'px';
	}

	// fix IE 6 flickering bug by forcing caching of background images
	// based on recommendation from http://support.microsoft.com/kb/823727/
	if ($.browser.msie && $.browser.version == 6 && document.ExecCommand) {
		try {
			document.ExecCommand("BackgroundImageCache", false, true);
		} catch(ex) { /* swallow errors */ }
	}

	// simulate :before and :after in IE
	if ($.browser.msie) {
		$('q')
			.prepend('<span class="before">&#x201c;</span>')
			.append('<span class="after">&#x201d;</span>');
		$('div#index ul#box-announcement li')
			.prepend('&raquo;&nbsp;');
	}

	// highlight text boxes when focus is gained;
	// must be called before any focus event is triggered
	$('form input[type="text"], form input[type="password"], form textarea, form select')
		.bind('focus', function() {
			$(this).parent('div').parent('div').addClass('active');
		})
		.bind('blur', function() {
			$(this).parent('div').parent('div').removeClass('active');
		});

	// animate messages
	$('div.animated').hide().slideDown(600);

	// initialise F1 links
	$('a.f1')
		.bind('click', function() {
			spawn(
				this.href,
				'f1',
				true,
				'width=650,height=550,dependent=yes,dialog=no,scrollbars=yes'
			);
			return false;
		});

	// initialise global search box
	$('#form-headsearch').validate({
		rules: {
			'q': {
				required: true,
				rangeLength: [1, 256]
			}
		},
		showErrors: function() {
			// do nothing
		}
	});

	// run local handler if present
	if (typeof(handler) != 'undefined' && handler && typeof(handler) == 'function') {
		handler.call(this);
	}

});

// [EOF]