/*
 * bring browser up to speed somewhat
 * NOTE: THESE PROTOTYPE METHODS ARE DUPLICATED IN util.js AND nondashboard-utils.js
 * 		KEEP THEM IDENTICAL
 */
if(!String.prototype.trim)
{
	String.prototype.trim = function()
	{
		var start = -1;
		var end = this.length;
		while(this.charCodeAt(--end) < 33);
		while(this.charCodeAt(++start) < 33);
		return this.slice(start, end + 1);
	};
};
if(!String.prototype.toBoolean)
{
	String.prototype.toBoolean = function()
	{
		switch(this.valueOf())
		{
			case "1":
				return true;
				break;
			case "0": case "": case "no": case "false":
			case "No": case "False": case "NO": case "FALSE":
				return false;
				break;
			default:
				return true;
		}
	};
}
if(!Array.prototype.indexOf)
{
	Array.prototype.indexOf = function(value, fromIndex)
	{
		var len = this.length;
		if(fromIndex == null)
			fromIndex = 0;
		else if(fromIndex < 0)
			fromIndex = Math.max(0, len + fromIndex);
		for(var i = fromIndex; i < len; i++)
		{
			if(this[i] === value)
				return i;
		}
		return -1;
	};
};

/*
 * commonspotNonDashboard namespace
 * 	Stuff that runs in another window so that prototype references that window, not the main dashboard window
 */
commonspotNonDashboard = {};
commonspotNonDashboard.util = {};

// we'll be moving to this namespace sometime soon, so I'll start it now...
commonspotLocal = window.commonspotLocal || {};
commonspotLocal.util = {};

commonspotLocal.window = window;
	
/*
 * shows or hides any number of passed elements, passed as objects or as IDs
 * elements argument can be:
 * 	- an elementID string, comma delimited for multiple
 * 	- an object containing html elements or strings
 *		- an array containing html elements or string elementIDs
 * notes:
 * 	showHideElements works by modifying inline style
 * 	showHideElementsUsingCSSClass does the same thing by adding or removing a css class; 
 * 		it's a better choice for table rows
 * 	copies of these functions in util.js work on objects in the top frame, regardless of where they're called from
 * 	copies of them in nondashboard-util.js work on local window
 */
commonspotLocal.util.showHideElements = function(show, elements, dspType)
{
   if(!dspType)
      dspType = 'block';
	var display = show ? dspType : 'none';
	var targets;
	if(typeof elements == 'string')
		targets = elements.split(',');
	else
		targets = elements;
   targets.each(function(elem)
	{
		$(elem).style.display = display;
	});
};

// see comments above for showHideElements
commonspotLocal.util.showHideElementsUsingCSSClass = function(show, elements)
{
	var targets = (typeof elements == 'string') ? elements.split(',') : elements;
	if(typeof targets !== "array")
	{
		targets.each(function(elem)
		{
			if(show)
				$(elem).removeClassName('showHideElements_hide');
			else
				$(elem).addClassName('showHideElements_hide');
		});
	}
};

commonspotNonDashboard.util.INNER_DIV_HEIGHT = 34;

/*
 * Function to display action-in-progress overlay.
 * 
 *		elementID - ID of element which is to be overlaid.
 * 		overlayDivStyle - CSS class name for overlaying div, optional.
 * 		overlayMessage - Custom message to be displayed, optional.
 */
commonspotNonDashboard.util.displayMessageOverlay = function(elementID, overlayDivStyle, overlayMessage)
{
	overlayDivStyle = overlayDivStyle || 'overlayDivStyle';
	overlayMessage = overlayMessage || 'Please Wait...';
	
	var underlayElement = $(elementID);
	
	if( underlayElement )
	{
		if(!$(elementID + 'OverlayDiv'))
		{
			// Create, configure, and style outerDiv.
			var outerDiv 				= document.createElement('div');
			outerDiv.id 				= elementID + 'OverlayDiv';
			
			$(outerDiv).addClassName(overlayDivStyle);
			
			// Create, configure, and style inner div.
			var innerDiv 				= document.createElement('div');
			innerDiv.id 				= elementID + 'innerDiv';
			
			$(innerDiv).addClassName('overlayInnerDivStyle');
			
			// Create, configure, and style innermost div.
			var innerMostDiv 			= document.createElement('div');
			innerMostDiv.id 			= elementID + 'innerMostDiv';
			innerMostDiv.innerHTML 	= overlayMessage;
			
			$(innerMostDiv).addClassName('overlayInnerMostDivStyle');
			
			// Append to DOM.
			innerDiv.appendChild(innerMostDiv);
			outerDiv.appendChild(innerDiv);			
			document.body.appendChild(outerDiv);
	 
			$(outerDiv).hide();
				
			// Give outerMostDiv the positional attributes of the element it needs to overlay.
			 
			Position.clone(underlayElement, 	// source
								outerDiv, 			// target
								true,					// setLeft
								true, 				// setTop
								true, 				// setWidth
								true, 				// setHeight
								0,						// offsetLeft
								0);					// offsetTop	
			
			/* Calculate and position inner div with reference to outer div position. */
			// Get outer div height.
			var outerDivHeight = parseInt($(outerDiv).getStyle('height').replace(/px/, ''), 10);	 
			
			// Calculate inner div top with respect to outer div.
			var innerDivTop = (outerDivHeight - commonspotNonDashboard.util.INNER_DIV_HEIGHT) / 2;
			innerDivTop 	 = (innerDivTop < 0) ? -innerDivTop : innerDivTop;			
			innerDivTop 	 = innerDivTop + 'px';
			
			// Set inner div top for vertical centering (Horizontal centering is taken care of by CSS).
			$(innerDiv).setStyle
			({
				top: innerDivTop
			});
			
			// Display overlay now.
			$(outerDiv).show();
			
			top.commonspot.overlayDivIDs = top.commonspot.overlayDivIDs || {};
			top.commonspot.overlayDivIDs[elementID] = true;
		}
	}
};

/*
 * Function to hide action-in-progress overlay.
 * 
 *		elementID - ID of element which is to be overlaid.
 */
commonspotNonDashboard.util.hideMessageOverlay = function(elementID)
{
	if(elementID)
	{
		var overlayDiv = $(elementID + 'OverlayDiv');
		if(overlayDiv)
		{
			overlayDiv.remove();
			overlayDiv = null;
		}
		if(top.commonspot && top.commonspot.overlayDivIDs && top.commonspot.overlayDivIDs[elementID])
			delete(top.commonspot.overlayDivIDs[elementID]);
	}
	// clean up anything we missed; shouldn't happen...
	if(commonspot.util.hasMembers(top.commonspot.overlayDivIDs))
	{
								/*var msg = "stray top.commonspot.overlayDivIDs:\n";
								for(id in top.commonspot.overlayDivIDs)
									msg += id + "\n";
								alert(msg);*/
									
		for(var id in top.commonspot.overlayDivIDs)
		{
			if(top.commonspot.overlayDivIDs.hasOwnProperty(id))
			{
				var overlayDiv = $(id + 'OverlayDiv');
				if(overlayDiv)
					overlayDiv.remove();
			}
		}
		overlayDiv = null;
		top.commonspot.overlayDivIDs = {};
	}
};

/*
 * This function does not belong to commonspotNonDashboard library .
 * It handles the error globally
 * This method is added here as this file is used in all dialogs
 * dmerrill 3/27/09: TODO: kill this, when it's removed from callers. ajax engine does generic error handling by itself.
 */
handleCommandError = function(commandError)
{
	alert(commandError);
};

/**
 * commonspotLocal.util.getValue
 * general purpose utility to read the value from html objects, and javascript objects that support it
 * @see \cs\devdocs\javascript-function-docs.js
 */ 
commonspotLocal.util.getValue = function(objID, format, standardOnly)
{
	var value = "";
	var v = "";
	var obj = $(objID);
	if(!obj)
	{	// didn't find it by id, look by name and take first one if any
		obj = document.getElementsByName(objID);
		obj = obj && obj[0];
	}
	if(!obj)
		throw new Error("[getValue] Element not found, id=" + objID);
	if(!standardOnly)
		var getValueMethod = commonspotLocal.util.getGetValueMethod(obj);
	if(getValueMethod)
		return getValueMethod(obj, format);
	else if((!obj.nodeName) && obj.getValue) // non-html obj w getValue method -- custom object
		value = obj.getValue();
	else
	{
		var type = obj.type || obj.nodeName;
		if(!type)
			throw new Error("[getValue] Element has no type, id=" + objID);
		type = type.toLowerCase();
		switch(type)
		{
			// basic text enterables get trimmed, value in field itself too
			case "text": case "password": case "textarea": case "hidden": case "button":
				value = $F(obj);
				if(typeof value === "string")
				{
					v = value.trim();
					if(v !== value)
						commonspotLocal.util.setValue(obj, value);
				}
				break;
				
			// single selects rtn selected value
			case "select-one":
				value = $F(obj);
				break;
				
			// multiple selects rtn concat of selected values 
			case "select-multiple":
				value = $F(obj).join(",");
				break;
				
			// single checkboxes and radios (single radio???) rtn 1 or 0, multiples rtn concat of checked values
			case "checkbox": case "radio":
				var objName = obj.name;
				if(!objName) // no name, assume there's only one, silly for radios, but anyway...
					value = obj.checked ? 1 : 0;
				else if(type == "checkbox")
				{
					var checkboxes = document.getElementsByName(objName)
					if(checkboxes.length === 1 && !checkboxes[0].getAttribute("cs_useCheckboxValue"))
						value = checkboxes[0].checked ? 1 : 0;
					else // multiple checkboxes w this name; concat values of checked ones
					{
						var checkedItems = $A(checkboxes).findAll(function(e){return e.checked});
						value = checkedItems ? checkedItems.pluck("value").join(",") : "";
					}
				}
				else // radio
				{
					var checkedItems = $A(document.getElementsByName(objName)).find(function(e){return e.checked});
					value = (checkedItems && checkedItems.value) ? checkedItems.value : "";
				}
				break;
				
			// basic non-form html elements rtn their innerHTML
			case "div": case "p": case "span": case "li":
				value = obj.innerHTML.trim();
				break;
				
			// else complain
			default:
				throw new Error("[getValue] Object is an unsupported type: " + type + ",  id=" + objID);
		}
	}
	value = (value == null) ? "" : value;
	if(!format)
		return value;
	switch(format)
	{
		case "trim": // under-enterable field types trim always; only need this for fixed-value types w sloppy data, rare
			value = (typeof value === "string") ? value.trim() : value;
			break;
		default:
			if(typeof format === "function") // custom formatter
				value = format(value);
			else
				throw new Error("[getValue] Unsupported format: " + format);
	}
	return value;
};

/**
 * commonspotLocal.util.setValue
 * general purpose utility for setting the value of an html object, and javascript objects that support it
 * @see \cs\devdocs\javascript-function-docs.js
 */
commonspotLocal.util.setValue = function(objID, value, decodeEntities, standardOnly)
{
	var obj = $(objID);
	if(!obj)
	{
		obj = document.getElementsByName(objID);
		obj = obj && obj[0];
	}
	if(!obj)
		throw new Error("[setValue] Element not found, id=" + objID);
	else if(typeof value == "undefined")
		throw new Error("[setValue] Value is undefined, id=" + objID);
	else if(value == null)
		throw new Error("[setValue] Value is null, id=" + objID);
	// only decodes a few html entities, but that's all spry encodes, and our code that emulates it
	if(decodeEntities && typeof value === "string")
		value = commonspot.util.xml.decodeEntities(value);
	if(!standardOnly)
		var setValueMethod = commonspotLocal.util.getSetValueMethod(obj);
	if(setValueMethod)
	{
		setValueMethod(obj, value);
		return;
	}
	else if((!obj.nodeName) && obj.setValue) // non-html obj w setValue method -- custom object
	{
		obj.setValue(value);
		return;
	}
	var type = obj.type || obj.nodeName;
	if(!type)
		throw new Error("[setValue] Element has no type, id=" + objID);
	type = type.toLowerCase();
	switch(type)
	{
		case "text": case "textarea": case "hidden": case "password": case "button":
			obj.value = value;
			break;
		case "select-one":
		case "select-multiple":
			value = value.toString().toLowerCase();
			var options = $A(obj.options);
			if(type == "select-one")
				options.find(function(elem){return elem.selected = elem.value.toLowerCase() == value;});
			else // multiple
			{
				value = "," + value + ",";
				options.each(function(elem){elem.selected = value.indexOf("," + elem.value.toLowerCase() + ",") >= 0;});
			}
			break;
		case "checkbox": case "radio":
			value = value.toString().toLowerCase();
			var options = obj.name ? $A(document.getElementsByName(obj.name)) : [obj]; // if no name, assume only one
			if(options.length == 1 && !options[0].getAttribute("cs_useSingleCheckboxValue")) // only one element; treat value as boolean, ignoring elem value
				obj.checked = value.toBoolean();
			else if(type == "checkbox") // checkboxes, treat value as a list
			{
				value = "," + value + ",";
				options.each(function(elem){elem.checked = (value.indexOf("," + elem.value.toLowerCase() + ",") >= 0);});
			}
			else // radios, single value, stop when found
				options.find(function(elem){return elem.checked = (value == elem.value.toLowerCase());});
			break;
		case "div": case "p": case "span": case "li":
			obj.innerHTML = value;
			break;
		default:
			throw new Error("[setValue] Object is an unsupported type: " + type + ",  id=" + objID);
	}
	value = (value == null) ? "" : value;
	return value;
};

// sets an html single select to passed value, looking at option texts, rather than values
commonspotLocal.util.setSelectFromOptionText = function(select, value)
{
	var select = $(select);
	for (var i = 0; i < select.options.length; i++)
	{
		if (select.options[i].text == value)
		{
			select.options[i].selected = true;
			break;
		}
	}
};

commonspotLocal.util.getGetValueMethod = function(field, root, defaultMethod)
{
	return commonspotLocal.util.getInheritableAttributeMethod(field, "getValueMethod", "input,label", root, defaultMethod);
};

commonspotLocal.util.getSetValueMethod = function(field, root, defaultMethod)
{
	return commonspotLocal.util.getInheritableAttributeMethod(field, "setValueMethod", "input,label", root, defaultMethod);
};
		
commonspotLocal.util.getInheritableAttributeMethod = function(field, attribute, followedTags, root, defaultMethod)
{
	var method = defaultMethod;
	var methodName = commonspotLocal.util.getInheritableAttribute(field, attribute, "input,label");
	if(methodName)
	{
		method = commonspotLocal.util.resolveDotPathExpr(methodName, root);
		if(!method)
			throw new Error("[getInheritableAttributeMethod] Specified method could not be found for field '" + field.name + "': " + methodName);
	}
	return method;
};

// returns an html attribute that may or may not be present in obj and ancestor tags
// continues looking up dom tree, but only as long as the tag's nodeName is found in followedTags
commonspotLocal.util.getInheritableAttribute = function(obj, attribute, followedTags)
{
	if((!obj) || (!attribute))
		return null;
	var atr = obj.getAttribute(attribute);
	if(!followedTags)
		return atr;
	if((!atr) && obj.parentNode && followedTags.toLowerCase().indexOf(obj.nodeName.toLowerCase()) >= 0)
		atr = commonspotLocal.util.getInheritableAttribute(obj.parentNode, attribute, followedTags);
	return atr;
};

// returns a comma delimited list of the values of checked checkboxes with the requested name
// returns empty str if no checkboxes w that name are found or none are checked
commonspotLocal.util.getCheckedValuesList = function(checkboxName)
{
	var checkboxes = document.getElementsByName(checkboxName)
	var checkedItems = $A(checkboxes).findAll(function(e){return e.checked});
	var value = checkedItems ? checkedItems.pluck("value").join(",") : "";
	return value;
}

// resolves a dot-pathed string as a value in the requested root obj, which defaults to the current window
// ex: "some.namespace.someFunctionOrValue" returns some.namespace.someFunctionOrValue
// returns null if some step on that path doesn't exist (doesn't throw)
commonspotLocal.util.resolveDotPathExpr = function(expr, root)
{
	var ref = root || window;
	if(!ref)
		return null;
	if(!expr)
		return ref;
	var aPath = expr.split(".");
	for(var i = 0; i < aPath.length; i++)
	{
		if(typeof ref[aPath[i]] !== "undefined")
			ref = ref[aPath[i]];
		else
			return null;
	}
	return ref;
};


// TODO: this is terrible; has hard coded var name 'tree', needs to be an obj w getValue, setValue, maybe render
commonspotLocal.util.getTreeValue = function(defaultToRoot)
{
	var id = "";
	var text = "";
	if(typeof tree === "object") // If tree popup is displayed.
	{
		// If any node is selected, get selected node ID, else select root ID as default.
		var treeNode = tree.selModel.getSelectedNode();
		if(treeNode || defaultToRoot)
			id = parseInt(treeNode ? treeNode.attributes.id : root.attributes.id);
		text = commonspotLocal.util.getValue('selectedNodeTextContainer');
	}
	return {id: id, text: text};
};

commonspotLocal.util.addRemoveClassName = function(add, className, elements)
{
	if(add)
		$(elements).addClassName(className);
	else
		$(elements).removeClassName(className);
}
;
/*
 * Function to make text wrappable (replace any occurances of
 * chars like .,/,\,,,-,_,@ with the corresponding char and an 1x1 img
 *
 *		str - String to make it wrappable.
 * 		return str with the above chars replaced w the corresponding char + 1x1 img.
 */
commonspotLocal.util.makeWrappable = function(str)
{
	if ( typeof str == 'undefined' || str == '' || str == null )
		return '';
		
	var img = '<img src="/commonspot/images/blank.gif" height="1" width="1">';
	var tmpMark = '1ZZ1';
	var pattern = /[\.]|[\/]|[\\]|[,]|[-]|[_]|[@]|[\|]/g;
	var aSpecialChars = [".","\\","/","|"];
	var aMatchedChars = [];
	var aReplacementChars = [];
	var i = 0;
	
	while((result = pattern.exec(str)) != null)
	{
		aReplacementChars[i] = result[0];
		if(aSpecialChars.indexOf(result[0])>=0)
			aMatchedChars[i++] = '\\' + result[0];
		else
			aMatchedChars[i++] = result[0];
	}
	
	aMatchedChars = aMatchedChars.uniq();
	aReplacementChars = aReplacementChars.uniq();
	
	for (var j=0; j<aMatchedChars.length;j++)
	{
		str = str.replace(new RegExp(aMatchedChars[j],'g'),aReplacementChars[j]+tmpMark);
	}
	
	str = str.replace(new RegExp(tmpMark, 'g'), img);	
	
	return str;
};

commonspotLocal.util.log = function()
{
	if(window.top && top.console && top.console.log)
	{
		try { top.console.log.apply(false, arguments); }
		catch(e) { var err = 1; }
		if(err) // IE8 console, far as I know; takes only one argument, text only
		{
			try
			{
				var aOutput = [arguments.length];
				for(var i = 0; i < arguments.length; i++)
					aOutput[i] = arguments[i];
				top.console.log(aOutput.join("\t"));
			}
			catch(e){} // nevermind
		}
	}
};

// call this when a cmd error that makes a NON-LIGHTBOX dlg unusable occurs
commonspotLocal.util.handleFatalNonDialogCmdError = function(updateObjID)
{
	$(updateObjID).update('<div style="margin: 10px 30px; font-style: italic;">Data is unavailable.</div>');
};

/*
 * back end (UDF.data.deleteMultiple) catches errors during multiple deletes, and aggregates them into a result struct
 * 	this appears to the client like a successful cmd, and we show that result here in a single alert
 * if only one item was deleted, back end code doesn't catch, and the throw with its error details comes through
 * 	calling code should detect that (this.hasAnyError usually) and NOT call this;	the ajax layer will have alerted the error already
 * 	this lets a user delete a single item at a time for more details
 * resultDataset is a SimpleJSDataset, whose data is an object like this:
 * 	{success: countOfItemsDeleted, securityerror: countOfItemsWithSecurityFailures, othererror: countOfItemsWithOtherFailures}
 */
commonspotLocal.util.deleteMultipleOnComplete = function(resultDataset, refreshCallback)
{
	var data = resultDataset.getData();
	for(fld in data)
		data[fld] = parseInt(data[fld]);
	if((data.success > 0) && (typeof refreshCallback === 'function'))
		refreshCallback();
	if(data.securityerror || data.othererror)
	{
		var msgs = [];
		msgs.push(commonspot.util.plural(data.success, ' item') + ' deleted');
		if(data.securityerror)
			msgs.push(commonspot.util.plural(data.securityerror, ' item was', 'items were') + ' not deleted because you do not have sufficient rights');
		if(data.othererror)
			msgs.push(commonspot.util.plural(data.othererror, ' item was', 'items were') + ' not deleted for other reasons');
		commonspot.dialog.client.alert(msgs, {title: 'Error Deleting'});
	}
	return data.success;
};

/*
	This generic function checks for string upto X position, 
	if the string does not have space then insert 1X1 blank.gif after each X positions. 
*/
commonspotLocal.util.wrapAt = function(text, position)
{
	if (text.length <= position)
		return text;
		
	var words = text.split(' ');
	var str = '';
	var k = 0;	
	var output = new Array();
	var currSubstr = '';
	
	for (var i=0; i<words.length; i++) 
	{
		str = words[i];
		if (str.length > position)
		{
			for (var j=0; j<str.length; j=j+position)
			{
				currSubstr = str.substr(j,position);
				output[k] = currSubstr;
				if (currSubstr.length == position)
					 output[k] += '<img src="/commonspot/images/blank.gif" height="1" width="1">';
				k++;
			}
		}
		else
		{
			output[k] = str;
			k++;
		}
	}	
	return output.join(' ');
}

/*
 * Function to select or deselect all the check boxes.
 */
commonspotLocal.util.selectDeselectAll = function(formID, checkboxName, checked)
{
	var chk = $(formID)[checkboxName];
	var chkcount = chk.length;
	if(typeof chkcount == 'undefined')
		chk.checked = checked;
	else
	{
		for(var i=0;i<chkcount;i++)
			chk[i].checked = checked;
	}
}

