/// <reference path="C:\References\jquery-1.4.1-vsdoc.js" />

// ********************************************************************
// 
// Manheim Retail Marketing
// ********************************************************************
// Copyright © 2010 Manheim Retail Marketing.
//
//  Summary
// ******************************
// $Workfile: Workflow.js $
// Notes:		Provides common tracing functionality.
//
// File History Information
// ************************
// Created on:					    
// Last Modified:					$Modtime: 26/08/10 12:38 $
// Original Author:					Marc Lancashire
// Last Modified by:				$Author: Lancashirem $
// Last JS Lint:					07/12/2009 11:40
//
// Source Control Information
// **************************
// File Version:					$Revision: 13 $
// VSS Location:					$Archive: /Manheim.Portfolio/Manheim.Portfolio.Web.UI.Client.Assets/assets/js/mrm/lib/utilities/1.0/Workflow.js $
//
// ********************************************************************

// Check / create namespace
if (!mrm.global.isNamespaceDefined("mrm.lib.utilities"))
{
	mrm.global.createNamespace("mrm.lib.utilities", "1.0");
}


// Class definition
mrm.lib.utilities.Workflow = Object.subClass
(
{
	
	/*
	=============================
	CONSTANTS
	=============================
	*/
	"TRACE"						: false, // this can be overidden on a control by control basis to refine the tracing output
	"TRACE_PREFIX"				: "mrm.lib.utilities.worflow",	// this can be overidden on a control by control basis to set the trace prefix
	"_tracing"					: null,
	
	"ARGUMENT_WORKFLOW"			: "wflw",
	"WORKFLOW_DELIMITOR"		: "_",
	
	/*
	=============================
	CONSTRUCTOR
	=============================
	*/
	
	"init" : function (tracing)
	{
		if (tracing)
		{
			this._tracing = tracing;
		}
		else if(mrm.global.utilities.tracing)
		{
			this._tracing = mrm.global.utilities.tracing;
		}
		else if (mrm.global.isClassDefined("mrm.lib.utilities.Tracing"))
		{
			this._tracing = new mrm.lib.utilities.Tracing(this.TRACE, this.TRACE_PREFIX);
		}
		this._configureStringPrototypeMethods();
	},
		/*
	 =============================
	 INTERNAL RUN-TIME PROPERTIES
	 =============================
	 */
	"_configureStringPrototypeMethods" : function ()
	{
		/**
		 * String.prototype.startsWith()
		 *
		 * @runtimeScope = string object
		 *
		 * @param	string [String]				test string
		 * @param	caseSensitive [Boolean]		
		 * @return	[boolean]		
		 */
		String.prototype.startsWith = function(string, caseSensitive)
		{
			if (caseSensitive) return (string == this.substring(0, string.length));
			else return (string.toLowerCase() == this.substring(0, string.length).toLowerCase());
		}

		/**
		 * String.prototype.endsWith()
		 *
		 * @runtimeScope = string object
		 *
		 * @param	string [String]				test string
		 * @param	caseSensitive [Boolean]		
		 * @return	[boolean]	
		 */
		String.prototype.endsWith = function(string, caseSensitive)
		{
			if (caseSensitive) return (string == this.substring(this.length - string.length));
			else return (string.toLowerCase() == this.substring(this.length - string.length).toLowerCase());
		}
		
	},
	
	/*
	=============================
	PUBLIC MEMBERS
	=============================
	*/
	//Get a paramter value from the parameter name
	"getQueryStringParam" : function (url, name)
	{
	  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
	  var regexS = "[\\?&]"+name+"=([^&#]*)";
	  var regex = new RegExp( regexS );
	  var results = regex.exec(url);
	  if( results == null )
		return "";
	  else
		return results[1];
	},
	
	// Remove parameter value
	"removeQueryStringParameter" : function (url, parameterName)
	{
		var startIndex, endIndex, startPart, endPart;
		startIndex = url.indexOf(parameterName);
		endIndex = url.indexOf("&", startIndex);
		
		if (startIndex >= 0)
		{
			startPart = url.slice(0, startIndex - 1);
			endPart = "";
			
			if (endIndex >= 0)
			{
				endPart = url.slice(endIndex);
			}
			
			url = startPart + endPart;
		}
		
		return url;
	},
	
	// Add or update an exsisting parameter on a query string
	"addQueryStringParameter" : function (url, parameterName, parameterValue)
	{
		var startIndex, endIndex, startPart, endPart;
		startIndex = url.indexOf(parameterName) + parameterName.length + 1;
		endIndex = url.indexOf("&", startIndex);
		
		if (startIndex >= 0)
		{
			startPart = url.slice(0, startIndex);
			endPart = "";
			
			if (endIndex >= 0)
			{
				endPart = url.slice(endIndex);
			}
			
			url = startPart + parameterValue + endPart;
		}
		else
		{
			// ensure we have ?
			if (url.indexOf("?") < 0)
			{
				url = url + "?";
			}
			// ensure we have a & if nessary
			else if (
						(url.indexOf("?") >= 0) &&
						(url.charAt(url.length - 1) !== "?") &&
						(url.charAt(url.length - 1) !== "&")
					)
			{
				url = url + "&";
			}
			
			// add new parameter and value
			url = url + parameterName + "=" + parameterValue;
		}
		
		return url;
	},
	
	// get a workflow key from an element, the element must have either a action or href attribute
	"getScopedWorkflowKey" : function (element, workflowAction, keyLevel)
	{
		// form.action e.g. /results.aspx?wflw=se_de_se
		
		// get url
		var submitUrl = ($(element).attr("action")) ? $(element).attr("action") : $(element).attr("href");
		
		return this.getScopedWorkflowKeyFromUrl(submitUrl, workflowAction, keyLevel);
	},
	
	/*
	Get a workflow key from a url, subsitutes the end action for the provided action.
	The keyLevel parameter is used if only a certain part of the key should be returned.
	
	keyLevel:
		null	= Full
		-1		= Full
		0+		= Is the selectable part of the key starting from the left
	*/
	"getScopedWorkflowKeyFromUrl" : function (url, workflowAction, keyLevel)
	{
		var submitUrl = url;
		
		// get indexes
		var startIndex = submitUrl.indexOf(this.ARGUMENT_WORKFLOW) + this.ARGUMENT_WORKFLOW.length + 1; // start after wflw=
		var endIndex = submitUrl.indexOf("&", startIndex);
		
		// get full workflow
		var wflw;
		if (endIndex !== -1)
		{
			wflw = submitUrl.substring(startIndex, endIndex);
		}
		else if (startIndex > this.ARGUMENT_WORKFLOW.length + 1)
		{
			wflw = submitUrl.substring(startIndex);
		}
		else
		{
			return null;
		}
		
		if (workflowAction)
		{
			// now we have the scoped workflow trim off the action + "_" and add the relavant search action
			wflw = wflw.substring(0, wflw.length - 3) + workflowAction;
		}
		
		if (keyLevel && keyLevel > 0)
		{
			wflw = this.getWorkflowKeyPart(wflw, keyLevel);
		}
		
		return wflw;
	},
	
	/*
	Get a part of the work flow key.
	
	keyLevel:
		null	= Full
		0<		= Full
		1+		= Is the selectable part of the key starting from the left	
	*/
	"getWorkflowKeyPart" : function (workflowKey, keyLevel)
	{
		var wflw = workflowKey;
		var wflwParts = workflowKey.split(this.WORKFLOW_DELIMITOR);
		
		// ensure we can't go beyond the array bound
		keyLevel = (keyLevel > wflwParts.length) ? wflwParts.length : keyLevel;
		
		// get workflow key part
		if (keyLevel <= wflwParts.length)
		{
			wflw = "";			
			for (var i = 0; i < keyLevel; i = i + 1)
			{
				wflw += wflwParts[i];
				
				if (i < (keyLevel - 1))
				{
					wflw += this.WORKFLOW_DELIMITOR;
				}
			}
		}
		
		return wflw;
	},
	
	/*
	Takes a response from a standard workflow ajax response and renders the returned controls
	to the DOM.
	
	Normal Content Response (result)
	{
		"status"	: "work flow state",
		"arguments"	:
			{	"argument name" : "argument value",
				"argument name" : "argument value"
			},
		"worflowaction" "FavouritesAdd",
		"controls"	:
			[
				{
					"controlName"		: "",
					"targetDomId"		: "",
					"content"			: "",
					"placementType"		: ""
				},
			]
	}
	*/
	"processResultControls" : function (result, rebindControls, jqContext)
	{
		this.addTrace("Started: Processing ajax control results");
		
		rebindControls = (rebindControls !== undefined) ? rebindControls : true;
		
		if (!result || !result.controls)
		{
			return;
		}
		
		for (var i = 0; i < result.controls.length; i = i + 1)
		{
			this.addTrace("Start: Get next control reference.");
			var controlReference = result.controls[i];
			this.addTrace("End: Get next control reference.");
			
			this.addTrace("Start: Use JQuery to find DOM element to replace (" + result.controls[i].targetDomId + ").");
			var target = this.getControlTarget(jqContext, result.controls[i].targetDomId);
			this.addTrace("End: Use JQuery to find DOM element to replace (" + result.controls[i].targetDomId + ").");
			
			if (target.length > 0)
			{
				this.addTrace("Processing " + controlReference.controlName + " control using '" + controlReference.placementType + "' rendering method.");
				
				if (controlReference.placementType === "Replace")
				{
					target = target.replaceWith(result.controls[i].content);
					this.addTrace("Replaced content for " + controlReference.controlName + " control.");
				}
				else if (controlReference.placementType === "InsertAdd")
				{
					target = target.append(result.controls[i].content);
					this.addTrace("Appended content for " + controlReference.controlName + " control.");
				}
				else
				{
					// InsertReplace
					target = target.html(result.controls[i].content);
					this.addTrace("Replaced inner html for " + controlReference.controlName + " control.");
				}

				// re-get target after replacement
				target = this.getControlTarget(jqContext, result.controls[i].targetDomId);

				// run reinit control
				if (rebindControls)
				{
					this.bindResultControl($(result.controls[i].targetDomId));
				}

				// boardcast updated via ajax event
				var ajaxUpdateEvent = $.Event("AJAX-CONTENT-UPDATE");
				target.trigger(ajaxUpdateEvent, [result, result.controls[i]]);
				
				this.addTrace("Finished re bind for " + controlReference.controlName + " control.");
			}
		}
		
		this.addTrace("Finished: Processing ajax control results");
	},	
	
	"getControlTarget" : function(jqContext, targetSelector)
	{
		var jqTarget;
		
		if (jqContext)
		{
			jqTarget = jqContext.find(targetSelector);
		}
		else
		{
			jqTarget = $(targetSelector);
		}

		return jqTarget;
	},

	// find and return a control with the provided name using the provided ajax workflow result response
	"findResultDataControl" : function (result, controlName)
	{
		if (!result || !result.controls)
		{
			return null;
		}
		
		for (var i = 0; i < result.controls.length; i = i + 1)
		{
			if (result.controls[i].controlName === controlName)
			{
				return result.controls[i];
			}
		}
		
		return null;
	},
	
	// find and return a argument value with the provided name using the provided ajax workflow result response
	"findResultArgument" : function (result, argumentName)
	{
		if (!result || !result.arguments || !(argumentName in result.arguments))
		{
			return null;
		}
		
		return result.arguments[argumentName];
	},
	
	// add re-bind controls when inserted
	/* TODO:
			Need to add in config what control replacements should run what reinit's, add the reinit processors to an array
			and then pick out the reinit's and run them over the jqContainer that we are inserting.
	*/
	"bindResultControl" : function (jqContainer)
	{
		if (window.fix_PNGs)
		{
			if (jqContainer.find(".png").length > 0)
			{
				fix_PNGs(jqContainer);
			}
			else
			{
				fix_PNG(jqContainer);
			}
		}
	},
	
	
	// handles the tracing of message, will check if a tracing utility has been setup
	"addTrace" : function (message)
	{
		if (this._tracing)
		{
			this._tracing.addTrace(message);
		}
	},
			/*
	 =============================
	 DATA MANIPULATION
	 =============================
	*/
	/*
	 * mergeDataIntoUniqueList ..... [previously know as 'GetUniqueLists']
	 *
	 * Merge 2 delimited (common delimiter) data lists into one, removing data duplications
	 * 
	 * @param	dataListA	[String]	String of delimited data values
	 * @param	dataListB	[String]	String of delimited data values
	 * @param	delimiter	[String]	String representing data separater used in listA and listB
	 */
		"mergeDataListsIntoUniqueList" : function (dataListA, dataListB, delimiter) 
		{
			//split the two lists into arrays
			var a = dataListA.split(delimiter);
			var b = dataListB.split(delimiter);
			
			// Add initial delimiter for existance checking (cleaned and removed at end);
			var returnString = delimiter;

			//cycle the first list and add the values to the return string if they aren't already present
			for (var i = 0; i < a.length; i++) {
				if (returnString.indexOf(delimiter + a[i] + delimiter) == -1 && a[i] != '') {
					returnString += a[i] + delimiter;
				}
			}

			//now cycle the second list and if any values aren't present then add them to the return list
			for (var j = 0; j < b.length; j++) {
				if (returnString.indexOf(delimiter + b[j] + delimiter) == -1 && b[j] != '') {
					returnString += b[j] + delimiter;
				}
			}

			//trim any leading and trailing separators
			if (returnString.startsWith(delimiter, true)) returnString = returnString.substring(1);
			if (returnString.endsWith(delimiter, true)) returnString = returnString.substring(0, returnString.length-1);
			
			//return the finished value list
			return returnString;
		},
	
	/*
	 * removeValuesFromDataList ..... [previously know as 'removeValuesFromList']
	 *
	 * Remove a series of data values from a defined list of delimited data
	 * 
	 * @param	dataList	[String]	String of delimited data values from which to remove 'valuesList' data
	 * @param	valuesList	[String]	String of delimited data values to remove from 'dataList'
	 * @param	delimiter	[String]	String representing data separater used in listA and listB
	 */
	"removeValuesFromDataList" : function (dataList, valuesList, delimiter) 
	{
		//split both lists into arrays
		var data	= dataList.split(delimiter);
		var values	= valuesList.split(delimiter);

		//the string of values to be returned   
		var returnString = '';

		//cycle the main list of values
		for (var i = 0; i < data.length; i++) 
		{
			var item = data[i];
			//check we have a valid listItem to be processed
			if (item != '') 
			{
				var add = true;
				//cycle the values array to see if this item should be added
				for (var j = 0; j < values.length; j++) 
				{
					if (item == values[j]) add = false;
				}

				//if add is still set to true then we can add it
				if (add) returnString += item + delimiter;
			}
		}

		//trim any leading and trailing separators
		if (returnString.endsWith(delimiter, true)) returnString = returnString.substring(0, returnString.length-1);

		//return the finished value list
		return returnString;
	}
	
	/*
	=============================
	PRIVATE MEMBERS
	=============================
	*/	
}
);
