/**
 * Calendars II
 * 2008 Principle Inc.
 *
 * The Wizard: Common Stuff
 *
 *
 * Creates the WIZ namespace, parent of all the Wizard-related Modules.
 * Defines some shared functions/properties in the WIZ namespace.
 * Also defines some new Wizard-related jQuery extensions.
 *
 * Requires:
 * 		jQuery
 *
 *
 *
 *
 *
 *
 */ 


/*** The Wizard Namespace **********************************************************************/
if (typeof WIZ == "undefined" || !WIZ) {
	/**
	 * The WIZ global namespace object.  In case WIZ has already been defined,
	 * the existing WIZ object will not be overwritten, so that defined namespaces are preserved.
	 */
    var WIZ = {};
}
/*** The Wizard Namespace **********************************************************************/



/**
 * Settings for TinyMCE
 */
WIZ.tinyMCESettings = {
	//
	// minimal init
	//
	init: {
		mode:								"none"
	},
	
	//
	// Municipal: text slots
	//
	full: {
		
		// General
		mode:								"none",
		
		button_tile_map: 					true, // speeds up loading
		
		plugins: 							"safari,paste,spacing",
		
		// Cleanup options
		entity_encoding: 					"numeric",
		
		//inline_styles:					false,
		inline_styles:						true,
		
		convert_fonts_to_spans:				true,
		fix_nesting:						true, // fixes invalid nesting
		fix_list_elements:					true, // fixes invalid list nesting
		
		valid_elements:						"@[style]," +
											"strong/b," +
											"em/i," +
											"u," +
											"ul,ol,li," +
											"blockquote," +
											"h1,h2,h3," +
											"br," +
											"p," +
											"span",
		
		// Theme options
		skin: 								"default",
		theme: 								"advanced",
		theme_advanced_toolbar_location: 	"top",
		theme_advanced_toolbar_align: 		"left",
		
		// Buttons
		theme_advanced_buttons1: 			"formatselect,fontselect,fontsizeselect," +
											"forecolor," +
											"bold,italic,underline," +
											"justifyleft,justifycenter,justifyright,justifyfull,spacing," +
											"bullist,numlist,outdent,indent,charmap,undo,redo,removeformat,code",
		theme_advanced_buttons2: 			"",
		theme_advanced_buttons3: 			"",
		
		// Font options
		//theme_advanced_font_sizes:			"8=8pt,9=9pt,10=10pt,11=11pt,12=12pt,14=14pt,16=16pt,18=18pt,20=20pt,22=22pt,24=24pt,26=26pt,28=28pt,36=36pt,48=48pt,72=72pt",
		theme_advanced_font_sizes:			"10%=10%,20%=20%,30%=30%,40%=40%,50%=50%,60%=60%,70%=70%,80%=80%,90%=90%,95%=95%,100%=100%,105%=105%,110%=110%,120%=120%,130%=130%,140%=140%,150%=150%,160%=160%,170%=170%,180%=180%,190%=190%,200%=200%,225%=225%,250%=250%,275%=275%,300%=300%,350%=350%,400%=400%",
		theme_advanced_fonts:				"Arial=arial,Tahoma=tahoma,Verdana=verdana,Georgia=georgia,Comic Sans=Comic Sans MS",
		
		// Content CSS
		content_css:						"tinyMCE.css?" + new Date().getTime(),
		
		// Custom styles
		theme_advanced_styles:				"Body Text=commonBody, Header=commonHeader, Sub-Header=commonSubheader, Header 3=commonHeader3",
		theme_advanced_blockformats:		"p,h1,h2,h3",
		
		
		// Callbacks
		setup: function (ed) {
			ed.onBeforeRenderUI.add(function(ed, cm) {
				WIZ.BUILDER.mceBeforeRenderCallback(ed);
			});
			ed.onPostRender.add(function(ed, cm) {
				WIZ.BUILDER.mceAfterRenderCallback(ed, cm);
			});
			ed.onInit.add(function(ed) {
				// WIZ.BUILDER.mceInitCallback(ed);
				ed.focus();
			});
			// Gets executed after DOM to HTML string serialization
			ed.onPostProcess.add(function(ed, o) {				
				// State get is set when contents is extracted from editor
				//console.log('post process');
				//o.content = normalizeSpans(o.content);
			});
		}
	},
	
	//
	// Municipal: banners, dayboxes
	//
	simple: {
		
		// General
		mode:								"none",
		
		button_tile_map: 					true, // speeds up loading
		
		plugins: 							"safari,paste",
		
		// Cleanup options
		entity_encoding: 					"numeric",
		
		inline_styles:						false,
		convert_fonts_to_spans:				true,
		fix_nesting:						true, // fixes invalid nesting
		fix_list_elements:					true, // fixes invalid list nesting
		
		valid_elements:						"@[style]," +
											"strong/b," +
											"em/i," +
											"u," +
											"ul,ol,li," +
											"blockquote," +
											"h1,h2,h3,h4,h5,h6," +
											"br," +
											"p," +
											"span",
		
		// Theme options
		skin: 								"default",
		theme: 								"advanced",
		theme_advanced_toolbar_location: 	"top",
		theme_advanced_toolbar_align: 		"left",
		
		// Buttons
		theme_advanced_buttons1: 			"fontsizeselect," +
											"forecolor," +
											"bold,italic," +
											"justifyleft,justifycenter,justifyright,justifyfull," +
											"charmap," +
											"undo,redo,removeformat",
		theme_advanced_buttons2: 			"",
		theme_advanced_buttons3: 			"",
		
		// Font options
		//theme_advanced_font_sizes:			"8=8pt,9=9pt,10=10pt,11=11pt,12=12pt,14=14pt,16=16pt,18=18pt,20=20pt,22=22pt,24=24pt,26=26pt,28=28pt,36=36pt,48=48pt,72=72pt",
		theme_advanced_font_sizes:			"10%=10%,20%=20%,30%=30%,40%=40%,50%=50%,60%=60%,70%=70%,80%=80%,90%=90%,95%=95%,100%=100%,105%=105%,110%=110%,120%=120%,130%=130%,140%=140%,150%=150%,160%=160%,170%=170%,180%=180%,190%=190%,200%=200%,225%=225%,250%=250%,275%=275%,300%=300%,350%=350%,400%=400%",
		theme_advanced_fonts:				"Arial=arial,Tahoma=tahoma,Verdana=verdana,Georgia=georgia,Comic Sans=Comic Sans MS",
		
		// Content CSS
		content_css:						"tinyMCE.css?" + new Date().getTime(),
		
		// Callbacks
		setup: function (ed) {
			ed.onInit.add(function(ed) {
				ed.focus();
			});
			// Gets executed after DOM to HTML string serialization
			ed.onPostProcess.add(function(ed, o) {				
				// State get is set when contents is extracted from editor
				//console.log('post process');
				//o.content = normalizeSpans(o.content);
			});
		}
	},
	
	//
	// BYO: text slots
	//
	byo: {
		
		// General
		mode:								"none",
		
		button_tile_map: 					true, // speeds up loading
		
		plugins: 							"safari,paste,spacing",
		
		// Cleanup options
		entity_encoding: 					"numeric",
		
		//inline_styles:					false,
		inline_styles:						true,
		
		convert_fonts_to_spans:				true,
		fix_nesting:						true, // fixes invalid nesting
		fix_list_elements:					true, // fixes invalid list nesting
		
		valid_elements:						"@[style]," +
											"strong/b," +
											"em/i," +
											"u," +
											"ul,ol,li," +
											"blockquote," +
											"h1,h2,h3," +
											"br," +
											"p," +
											"span",
		
		// Theme options
		skin: 								"default",
		theme: 								"advanced",
		theme_advanced_toolbar_location: 	"top",
		theme_advanced_toolbar_align: 		"left",
		
		// Buttons
		theme_advanced_buttons1: 			"fontselect,fontsizeselect," +
											"forecolor," +
											"bold,italic,underline," +
											"justifyleft,justifycenter,justifyright,justifyfull,spacing," +
											"bullist,numlist,outdent,indent,charmap,undo,redo,removeformat,code",
		theme_advanced_buttons2: 			"",
		theme_advanced_buttons3: 			"",
		
		// Font options
		//theme_advanced_font_sizes:		"8=8pt,9=9pt,10=10pt,11=11pt,12=12pt,14=14pt,16=16pt,18=18pt,20=20pt,22=22pt,24=24pt,26=26pt,28=28pt,36=36pt,48=48pt,72=72pt",
		theme_advanced_font_sizes:			"10%=10%,20%=20%,30%=30%,40%=40%,50%=50%,60%=60%,70%=70%,80%=80%,90%=90%,95%=95%,100%=100%,105%=105%,110%=110%,120%=120%,130%=130%,140%=140%,150%=150%,160%=160%,170%=170%,180%=180%,190%=190%,200%=200%,225%=225%,250%=250%,275%=275%,300%=300%,350%=350%,400%=400%",
		theme_advanced_fonts:				"Arial=arial,Tahoma=tahoma,Verdana=verdana,Georgia=georgia,Comic Sans=Comic Sans MS",
		
		// Content CSS
		content_css:						"tinyMCE.css?" + new Date().getTime(),
		
		// Custom styles
		theme_advanced_styles:				"Body Text=commonBody, Header=commonHeader, Sub-Header=commonSubheader, Header 3=commonHeader3",
		theme_advanced_blockformats:		"p,h1,h2,h3",
		
		
		// Callbacks
		setup: function (ed) {
			ed.onBeforeRenderUI.add(function(ed, cm) {
				WIZ.BUILDER.mceBeforeRenderCallback(ed);
			});
			ed.onPostRender.add(function(ed, cm) {
				WIZ.BUILDER.mceAfterRenderCallback(ed, cm);
			});
			ed.onInit.add(function(ed) {
				// WIZ.BUILDER.mceInitCallback(ed);
				ed.focus();
			});
			// Gets executed after DOM to HTML string serialization
			ed.onPostProcess.add(function(ed, o) {				
				// State get is set when contents is extracted from editor
				//console.log('post process');
				//o.content = normalizeSpans(o.content);
			});
		}
	}
};





/**
 * Tooltips for CForms.
 * Displays CForm help and validation messages as tooltips.
 * Tooltips are automatically re-positioned so as not to go off the right or
 * bottom edges of the screen.
 * 
 * Settings:
 * 		{number} delay			- the delay before appearing (in milliseconds)
 * 		{object} offset			- the x & y positions of the tooltip relative to the anchor point
 * 		{number} maxWidth		- the maximum width of the tooltip
 *
 * All settings are optional.
 */

WIZ.tooltip = {
	settings: {
		delay: 250,
		offset: {
			x: 7,
			y: 0
		},
		maxWidth: 300
	},
	_position: function (oTip, oAnchor) {
		var tip = oTip,
			a = oAnchor;
		
		// enforce max-width
		if (this.settings.maxWidth && this.settings.maxWidth > 0) {
			if (tip.width() > this.settings.maxWidth) {
				tip.css({
					width: this.settings.maxWidth + 'px',
					height: 'auto'
				});
			}
		}
		
		// set position
		tip.css({
			left: a.position().left + this.settings.offset.x,
			top: a.position().top + this.settings.offset.y
		});
		
		// adjust position if tooltip would appear offscreen
		var pos = {tip: {}, win: {}};
		//var pageSize = WIZ.getPageSize();
		
		pos.tip.left = parseInt(tip.css('left'), 10);
		pos.tip.top = parseInt(tip.css('top'), 10);
		pos.tip.right = pos.tip.left + tip.width();
		//pos.tip.bottom = pos.tip.top + tip.height();
		
		var diffRight = pos.tip.right - $(window).width();
		//var diffBottom = pos.tip.bottom - $(window).height();
		
		if ( diffRight > 0 ) {
			tip.css('left', pos.tip.left - diffRight - 45 + 'px'); // 45px is an arbitrary amount of clearance from the right edge.
		}
		//if ( diffBottom > 0 ) {
		//	tip.css('top', pos.tip.top - diffBottom + 'px');
		//}
		
	},	
	show: function (anchor) {
		$("div.form_message_help").hide(); // hide all the opened tip before opening a new one - kenny
		
		var a = $(anchor),
			tip;
		
		// which message to show?
		if (a.hasClass('form_error')) {
			tip = a.siblings("div.form_message_error");
		} else {
			tip = a.siblings("div.form_message_help");
		}
		
		// size & position the tooltip
		this._position(tip, a);
		// show the tooltip
		tip.show();
	},
	hide: function (anchor) {
		
		var a = $(anchor);
		var tip;
		var timer;
		
		
		// which message to hide?
		if (a.hasClass('form_error')) {
			tip = a.siblings("div.form_message_error");
		} else {
			tip = a.siblings("div.form_message_help");
		}
		
		// hide the tooltip
		timer = setTimeout(function () {
			tip.hide();
		}, this.settings.delay);
		//tip.hide();
		
	}
};



/**
 * Step Navigation Controller.
 * Co-ordinates navigation between Wizard steps.
 *
 * Methods:
 * 		config(options)				- used to change settings after instantiation.
 *
 * Settings:
 * 		{boolean} warnOnLeave		- if true, warn the user about unsaved changes before leaving the page.
 * 		{function} saveFunc(url)	- a custom function to run when navigating requires saving.
 *
 * Settings can be defined at page level, otherwise defaults will be used.
 *
 * Usage Example:
 * 		We want to call a function to save data when the user chooses to leave the step.
 * 		If the data is saved successfully, go ahead with the navigation.
 * 		
 * 		Somewhere in the step's code, we can set that up by doing this:
 *
 * 			WIZ.navigate.config({
 * 				saveFunc: function(url) {					<--- define a save function
 * 					conduit.exchangeJSON({
 * 						url: the_url,
 * 						params: the_params,
 * 						success: function (data) {
 * 							WIZ.navigate.go(url);			<--- on success, use WIZ.navigate.go()
 *						}
 *					})
 *				}
 * 			});
 * 
 */
WIZ.navigate = function () {
	var settings = {
		warnOnLeave: true
		//saveFunc: function (url) {}
	};
	
	return {
		config: function (options) {
			// Configures settings after instantiation.
			$.extend(settings, options);
		},
		getSettings: function () {
			return settings;
		},
		go: function (url) {
			// Navigates to the url of the requested step.
			// Called internally to actually request the new page.
			// Can also be used to manually go to a given url.
			window.location.href = WIZ.baseHref(url);
			return false;
		},
		
		prevStep: function (url) {
			// Navigates to the previous step.
			// Called when a user clicks the "Previous Step" button.
			if (settings.warnOnLeave === true) {
				WIZ.notify("Unsaved changes will be lost.<br/><br/>Are you sure you want to leave this step?", {
					title: "Warning",
					buttons: {
						"Yes": function () {
							WIZ.navigate.go(url);
						},
						"No": function () {
							$(this).dialog("close");
						}
					}
				});
			} else {
				this.go(url);
			}
			return false;
		},
		nextStep: function (url) {
			// Navigates to the next step.
			// Called when a user clicks the "Previous Step" button.
			// Calls an optional "save" function if needed.
			if (settings.saveFunc) {
				settings.saveFunc(url);
			} else {
				this.go(url);
			}
			return false;
		},
		gotoStep: function (url) {
			// Navigates to a particular step.
			// Called when a user clicks on a Tab.
			// Calls an optional "save" function if needed.
			if (settings.saveFunc) {
				settings.saveFunc(url);
			} else {
				this.go(url);
			}
			return false;
		}
	};
}();





/**
 * Default Dialog Box Settings
 *
 * These are the default settings for the jQuery UI Dialog plugin.
 * This provides us with a default look-and-feel for our site's dialog box widgets,
 * which allows us to maintain site-wide consistency.
 *
 * You should 'extend' the default settings with your own when creating new dialogs.
 * Any options you provide will be used, while the rest of the settings remain at their defaults.
 *
 * If you are making many dialog boxes with similar custom options, you can 'merge' your options with
 * the defaults to create new default settings. These settings will apply to all dialog boxes created
 * during the lifecycle of the WIZ object (which is typically one web page.)
 *
 *
 * Examples:
 *
 * 1. To create a dialog box:
 *
 *		$(elem).dialog(options);	<<< Creates a dialog box using the element specified as content.
 *
 *
 *
 * 2. To extend the default settings with your own:
 *
 *		var options = {
 *			title: "My Dialog Title",
 *			height: 300,
 *			width: 500
 *		};
 *		options = $.extend( {}, WIZ.dialogDefaults, options );		<<< Note: {} as first parameter.
 *
 *
 *
 * 3. To replace the default settings:
 *
 *		var options = {
 *			title: "A New Default Title",
 *		};
 *		$.extend( WIZ.dialogDefaults, options );					<<< Note: only two parameters.
 *
 */
 WIZ.dialogDefaults = {
	height: 175,
	width: 350,
	minHeight: 175,
	minWidth: 350,
	modal: true,
	draggable: false,
	resizable: true,
	overlay: { // Dave has chosen 80% black as the overlay colour.
		opacity: 0.8,
		background: "#000"
	},
	buttons: {
		"Close": function () {
			$(this).dialog("close");
		}
	}
};
/* Please leave a note if you change a global setting.  -mike*/



/**
 * Default Popup Settings
 *
 * These are the default settings for jQuery UI "Popup"-style dialogs.
 * Usage is the same as for dialogDefaults (see above).
 */
WIZ.popupDefaults = {
	height: 600,
	width: 930,
	modal: true,
	draggable: false,
	resizable: false,
	overlay: WIZ.dialogDefaults.overlay,
	//zIndex: 20000,
	buttons: {
		"Close": function () {
			$(this).popup("close");
		}
	},
	close: function () {
		$(this).popup("destroy");
	}
};



/**
 * Default Tooltip Settings
 *
 * These are the default settings for the jQuery Tooltip plugin.
 * Usage is the same as for dialogDefaults (see above).
 */
WIZ.tooltipDefaults = {
	track: false,
	delay: 250,
	showURL: false,
	extraClass: "standardWidth",
	opacity: 0.85,
	showBody: "[] "
};



/**
 * Function:	notify()
 *
 * Summary:		Creates, shows and then destroys a simple dialog box using the jQuery UI Dialog plugin.
 *				The Dialog plugin uses the global default dialog settings stored in WIZ.dialogDefaults.
 *
 * Params:		{string} message	The message you wish to display in the Dialog box.
 * 				{object} options	Configuration overrides.
 *
 */
WIZ.notify = function(msg, options) {
	var settings = WIZ.dialogDefaults;
	var html;
	var message = (msg && typeof msg == 'string') ? msg : 'No message.';
	
	settings.title = 'Notice';
	settings.zIndex = 1100;
	
	settings.close = function () {
		$(this).remove(); // once closed, get rid of the html for this dialog.
	};

	// merge options with inherited settings.
	settings = $.extend( {}, settings, options );

	html = '<div>' + msg + '</div>';
	$(html).appendTo("body").dialog(settings).show();
};



/**
 * Function:	prompt()
 *
 * Summary:		Creates, shows and then destroys a prompt dialog box using the jQuery UI Dialog plugin.
 *				The Dialog plugin uses the global default dialog settings stored in WIZ.dialogDefaults.
 *				This dialog allows user to define custom callback upon Yes is selected
 *
 * Params:		{string} title				The title you wish to display in the Dialog box.
 *				{string} msg				The message you wish to display in the Dialog box.
 *				{string} callbackFuncOnYes	Call back function upon yes is clicked
 *				{array}  params				Arguments to pass to callback (optional)
 */
WIZ.prompt = function(title, msg, callbackFuncOnYes, params) {	
	var options = {};
	var html;
	
	options.title = title;
	options.close = function () {
		$(this).remove(); // once closed, get rid of the html for this dialog.
	};
	options.buttons = {
			"Yes": function() { 
				callbackFuncOnYes(params);
				$(this).dialog("close"); 
			},  
			"No": function() { 
				$(this).dialog("close"); 
			} 
	};
	options = $.extend({}, WIZ.dialogDefaults, options);
	
	html = '<div>' + msg + '</div>';
	$(html).appendTo("body").dialog(options).show();
};



/**
 * Function:	log(message)
 *
 * Summary:		Logs a message to the console (if Firebug is enabled) or else alerts it.
 *
 * Params:		{string} message			The message you wish to log.
 */
WIZ.log = function (s) {
	if (typeof console != "undefined" && typeof console.debug != "undefined") {
		console.log(s);
	} else {
		alert(s);
	}
};


/**
 * Function:	fitToSize(origSize, maxSize)
 *
 * Summary:		Scales a box to fit the specified maximum dimensions.
 *
 * Params:		{object} origSize		The dimensions of the box to be resized {w, h}
 * 				{object} maxSize		The maximum dimensions to which the box will be scaled {w, h}
 *
 * Returns:		{object} newSize		The scaled dimensions and calculated scale {w, h, scale}.
 * 
 */
WIZ.fitToSize = function(orig, max) {
	var log = WIZ.log;
	var scale = 1.0;
	var newSize = {};
	var ratio_orig = orig.w / orig.h;
	var ratio_to_fit = max.w / max.h;
	//
	//log('fitToSize(' + orig.w + '*' + orig.h + ' => ' + max.w + '*' + max.h + ')');
	//log('   ratio=' + ratio_orig + ', ratio_to_fit=' + ratio_to_fit);
	//
	
	if (ratio_orig < ratio_to_fit) { // orig is narrower, so fit by height
		//log('resize by height');
		newSize.h = max.h;
		newSize.w = newSize.h * ratio_orig;
	} else { // orig is wider, so fit by width
		//log('resize by width');
		newSize.w = max.w;
		newSize.h = newSize.w / ratio_orig;
	}

	// add the scale to the return object.
	newSize.scale = newSize.h / orig.h;
	
	// round to int
	newSize.w = Math.round(newSize.w);
	newSize.h = Math.round(newSize.h);
	//
	//log('==> (' + newSize.w + '*' + newSize.h + ') scale: ' + newSize.scale + ':1');
	//
	
	return newSize;
};


/**
 * Function:	instruction(elem)
 *
 * Summary:		Displays a help message in a popup.
 *
 * Params:		{string} elem	A jQuery selector for the element containing the message (eg: '#somediv').
 */
WIZ.instruction = function (contentElem) {
	var html;
	var container = $("div#instructionWindow"); // alias
	var elem = contentElem;
	//var anchor = (anchorElem) ? $(anchorElem) : $("div#content");
	var message = 'No instructions here yet.';
	
	container.empty();
	
	html =	'<img id="instructionShadow" src="images/popup_shadow.png"/>' +
			'<div id="instructionBody">' +
				'<div id="instructionHeader">' +
					'<span>Instructions</span><img src="images/icon_close.gif" id="closeInstructionsButton" title="Close"/>' +
					'<div class="clear"/></div>' +
				'<div id="instructionContent"/>' +
			'</div>';
	container.append(html);
	// IE6 Fix for selects showing through layers:
	if ($.browser.msie && $.browser.version < 7) {
		$("img#instructionShadow").remove();
		container.prepend('<iframe id="ie6fix" src=""></iframe>');
		$("div#instructionBody").css('border', '2px solid #000');
	}
	
	if ($(contentElem).length > 0) {
		message = $(elem).html() + '<br/>';
	}
	container.find("div#instructionContent").html(message);
	
	container.find("img#closeInstructionsButton").click(function () {
		container.css('visibility', 'hidden');
		container.empty();
		return false;
	});
	//$("img#instructionShadow").ifixpng();
	container.css({
		'top': '0px',
		'left': 'auto',
		'right': '0px',
		visibility: 'visible',
		'z-index': 9999
	});
	container.draggable({
		handle: 'div#instructionHeader',
		containment: 'window',
		start: function () {
			$("img#instructionShadow").hide();
		},
		stop: function () {
			$("img#instructionShadow").show();
		}
	});
};


/**
 * Function:	arrayToMap(array, key)
 *
 * Summary:		Creates a map out of an array of objects, using the supplied key.
 *
 * Params:		{string} key		-The key to use.
 *
 * Returns:		{object} the_map	-The resulting map object.
 */
WIZ.arrayToMap = function (the_array, key) {
	var the_map = {};
	var len = the_array.length;
	
	if (len > 0) {
		for (var i=0; i<len; i++) {
			the_map[the_array[i][key]] = the_array[i];
		}
	}
		 
	return the_map;
};


/**
 * Apply the base href to a url (if it exists).
 * Needed for working around Cocoon.
 */
WIZ.baseHref = function (loc) {
	// loc is the relative path your wish to redirect to
	var b = document.getElementsByTagName('base');
	if (b && b[0] && b[0].href) {
		loc = b[0].href + loc;
	}
	return loc;
};


/**
 * Returns the size of the screen area.
 *
 * source: Thickbox
 */
WIZ.getPageSize = function () {
	var de = document.documentElement;
	var w = window.innerWidth || self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
	var h = window.innerHeight || self.innerHeight || (de&&de.clientHeight) || document.body.clientHeight;
	
	var oPageSize = {
		w: w,
		h: h
	};
	return oPageSize;
};



/*** jQuery Extensions/Utilities **************************************************/


/**
 * Function:	Collapses/expands a date list container.
 *
 * Usage: 		To be called on a jQuery set of one or more nodes.
 *
 * Extends jQuery.
 */
(function ($) {
	$.fn.collapse = function () {
		var boxBody,
			img,
			me,
			anim = 'easeOutCubic';
			
		return this.each(function () {
			me = $(this);
			img = me.find("img.collapseButton");
			boxBody = me.siblings("div.boxBody");
			
			boxBody.slideToggle({easing: anim}); // collapse/expand using jQuery animation
			img.toggleImageOnOff(); // toggle collapse button image
		});
	};
})(jQuery);


/**
 * Function:	Toggle between two images based on their src attribute.
 *
 * Usage:		To be called on a jQuery set of one or more image nodes.
 *				Image names should follow the pattern: name_on.ext and name_off.ext,
 *				eg. "button_on.gif" and "button_off.gif".
 *
 * Extends jQuery.
 *
 */
(function ($) {
	$.fn.toggleImageOnOff = function () {
		var new_src;

		return this.each(function () {
			// if src contains '_off', then replace it with '_on',
			// if src contains '_on', then replace it with '_off',
			// otherwise do nothing.
			if (this.src.indexOf('_off.') != -1)
			{
				new_src = this.src.replace(/\_off\./, "_on.");
			}
			else if (this.src.indexOf('_on.') != -1)
			{
				new_src = this.src.replace(/\_on\./, "_off.");
			}
			
			this.src = new_src; // set the src of the image to the new value
		});
	};
})(jQuery);

//-------------------------------------------------------------------

//
// convert attribute string to JSON representation
//
function attributeStringToJSON(string) {
	var plist = string.split(';');
	var json = {};
	for(var i=0; i<plist.length; i++) {
		if( plist[i]['split'] ) {
			var tmp = plist[i].split(':');
			if( tmp.length == 2 ) {
				json[tmp[0]] = tmp[1];
			}
		}
	}
	return json;
}

//
// convert JSON attribute object to string representation
//
function jsonToAttributeString(json) {
	var attr = '';
	for(var key in json) {
		attr += key + ':' + json[key] + ';';
	}
	return attr;
}

//
// checks string's beginning
//
function startsWith(text, start) {
	return (text.match("^"+start)==start);
}

//
// check string's ending
//
function endsWith(text, start) {
	return (text.match(start+"$")==start);
}

function normalizeSpans( text ) {
	
	var originalText = $('<root>' + text + '</root>');
	//
	// replace all strong elements with a span with appropriate style
	//
	originalText.find('strong').each( function(ev) {
		$(this).replaceWith('<span style="font-weight:bold;">' + $(this).html() + '</span>');
	});
	//
	// replace all em element's with a span with appropriate style
	//
	originalText.find('em').each( function(ev) {
		$(this).replaceWith('<span style="font-style:italic;">' + $(this).html() + '</span>');
	});	
	//
	// search through spans
	//
	originalText.find('span').each(function(ev) {
		var $span = $(this);
				
		var numberOfSpanChildren      = $span.children('span').length;
		
		if( 	numberOfSpanChildren == 1 
		   &&	startsWith( $span.html(), '<span'  ) 
		   &&	endsWith(   $span.html(), '</span>') ) {
		//---------------------------------------	
			$child = $span.children('span');
			var spanAttrJSON  = attributeStringToJSON(  $span.attr('style')  );
			var childAttrJSON =  attributeStringToJSON( $child.attr('style') );
			var finAttr = jsonToAttributeString( $.extend(spanAttrJSON, childAttrJSON) );
			var el = '<span style="' + finAttr + '">' + $child.html() + '</span>';
			$(this).replaceWith( normalizeSpans(el) );
		}
	}); 
	
	var content = originalText.html();
	// jquery html() won't return correct xhtml style br tags
	// it's only will be br tags, as specified in current configuration for tinyMCE
	content = content.replace(/<br>/gi, '<br />');
	return content;
}
