/**
 * @author Andrew Masri */




///////////////////// In-place ajax input text editor (single-line) //////////////////////////////////////////////////////////

//convert any element into an in-place editor (using 'input' form field)
function editText(id, autocompleteArray) {
	quitOpenEditors();	//close any open editors

	//if id is an element object convert it to an id attribute
	if (typeof id == 'object') {
		var source = jQuery(id);
		id = source.attr('id');
	} else {
		var source = jQuery('#'+id);
	}
	
	if (!source.length) { alert('System error: argument id="'+id+'" not found in function editText()'); }

	var sourceStyles = getStyles(source, ['background-color', 'font-family', 'font-weight', 'color', 'textAlign', 'line-height', 'fontSize', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'display']);	

	//wrap the source element in a div and then hide it (this enables us to retain the 'display' style of the source element)
	source.wrap('<span class="editorWrapper"></span>');
	source.wrap('<div class="editorWrapper" style="display:none"></div>');

	//append editable input element after the hidden div
	source.parent().parent().append('<input id="edit-'+id+'" type="text"/>');

	var editor = jQuery('#edit-'+id);

	//setup the input element's attributes
	editor.addClass(source.attr('class')+' '+source[0].nodeName.toLowerCase()+' '+'inPlaceEditor');

	//initial value of input element
 	var initialValue = source.html();
	initialValue = initialValue.replace(/&amp;/g, '&');
	initialValue = initialValue.split('<')[0];	//remove any html tags (eg. Draft blog marker)
	initialValue = jQuery.trim(initialValue);
	switch (initialValue.toLowerCase()) {
		case 'edit':
		case 'click to edit':
		case 'none':
		case 'empty':
		case '___':
		case '---':
		case '':
			editor.val('');	//these values mean that there is no value yet
			break;
		default:
			editor.val(initialValue);	//convert '&' html entity to text
	}
	editor.data('initialValue', editor.val());	//record the initial value - later we'll use this to determine whether the text is modified
log('edit', id+' : '+initialValue);

	//copy the style settings from the source element to the input element
	applyStyles(editor, sourceStyles);

//	editor.css('backgroundColor', 'transparent');
	if (!jQuery.browser.webkit) { editor.css('border', '3px solid #679ed9'); }
	editor.attr('size', (editor.val().length < 7) ? 7 :  editor.val().length); 	//size of input field in chars


	//setup the input element's event handlers
	editor.blur(function() {
log('lostFocus', this.id);
 		if (typeof autocompleteArray != 'undefined') {
			setTimeout("saveEditor('edit-" + id + "')", 200); //the small delay is necessary to enable the autocomplete selection to take effect
		}
		else {
			saveEditor(this.id);
		}
	});
	editor.focus();
	editor.select();

	//handle return/esc/tab keystrokes
	editor.keydown(function(event) {
	  	detectKey(event, this.id);
	});

	//attach any autocomplete array data to the editor (using the jQueryUi autocomplete widget)
//alert(typeof autocompleteArray)
	if (typeof autocompleteArray == 'object' && autocompleteArray.length) {
		editor.autocomplete({
			source: autocompleteArray
		});
	}
}







///////////////////// In-place ajax textarea editor (multi-line) //////////////////////////////////////////////////////////


//convert any element into an in-place editor (using 'textarea' form field)
function editTextArea(id) {
	quitOpenEditors();	//close any open editors

	//if id is an element object convert it to an id attribute
	if (typeof id == 'object') {
		var source = jQuery(id);
		id = source.attr('id');
	} else {
		var source = jQuery('#'+id);
	}
	if (!source.length) { alert('System error: argument id="'+id+'" not found in function editTextArea()'); }

	//copy the source element styles before wrapping it in divs and hiding it
	var sourceStyles = getStyles(source, ['background-color', 'font-family', 'font-weight', 'color', 'textAlign', 'line-height', 'fontSize', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'width', 'height']);	

	//wrap the source element in a div and then hide it (this enables us to retain the 'display' style of the source element)
	source.wrap('<span class="editorWrapper"></span>');
	source.wrap('<div class="editorWrapper" style="display:none"></div>');

	//append editable input element after the hidden div
	source.parent().parent().append('<textarea id="edit-'+id+'"></textarea>');

	var editor = jQuery('#edit-'+id);

	//setup the input element's attributes
	editor.addClass(source.attr('class')+' '+source[0].nodeName.toLowerCase()+' '+'inPlaceEditor');

	//initial value of input element
	var initialValue = source.html();
	initialValue = initialValue.replace(/<br>|<br \/>|<br\/>/gi, '\r');	//remove html line breaks - \r seems to work in firefox and ie
	initialValue = initialValue.replace(/&amp;/g, '&');	//convert '&' html entity to text
	initialValue = jQuery.trim(initialValue);	//record the initial value - later we'll use this to determine whether the text is modified
	switch (initialValue.toLowerCase()) {
		case 'edit':
		case 'click to edit':
		case 'click here to comment...':
		case 'none':
		case 'empty':
		case '___':
		case '---':
		case '':
			editor.text('');	//these values mean that there is no value yet
			break;
		default:
			editor.text(initialValue);
	}
	editor.data('initialValue', editor.text());	//record the initial value - later we'll use this to determine whether the text is modified


	//copy the style settings from the source element to the input element
	applyStyles(editor, sourceStyles);

	//textarea editor styles and width check
	if (!jQuery.browser.webkit) { editor.css('border', '3px solid #679ed9'); }
	editor.css('display', 'inline');
	if (editor.css('width') == 'undefined' || editor.width() < 100) editor.css('width', '100%');	//the width of an empty field may be too narrow - fill the available space 
	editor.css('min-height', '44px');
	editor.css('height', 'auto');
	editor.css('overflow', 'hidden');


	//set height of textarea based on content
	if (editor.css('height') == 'auto') {
		editor.css('height', '44px');	//force height to be something numeric for auto height to work
	}
	if (editor.height() < editor[0].scrollHeight) {
		editor.css('height', editor[0].scrollHeight + 20 +'px'); //expand textarea to eliminate vertical scrollbar
	}


	//setup the input element's event handlers
	editor.blur(function() {
log('lostFocus', id);
 		saveEditor(this.id);
	});
	editor.focus();
	editor.select();

	//handle return/esc/tab keystrokes
	editor.keydown(function(event) {
	  	detectKey(event, this.id);
	});
}




function saveEditor(id) {
	var editor = jQuery('#'+id);
	if (!editor.length) {
		return;	//the editor has already been destroyed
	}

	var sourceId = id.replace(/edit-/, '');
	var editedValue = jQuery.trim(editor.val());

	//retrieve initialValue (differs between browsers due to newline implementations)
	var initialValue = editor.data('initialValue');
	initialValue = initialValue.replace(/\r\n/gi, '\n');
	initialValue = initialValue.replace(/\r/gi, '\n');

	if (editedValue != initialValue) {
		initialValue = editedValue; //prevent multiple save requests
		editedValue = editedValue.myEncode();
		Post.Send('id='+sourceId+'&pageId='+pageId+'&value='+editedValue, site_url('dataserver/ajaxSaveField'));
log('saved', id+' --> '+editedValue);
	}

	quitEditor(id);
}



function quitEditor(id) {
	jQuery('#'+id).unbind('keydown');	//stop tracking keyboard shortcuts

	var editor = jQuery('#'+id);
	if (editor.length) {
		editor.remove(); //remove the editor
		//remove the hidden div from around the source element in order to reveal the source element
		var sourceId = id.replace(/edit-/, '');
		var source = jQuery('#' + sourceId);

		while (source.parent().hasClass('editorWrapper')) {
			source.unwrap(); //remove the hidden div wrapper and span container tags
		}
log('removed', id);
	}
}


//close any open editors - this can happen in Firefox when using tab indexes to step through editable fields - happens when tabbing from last field (problem not fully understood) 
function quitOpenEditors() {
	jQuery('.inPlaceEditor').each(function() {
		saveEditor(this.id);
	});
}	




//called by in-place editors when a key is pressed
function detectKey(event, id) {
	var character = (event.which) ? event.which : event.keyCode;

	var editor = jQuery('#'+id);
	if (!editor.length) {
		return;	//the editor getting the keydown event doesn't exist anymore
	}

	var elementType = editor[0].nodeName.toLowerCase();

	//vertical auto-grow (textarea only)
	if ((elementType == 'textarea') && ((editor[0].scrollHeight - editor.height()) > 10)) {
		editor.animate({ height: ((editor[0].scrollHeight + 30) + 'px')	}, 500); 	//expand textarea to eliminate vertical scrollbar
	}

	//horizontal auto-grow (input field only)
	if ((elementType == 'input') && (editor.attr('size') < editor.val().length )) {
		editor.attr('size', editor.val().length); 	//size of input field in chars
	}


	switch (character) {
		case 13:	//RETURN
			//in a textarea field <RETURN> is treated as a line break character rather than a save command
			if (elementType != 'textarea') {
log('keydown', id+' <- RETURN');
				event.preventDefault();
				saveEditor(id);
			}
			break;
		case 9:		//TAB - used in combination with tabindexes
log('keydown', id+' <- TAB');
			event.preventDefault();
			saveEditor(id);
			nextTabIndex(id);
			break;
		case 27:	//ESCAPE (note that Safari does not provide the ESC key and firefox uses char 0 which seem to conflict with the delete key)
log('keydown', id+' <- ESCAPE');
			event.preventDefault();
			quitEditor(id);	//discard the editor and return to normal display
			break;
		default:
			break;
	}
	return false;
}



//open the next tabIndex field for editing
function nextTabIndex(id) {
	var sourceId = id.replace(/edit-/, '');
	var source = jQuery('#'+sourceId);	//the field that has just been tabbed from
	
	var tabIndex = parseInt(source.attr('tabindex'));	//get the tabIndex of the open editor 

	if (isNaN(tabIndex)) { return; }	//doesn't have a tabIndex

	//get next tabindex (may wrap around)
	var nextTabIndex = tabIndex+1;	
	nextTabIndex = jQuery('[tabindex='+nextTabIndex+']');

	
	if (!nextTabIndex.length) { 
		nextTabIndex = jQuery('[tabindex="0"]');		//wrap around to first tabIndex;
		if (!nextTabIndex.length) { 
			nextTabIndex = jQuery('[tabindex="1"]');	//wrap around to other possible first tabIndex;
		}
	}


	if (!nextTabIndex.length) {	return; } 

	nextTabIndex.trigger('click');		//trigger onclick (open in-place editor) event on the tab-indexed element
}



//////////////////////// Checkbox handler ////////////////////////////////////////////////////////////////////////

//save the radio button value to the database (the id of the enclosing form encodes the database table/id info)
function saveRadioButton(element) {
	if (typeof pageId == 'undefined') {var pageId=0;}
	element = jQuery(element);
	var value = element.val();
	var id = element.attr('name');
	Post.Send('id='+id+'&pageId='+pageId+'&value='+value, site_url('dataserver/ajaxSaveField'));
	displayResult('#'+id+'-result');
}




//save the checkbox value to the database (the id of the checkbox encodes the database table/id info)
function saveCheckbox(element) {
	var id = element.id;
	var value = (element.checked) ? 1 : 0;
	Post.Send('id='+id+'&pageId='+pageId+'&value='+value, site_url('dataserver/ajaxSaveField'));
	displayResult('#'+id+'-result');

	//are there duplicate checkboxes (whose class == this id) - their state must also be changed
	jQuery('.'+id).attr('checked', element.checked);

	//is there a child element that should be revealed hidden based on the checkbox value
	if (value) {
		jQuery('.childOf-'+id).fadeIn('slow');
	} else {
		jQuery('.childOf-'+id).fadeOut('slow');
	}
}


//saves the user/group/membership setting via ajax
function saveGroupMembership(element) {
	var id = element.id;
	var isMember = (element.checked) ? 1 : 0;
	Post.Send('id='+id+'&isMember='+isMember, site_url('dataserver/ajaxGroupMembership'));
}



//this function shouldn't be necessary because child elements should be hidden in php
//this function hides child elements of any parent elements that are not checked
jQuery(document).ready(function() {
	jQuery('input:checkbox:not(:checked)').each(function() {
		jQuery('.childOf-'+this.id).css('display', 'none');
	});
});



//////////////////////// Dropdown handler ////////////////////////////////////////////////////////////////////////

//save the selected value to the database (the id of the checkbox encodes the database table/id info)
function saveDropdown(element) {
    if (typeof element != 'object') {
        alert('error: saveDropdown(element) not passed an element object');
        return;
    }
    element = jQuery(element);
    var value = element.val().myEncode();
    var id = element.attr('name');
	
	//auto-save to server if the element id contains table-id data 
	if (id.split('-').length > 1) {
		Post.Send('id='+id+'&pageId='+pageId+'&value='+value, site_url('dataserver/ajaxSaveField'));
		displayResult('#'+id+'-result');
	}

}








//////////////////////// content add/remove button handlers ////////////////////////////////////////////////////////////////////////



//the callback function (optional) enables the use of a custom delete function (eg. for the dhtmlx tree delete fn)
function removeContent(id, redirectUrl, callback) {
	table = id.split("-");
	table = table[0];

	switch (table) {
		case 'blogcategories':
			confirmMessage = 'Are you sure you want to delete this Category?  It could result in some blog articles having no category!';
			break;

		case 'pages':
			confirmMessage = 'Are you sure you want to delete this page?  You will lose any content on the page!';
			break;

		case 'project':
			confirmMessage = 'Are you sure you want to delete this Project?';
			break;

		case 'contacts':
			confirmMessage = 'Are you sure you want to delete that Contact?';
			break;

		case 'contracts':
			confirmMessage = '<p>Are you sure you want to delete this Agreement?</p><p>Any associated Invoices will also be deleted permanently - including those already submitted for payment!</p>';
			break;

		case 'contract_projects':
			confirmMessage = '<p>Are you sure you want to delete this Project?</p><p>Any associated Agreements and Invoices will also be deleted permanently!</p>';
			break;

		case 'contract_phases':
			confirmMessage = '<p>Are you sure you want to delete this Stage?</p><p>The Stage will also be deleted permanently from any associated invoices - including those already submitted for payment!</p>';
			break;

		case 'contract_invoices':
			confirmMessage = 'Are you sure you want to delete this Invoice?';
			break;

		case 'images':
			confirmMessage = 'Are you sure you want to delete the selected image?';
			break;

		case 'groups':
			confirmMessage = 'Are you sure you want to delete the selected Group?';
			break;

		case 'users':
			confirmMessage = 'Are you sure you want to delete the selected User?';
			break;

		case 'awards':
			confirmMessage = 'Are you sure you want to delete the selected Award?';
			break;


		case 'content':
		default:
			confirmMessage = 'Are you sure you want to permanently delete the selected page content?';
	}

	//create the dialog placeholder
	if (jQuery('#confirmDialog').length == 0) {
		jQuery('body').append('<div id="confirmDialog" style="display:none" title="Please Confirm"><p>' + confirmMessage + '</p></div>');
	} else {
		jQuery('#confirmDialog').html('<p>' + confirmMessage + '</p>');
	}

	jQuery("#confirmDialog").dialog({
		resizable: false,
		modal: true,
		buttons: {
			'Confirm': function() {
				jQuery(this).dialog('close');

				//delete row from database (id encodes the table-rowId information)
				if (typeof callback == 'undefined') {
					Post.Send('id=' + id + '&pageId=' + pageId + ((redirectUrl) ? '&redirectUrl=' + redirectUrl : ''), site_url('dataserver/ajaxDeleteRow'));
				} else {
					if (typeof callback != 'function') {
						alert('System error: the callback function specified is not a function');
					} else {
						callback();
					}
				}
			},
			'Cancel': function() {
				jQuery(this).dialog('close');
			}
		}
	});
}



function removeComment(id) {
	Post.Send('id='+id+'&pageId='+pageId, site_url('dataserver/ajaxDeleteRow'));
}


function dispatchNewsletter(){
	jQuery('#ajaxNotifyResult').html('<blink class="error">Processing. Please wait...</blink>');
	jQuery('.preSend').css('display', 'none');
	jQuery('.postSend').css('display', 'inline');

	var notifyGroup = jQuery('[name=notifyGroup]').val();
	var subject = jQuery('#subject').val();

	Post.Send('notifyGroup=' + notifyGroup + '&subject=' + subject, base_url + 'dataserver/ajaxSendNewsletter');
}






function submitInvoice(id){

	if (jQuery('#submitInvoice').length == 0) {
		jQuery('body').append('<div id="submitInvoice" title="Confirm Invoice Submittal"></div>');
	}

	var $submitInvoiceContent = jQuery('#submitInvoice');

	var attachedInvoices = jQuery('#fileList-subcontracts-invoices-' + id).find('a').length;
	if (!attachedInvoices) {
		$submitInvoiceContent.html('<img src="' + site_url("img/manascisaac/bluePaperclip.png") + '" style="height:20px;"> <span style="color:#554"><strong>Invoice <span style="color:blue">' + id + '</span></strong> <span style="font-size:smaller">(draft)</span></span><br /><br /><p>Please attach an electronic copy of your paper invoice before submitting for payment!</p><br /><p>Click on the <img src="' + site_url("img/manascisaac/bluePaperclip.png") + '" style="height:20px;"> to upload your invoice.</p>');

		$submitInvoiceContent.dialog({
			resizable: true,
			width: 'auto',
			modal: true,
			buttons: {}			
		});

		return false;
	}


	busyStart('submitInvoice()');

	//load the specified url content via ajax call into the dialog placeholder
	$submitInvoiceContent.load(site_url('subconsulting/submitInvoice/'+id), function(){
		$submitInvoiceContent.dialog({
			resizable: true,
			width: 'auto',
			modal: true,
			buttons: {
				'Submit Invoice': function() {
					jQuery(this).dialog('close');

					//compile a list of selected email recipients
					var recipientIds = '';
					jQuery('input.recipients:checked').each(function() {
					    if (recipientIds.length) {
							recipientIds += ',';
						}
						recipientIds += jQuery(this).val();
					});

					var message = encodeURIComponent(jQuery('#submitInvoiceIntro').html()+jQuery('#submitInvoiceNotes').val());
					var subject = jQuery('#submitInvoiceSubject').text();
					Post.Send('id=invoiceSubmitted-'+id+'&value=1&recipientIds='+recipientIds+'&subject='+subject+'&message='+message, site_url('dataserver/ajaxSaveField'));
				},
				'Cancel': function() {
					jQuery(this).dialog('close');
				}
			}
		});

		busyStop();
	});

}






function approveInvoice(id) {
	busyStart('approveInvoice()');

	if (jQuery('#approveInvoice').length == 0) {
		jQuery('body').append('<div id="approveInvoice" title="Confirm Invoice Approval"></div>');
	}

	//load the specified url content via ajax call into the dialog placeholder
	jQuery('#approveInvoice').load(site_url('subconsulting/approveInvoice/'+id), function() {
		jQuery("#approveInvoice").dialog({
			resizable: true,
			width: 'auto',
			modal: true,
			buttons: {
				'Approve Invoice': function() {
					jQuery(this).dialog('close');

					//compile a list of selected email recipients
					var recipientIds = '';
					jQuery('input.recipients:checked').each(function() {
					    if (recipientIds.length) {
							recipientIds += ',';
						}
						recipientIds += jQuery(this).val();
					});

					var message = encodeURIComponent(jQuery('#approveInvoiceIntro').html()+jQuery('#approveInvoiceNotes').val());
					var subject = jQuery('#approveInvoiceSubject').text();
					Post.Send('id=invoiceApprovedByLeader-'+id+'&value=1&recipientIds='+recipientIds+'&subject='+subject+'&message='+message, site_url('dataserver/ajaxSaveField'));
				},
				'Cancel': function() {
					jQuery(this).dialog('close');
				}
			}
		});

		busyStop();
	});
}





function rejectInvoice(id) {

	busyStart('rejectInvoice()');

	if (jQuery('#rejectInvoice').length == 0) {
		jQuery('body').append('<div id="rejectInvoice" title="Confirm Invoice Rejection"></div>');
	}

	//load the specified url content via ajax call into the dialog placeholder
	jQuery('#rejectInvoice').load(site_url('subconsulting/rejectInvoice/'+id), function() {
		jQuery("#rejectInvoice").dialog({
			resizable: true,
			width: 'auto',
			modal: true,
			buttons: {
				'Reject Invoice': function() {
					jQuery(this).dialog('close');

					//compile a list of selected email recipients
					var recipientIds = '';
					jQuery('input.recipients:checked').each(function() {
					    if (recipientIds.length) {
							recipientIds += ',';
						}
						recipientIds += jQuery(this).val();
					});

					var message = encodeURIComponent(jQuery('#rejectInvoiceIntro').html()+jQuery('#rejectInvoiceReason').val());
					var subject = jQuery('#rejectInvoiceSubject').text();
					Post.Send('id=invoiceRejected-'+id+'&value=1&recipientIds='+recipientIds+'&subject='+subject+'&message='+message, site_url('dataserver/ajaxSaveField'));
				},
				'Cancel': function() {
					jQuery(this).dialog('close');
				}
			}
		});

		busyStop();
	});

}





function addContent(pageRef, title, redirectUrl) {
	Post.Send('pageRef='+pageRef+'&title='+title+'&pageId='+pageId+'&redirectUrl='+encodeURIComponent(redirectUrl), site_url('dataserver/ajaxInsertContent'));
}


//this function uses the ajaxSaveField server-side function to create a new row - the element id contains the row information
function newRow(element) {
	switch (typeof element) {
		case 'string':
			var id = element;
			break;

		case 'object':
			var id = jQuery(element).attr('id');
			break;

		default:
			alert('a system error occurred');
	}

	Post.Send('id='+id+'-0', site_url('dataserver/ajaxSaveField'));
}


function newBlogCategory() {
	Post.Send('id=blogCategory-0&value=New Category', site_url('dataserver/ajaxSaveField'));
}



function newBlogArticle(redirectUrl) {
	var dataset = 'dataset[parentId]=0&dataset[title]=New%20Blog%20Article&dataset[pageType]=blog&dataset[display]=0';
	Post.Send(dataset+'&redirectUrl='+redirectUrl, site_url('dataserver/insertPage'));
	displayResult('#server-result');
}



function newProject(redirectUrl) {
	var dataset = 'dataset[parentId]=0&dataset[title]=New%20Project&dataset[pageType]=portfolio&dataset[display]=1';
	Post.Send(dataset+'&redirectUrl='+redirectUrl, site_url('dataserver/insertPage'));
	displayResult('#server-result');
}

//subcontracting system ajax handlers
function newContract(project) {
	if (typeof project == 'undefined') {
		Post.Send('id=contract-0', site_url('dataserver/ajaxSaveField'));
	} else {
		Post.Send('id=contractProject-0&value='+project, site_url('dataserver/ajaxSaveField'));
	}
}

function newContractProject() {
	Post.Send('id=subcontractingProject-0', site_url('dataserver/ajaxSaveField'));
}


function newInvoice(contractId) {
	Post.Send('id=invoice-0&pageId='+contractId, site_url('dataserver/ajaxSaveField'));
}

function newContractPhase(contractId) {
	Post.Send('id=contractPhase-0&pageId='+contractId, site_url('dataserver/ajaxSaveField'));
}

function newFollowupAction() {
	Post.Send('id=followups-id-0', site_url('dataserver/ajaxSaveField'));
}


function newProduct() {
	Post.Send('id=products-id-0', site_url('dataserver/ajaxSaveField'));
}

function newEvent() {
	Post.Send('id=events-id-0', site_url('dataserver/ajaxSaveField'));
}


//Confirm dialog popup (for example when user hits the delete button)
 function confirmation(message, url) {
	if (confirm(message)) {
		window.open(url, '_self');
	}
}



//ajax email message handler
function sendMessage(fromEmail, recipientIds, subject, message, ccSender, fromName, successMessage) {
	if (fromName == undefined) {fromName='';}
	if (successMessage == undefined) {successMessage='';}
	ccSender = (ccSender) ? 1 : 0;
	Post.Send('fromEmail='+fromEmail+'&fromName='+fromName+'&recipientIds='+recipientIds+'&subject='+subject+'&message='+message+'&ccSender='+ccSender+'&random='+Math.random()*10000+'&successMessage='+successMessage, site_url('dataserver/sendEmailMessage'));
}




//this function is compatible with php's rawurldecode (use this instead of encodeURIComponent)
String.prototype.myEncode = function() {
	return encodeURIComponent(this);
}

//this function is compatible with php's urlencode (use this instead of decodeURIComponent)
String.prototype.myDecode = function() {
	return decodeURIComponent(this.replace(/\%2526/g, '&').replace(/\%2520/g, ' ').replace(/\%252520/g, ' '));
}

//convert string to title case
String.prototype.toTitleCase = function() {
	return this.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}

//remove multiple spaces from string 
String.prototype.compactSpaces = function() {
	return this.replace(/ +/g, " ");
}

//trim string as in php trim function
String.prototype.trim = function() {
	return jQuery.trim( this );
}


//returns an array of styles for the specified element
function getStyles(element, styles) {
	switch (typeof element) {
		case 'string':
			element = jQuery(element);
			break;
			
		case 'object':
			break;

		default:
			alert('system error: invalid element type in getStyle(element)');		
	} 
	
	var copiedStyles = {};

	jQuery.each(styles, function(key, value) {
		copiedStyles[value] = element.css(value);
	});	
	
	return copiedStyles;
}



//applies the array of styles to the specified element
function applyStyles(element, styles) {
	switch (typeof element) {
		case 'string':
			element = jQuery(element);
			break;
			
		case 'object':
			break;

		default:
			alert('system error: invalid element type in applyStyles(element)');	
	} 

	jQuery.each(styles, function(key, value) {
		element.css(key, value);
		log('updated css', element.attr('id')+': '+key+'-->'+value);
		
	});	
}




//returns an array of styles for the specified element
function getAttributes(element, attributes) {

	switch (typeof element) {
		case 'string':
			element = jQuery(element);
			break;
			
		case 'object':
			break;

		default:
			alert('system error: invalid element type in getAttributes(element)');	
	} 
	
	var copiedAttributes = {};

	jQuery.each(attributes, function(key, value) {
		copiedAttributes[value] = element.attr(value);
//		log('copied attribute', element.attr('id')+': '+value+'-->'+element.attr(value))
	});	
	
	return copiedAttributes;
}



//applies the array of styles to the specified element
function applyAttributes(element, attributes) {
	switch (typeof element) {
		case 'string':
			element = jQuery(element);
			break;
			
		case 'object':
			break;

		default:
			alert('system error: invalid element type in applyAttributes(element)');
	} 

	jQuery.each(attributes, function(key, value) {
		element.attr(key, value);
		log('updated attribute', element.attr('id')+': '+key+'-->'+value);
	});	
}





