// all_wc

//* WebSphere Commerce
//* Licensed Materials - Property of IBM
//* (c) Copyright IBM Corp. 2007

//*******************************************************************
// wc/nls/common.js
dojo.provide("wc.nls.common");

({
	communicationError: "Communication error."
})

//*******************************************************************
// wc/service/common.js
if(!dojo._hasResource["wc.service.common"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["wc.service.common"] = true;

dojo.provide("wc.service.common");
dojo.requireLocalization("wc", "common", null, "ROOT,en,en-us");

wc.service.services = {
	// summary: Map of all declared services.
	// description: The wc.service.services object stores all of the currently declared
	//		services. The service ID is the property name.
};

wc.service.getServiceById = function (id) {
	// summary: Get the declared service with the specified ID.
	// description: Get the service that was declared under the specified
	//		identifier. If the service was not declared then this function
	//		will return "undefined".
	// returns: The service with the specified ID.
	return wc.service.services[id];
};

wc.service.declare = function (initProperties) {
	// summary: Declare a new service with the specified ID.
	// description: This function declares a new service and initializes it
	//		with the specified initialization properties. The initialization properties
	//		are "mixed in" with the new service's properties.
	// returns: The new service.
	// initProperties: Object
	//		The initialization properties.
	var service = new wc.service.Service(initProperties);
	this.register(service);
	return service;
};

wc.service.register = function (service) {
	// summary: Register the specified service.
	// description: This function registers the specified service.
	// service: wc.service.Service
	//		The service to be registered.
	this.services[service.id] = service;
};

wc.service.invoke = function (serviceId, parameters) {
	// summary: Invokes the specified service.
	// description: This function finds the registerd service with the
	//		specified service ID and invokes the service using the specified
	//		parameters.
	// serviceId: String
	//		The service ID.
	// parameters: Object
	//		The service parameters.
	console.debug(" wc.service.invoke  : " + parameters);
	var service = this.getServiceById(serviceId);
	if (service) {
		service.invoke(parameters);
	}
	else {
		console.debug("Attempt to invoke an unregistered service: " + serviceId);
	}
};

dojo.declare("wc.service.Service", null, {
	// summary: Service class.
	// description: This class provides support for invoking a service. A service is a server URL
	//		that performs a server object create, update, delete or other server processing. When
	//		the service completes successfully, a model changed event will be sent to any subscribed
	//		listeners.
	// id: String
	//		The unique ID of this service.
	// actionId: String
	// 		An identifier for the action performed by this service. This value will be used in the
	//		construction of the topic name for the model changed event. The model changed event name will be
	//		of the form "modelChanged/actionId".
	// url: String
	//		The URL for this service.
	// formId: String
	//		The ID of the form element that will be posted to the URL. A service does not necessarily have
	//		to be associated with a form element.
	constructor: function (initProperties) {
		// summary: Service initializer.
		// description: Initializes the new refresh controller.
		// initProperties: Object
		//		Initialization properties. The properties are mixed in with the new refresh controller's
		//		properties.
		dojo.mixin(this, initProperties);
	},

	id: undefined,
	actionId: undefined,
	url: undefined,
	formId: undefined,

	validateParameters: function (parameters) {
		// summary: Validate that the service parameters are correct.
		// description: This function examines the service parameters to determine if the service
		//		is ready to be invoked. The function will be called from the invoke function before
		//		the service is invoked. The default implementation of this function returns "true".
		//		This function may be replaced by passing in a new version with the initProperties object
		//		when the service is constructed.
		// returns: "true" if the parameters are valid
		// parameters: Object
		//		The parameters Object that was passed to the invoke function.
		return true;
	},
	
	validateForm: function (formNode) {
		// summary: Validate that the form values are correct.
		// description: This function examines the specified form element to determine if the service
		//		is ready to be invoked. The function will be called from the invoke function before
		//		the service is invoked. The default implementation of this function returns "true".
		//		This function may be replaced by passing in a new version with the initProperties object
		//		when the service is constructed.
		// returns: "true" if the form values are valid
		// parameters: Element
		//		The form element that has the id specified by the "formId" property.
		return true;
	},

	successTest: function (serviceResponse) {
		// summary: Test for a successful service invocation.
		// description: This function examines the specified service response object to determine
		//		if the service was successful or not. The function will be called after the response
		//		was received from the service. The default implementation will return true if there
		//		is no "errorMessage" property in the service response object. This function may be
		//		replaced by passing in a new version with the initProperties object when the service
		//		is constructed.
		// returns: "true" if the service request is successful
		// serviceResponse: Object
		//		The service response object. This object is the JSON Object returned by the service
		//		invocation.
		return !serviceResponse.errorMessage && !serviceResponse.errorMessageKey;
	},

	successHandler: function (serviceResponse) {
		// summary: Perform processing after a successful service invocation.
		// description: This function will be called after a successful service invocation to allow
		//		for any post service processing. The default implementation does nothing. This function
		//		may be replaced by passing in a new version with the initProperties Object when the
		//		service is constructed.
		// serviceResponse: Object
		//		The service response object. This object is the JSON Object returned by the service
		//		invocation.
	},

	failureHandler: function (serviceResponse) {
		// summary: Perform processing after a failed service invocation.
		// description: This function will be called after a failed service invocation to handle
		//		any error processing. The default implementation alerts the user with the error
		//		message found in the service response object. This function may be replaced by passing
		//		in a new version with the initProperties Object when the service is constructed.
		// serviceResponse: Object
		//		The service response object. This object is the JSON Object returned by the service
		//		invocation.
		var message = serviceResponse.errorMessage;
		if (message) {
			alert(message);
		}
		else {
			message = serviceResponse.errorMessageKey;
			if (message) {
				alert(message);
			}
			else {
				alert("Service request error.");
			}
		}
	},
	
	invoke: function (parameters) {
		// summary: Invoke the service.
		// description: This function will asynchronously invoke the configured service URL with
		//		the specified parameters. If this service was configured with a form ID, then the
		//		form values will be posted to the URL. When the service completes successfully,
		//		a "modelChanged" event will be published to any listeners. If the "actionId" property
		//		has been configured, then an event with the topic name "modelChanged/actionId" will also
		//		be published.
		function getIds(idType) {
			var myId = "";
			if (parameters && parameters[idType]) {
				myId = parameters[idType];
			}
			if (myId == "" && formNode != null && formNode[idType]) {
				myId = formNode[idType];
				if (formNode[idType].value != null) {
					myId = formNode[idType].value;
				}
			}
			if (myId == "" && this.url) {
				var temp = this.url;
				if (temp.indexOf(idType) != -1) {
					temp = temp.substring(temp.indexOf(idType));
					var tokens = temp.split("&");
					var tokens2 = tokens[0].split("=");
					myId = tokens2[1];
				}
			}
			return myId;
		}

		var valid = true;
		
		var formNode = null;
		if(this.formId) {
			formNode = document.getElementById(this.formId);
		}
		
		if (formNode) {
			valid = this.validateForm(formNode);
		}
		if (valid) {
			valid = this.validateParameters(parameters);
		}
		if (parameters) {
			if (!parameters.requesttype) {
				parameters.requesttype = 'ajax';
			}
		} else {
			parameters = [];
			parameters.requesttype = 'ajax';
		}
		
		console.debug("service formId = " + this.formId);
		if (valid) {
			dojo.xhrPost({
				url: this.url,				
				handleAs: "json-comment-filtered",
				form: formNode,
				content: parameters,
				service: this,
				load: function(serviceResponse, ioArgs) {
					var service = ioArgs.args.service;
					serviceResponse.serviceId = service.id;
					serviceResponse.actionId = service.actionId;
					console.debug("Service response action id : " + serviceResponse.actionId );
					for (var prop in serviceResponse) {
						console.debug("  " + prop + "=" + serviceResponse[prop]);
					}
					if (service.successTest(serviceResponse)) {
						service.successHandler(serviceResponse);

						console.debug("success: publishing modelChanged event")
						dojo.publish("modelChanged", [serviceResponse]);
												
						if (service.actionId) {
							console.debug("success: publishing modelChanged/" + service.actionId + " event");
							dojo.publish("modelChanged/" + service.actionId, [serviceResponse]);
						}
					}
					else {
						// determine storeId, catalogId and langId to use in our redirect url
						var storeId = getIds("storeId");
						var catalogId = getIds("catalogId");
						var langId = getIds("langId");
						
						console.debug('error condition encountered - error code: ' + serviceResponse.errorCode);

						// error code: ERR_USER_NOT_LOGGED_ON
						// This error code is returned in the scenario where logon is required and user is not logged on
						if (serviceResponse.errorCode == '2500') {
							var myURL = serviceResponse.originatingCommand;
							myURL = myURL.replace('?', '%3F');
							myURL = myURL.replace(/&/g, '%26');
							myURL = myURL.replace(/=/g, '%3D');

							myURL = 'LogonForm?nextUrl=' + myURL + "&storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId;
							console.debug('error type: ERR_USER_NOT_LOGGED_ON - only registered user can invoke the command');
							console.debug('redirecting to URL: ' + myURL);	
							document.location.href = myURL;

						// error code: ERR_PASSWORD_REREQUEST 
						// This error code is returned in the scenario where password is required to proceed
						} else if (serviceResponse.errorCode == '2530') {
							var myURL = serviceResponse.originatingCommand;
							myURL = myURL.replace('?', '%3F');
							myURL = myURL.replace(/&/g, '%26');
							myURL = myURL.replace(/=/g, '%3D');
							myURL = 'PasswordReEnterErrorView?nextUrl=' + myURL + "&storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId;
							console.debug('error type: ERR_PASSWORD_REREQUEST - password is required');
							console.debug('redirecting to URL: ' + myURL);	
							document.location.href = myURL;

						// error code: ERR_SESSION_TIMEOUT
						// This error code is returned in the scenario where user's logon session has timed out
						} else if (serviceResponse.errorCode == '2510') {
							//redirect to a full page for sign in
							console.debug('error type: ERR_SESSION_TIMEOUT - use session has timed out');
							console.debug('redirecting to URL: ' + 'Logoff?URL=ReLogonFormView&storeId=' + storeId);
							document.location.href = 'Logoff?URL=ReLogonFormView&storeId=' + storeId;	
							
						// error code: ERR_PROHIBITED_CHAR
						// This error code is returned in the scenario where user has entered prohibited character(s) in the request
						} else if (serviceResponse.errorCode == '2520') {
							console.debug('error type: ERR_PROHIBITED_CHAR - detected prohibited characters in request');
							console.debug("redirecting to URL: " + "ProhibitedCharacterErrorView?storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId);	
							document.location.href = "ProhibitedCharacterErrorView?storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId;
						
						// error code: ERR_CSRF
						// This error code is returned in the scenario where a cross-site request forgery attempt was caught
						} else if (serviceResponse.errorCode == '2540') {
							console.debug('error type: ERR_CSRF - cross site request forgery attempt was detected');
							console.debug("redirecting to URL: " + "CrossSiteRequestForgeryErrorView?storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId);
							document.location.href = "CrossSiteRequestForgeryErrorView?storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId;
							
						} else {
							console.debug('calling service.failureHandler');
							service.failureHandler(serviceResponse);
						}
					}
				},
				error: function(errObj,ioArgs) {
					var messages = dojo.i18n.getLocalization("wc", "common");
					console.debug("Warning: communication error while making the service call"); // Communication error.
				}
			});
		}
	}
});

}

//*******************************************************************
// wc/render/Context.js
if(!dojo._hasResource["wc.render.Context"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["wc.render.Context"] = true;

dojo.provide("wc.render.Context");

//wd dojo.require("dojo.i18n.common");
dojo.requireLocalization("wc", "common", null, "ROOT,en,en-us");

wc.render.contexts = {
	// summary: Map of all declared render context objects.
	// description: The wc.render.context object stores all of the currently declared
	//		render context objects. The render context ID is the property name.
};

wc.render.getContextById = function (id) {
	// summary: Get the declared render context with the specified ID.
	// description: Get the render context that was declared under the specified
	//		identifier. If the render context was not declared then this function
	//		will return "undefined".
	// returns: The render context with the specified ID.
	return wc.render.contexts[id];
};

wc.render.updateContext = function (id, updates) {
	// summary: Update the specified render context with the specified updates.
	// description: This function retrieves the render context with the specified ID
	//		and applies the updates found in the specified "updates" object.
	// id: String
	//		The render context ID.
	// updates: Object
	//		The render context updates. The properties of the object are added to
	//		the render context. Properties that have an undefined value are removed
	//		from the render context.
	console.debug("wc.render.updateContext: " + id);
	wc.render.getContextById(id).update(updates);
};

wc.render.declareContext = function (id, properties, updateContextURL) {
	// summary: Declare a new render context with the specified ID.
	// description: This function declares a new render context and initializes it
	//		with the specified render context properties. The update context URL
	//		is used to report changes to the render context to the server.
	// returns: The new render context.
	// id: String
	//		The unique ID of the new render context.
	// properties: Object
	//		The initial render context properties.
	// updateContextURL:
	//		The URL that is used to notify the server of render context updates.
	if(this.contexts[id] != null && this.contexts[id] != ""){
		console.debug("Render context with id =  " + id + " already exits.Please use a different id");		
		return;
	}
	var context = new wc.render.Context(id, properties, updateContextURL);
	this.contexts[id] = context;
	return context;
};

dojo.declare("wc.render.Context", null, {
	// summary: Render context class.
	// description: This class manages the render context. Changes to the render context
	//		will be communicated to the server using an Ajax style request. Once the render
	//		context is successfully updated, an event will be published so that any listening
	//		widgets can refresh their content to reflect the updated render context.
	// id: String
	//		The ID of the render context.
	// properties: Object
	//		The current render context properties.
	// url: String
	//		The URL that is used to notify the server of changes to the render context.
	// contextChangedEventName: String
	//		The topic name of the event that is published when the render context is successfully
	//		updated. The topic name is the render context ID appended with "/RenderContextChanged".
	constructor: function (id, properties, updateRenderContextURL) {
		// summary: Render context initializer.
		// description: Initializes a new render context object.
		// id: String
		//		The ID of the new render context.
		// properties: Object
		//		The initial render context properties.
		// udateRenderContextURL: String
		//		The URL used to notify the server of render context updates.	
		this.id = id;
		this.properties = properties ? properties : {};
		this.url = updateRenderContextURL;
		this.contextChangedEventName = id + "/RenderContextChanged";
	},

	id: undefined,
	properties: undefined,
	url: undefined,
	contextChangedEventName: undefined,

	update: function (updates) {
		// summary: Render context update function.
		// description: This function will update the render context with the specified properties.
		//		Only properties specified in the "updates" object will be updated.
		//		If an update render context URL was declared for this render context, then the server is notified
		//		of the updates through an Ajax style request. When the update is complete, an event is published
		//		to notify listeners of the render context change.
		// updates: Object
		//		Render context updates.
		if (!this.properties) {
			this.properties = {};
		}
		if (this.url) {
			console.debug("wc.render.updateContext - url : " + this.url);
			var content = {};
			for (var name in updates) {
				var value = updates[name];
				if (typeof value == "undefined") {
					if (typeof content.clear == "undefined") {
						content.clear = [name];
					}
					else {
						content.clear.push(name);
					}
				}
				else {
					content["set_" + name] = value;
				}
			}

			dojo.xhrPost({
				url: this.url,
				mimetype: "text/json",
				handleAs: "json",
				content: content,
				properties: this.properties,
				successEventName: this.contextChangedEventName,
				load: function(data) {
					if (dojo.isArray(data.renderContextChanges)) {
						for (var i = 0; i < data.renderContextChanges.length; i++) {
							var name = data.renderContextChanges[i];
							console.debug("updating render context: " + request.properties[name] + " = " + data[name]);
							request.properties[name] = data[name];
						}
					}
					console.debug("publishing " + this.successEventName + " event");
					dojo.publish(this.successEventName, [data]);
				},
				error: function(response,ioArgs) {
					// handle error here - need to post an event to tell event listeners
					// that the render update didn't happen and the UI needs to resynch
					// with the local copy of the render context
					var messages = dojo.i18n.getLocalization("wc", "common");
					console.debug("Warning: communication error while updating the context values"); // Communication error.
				}
			});
		}
		else {
			console.debug("wc.render.updateContext - url not specified");
			
			// local render context
			var data = {
				renderContextChanges: []
			};
			for (var name in updates) {
				var value = updates[name];
				if (value != this.properties[name]) {
					data.renderContextChanges.push(name);
					//wd-todo
					if (typeof value == "undefined")  {
						delete this.properties[name];
					}
					else {
						this.properties[name] = value;
						data[name] = value;
						console.debug("updating render context: " + name + " = " + value);
					}
				}
			}
			console.debug("publishing " + this.contextChangedEventName + " event")
			dojo.publish(this.contextChangedEventName, [data]);
		}
	}
});

}

//*******************************************************************
// wc/render/RefreshController.js
if(!dojo._hasResource["wc.render.RefreshController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["wc.render.RefreshController"] = true;

dojo.provide("wc.render.RefreshController");

dojo.requireLocalization("wc", "common", null, "ROOT,en,en-us");
dojo.require("dojo.parser");

wc.render.refreshControllers = {
	// summary: Map of all declared refresh controllers.
	// description: The wc.render.refreshControllers object stores all of the currently declared
	//		refresh controllers. The refresh controller ID is the property name.
};

wc.render.getRefreshControllerById = function (id) {
	// summary: Get the declared refresh controller with the specified ID.
	// description: Get the refresh controller that was declared under the specified
	//		identifier. If the refresh controller was not declared then this function
	//		will return "undefined".
	// returns: The refresh controller with the specified ID.
	return wc.render.refreshControllers[id];
};

wc.render.declareRefreshController = function (initProperties) {
	// summary: Declare a new refresh controller with the specified ID.
	// description: This function declares a new refresh controller and initializes it
	//		with the specified initialization properties. The initialization properties
	//		are "mixed in" with the new refresh controller's properties.
	// returns: The new refresh controller.
	// initProperties: Object
	//		The initialization properties.
	var refreshController = new wc.render.RefreshController(initProperties);
	this.refreshControllers[initProperties.id] = refreshController;
	return refreshController;
};

dojo.declare("wc.render.RefreshController", null, {
	// summary: Refresh controller class.
	// description: This class controls refresh area widgets. It listens to changes
	//		in the render context and changes to the model and decides if the registered
	//		refresh areas should be updated.
	// id: String
	//		The ID of the refresh area.
	// renderContext: wc.render.Context
	//		The render context object that is associated with this refresh controller.
	// url: String
	//		The URL that is called by the "refresh" function to retrieve the refreshed content
	//		for the refresh area widget.
	// mimetype: String
	//		The MIME type used with the refresh URL. The default value is "text/html". Use "text/json"
	//		to indicate that the refresh content is JSON.
	// renderContextChangedHandler: Function
	//		If defined, this function will be called when a render context changed event is detected. It
	//		will be called once for each refresh area widget registered to this refresh controller.
	//		The function signature is (message, widget) where "message" is the render context changed
	//		event message and widget is the refresah area widget.
	// modelChangedHandler: Function
	//		If defined, this function will be called when a "modelChanged" event is detected. It will be called
	//		once for each refresh area widget registered to this refresh controller. The function signature is
	//		(message, widget) where "message" is the model changed event message and widget is the refresh area
	//		widget.
	// postRefreshHandler: Function
	//		If defined, this function will be called after a successful refresh. The function signature is
	//		(widget) where "widget" is the refresh area widget that was just refreshed.
	// currentRCProperties: Object
	//		The current render context properties. This object is used to determine what properties have
	//		actually changed since the last time a render context changed event was detected.
	// widgets: Object
	//		The Map of registered refresh area widgets. The property names are the widget identifiers.
	constructor: function (initProperties) {
		// summary: Refresh controller initializer.
		// description: Initializes the new refresh controller.
		// initProperties: Object
		//		Initialization properties. The properties are mixed in with the new refresh controller's
		//		properties.
		
		dojo.mixin(this, initProperties);
		this.syncRCProperties();
		if (dojo.isFunction(this.renderContextChangedHandler)) {		
			dojo.subscribe(this.renderContext.contextChangedEventName, this, "renderContextChanged");
		}
		if (dojo.isFunction(this.modelChangedHandler)) {
			dojo.subscribe("modelChanged", this, "modelChanged");
		}
		this.widgets = {};
	},

	id: undefined,
	renderContext: undefined,
	url: undefined,
	mimetype: "text/html",
	renderContextChangedHandler: undefined,
	modelChangedHandler: undefined,
	postRefreshHandler: undefined,
	currentRCProperties: undefined,
	widgets: undefined,
	formId: undefined,

	addWidget: function(widget) {
		// summary: Register the specified refresh area widget.
		// description: This function registers the specified refresh area widget to this refresh controller.
		//		This function is normally called by the "initialize" function of "wc.widget.RefreshArea".
		// widget: wc.widget.RefreshArea
		//		The refresh area widget that is to be controlled by this refresh controller.
		if (this.widgets[widget.id]) {
			console.debug("RefreshController.addWidget: duplicate widget ID " + widget.id);
		}
		this.widgets[widget.id] = widget;
		console.debug("REFRESH CONTROLLER "+ this.id +" ADDED THIS WIDGET..."+widget+" with id = "+widget.id);
	},
	
	removeWidget: function(widget) {
		// summary: Deregister the specified refresh area widget.
		// description: This function deregisters the specified refresh area widget from this refresh controller.
		//		This function is normally called by the "destroy" function of "wc.widget.RefreshArea".
		// widget: wc.widget.RefreshArea
		//		The refresh area widget that is no longer to be controlled by this refresh controller.
		
		if(typeof this.widgets == "undefined"){
			console.debug("this.widgets in RefreshController#removeWidget(widget) is not defined yet. No deletion is needed");
			return;	
		}
		delete this.widgets[widget.id];
	},

	syncRCProperties: function() {
		// summary: Synchronize the local copy of the render context properties with the current
		//		render context properties.
		// description: This function will synchronize the local copy of the render context properties
		//		with the render context properties of the wc.render.Context object associated with this
		//		refresh controller.
		if (this.renderContext) {
			var properties = {};
			var rc = this.renderContext.properties
			for (var prop in rc) {
				properties[prop] = rc[prop];
			}
			this.currentRCProperties = properties;
		}	
	},

	renderContextChanged: function(message) {
		// summary: Render context changed listener.
		// description: This function is called when a render context changed event occurs.
		//		It calls "renderContextChangedHandler" for each of the registered refresh area
		//		widgets and then it calls "syncRCProperties" to synchronize the local copy of the
		//		render context properties.
		// message: Object
		//		Render context changed event message.
		for (var widgetId in this.widgets) {
			console.debug("Call renderContext changed handler for the widget..."+this.widgets[widgetId]);
			this.renderContextChangedHandler(message, this.widgets[widgetId]);
		}
		this.syncRCProperties();
	},
	
	modelChanged: function(message) {
		// summary: Model changed listener.
		// description: This function is called when a model changed event occurs.
		//		It calls "modelChangedHandler" for each of the registered refresh area
		//		widgets.
		// message: Object
		//		Model changed event message.
		for (var widgetId in this.widgets) {
			this.modelChangedHandler(message, this.widgets[widgetId]);
		}
	},
	
	refreshHandler: function(widget, data) {	
		// summary: Default refresh handler.
		// description: This function is after a refresh request has been sent and
		//		the refresh content has been retrieved from the server. The default
		//		implementation is to call "widget.setInnerHTML" and pass it the data
		//		retrieved from the server. This function implementation should be replace
		//		if this is not the desired refresh behaviour.
		// widget: wc.widget.RefreshArea
		//		The refresh area widget.
		// data: String
		//		The data returned from the server. For the default implementation, this
		//		value is expected to be the HTML String.
		widget.setInnerHTML(data);			
	},

	refresh: function (widget, parameters) {
		// summary: Invoke an Ajax style request to refresh the specified widget.
		// description: This function invokes an Ajax style request to the refresh controller's
		//		refresh URL with the specified parameters. This function is normally called by
		//		the "refresh" function of "wc.widget.RefreshArea".
		// widget: wc.widget.RefreshArea
		//		The refresh area widget being refreshed.
		// parameters: Object
		//		The name/value pair parameters that are to be sent with this URL.
		
		var formNode = null;
		if(this.formId) {
			formNode = document.getElementById(this.formId);
		}
		
		if (parameters) {
			if (!parameters.requesttype) {
				parameters.requesttype = 'ajax';
			}
		} else {
			parameters = [];
			parameters.requesttype = 'ajax';
		}
		
		dojo.xhrPost({
			url: this.url,
			mimetype: this.mimetype,
			form: formNode,
			content: parameters,
			load: function(data) {
				function getIds(idType) {
					var myId = "";
					if (parameters && parameters[idType]) {
						myId = parameters[idType];
					}
					if (myId == "" && formNode != null && formNode[idType]) {
						myId = formNode[idType];
						if (formNode[idType].value != null) {
							myId = formNode[idType].value;
						}
					}
					if (myId == "" && this.url) {
						var temp = this.url;
						if (temp.indexOf(idType) != -1) {
							temp = temp.substring(temp.indexOf(idType));
							var tokens = temp.split("&");
							var tokens2 = tokens[0].split("=");
							myId = tokens2[1];
						}
					}
					return myId;
				}

				// determine storeId, catalogId and langId to use in our redirect url
				var storeId = getIds("storeId");
				var catalogId = getIds("catalogId");
				var langId = getIds("langId");

				var errorCodeBegin = data.indexOf('errorCode');
				if (errorCodeBegin != -1) {
					// get error code   
					var errorCodeEnd = data.indexOf(',', errorCodeBegin);
					var errorCodeString = data.substring(errorCodeBegin, errorCodeEnd);

					console.debug('error condition encountered - error code: ' + errorCodeString);
					// error code: ERR_SESSION_TIMEOUT
					// This error code is returned in the scenario where user's logon session has timed out
					if (errorCodeString.indexOf('2510') != -1) {
						//redirect to a full page for sign in
						console.debug('error type: ERR_SESSION_TIMEOUT - use session has timed out');
						console.debug('redirecting to URL: ' + 'Logoff?URL=ReLogonFormView&storeId='+storeId);	
						document.location.href = 'Logoff?URL=ReLogonFormView&storeId='+storeId;	

					// error code: ERR_PROHIBITED_CHAR
					// This error code is returned in the scenario where user has entered prohibited character(s) in the request
					} else if (errorCodeString.indexOf('2520') != -1) {
						console.debug('error type: ERR_PROHIBITED_CHAR - detected prohibited characters in request');
						console.debug("redirecting to URL: " + "ProhibitedCharacterErrorView?storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId);	
						document.location.href = "ProhibitedCharacterErrorView?storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId;
					
					// error code: ERR_CSRF
					// This error code is returned in the scenario where a cross-site request forgery attempt was caught
					} else if (serviceResponse.errorCode == '2540') {
						console.debug('error type: ERR_CSRF - cross site request forgery attempt was detected');
						console.debug("redirecting to URL: " + "CrossSiteRequestForgeryErrorView?storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId);
						document.location.href = "CrossSiteRequestForgeryErrorView?storeId=" + storeId + "&catalogId=" + catalogId + "&langId=" + langId;
					}

				} else {
					var controller = widget.controller;
					console.debug("RefreshController.refresh - calling refreshHandler for " + widget);
					controller.refreshHandler(widget, data);
					if (controller.postRefreshHandler != null) {
						console.debug("RefreshController.refresh - calling postRefreshHandler for " + widget);
						controller.postRefreshHandler(widget);
					}
				}
			},
			error: function(error) {
				// handle error here - what do you do when a refresh doesn't happen?
				var messages = dojo.i18n.getLocalization("wc", "common");
				console.debug("Warning: communication error while updating the refresh area"); // Communication error.
			}
		});
	},

	testForChangedRC: function(propertyNames) {
		// summary: Test for changes to the render context.
		// description: This function looks through the specified array of property names for
		//		a render context property that has changed since the last time a render context
		//		changed event was detected.
		// returns: true if any of the specified render context properties have changed
		// propertyNames: Array
		//		An array of property names to be tested.
		var change = false;
		for (var i = 0; i < propertyNames.length; i++) {
			var prop = propertyNames[i];
			if (this.currentRCProperties[prop] != this.renderContext.properties[prop]) {
				change = true;
				break;
			}
		}
		return change;
	}
});

}

//*******************************************************************
// wc/render/common.js

dojo.provide("wc.render.common");

dojo.require("wc.render.Context");
dojo.require("wc.render.RefreshController");

//*******************************************************************
// wc/widget/WCMenu.js

/** 
 * @fileOverview This javascript is used by CachedHeaderDisplay.jsp to display the header menu of the Madisons starter store.
 * @version 1.0
 */

if(!dojo._hasResource["wc.widget.WCMenu"]){ /*_hasResource checks added by build. Do not use _hasResource directly in your code. */
dojo._hasResource["wc.widget.WCMenu"] = true;

/* Import dojo classes. */
dojo.provide("wc.widget.WCMenu");  

dojo.require("dijit._Widget");
dojo.require("dijit._Container");
dojo.require("dijit._Templated");

/**
 *	The functions defined in this class enables initialisation of event listeners for WCMenu. Another set of functions act as callback when a key press or mouse click event occurs.  
 *  @class The WCMenu widget displays a menu with up to three columns of menu items, where the number of menu items per column is configurable. It supports a 'Show All' menu item which links to a configurable URL. 
 *  The WCMenu is primarily used for the header menu of the Madisons starter store, displaying a list of subcategories under each of the top categories. 
 *  The WCMenu contains menu items of type dijit.MenuItem, and supports containing extensions to the base dijit.MenuItem class.
 */
dojo.declare(
	"wc.widget.WCMenu", dijit.Menu,
{
		/* The text to display on the Show All menu item */
		showAllText: '',
		
		
		/* The fully resolved URL to be loaded when the Show All menu item is clicked. */
		showAllURL: '',
		
		/**
		 *  The maximum number of menu items to show in a column. If more items exist than can fit into one column, a second column becomes visible, and then a third. 
		 *  If all three columns are full, the remaining categories are not displayed. This value must be greater then 0. The default value is 6.
		 */
		maxItemsPerColumn: 6,
		
		/**
		 *  The 'Show All' link is displayed when this value is true, even if all the categories fit into the menu. Otherwise, the 'Show All' link is only displayed 
		 *  when there are too many categories to display in the menu. The default value is false.
		 */
		forceDisplayShowAll: false,
		
		/* Dojo attribute to tell parser to parse internal menu item widget for the show all menu item. */
		widgetsInTemplate : true,
		
		/**
		 *  When the bidi value is true, the menu acts as if a bi-directional language is being used in the store. While the layout of the menu is controlled by a CSS when a bidi language is used, 
		 *  the bidi parameter indicates to WCMenu to perform behavioral adjustments such as remapping the arrow keys to move the focus to the correct columns when clicked. The default value is false.
		 */
		bidi: false, 
		
		/* The HTML that is generated when this widget is parsed. */
		templateString:
				'<div dojoAttachPoint="mainNode" waiRole="menu" >'+
					'<table dojoAttachPoint="mainTable" cellpadding="0" cellspacing="0">'+
						'<tr class="wcmenu_menuItemsPosition">'+
							'<td>'+
								'<div class="wcmenu_columnPosition wcmenu_columnBorder wcmenu_columnPadding" dojoAttachPoint="containerNodeDiv" >'+
									'<table dojoAttachPoint="containerNodeTable" cellpadding="0" cellspacing="0" dojoAttachEvent="onkeypress:_onKeyPress">'+
										'<tbody dojoAttachPoint="containerNode" ></tbody>'+
									'</table>'+
								'</div>'+
							'</td>'+
							'<td >'+
								'<div dojoAttachPoint="containerNode2Div" class="wcmenu_columnPosition wcmenu_columnBorder wcmenu_columnPadding">'+
									'<table  dojoAttachPoint="containerNode2Table" cellpadding="0" cellspacing="0" dojoAttachEvent="onkeypress:_onKeyPress">'+
										'<tbody dojoAttachPoint="containerNode2" ></tbody>'+
									'</table>'+
								'</div>'+
							'</td>'+
							'<td>'+
								'<div dojoAttachPoint="containerNode3Div" class="wcmenu_columnPosition wcmenu_lastColumnBorder wcmenu_columnPadding" >'+
									'<table  dojoAttachPoint="containerNode3Table" cellpadding="0" cellspacing="0" dojoAttachEvent="onkeypress:_onKeyPress">'+
										'<tbody dojoAttachPoint="containerNode3"></tbody>'+
									'</table>'+
								'</div>'+
							'</td>'+
						'</tr>'+
						'<tr>'+
							'<td colspan="3">'+
								'<div dojoAttachPoint="showAllMainNode" class="wcmenu_showAllBorder wcmenu_showAllPadding">'+
									'<table cellpadding="0" cellspacing="0" class="wcmenu_showAllSize" dojoAttachEvent="onkeypress:_onKeyPress">'+
										'<tbody dojoAttachPoint="showAllContainer">'+
											'<div dojoType="dijit.MenuItem" dojoAttachPoint="showAllNode" dojoAttachEvent="onClick:showAll">'+
												'<span>'+
													'<a href="" dojoAttachPoint="menuItemLabel"></a>'+
												'</span >'+
											'</div>'+
										'</tbody>'+
									'</table>'+
								'</div>'+
							'</td>'+
						'</tr>'+
					'</table>'+
				'</div>"',
		
		
		/* An array of menu items in this menu. */
		children: [],
		
		/**
		 *  This function is called when a menu item needs to be focused.
		 */
		focus: function() {
				/*
					In the event that the text in the header of the menu (or the show all node) is wider then the sum of the
					widths of the columns of the menu then the difference in the widths will be divided
					amongst the columns thereby enlarging their widths to fill the remaining space so that the menu
					is at least as wide as the wider of the header or the show all node.
				  */
				  
				if (this.children.length > 0) {
					this.children[0].focus();
					this.focusedChild = this.children[0];
				}
				
					if (this.bidi && dojo.isMozilla && dojo.isFF < 3)
					{
						dojo.removeClass(this.containerNodeDiv, "wcmenu_columnPosition");
						dojo.removeClass(this.containerNode2Div, "wcmenu_columnPosition");
						dojo.removeClass(this.containerNode3Div, "wcmenu_columnPosition");	
					}
				
				if (this["columnsAdjusted"] == undefined || this["columnsAdjusted"] == false) {
					
					var nodeWidth = dojo.style(this.mainNode, "width");
					dojo.style(this.mainTable, "width", nodeWidth + "px");
					
					/* Only perform this function once for each menu. */
					this.columnsAdjusted = true;
					
					/**
					 * Find the width of the menu. The dojo widget that this menu inherits from sets the width of the menu
					 * to be at least that of the header text.
					 */
					var parentWidth = dojo.contentBox(this.mainNode).w;
					
					/* Find the width of the show all node. */
					var showAllNodeWidth = dojo.contentBox(this.showAllMainNode).w;
					showAllNodeWidth = (!this.showAllHidden())?showAllNodeWidth:0;
					
					
					/* Find the number of columns in the menu. */
					var numcolumns = (this.children.length > 2*this.maxItemsPerColumn)?3:(this.children.length > this.maxItemsPerColumn?2:1);
				
					/* Find the width of each column. */
					var column1Width = dojo.marginBox(this.containerNodeDiv).w;
					var column2Width = (numcolumns > 1)?dojo.marginBox(this.containerNode2Div).w:0;
					var column3Width = (numcolumns > 2)?dojo.marginBox(this.containerNode3Div).w:0; 
				
					/* Find the total width of all the columns. */ 
					var columnWidthSum = column1Width + column2Width + column3Width;
					
					
					/* Determine the difference between the sum of the width of the columns and the width of either the menu or the show all node. */
					var diff = (parentWidth > showAllNodeWidth)?(parentWidth - columnWidthSum):(showAllNodeWidth - columnWidthSum);
					
					
					/* If there is a difference then adjust the width of the columns. */
					if (diff != 0)
					{
						/* Determine the width to add to each column. */
						diff = Math.floor(diff/numcolumns);
						
						if (numcolumns == 1)
						{				
							var col1LeftMargin = isNaN(this.mainTable.style.marginLeft.split("px")[0])?0:this.mainTable.style.marginLeft.split("px")[0];
							var col1RightMargin = isNaN(this.mainTable.style.marginRight.split("px")[0])?0:this.mainTable.style.marginRight.split("px")[0];
							/* Modify the width of the main table containing all the columns, there is only one column in this case. */
							dojo.style(this.mainTable, "width", parentWidth - col1LeftMargin - col1RightMargin + "px");
						}
						
						if (numcolumns > 1)
						{
							/* Set the width of the table containing the columns to the width of the menu. */
							dojo.style(this.mainTable, "width", parentWidth + "px");
							
							/* Find the side of the borders around the columns. */
							var borderRightWidth = parseInt(this.getComputedStyle(this.containerNodeDiv, "borderRightWidth", "px"));
							var borderLeftWidth = parseInt(this.getComputedStyle(this.containerNodeDiv, "borderLeftWidth", "px"));
							borderRightWidth = (isNaN(borderRightWidth))?0:borderRightWidth;
							borderLeftWidth = (isNaN(borderLeftWidth))?0:borderLeftWidth;
							var borderRightWidth2 = parseInt(this.getComputedStyle(this.containerNode2Div, "borderRightWidth", "px"));
							var borderLeftWidth2 = parseInt(this.getComputedStyle(this.containerNode2Div, "borderLeftWidth", "px"));
							borderRightWidth2 = (isNaN(borderRightWidth2))?0:borderRightWidth2;
							borderLeftWidth2 = (isNaN(borderLeftWidth2))?0:borderLeftWidth2;
							
							/* Modify the width of the first column. */							
							dojo.style(this.containerNodeDiv, "width",  dojo.contentBox(this.containerNodeDiv).w + diff - borderRightWidth -  borderLeftWidth + 1 +  "px");
							
							/* Modify the width of the second column. */
							dojo.style(this.containerNode2Div, "width", dojo.contentBox(this.containerNode2Div).w + diff  - borderRightWidth2 -  borderLeftWidth2 + (numcolumns - 1) + "px");
							
							dojo.style(this.containerNodeTable, "width", "100%");
							dojo.style(this.containerNode2Table, "width", "100%");
						}
					
						if (numcolumns > 2)
						{
							/* Find the side of the borders around the columns. */
							var borderRightWidth2 = parseInt(this.getComputedStyle(this.containerNode2Div, "borderRightWidth", "px"));
							var borderLeftWidth2 = parseInt(this.getComputedStyle(this.containerNode2Div, "borderLeftWidth", "px"));
							borderRightWidth2 = (isNaN(borderRightWidth2))?0:borderRightWidth2;
							borderLeftWidth2 = (isNaN(borderLeftWidth2))?0:borderLeftWidth2;
						
							/* Modify the width of the third column. */
							dojo.style(this.containerNode3Div, "width", dojo.contentBox(this.containerNode3Div).w + diff + ((borderRightWidth2 > 1)?borderRightWidth2:0) + "px");
							dojo.style(this.containerNode3Table, "width", "100%");
						}
					}
				
				}
			},
			
		/**
		 * This function will retrieve the value of a computed style property from a computed style object.
		 * @param {object} The object in which to retrieve the style from.
		 * @param {string} styleItem The style property to retrieve.
		 * @param {string} splitchar A character to be removed from the result such as "px" or "em".
		 * 
		 * @returns: Either 0 or a value representing the style object's desired property.
		 */
		getComputedStyle: function(object, styleItem, splitchar) {
			
			var styleObject = dojo.getComputedStyle(object)[styleItem];
			
			var styleResult = styleObject.split(splitchar)[0];
			
			if (styleResult == undefined || styleResult.length == 0)
			{
				return 0;
			}
			return styleResult;
		},
		
		/**
		 *  This function specifies the action to perform when 'Show All' link is clicked.
		 */
		showAll: function() {
			loadLink(this.showAllURL);
		},
		
		/** 
		 *  This function is called during initial load to setup display of the menu header.
		 */
		startup: function() {
		
			/* Move the children menu items around so that they appear in the correct columns of the new maximum three column menu table */
			
			var allChildren = this.getDescendants();
			
			/* Remove show all node from list. */ 
			this.children = allChildren.slice(0, allChildren.length - 1);
			if (this.children.length == 0) {
				this.mainNode.style.visibility = "hidden";
			}
			var thirdColumnCount = 0;
			
			if (this.maxItemsPerColumn < 1) {
				this.maxItemsPerColumn = 1;
			}
			
			if (this.children.length > this.maxItemsPerColumn) {
				/* Determine how many elements will go into the third column. */
				if (this.children.length > 2*this.maxItemsPerColumn)
				{
					thirdColumnCount = this.children.length - (2*this.maxItemsPerColumn);
				}
			
				/* Add elements to second column if overflowing the first column. */
				for (var i = this.maxItemsPerColumn; i < this.children.length - thirdColumnCount; i++)
				{
					this.containerNode2.appendChild(this.children[i].domNode);
				}
				
				/* Add elements to the third column if overflowing the second column. */
				for (var i = 2*this.maxItemsPerColumn; i < this.children.length; i++)
				{
					this.containerNode3.appendChild(this.children[i].domNode);
				}
				
				/* Remove elements from the table if overflowing the third column. */
				for (var i = 3*this.maxItemsPerColumn; i < this.children.length; i++)
				{
					this.containerNode3.removeChild(this.children[i].domNode);
				}
			}
			
			
			/* If there is only one column then hide the second column and reformat the first column to look appropriate. */
			if (this.children.length <= this.maxItemsPerColumn) {
				this.containerNode2Div.style.padding = "0px";
				this.containerNodeDiv.style.border = "0px";
				if (!this.bidi) {
					this.containerNodeDiv.style.width = "100%";
					this.containerNodeTable.style.width = "100%";
				}
				this.mainTable.style.margin = "0px 3px 0px 3px";
				this.containerNodeDiv.style.padding = "0px 0px 0px 0px";
				this.containerNodeDiv.style.margin = "0px 0px 0px 0px";
				if (!dojo.isSafari) {
					this.containerNodeTable.style.padding = "0px 2px 0px 0px";
				}
				else {
					this.containerNodeTable.style.padding = "0px 0px 0px 1px";
				}
				this.containerNodeTable.style.margin = "0px 1px 3px auto";
				this.showAllMainNode.style.padding = "4px 0px 0px 0px";
				this.containerNodeDiv.style.padding = "0px 1px 0px 1px";
			}
			
			/* If there is less then three columns then hide the third column. */
			if (this.children.length <= 2*this.maxItemsPerColumn)
			{
				this.containerNode3Div.style.padding = "0px";
				this.containerNode2Div.style.border = "0px";
			}
			
			
			/* Add the Show All node to the correct place in the table. */
			this.showAllContainer.appendChild(this.showAllNode.domNode);
			
			/* Set the Show All text. */
			this.menuItemLabel.innerHTML = this.showAllText;
			if (this.forceDisplayShowAll == false && this.children.length <= 3*this.maxItemsPerColumn)
			{
				this.hideShowAllItem();
			}
		}, 
		
		/**
		 * This function hides the 'Show All' menu item.
		 */
		hideShowAllItem: function()
		{
			
			this.showAllContainer.removeChild(this.showAllNode.domNode);
			this.showAllMainNode.style.padding = "0px";
			this.showAllMainNode.style.visibility = "hidden";
			this.mainNode.style.padding = "3px 0px 0px 0px";
		},
		
		/**
		 *  This function checks to see if the 'Show All' menu item is hidden or not.
		 */ 
		showAllHidden: function()
		{
			if (this.showAllMainNode.style.visibility == "hidden")
			{
				return true;
			}
			else
			{
				return false;
			}
		},
		
		/**
		 *  This function changes the currently focused menu item.
		 *  @param {string} newFocus A pointer to the focused child of the menu.
		 *  @param {string} newElement A pointer to the new menu item to put into focus.
		 *  @param {string} e The event that was sent to change the focus.
		 *  
		 *  @return true if the method completed successfully.
		 */
		changeFocus: function(newFocus,newElement,e)
		{	
			newFocus._blur();
			dojo.stopEvent(e);
			newElement.focus();
			newFocus = newElement;
			this.focusedChild = newElement;
			return true;
		},
		
		/**
		 *  This is a callback function when a key is pressed while a menu item is in focus.
		 *  @param {string} e The key press event
		 */
		_onKeyPress: function(e){
			
				var eventComplete = false;
				
				if (e.keyCode == dojo.keys.UP_ARROW)
				{
					/* If at the Show All node then either go to the last item in the first column or if there is only one
					column then go to the end of the list. */
					if (this.focusedChild == this.showAllNode && !eventComplete)
					{
						if (this.children.length <= this.maxItemsPerColumn)
						{
							eventComplete = this.changeFocus(this.focusedChild,this.children[this.children.length-1],e);
						}
						else
						{
							eventComplete = this.changeFocus(this.focusedChild,this.children[this.maxItemsPerColumn-1],e);
						}
					}	
					
					/* If at the start of the second column then go to the Show All node. */
					if (this.focusedChild == this.children[this.maxItemsPerColumn] && this.children.length > this.maxItemsPerColumn  && !eventComplete || (this.focusedChild == this.children[0] && this.children.length <= this.maxItemsPerColumn)  && !eventComplete)
					{
						if (this.showAllHidden() == false)
						{
							eventComplete = this.changeFocus(this.focusedChild,this.showAllNode,e);
						} 
						else
						{
							if (this.focusedChild == this.children[this.maxItemsPerColumn])
							{
								eventComplete = this.changeFocus(this.focusedChild,this.children[this.maxItemsPerColumn-1],e);
							}
						}
					}
					
					/* If at the end of a multi-column list then return to the start of the list. */
					if (this.focusedChild == this.children[0] && this.children.length > this.maxItemsPerColumn  && !eventComplete)
					{
						if (this.children.length > 3*this.maxItemsPerColumn)
						{
							eventComplete = this.changeFocus(this.focusedChild,this.children[3*this.maxItemsPerColumn - 1],e);
						}
						else
						{
							eventComplete = this.changeFocus(this.focusedChild,this.children[this.children.length-1],e);
						}
					}
				
					/* If at the start of the third column then go to the end of the second column. */
					if (this.focusedChild == this.children[((this.maxItemsPerColumn*2))]  && !eventComplete)
					{
						eventComplete = this.changeFocus(this.focusedChild,this.children[(this.maxItemsPerColumn*2)-1],e);
					}
				}
				
				if (e.keyCode == dojo.keys.RIGHT_ARROW)
				{
					
					if (this.bidi)
					{
						this.moveToNextColumn(e);
					}
					else
					{
						this.moveToPreviousColumn(e);
					}
				}
				
				if (e.keyCode == dojo.keys.LEFT_ARROW)
				{
					if (this.bidi)
					{
						this.moveToPreviousColumn(e);
					}
					else
					{
						this.moveToNextColumn(e);
					}				
				}
				
				
				if (e.keyCode == dojo.keys.DOWN_ARROW)
				{
					
					/* If at the end of a multi-column list then return to the start of the list */
					if (this.focusedChild == this.children[this.children.length - 1] && this.children.length > this.maxItemsPerColumn  && !eventComplete)
					{
						eventComplete = this.changeFocus(this.focusedChild,this.children[0],e);
					}
				
					/* If at the end of the second column then go to the start of the third column */
					if (this.focusedChild == this.children[((this.maxItemsPerColumn*2)-1)]  && !eventComplete)
					{
						eventComplete = this.changeFocus(this.focusedChild,this.children[this.maxItemsPerColumn*2],e);
					}
					
					/* If at the Show All node then either go to the next column or if there is only one
					column then go to the start of the list */
					if (this.focusedChild == this.showAllNode  && !eventComplete)
					{
						if (this.children.length <= this.maxItemsPerColumn)
						{
							eventComplete = this.changeFocus(this.focusedChild,this.children[0],e);
						}
						else
						{
							eventComplete = this.changeFocus(this.focusedChild,this.children[this.maxItemsPerColumn],e);
						}
						
					}	
					
					/* If at the end of the first column (either because the maxItemsPerColumn is reached or there are no more items) then go to the Show All node */
					if (this.focusedChild == this.children[this.maxItemsPerColumn-1]  && !eventComplete || (this.children.length <= this.maxItemsPerColumn && this.focusedChild == this.children[this.children.length-1])  && !eventComplete)
					{
						if (this.showAllHidden() == false)
						{
							eventComplete = this.changeFocus(this.focusedChild,this.showAllNode,e);
						}
						else
						{
							eventComplete = (this.focusedChild == this.children[this.children.length-1])?this.changeFocus(this.focusedChild,this.children[0],e):this.changeFocus(this.focusedChild,this.children[this.maxItemsPerColumn],e);
						}
					}	
				}
				
				
			},
		
			/**
			 *  This function changes the focus to the menu item in the same row but the previous column if available, 
			 *  or else wraps around to the last available column in the same row.
			 *  @param {string} e The key press event.
			 */
			moveToPreviousColumn: function(e) {
					
					var focusIndex = -1;
					
					if (this.focusedChild != this.showAllNode)
					{
						/* Find the index of the currently focused node in the array.  */
						for (var i = 0; i < this.children.length; i++)
						{
							if (this.focusedChild == this.children[i])
							{
								focusIndex =  i;
								break;
							}
						}
						
						var toManyToFit = false;
						if (this.children.length > 3*this.maxItemsPerColumn)
						{
							toManyToFit = true;
						}
						
						if ((this.children.length - 1 >= focusIndex + this.maxItemsPerColumn) && !toManyToFit || (toManyToFit && (3*this.maxItemsPerColumn > focusIndex + this.maxItemsPerColumn)))
						{	
							
							this.changeFocus(this.focusedChild,this.children[focusIndex + this.maxItemsPerColumn],e);
						}
						else
						{
							
							this.changeFocus(this.focusedChild,this.children[focusIndex % this.maxItemsPerColumn],e);
						}
					}
			},
			
			/**
			 *  This function changes the focus to the menu item in the same row but the next column if available, 
			 *  or else wraps around to the first available column in the same row.
			 *  @param {string} e The key press event.
			 */
			moveToNextColumn: function(e) {
					
					var focusIndex = -1;
					if (this.focusedChild != this.showAllNode)
					{
						/* Find the index of the currently focused node in the array. */
						for (var i = 0; i < this.children.length; i++)
						{
							if (this.focusedChild == this.children[i])
							{
								focusIndex =  i;
								break;
							}
						}
						
						if (focusIndex - this.maxItemsPerColumn >= 0)
						{	
							this.changeFocus(this.focusedChild,this.children[focusIndex - this.maxItemsPerColumn],e);
						}
						else
						{
							var row = focusIndex % this.maxItemsPerColumn + 1;
							
							var toManyToFit = false;
							if (this.children.length > 3*this.maxItemsPerColumn)
							{
								toManyToFit = true;
							}
							
							var rem = (!toManyToFit)?(this.children.length % this.maxItemsPerColumn):(0);
							var lastIndex = 0;
							var newIndex = 0;
							
							var toManyToFit = false;
							if (this.children.length > 3*this.maxItemsPerColumn)
							{
								toManyToFit = true;
							}
							
							if (row <= rem && rem > 0)
							{
								newIndex = (!toManyToFit)?(this.children.length - 1 - (rem - row)):(3*this.maxItemsPerColumn - this.maxItemsPerColumn - (rem - row));
							}
							else
							{
								newIndex = (!toManyToFit)?this.children.length - 1 - rem  - (this.maxItemsPerColumn - row):((3*this.maxItemsPerColumn - (3*this.maxItemsPerColumn%this.maxItemsPerColumn) - 1 - (this.maxItemsPerColumn - row)));
							}
							
							this.changeFocus(this.focusedChild,this.children[newIndex],e);
						}
					}	
	}	
}
);

}

//*******************************************************************
// wc/widget/WCDialog.js
if(!dojo._hasResource["wc.widget.WCDialog"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["wc.widget.WCDialog"] = true;
dojo.provide("wc.widget.WCDialog");

/** 
 * @fileOverview This javascript is used to display the mini shopping cart on the header.
 * @version 1.0
 */

/* Import dojo classes */
dojo.require("dijit.Dialog");

/**
 * The functions defined in this class enables initialisation of event listeners for WCDialog. Another set of functions act as callback when a key press or mouse click event occurs.   
 * @class The WCDialog widget is an extension of dijit.Dialog. The difference between WCDialog and dijit.Dialog Dialog is that, WCDialog disappears after a specified timeout period, 
 * also it positions itself around a specific node whereas dijit.Dialog displays in the middle of the page. 
 */

dojo.declare(
	"wc.widget.WCDialog",
	[dijit.Dialog], {
		
		/* Flag which indicates whether the dialog is to be closed after 'timeOut' milliseconds. The default value is true. */
		closeOnTimeOut:true,
		/* The amount of time, in milliseconds, the dialog remains open. The default value is 4500. */
		timeOut:4500,
		/* The element the dialog can be attached to. The default value is the mini shopping cart element. */
	    relatedSource:"",
		/* The x axis page coordinate where the dialog is displayed. */
		x:"",
		/* The y axis page coordinate where the dialog is displayed. */
		y:"",
		/* The current display status of the dialog. */
		displayStatus:false,
		
		/**
		 *  This function is used to display the dialog.This makes the dialog listen to onClick event and allow _onMouseClick function to handle it.
		 *  This hides the title bar, also starts the timer to close this dialog after timeOut milliseconds if closeOnTimeOut is set to true.
		 *  If closeOnTimeOut is set to false, we have to explicitly call hide function to close the dialog.
		 */

		show:function(event){
			if(this.displayStatus)return;
			if(this._showTimer){
				clearTimeout(this._showTimer);
				delete this._showTimer;
			}

			this.inherited("show",arguments);
			
			 /* Parent dojo dialog handles only _onKey event. We will be handling onmousedown event also. */ 
			
			this._modalconnects.push(dojo.connect(document.documentElement, "onmousedown", this, "_onMouseClick"));

			dojo.style(this.titleNode, "display", "none");
			dojo.style(this.titleNode, "visibility", "hidden");

			dojo.style(this.closeButtonNode, "display", "none");
			dojo.style(this.closeButtonNode, "visibility", "hidden");

			dojo.style(this.closeText, "display", "none");
			dojo.style(this.closeText, "visibility", "hidden");

			/* If closeOnTimeOut is true, then start a timer to close this dialog after timeOut milli seconds. */
			if(this.closeOnTimeOut){
				this._showTimer = setTimeout(dojo.hitch(this,"hide"),this.timeOut);
			}
			
			/** 
			 * If we dont stop this event from propogating further up, then dialog doesnt work properly.
			 * If the dialog is opened on click of button, then this onclick event will propogate and it will 
			 * call _onMouseClick function which closes the dialog immediately. 
			 */
			if(event != null)dojo.stopEvent(event);
			this.displayStatus = true;
		},
		
		/**
		 *  This function positions this dialog widget at the give x and y coordinates if passed in. This function positions this 
		 *  dialog widget relative to the relatedSource element passed in. It can also display the dialog just below this relatedSource 
		 *  element and left margin will be same for both. If not passed, then this will delegate it to parent dojo dialog to position it 
		 *  at the center of the window. 
		 */

		_position: function(){
			
			if(this.x != null && this.y != null){
				var style = this.domNode.style;
				style.left = this.x+"px";
				style.top = this.y+"px";
			}
			else if(this.relatedSource != null && this.relatedSource != ""){
				/* This code always positions the dialog around the relatedSource node. */
				var aroundNode = dojo.byId(this.relatedSource);
				var align = this.isLeftToRight() ? {'BR': 'BL', 'BL': 'BR'} : {'BL': 'BR', 'BR': 'BL'};
				var pos = dijit.placeOnScreenAroundElement(this.domNode, aroundNode, align);
			}
			else{
				this.inherited("_position",arguments);
			}
		},
		
		/**
		 *  This function is used to retposition and reset the timer. This function is called when 
		 *  the dialog is already created and associated with some other source.
		 */
		rePosition:function(source){
			
			this.relatedSource = source;
			this._position();
			if(this.closeOnTimeOut){
				if(this._showTimer){
					clearTimeout(this._showTimer);
					delete this._showTimer;
				}
				this._showTimer = setTimeout(dojo.hitch(this,"hide"),this.timeOut);
			}
		},
		
		/**
		 *	This function is used to cancel the timer. If same dialog is associated with two or more buttons or divs and if its showing somewhere
		 *  else, then then timer would be set and hence cancel that timer.
		 */
		hide:function(){
			
			if(this._showTimer){
				clearTimeout(this._showTimer);
				delete this._showTimer;
			}
			if (this.domNode) {			
				this.inherited("hide",arguments);
				this.displayStatus = false;
			}
		},
		
		/* This function is used to get the current display status of the dialog. */
		getDisplayStatus:function(){
			return this.displayStatus;
		},
		
		/* This function is used to set the display status of the dialog. */
		setDisplayStatus:function(status){
			this.displayStatus = status;
		},
		
		/**
		 *  This function cancels the timer function which was setup to close this dialog after timeOut milliseconds.
		 *  This is called from mousePress and onKey event handlers when the event takes places inside the dialog, so that we wont abruptly
		 *  close the dialog when user is doing something inside the dialog widget.
		 */
		cancelCloseOnTimeOut:function(){

			if(this._showTimer){
				clearTimeout(this._showTimer);
				delete this._showTimer;
			}
		},

		/**
		 *  This is a callback event when a key is pressed. It checks whether the key press event is inside the dialog,
		 *  if so user is using dialog and hence cancel the closeOnTimeOut event. Do not close the dialog unless user explicitly 
		 *  comes out of it by clicking ESC key or by clicking the mouse outside dialog area.
		 */
		_onKey:function(event){
			/**
			 * We have to handle it first and then delegate it to DOJO Dialog, because dojo will stop propogating this event and we may
			 * not get chance to handle this if dojo handles it first.
			 */
			var node = event.target;
			while(node){
				if(node == this.domNode){
					this.cancelCloseOnTimeOut();	
				}
				node = node.parentNode;
			}
			this.inherited("_onKey",arguments);

		},
		
		/**
		 *  This function handles the mouse click events. If the mouse is clicked inside dialog widget area, then cancel the closeOnTimeOut feature, 
		 *  so that dialog doesnt close on time out. If mouse is clicked outside dialog widget area then close the dialog immediately.
		 *  @param {string} evt The mouse click event.
		 */
		_onMouseClick: function(evt){
			
			var node = evt.target;
			var close = true;
			while(node){
				if(node == this.domNode){
					close = false;
				}
				node = node.parentNode;
			}
			if(close){
				this.hide()
			}
			else{
				this.cancelCloseOnTimeOut();
			}
		}
	}
);

}

//*******************************************************************
// wc/widget/ScrollablePane.js
if(!dojo._hasResource["wc.widget.ScrollablePane"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["wc.widget.ScrollablePane"] = true;

/** 
 * @fileOverview This javascript is used in CachedTopCategoriesDisplay.jsp to display a scrolling thumbnail picker in the home page of Madisons starter store.
 * @version 1.0
 */

/* Import dojo classes */
dojo.provide("wc.widget.ScrollablePane");

dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("dojo.parser");

/**
 *  The functions defined in this class enables initialisation of event listeners for ScrollablePane. Another set of functions act as callback when a key press or mouse click event occurs.
 *  @class The ScrollablePane widget displays a scrolling thumbnail picker that displays product images and details. It provides a scrolling effect for the items or images contained within the content of the widget. 
 *  Each item within the scrollable pane should be in contained in a ContentPane widget. The scrollable pane supports both automatic and manual scrolling of the items displayed in the widget.
 */
dojo.declare("wc.widget.ScrollablePane",
	[dijit._Widget, dijit._Templated],
	{
	
	
	/* Identifier of the widget */
	identifier:null,
	/*Indicates the width, in pixels, of each scrollable item.*/
	itemSize: 135,
	
	/**
	 * This flag indicates whether the scrollable pane scrolls automatically or manually. 
	 * When the value is true, the scrollable pane scrolls automatically. The default value is false.
	 */
	autoScroll: false,

	/*Indicates the total number of items to show at one time. */
	totalDisplayNodes: 4,

	/*Indicates the button size, in pixels. */
	buttonSize: 45,
	/**
	 * Indicates whether to display the thumbnails horizontally or vertically. When the value is true, the thumbnails are displayed horizontally. 
	 * When the value is false, the thumbnails are displayed vertically. The default value is true.
	 */
	isHorizontal: true,
	
	/** 
	 * This indicates whether the scrollable pane is vertical or horizontal scrollable. Possible values are 'width' and 'height'. 
	 * If the value is 'width', then it is horizontal scrollable, else if the value is set to 'height', it is vertical scrollable.
	 */
	sizeProperty: 'width',
   
	/* This is the total number of items in the queue. */
	totalItems: 0,
	
	/* This the current right item index after an animation. */
	last: 0,
	
	/* This the current left item index after an animation. */
	first:0,
	
	/* This is the left most item in the queue. */
	firstEnd:0,
	
	/* This is the right most item in the queue. */
	lastEnd:0,
	
	/* This is the locking variable which locks the animation. */
	lock:false,
    /**
	 *  Indicates the direction of the scrolling animation. The acceptable values are -1 and 1. The default value is 1, where the animation scrolls to the left, 
	 *  and new products appear from the right when displayed horizontally. The animation scrolls up, and new products appear from the bottom when displayed vertically.
	 */
	direction:-1,
	/*Indicates the delay time, in milliseconds, of the animation.*/
	delay:2000,
	
	/* This indicates the state of animation. */
	state: null,
	/* Indicates the scrolling direction. The acceptable values are -1 and 1. The default value is 1. */
	autoScrollDir: 1,
	
	/*The alternative (alt) text for the left button control. */
	altPrev: 'Scroll Left',
	
	/*The alternative (alt) text for the right button control. */
	altNext: 'Scroll Right',

	
	/* This is the template string for the widget. */
	templateString:"<div dojoAttachPoint=\"outerNode\" class=\"thumbOuter\">\n\t<div dojoAttachPoint=\"navPrev\" tabindex=\"0\" class=\"navPrev\"><img src=\"#\" dojoAttachPoint=\"navPrevImg\" alt=\"\"/></div>\n\t<div dojoAttachPoint=\"thumbScroller\" class=\"thumbScroller\" valign=\"middle\" >\n\t  <div dojoAttachPoint=\"thumbsNode\" class=\"thumbsNode\"></div>\n\t</div>\n\t<div dojoAttachPoint=\"navNext\" tabindex=\"0\" class=\"navNext\"><img src=\"#\" dojoAttachPoint=\"navNextImg\" alt=\"\"/></div>\n</div>\n", 

	
	/* This is the image path for the widget images. */
	tempImgPath: dojo.moduleUrl("wc.widget", "images/trasparent.gif"),
	
	/**
	 *  This method enables horizontal scrolling of the Scrollable pane widget
	 *  @param {array} args The array containing the node to scroll horizontally.
	 *  
	 *  @return {object} returns animation of type dojo._Animation.
	 */
	scrollHorizontal: function(args){
		
		var node = (args.node = dojo.byId(args.node));
		
		var left = null;
		
		var init = (function(n){
			return function(){
				var cs = dojo.getComputedStyle(n);
				var pos = cs.position;
				left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
				if(pos != 'absolute' && pos != 'relative'){
					var ret = dojo.coords(n, true);
					
					left = ret.x;
					n.style.position="absolute";
					
				}
			};
		})(node);
		init();

		var anim = dojo.animateProperty(dojo.mixin({
			properties: {
				left: { end: args.left||0 }
			}
		}, args));
		dojo.connect(anim, "beforeBegin", anim, init);

		return anim; /* dojo._Animation */
	},
	
	/**
	 *  This method enables vertical scrolling of the Scrollable pane widget.
	 *  @param {array} args The array containing the node to scroll vertically.
	 *  
	 *  @return {object} returns animation of type dojo._Animation.
	 */
	scrollVertical: function(args){
		
		var node = (args.node = dojo.byId(args.node));
		
		var top = null;
		var left = 0;
		
		var init = (function(n){
			return function(){
				var cs = dojo.getComputedStyle(n);
				var pos = cs.position;
				top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
				
				if(pos != 'absolute' && pos != 'relative'){
					var ret = dojo.coords(n, true);
					top = ret.y;
					
					n.style.position="absolute";
					
					n.style.left=left+"px";
				}
			};
		})(node);
		init();

		var anim = dojo.animateProperty(dojo.mixin({
			properties: {
				top: { end: args.top||0 }
				
			}
		}, args));
		dojo.connect(anim, "beforeBegin", anim, init);

		return anim; /* dojo._Animation */
	},


	/**
	 *  This functions initialises styles and listeners.
	 */
  postCreate: function(){
				
		this.widgetid = this.id;
		this.inherited("postCreate",arguments);
		var ieCurrection = 0;
		if(dojo.isIE<=6) ieCurrection = 15;
				
		
		var outerNodeSize = (this.itemSize * this.totalDisplayNodes) + this.buttonSize;
		
		console.log("postCreate: itemSize = " + this.itemSize);
		
		this.scrollerSize = this.itemSize * this.totalDisplayNodes ;

		this.navPrevImg.setAttribute("src", this.tempImgPath);
		this.navNextImg.setAttribute("src", this.tempImgPath);

		this.navPrevImg.setAttribute("alt", this.altPrev);
		this.navNextImg.setAttribute("alt", this.altNext);

		
		this.sizeProperty = this.isHorizontal ? "width" : "height";
	
		dojo.style(this.outerNode, "textAlign","center");
		dojo.style(this.outerNode, this.sizeProperty, outerNodeSize +"px");
		dojo.style(this.thumbScroller, this.sizeProperty, this.scrollerSize - ieCurrection + "px");

		this.init();
	},
	
	/**
	 *  This function creates DOM nodes for thumbnail images and initializes their listeners.
	 */
	init: function(){
		
		dojo.connect(this.navNext, "onclick", this, "prev");
		dojo.connect(this.navPrev, "onclick", this, "next");

		dojo.connect(this.navNext, "onkeypress", this, function(event){
			if(event.type=="keypress"){
				if(event.keyCode==13){
					this.prev();
				}
			}
		});
		dojo.connect(this.navPrev, "onkeypress", this, function(event){
			if(event.type=="keypress"){
				if(event.keyCode==13){
					this.next();
				}
			}
		});

		dojo.connect(this.thumbScroller, "onmouseover", this, "pause");
		dojo.connect(this.thumbScroller, "onmouseout", this, "play");
				
		var items = this.setDataStore("dijit.layout.ContentPane");
		this.items = items;
		this.totalItems = items.length;
		
		dojo.style(this.thumbsNode, this.sizeProperty, this.itemSize * (this.totalItems) + "px");
		this.appendItems(this.items);
				

		this.first = 0;
		this.last = this.totalDisplayNodes - 1;
		if(this.totalItems < this.totalDisplayNodes) this.last = this.totalItems - 1;

		this.firstEnd = 0;
		this.lastEnd = this.totalItems - 1;

		console.debug("ScrollablePane: init: items.length = " + items.length);
		if(this.autoScroll)
			this.autoScroller();
		return true;
	},

	/**
	 *  This function will take the nodetype as parameter and returns all the nodes in the scroll area with that node type.
	 *  @param {string} nodeType The type of the node to be retrieved.
     *
	 *  @return {array} All the nodes of the specified node type.
	 */
	setDataStore: function(nodeType) {
		var nodes = dojo.query("[dojoType=" + nodeType + "]", this.srcNodeRef);
		return nodes;
	},
	
	/**
	 * This function removes HTML attribute tabindex for an item in the scrollable area.  
	 * The tabIndex is removed for items which are hidden. These items should not be accessible via the keyboard.
	 * It will remove tabindex for all the children in that item and returns the item. 
	 *
	 * @param {string} item The item for which tabindex should be removed.
	 *  
	 * @return {string} The item after removing the tabindex.
	 */
	removeTabIndex: function(item){
		var a = item.getElementsByTagName('a');
		var p = item.getElementsByTagName('p');
		for(var x = 0 ;x+1<=a.length;x++){
			a[x].setAttribute('tabIndex',-1);
		}
		for(var x = 0; x+1<=p.length;x++){
			p[x].setAttribute('tabIndex',-1);
		}
		return item;
	},
	
	/**
	 * This function adds HTML attribute tabindex for an item in the scrollable area.  
	 * The tabIndex is added for items which are displayed. These items should be accessible via the keyboard.
	 * It will add tabindex for all the children in that item and returns the item. 
	 *
	 * @param {string} item The item for which tabindex should be added.
	 *  
	 * @return {string} The item after adding the tabindex.
	 */

	addTabIndex: function(item){
		var a = item.getElementsByTagName('a');
		var p = item.getElementsByTagName('p');
		for(var x = 0 ;x+1<=a.length;x++){
			a[x].setAttribute('tabIndex',0);
		}
		for(var x = 0; x+1<=p.length;x++){
			p[x].setAttribute('tabIndex',0);
		}
		return item;
	},
	 
	/**
	 *  This method is used to append the items to the scrollable node on the initial load of the scrollable area.
	 */
	appendItems: function(items){
		
		for (var i=0; i<items.length; i++) {
			if(i>=this.totalDisplayNodes){
				this.loadImage(this.removeTabIndex(items[i]), 1);
			}
			else{
				this.loadImage(this.addTabIndex(items[i]), 1);
			}
		}	 
	},
	
	/**
	 *  This method will load all the items from the scrollable area.
	 *  This method will create new nodes with all data and will append them to thumbNode or insert them at first.
	 */
	loadImage: function(data, position){	
		
		console.log("ScrollablePane: loadImage: position = " + position);
		if(position == 1)
			this.thumbsNode.appendChild(data);
		else this.thumbsNode.insertBefore(data,this.thumbsNode.firstChild);
	},
	
	/**
	 *  This function is used internally to start or restart the animation after direction changes.
	 */
	play: function(){
		
		this.autoScroll = this.state;
		if(this.autoScroll)
			this.autoScroller();

	},
	
	/**
	 *  This method is used internally to pause the animation while switching the directions.
	 */
	pause: function(){
		
		this.state = this.autoScroll;
		this.autoScroll = false;
	},
	
	/**
	 *  This method gives the auto scroll effect. This method will call prev or next methods based 
	 *  on the direction and will keep auto scrolling.
	 */
	autoScroller: function(){
		
		if(this.autoScrollDir == 1)this.next();
		else this.prev();
		
	},
	
	/**
	 *  This method will set the direction of the scroll and then calls showThumbs method to scroll the right side items.
	 */
    next: function() {
	   
	   this.autoScrollDir = 1;
		if(!this.lock){
			this.lock=true;
		
			/* summary: Displays the next page of images */
			this.showThumbs(1);
		}
	},
	
	/**
	 *  This method will set the direction of the scroll and then calls showThumbs method to scroll the left side items.
	 */
	prev: function(){
		
		this.autoScrollDir = -1;
		if(!this.lock){
			this.lock=true;
			
			/*Displays the next page of images. */
			this.showThumbs(-1);
		}
	},
		
	/**
	 *  This method will adjust the left nodes after an animation. This method will remove the hidden nodes from the right and adds them to leftmost position.
	 */
	adjustPrevScroll: function(){
		
		var i = this.firstEnd;
		while(i!=this.first){
			this.thumbsNode.removeChild(this.thumbsNode.firstChild);
			this.loadImage(this.items[i],1);
			this.lastEnd = i;
			i= i+1;
			if(i >= this.totalItems ) i = 0;
		}
		this.direction = -1;
		this.firstEnd = this.first ; 
		if(this.isHorizontal) this.thumbsNode.style.left = 0 + 'px';
		else this.thumbsNode.style.top = 0 + 'px';
	},
	
	/**
	 *  This method will adjust the right nodes after an animation. This method will remove the hidden nodes from the left and adds them to rightmost position.
	 */

	adjustNextScroll: function(){

		var adjust = (((this.totalItems) - this.totalDisplayNodes) * (this.itemSize) * -1);
		
		var i = this.lastEnd;
		while(i!=this.last){
			this.thumbsNode.removeChild(this.thumbsNode.lastChild);
			this.loadImage(this.items[i],-1);
			this.firstEnd = i;
			i= i-1;
			if(i < 0 ) i = this.totalItems - 1;
		}
		this.direction = 1;
		this.lastEnd = this.last; 
		if(this.isHorizontal) this.thumbsNode.style.left = adjust + 'px';
		else this.thumbsNode.style.top = adjust + 'px';
	},
	
	/**
	 * This method will animate the items with given delay and will scale the items in the given direction.
	 * @param {boolean} idx Flag to determine the scrolling direction.
	 */
	showThumbs: function(idx){
		
		if(this.totalItems > this.totalDisplayNodes){
			if(idx == 1){
				if(this.direction != 1) this.adjustNextScroll();
				var adjust = (((this.totalItems) - this.totalDisplayNodes) * (this.itemSize) * -1);
				var slideSize =  adjust + this.itemSize;
				
				if(this.isHorizontal) slideNext = this.scrollHorizontal({node: this.thumbsNode ,duration: this.delay, left: slideSize });
				else slideNext = this.scrollVertical({node: this.thumbsNode ,duration: this.delay, top: slideSize });
				
				slideNext.play();
				dojo.connect(slideNext, "onEnd", this , this.nextConnect);
			}
			else{
				if(this.direction != -1) this.adjustPrevScroll();
					
				if(this.isHorizontal) slidePrev = this.scrollHorizontal({node: this.thumbsNode ,duration: this.delay, left: - this.itemSize });
				else slidePrev = this.scrollVertical({node: this.thumbsNode ,duration: this.delay, top: - this.itemSize });
				
				slidePrev.play();
				dojo.connect(slidePrev, "onEnd", this , this.prevConnect);
			}
		}
		
	},
	/**
	 *  This method will do the post operations for next function, like removing and appending nodes and adjusting top and left parts of the scrollable area. 
	 */
	nextConnect: function(){
				
		this.thumbsNode.removeChild(this.thumbsNode.lastChild);

		var adjust = ((this.totalItems - this.totalDisplayNodes) * (this.itemSize) * -1);
		
		if(this.isHorizontal) this.thumbsNode.style.left = adjust + 'px';
		else this.thumbsNode.style.top = adjust + 'px';
		
		this.loadImage(this.removeTabIndex(this.items[this.last]), -1);
		this.addTabIndex(this.thumbsNode.childNodes[this.totalItems - this.totalDisplayNodes]);
		this.first = this.first - 1;
		if(this.first<0) this.first = this.totalItems - 1;
		this.firstEnd = this.last;
		this.last = this.last - 1;
		if(this.last<0) this.last = this.totalItems - 1;
		this.lastEnd = this.last;

		this.lock=false;
		if(this.autoScroll) this.autoScroller();
	},
	
	/**
	 *  This method will do the post operations for prev function, like removing and appending nodes and adjusting top and left parts of the scrollable area. 
	 */

	prevConnect: function(){

		this.thumbsNode.removeChild(this.thumbsNode.childNodes[0]);
		
		if(this.isHorizontal) this.thumbsNode.style.left = 0 + 'px';
		else this.thumbsNode.style.top = 0 + 'px';

		this.loadImage(this.removeTabIndex(this.items[this.first]),1);
		this.addTabIndex(this.thumbsNode.childNodes[this.totalDisplayNodes - 1]);
		this.lastEnd = this.first;
		this.first = this.first+1;
		if(this.first >= this.totalItems) this.first = 0;
		this.firstEnd = this.first;
		this.last = this.last+1;
		if(this.last >= this.totalItems) this.last = 0;

		this.lock=false;
		if(this.autoScroll) this.autoScroller();

	}
});

}

//*******************************************************************
// wc/widget/WCDropDownButton.js
if(!dojo._hasResource["wc.widget.WCDropDownButton"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["wc.widget.WCDropDownButton"] = true;
dojo.provide("wc.widget.WCDropDownButton");

/** 
 * @fileOverview This javascript is used by CachedHeaderDisplay.jsp to display a top-level category which has subcategories associated with it.
 * @version 1.0
 */

/* Import dojo classes */
dojo.require("dijit.form.Button");

/**
 *  The functions defined in this class enables initialisation of event listeners for WCDropDownButton. Another set of functions act as callback when a key press or mouse click event occurs. 
 *  @class This class extends the dijit.form.DropDownButton to add the following functionality:
 *  -When hovering over the button, the drop down menu is displayed.
 *  -When clicking on the button, the page is replaced and the page corresponding to the URL specified in the 'url' property is loaded.
 */

dojo.declare("wc.widget.WCDropDownButton", dijit.form.DropDownButton,{
/* The fully resolved URL to be loaded when the menu button is clicked. */
url: null,
/* This is the template string for the widget. */
templateString:"<div class=\"dijit dijitLeft dijitInline\"\n\tdojoAttachEvent=\"onmouseenter:_onMouse,onmouseleave:_onMouse,onmousedown:_onMouse,onclick:_onDropDownClick,onkeydown:_onDropDownKeydown,onblur:_onDropDownBlur,onkeypress:_onKey\"\n\t><div class='dijitRight'>\n\t<div class=\"dijitStretch dijitButtonNode dijitButtonContents\" type=\"${type}\"\n\t\tdojoAttachPoint=\"focusNode,titleNode\" waiRole=\"button\" waiState=\"haspopup-true,labelledby-${id}_label\"\n\t\t><div class=\"dijitInline ${iconClass}\" dojoAttachPoint=\"iconNode\"></div\n\t\t><span class=\"dijitButtonText\" \tdojoAttachPoint=\"containerNode,popupStateNode\"\n\t\tid=\"${id}_label\">${label}</span\n\t\t><span class='dijitA11yDownArrow'>&#9660;</span>\n\t</div>\n</div></div>\n",
		 
		/**
		 * This function initialises the event listeners and style classes for WCDropDownButton.
		 */
		  postCreate: function(){		
			this.titleNode.title=this.title;
			this.inherited("postCreate",arguments);
			dojo.connect(this.domNode,"onmouseenter",this,"_toggleDropDown");
			dojo.connect(this.domNode,"onfocus",this,"_onKey");
		},
			/**
		     *  This is a callback function when the user presses a key on menu popup node.
			 *  @param {string} e The key press event.
			 */
			_onKey: function(e){
			this.inherited("_onKey",arguments);
			if(!e.shiftKey){
				if (e.keyCode == dojo.keys.ENTER)
				{
				if(this.url) 
					{
						dojo.stopEvent(e);
						document.location.href = this.url;
					}
				}
			}

			if(e.shiftKey){
					if (e.keyCode==dojo.keys.ENTER){
					if(!this.dropDown || this.dropDown.domNode.style.display=="none"){	
							dojo.stopEvent(e);
							return this._toggleDropDown();
						}
					}
			}
	},			
				/**
	             *  This is a callback function when the user mouse clicks on menu popup node.
				 *  @param {string} e The mouse click event.
				 */
				_onDropDownClick: function(e){
					this.inherited("_onDropDownClick",arguments);
					if(this.url)
					{
					dojo.stopEvent(e);
					document.location.href = this.url;
					}	
				
				}
	}
);

}

//*******************************************************************
// wc/widget/RefreshArea.js
//********************************************************************

/** 
 * @fileOverview This javascript is used in store pages which have a section of a page which needs to be dynamically updated with new content.
 * @version 1.0
 */

 /* Import dojo classes */
dojo.provide("wc.widget.RefreshArea");

dojo.require("dijit._Widget");
dojo.require("dijit.layout.ContentPane");
dojo.require("wc.render.RefreshController");
dojo.require("dojo.parser");

/**
 * The functions defined in this class helps in initialising, updating and destroying a RefreshArea.
 * @class The RefreshArea widget is used to wrap a Document Object Model (DOM) node that is refreshed by replacing the innerHTML property with new content loaded from the server. 
 * That is, a refresh area is a section of a page which is dynamically updated with new content. A refresh area widget is associated with a registered refresh controller 
 * of type wc.render.RefreshController, that handles listening for events that require this widget to be refreshed.
 */
dojo.declare(
	
	"wc.widget.RefreshArea",
	
	[dijit._Widget, dijit.layout.ContentPane],	
	
	{		    	    
		
		/* The unique identifier of the refresh controller associated with the refresh area widget.*/
		controllerId: "",
		/* The unique identifier of the refresh area widget used to distinguish this refresh area from
		 * other refresh areas being controlled by the same refresh controller
		 */
		objectId: "",

		/** The refresh controller instance that is controlling this refresh area. This value is initialized
		 * by locating the refresh controller with the controller ID specified by "controllerId".
		 */
		controller: null,	

		/**
		 * This function initialises the refresh area widget.The refresh area widget is registered to the refresh controller that
		 * matches the "controllerId" attribute. This function is called by the dojo widget framework.
		 */
		startup: function() {

			this.controller = wc.render.getRefreshControllerById(this.controllerId);

			if (!this.controller) {

				throw new Error('Could not locate RefreshController "' + this.controllerId + '".');
			}

			console.debug("Adding.. "+this.id+" to ...."+this.controllerId);
			this.controller.addWidget(this);
			this.containerNode = this.domNode;
			return this.inherited("startup", arguments);
		},		
		
		/**
		 *  This function is used to destroy the refresh area widget.
		 *  The refresh area widget is deregistered from the refresh controller that is controlling this refresh area. 
		 *  This function is called by the dojo widget framework.
		 */

		destroy: function() {
			
			this.controller.removeWidget(this);
			return this.inherited("destroy", arguments);
		},
		
		/**
		 *  This function is used to refresh the content of the refresh area widget.
		 *  The refresh controller calls the refresh method of the RefreshArea when it recieves an event that the area needs to be refreshed.  
		 *  This method then delegates back to the refresh function of the refresh controller after adding objectId to the parameters array.
		 *  @param {array} parameters This contains parameters to be used during refresh.
		 */
		refresh: function(parameters) {
		
			if (!parameters) {
				parameters = {};
			}
			parameters.objectId = this.objectId;
			this.controller.refresh(this, parameters);
								
		},
		
		/**
		 *  This function is used to update the inner HTML of the refresh area with the new content loaded from the server .
		 *  This function is normally called by the refresh controller to update the content of the Refresh area.
		 *  It also ensures that any Dojo child widgets are properly destroyed and reinitialized. 
		 *  @param {string} html The new content loaded from the server. 
		 */
		setInnerHTML: function (html) {
		
			this.destroyDescendants();
			this.containerNode.innerHTML=html;
			dojo.parser.parse(this.containerNode);
			
		}
	}
);

//*******************************************************************
// wc/widget/RangeSlider.js
if(!dojo._hasResource["wc.widget.RangeSlider"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["wc.widget.RangeSlider"] = true;

/** 
 * @fileOverview This javascript is used by FastFinderNavDisplay.jspf to display a slider on the Fast Finder page. 
 * @version 1.0
 */

 /* Import dojo classes */
dojo.provide("wc.widget.RangeSlider");

dojo.require("dojo.dnd.move");
dojo.require("dijit._Widget");
dojo.require("dojo.currency");
dojo.require("dijit._Container");
dojo.require("dojo.i18n");
dojo.require("dijit.form.Slider") ;
dojo.require("dijit.form._FormWidget");

/**
 * The functions defined in this class helps the user to select price range values for a product.
 * @class The RangeSlider widget displays a slider on the Fast Finder page, used to select price range values for a product. 
 * The range slider contains two sliding handles which define the price range, where the left handle indicates the minimum price, 
 * and the right handle indicates the maximum price.
 */
 
 dojo.declare("wc.widget.RangeSlider",[dijit.form._FormWidget, dijit._Container],{
 		
	  /*This points to the file that contains template string for the widget. */
 	   templatePath:dojo.moduleUrl ("wc.widget","templates/RangeSlider.html"),

	   isContainer: true,

	   /* The left handle of the slider.*/
	   firstHandle: null,
	   
	   /* The right handle of the slider.*/
	   secondHandle: null,
	   
	   /* The slider bar on which the left and right handle move while selecting a price range. */
	   rangeSelectorBar: null,
	   
	   startRange : 0,	
	   	/* Total range represented by the widget i.e. for 0-100 it will be 100, and for 20-30 it will be 10. */			
		 totalRange : 100,					 
												
		/* Indicates the default initial position for the left handle.*/
		 defaultStart : 0,	
			 
		/* Indicates the default initial position for the right handle */
		 defaultEnd : 0,					 
		
		/* Indicates whether the slider bar can be clicked to move the handles to the nearest point clicked on the bar. The default value is true.*/
		 clickSelect : true,
		
		 /* value will be incremented by 1 for each move. */
		 stopCount : 0,						
		 snapToGrid : true,
		
		 /* Indicates whether a handle can be dragged to a new location using the mouse pointer. */
		 activeDrag : true,					
												
		/** Indicates the increment value (in multiples) of the range slider. 
		 *  For example, when the increment value is set to 5, the values of the range slider are displayed as 5, 10, 15, 20, etc.
		 */
		 incrementValue : 1,				
		 
		 /* Indicates the number of decimal places in the price. default value is 0. */
		 decimalPoints : 0,
		
		 /* Indicates whether the tool tip is available for display or not. The default value is true. */
		 showTooltip : true,				
												
		 /* Indicates whether the tool tip is always displayed, regardless of user input. The default value is true. */
		 showTooltipAllTime : true,			
												
		 /** Position for the tooltip relative to the first sliding button.
		  *  Its value should be a JavaScript Array object, 
		  *	 the first value of this array should give relative position horizontally and
		  *	 the second value of this array should give relative position vertically.
		  */
		 ralativePositionFirst : [-1,-1],	

         /** Position for the tooltip relative to the second sliding button.
		  *  Its value should be a JavaScript Array object, 
		  *	 the first value of this array should give relative position horizontally and
		  *	 the second value of this array should give relative position vertically.
		  */
		 ralativePositionSecond : [1,-1],
		
		/* The prefix for the upper and lower value for the widget. */
		 prefix : "",
		/* The suffix for the upper and lower value for the widget.*/
		 suffix : "",						
		/*Indicates the currency code, if the values are prices*/
		 currencyCode : "",

		/** Handle for tooltip
		 * Actually the ToolTip widget which dojo provides cannot be used here.
		 * Because we need to change the caption of tooltip very frequently. and 
		 * caption for Tooltip widget which Dojo provides cannot be changed once it is created.
		 */
		 /*Handle for tooltip <div> element associated with first sliding button.*/ 
		 firstTooltip : null,	
		 /*Handle for tooltip <div> element associated with second sliding button.*/
		 secondTooltip : null,	
		 /* Holds the 'upper' and 'lower' value for the widget */
		 currentValue : new Object(),		
		
		 _mousePixelCoord: "pageX",
		 _startingPixelCount: "t",
		 _pixelCount: "w",
		 rangeSliderTitle: "",
		 /*The translated string that is assigned to the left handle and is read by screen readers when it is in focus.*/
		 firstHandleTitle : "",
		 /*The translated string that is assigned to the right handle and is read by screen readers when it is in focus.*/
		 secondHandleTitle : "",
		 emptyImagePath : "",
		  
		 /**
		  *  To understand the process by which widgets are instantiated, it is critical to understand what other methods create calls and
		  *  which of them you'll want to override. Of course, adventurous developers could override create entirely, but this should
		  *	 only be done as a last resort.	Below is a list of the methods that are called, in the order
		  *  they are fired, along with notes about what they do and if/when
		  *  you should over-ride them in your widget:
		  *  postMixInProperties: a stub function that you can over-ride to modify variables that may have been naively assigned by	mixInProperties
		  *  # widget is added to manager object here
		  *  buildRendering	Subclasses use this method to handle all UI initialization Sets this.domNode.Templated widgets do this automatically
		  *  and otherwise it just uses the source dom node.
		  *  postCreate	a stub function that you can over-ride to modify take actions once the widget has been placed in the UI
		  *  store pointer to original dom tree
		  */
		  	create: function(params, srcNodeRef){		

		this.srcNodeRef = dojo.byId(srcNodeRef);

		/* For garbage collection.  An array of handles returned by Widget.connect()*/
		/* Each handle returned from Widget.connect() is an array of handles from dojo.connect()*/
		this._connects=[];

		/* _attaches: String[] */
		/* 		names of all our dojoAttachPoint variables */
		this._attaches=[];

		/*mixin our passed parameters */
		if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
		if(params){
			dojo.mixin(this,params);
		}
		this.postMixInProperties();

		/** generate an id for the widget if one wasn't specified
		 * (be sure to do this before buildRendering() because that function might
		 * expect the id to be there.
		 */
		if(!this.id){
			this.id=dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
		}
		dijit.registry.add(this);

		this.buildRendering();

		/** Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
		 * The placement of these attributes is according to the property mapping in attributeMap.
		 * Note special handling for 'style' and 'class' attributes which are lists and can
		 * have elements from both old and new structures, and some attributes like "type"
		 * cannot be processed this way as they are not mutable.
		 */
		if(this.domNode){
			for(var attr in this.attributeMap){
				var mapNode = this[this.attributeMap[attr]] || this["domNode"];
				var value = this[attr];
				if(typeof value != "object" && (value !== "" || (params && params[attr]))){
					switch(attr){
					case "class":
						dojo.addClass(mapNode, value);
						break;
					case "style":
						if(mapNode.style.cssText){
							mapNode.style.cssText += "; " + value;
						}else{
							mapNode.style.cssText = value;
						}
						break;
					default:
						mapNode.setAttribute(attr, value);
					}
				}
			}
		}

		if(this.domNode){
			this.domNode.setAttribute("widgetId", this.id);
		}
		this.postCreate();

		/* If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC. */
		if(this.srcNodeRef && !this.srcNodeRef.parentNode){
			delete this.srcNodeRef;
		}	
	}, 
		   /**
		 * stub function! sub-classes may use as a default UI initializer function. 
		 * The UI rendering will be available by the time this is called from buildRendering. 
		 * If buildRendering is over-ridden, this function may not be fired!
		 **/
		postCreate : function(){
	
			this.initWidget();
			
			this.inherited('postCreate', arguments);
		},
		
		
		/**
		 * This function basically initialize the widget.
		 */
		initWidget: function(){
			this.pixelsOnSlider = dojo.contentBox(this.rangeSelectorBar).w;
			console.debug(" pixelsOnSlider : " + this.pixelsOnSlider);
			this.endRange = this.startRange + this.totalRange;
			this.pixelsPerUnit = (this.pixelsOnSlider) / this.totalRange;
			console.debug(" pixelsPerUnit : " + this.pixelsPerUnit);
			this.noOfDecimalUnits = (this.totalRange * (Math.pow(10,this.decimalPoints))) / this.incrementValue;
			var x = Math.log(this.noOfDecimalUnits / this.pixelsOnSlider) * Math.LOG10E;
			if(x>0){
				console.debug(	"RangeSlider Widget: The whole range (along with decimal values) cannot be represented by the specified width of the widget. " +
							"Please set decimalPoints value to " + Math.floor(this.decimalPoints - x) + " or less, OR " +
							"Set the width of the widget to " + (this.noOfDecimalUnits) + " Pixels.");
				
				console.debug(	"RangeSlider Widget: The decimalPoints value is set to " + Math.floor(this.decimalPoints - x) + " from its origional value " + this.decimalPoints);
			
			}

			if(this.pixelsPerUnit < 1){
				console.debug(	"RangeSlider Widget: The whole range cannot be represented by the specified width of the widget. " +
							"Please decrese the range  by " + dojo.number.round((1 - this.pixelsPerUnit)*this.totalRange)+ " Units OR " +
							"Increase the width of the widget by " + dojo.number.round((1 - this.pixelsPerUnit)*this.totalRange)+" Pixels.");
			}
			
			
			if(this.defaultStart < this.startRange || this.defaultStart >= this.endRange){
				this.defaultStart = this.startRange;
			}
			if(this.defaultEnd > this.endRange || this.defaultEnd <= this.startRange){
				
				this.defaultEnd = this.endRange;
			}
			console.debug(	"defaultStart " + this.defaultStart);
			console.debug(	"defaultEnd " + this.defaultEnd);

			this.currentValue.lower = this.defaultStart;
			this.currentValue.upper = this.defaultEnd;

			/* Setup the two sliders. */
			this.setupSlider(this.firstHandle, "first");
			this.setupSlider(this.secondHandle, "second");

				
			if (this.clickSelect) {
			
				dojo.connect (this.rangeSelectorBar, "onclick", this, "onSliderBarClick");
			}
    
						
			if(typeof window != "undefined") {
				dojo.connect(window, 'onresize', this, 'onWindowResized');	/* window resize. */
			}
		
		},
/**
		 * This function initializes the slider for the widget. It positions the two sliding buttons and make them dragable.
		 * If their initial position is specified (using defaultStart, defaultEnd parameters) 
		 * it will position the buttons to specified position, otherwise it will position them to default position.
		 * 
		 * @param handle			One of the slider handle.
		 *							There are two handles for this widget, firstHandle and secondHandle, 
		 *							these handles represent the two sliding buttons on the widget respectively.
		 *
		 * @param name				A logical name for the handle. It should be either 'first' or 'second'
		 *							'first' indicates that you are initializing the first slider button and 
		 *							'second' indicates that you are initializing the first slider button.
		 **/
		setupSlider : function (handle, name) {
		  var _self = this;
	    var mover = function( e){
			wc.widget.SliderDragMove.apply(this, arguments);
			this.widget = _self;
		  };
		 dojo.extend(mover,wc.widget.SliderDragMove.prototype);
		 this._movable = new dojo.dnd.Moveable(handle, {mover: mover});

			handle.name = name;
				
			/* Calculate slider buttons initial position. */
			
			this.startLimit =dojo.coords(this.rangeSelectorBar, true).x - dojo.contentBox(this.firstHandle).w/2;
		
			this.endLimit = this.startLimit + this.pixelsOnSlider;
			
			var ralativePosition = [];

			if(name == "first"){
				
				ralativePosition[0] = (this.ralativePositionFirst[0] < 0)? (this.ralativePositionFirst[0] ) : (this.ralativePositionFirst[0] + dojo.contentBox(this.firstHandle).w);
				ralativePosition[1] = (this.ralativePositionFirst[1] < 0)? (this.ralativePositionFirst[1] + 25) : (this.ralativePositionFirst[0] + dojo.contentBox(this.firstHandle).h);
      
				var pixelValue = (this.currentValue.lower - this.startRange) * this.pixelsPerUnit + dojo.coords(this.rangeSelectorBar, true).x - dojo.contentBox(handle).w/2;
				
				if(this.showTooltip){
					handle.tempHandle = this.firstTooltip;
					dojo.style(this.firstTooltip,"display","none");
				  if(this.showTooltipAllTime){
						dojo.style(this.firstTooltip,"display","");
					}
				}else{
					dojo.style(this.firstTooltip,"display","none");
				}
			}
			else if(name == "second"){
				
				ralativePosition[0] = (this.ralativePositionSecond[0] < 0)? (this.ralativePositionSecond[0] - 30) : (this.ralativePositionSecond[0] + dojo.contentBox(this.secondHandle).w);
				ralativePosition[1] = (this.ralativePositionSecond[1] < 0)? (this.ralativePositionSecond[1] + 25) : (this.ralativePositionSecond[0] + dojo.contentBox(this.secondHandle).h);
				var pixelValue = (this.currentValue.upper - this.startRange) * this.pixelsPerUnit + dojo.coords(this.rangeSelectorBar, true).x - dojo.contentBox(handle).w/2;
			
				if(this.showTooltip){
					
					handle.tempHandle = this.secondTooltip;
					dojo.style(this.secondTooltip,"display","none");
					if(this.showTooltipAllTime){
            dojo.style(this.secondTooltip,"display","");
					}
				}else{
					  dojo.style(this.secondTooltip,"display","none");
				}
			}else{
				console.debug("RangeSlider Widget: Something is wrong with name:" + name + " in this.setupSlider(handle, name)");
			}
			
			if(this.snapToGrid){
					pixelValue = this.getPixelValue(this.getUnitValue(pixelValue));
			}
			
			/* Set the slider buttons to their initial position. */
			handle.style.left = pixelValue + "px";
			handle.style.top = dojo.coords(this.rangeSelectorBar, true).y + dojo.contentBox(this.rangeSelectorBar).h/2 - dojo.contentBox(this.firstHandle).h/2 -2 + "px";
      
			if(dojo.isOpera!=0)
			{
				handle.style.top = parseInt(handle.style.top) + window.pageYOffset +"px";
			}
			
			/* set the both tooltips initial position. */
			if(this.showTooltip){
				handle.tempHandle.style.position = "absolute";
			if(name == "first")
			{
				handle.tempHandle.style.top = dojo.coords(handle, true).y  + parseInt(ralativePosition[1])+ "px";
				handle.tempHandle.style.left = dojo.coords(handle, true).x  + parseInt(ralativePosition[0]) + "px";
			}
			else
			{
				handle.tempHandle.style.top = dojo.coords(handle, true).y  + parseInt(ralativePosition[1])-40+ "px";
				var endOfRange = this.endRange;
				if(Math.floor(endOfRange)!=this.endRange)
				{
					handle.tempHandle.style.left = dojo.coords(handle, true).x  + parseInt(ralativePosition[0]) - dojo.contentBox(handle.tempHandle).w + 16 + "px";
				}
				else
				{
					handle.tempHandle.style.left = dojo.coords(handle, true).x  + parseInt(ralativePosition[0]) - dojo.contentBox(handle.tempHandle).w + "px";
				}
			}
		
			}
			if(dojo.isOpera!=0)
			{
				handle.tempHandle.style.top = parseInt(handle.tempHandle.style.top) + window.pageYOffset +"px";
			}
			
			this.valueChanged(name);
			
		},
		
		destroy: function(){
		this._movable.destroy();
		this.inherited('destroy', arguments);	
	},
/**
		 * This function rounds the value using dojo.math.round() function.
		 * It uses the userdefined decimalPoints parameter while rounding the value. 
		 * 
		 * @param value				The value to be rounded.
		 **/
		round : function(value){
			return dojo.number.round(value,this.decimalPoints);
		},
		
		/**
		 * This function positions the slider button at specified unit value.
		 * It means you just need to pass the value where you want the slider to move. 
		 * 
		 * @param node				The DOM node which represents one of the slider buttons, you want to move.
		 *
		 * @param unitValue			The value on the slider where you want to position the slider button.
		 *							e.g. 10 OR 120
		 **/
		setUnitPosition : function(node,unitValue){
			var pixelValue = (unitValue - this.startRange) * this.pixelsPerUnit + this.startLimit;
			this.setPixelPosition(node,pixelValue);
		},
		
		
		
		/**
		 * This function positions the slider button at specified pixel value.
		 * It means you need to pass the actual pixel value on the screen, where you want the slider to move. 
		 * 
		 * @param node				The DOM node which represents one of the slider buttons, you want to move.
		 *
		 * @param pixelValue		The pixel value on the screen (X axis) where you want to position the slider button.
		 *							It will be constrained within the slider bar, So you cannot move it beyond the 
		 *							slider bar limits
		 *							e.g. 500 OR 1500
		 **/
		setPixelPosition : function(node,pixelValue){
			
			this.currentHandle = node;
			var currentValue = pixelValue;
			
			/* We are setting the limits +/- 5 pixels from their origional positions. */
			/* This will allow us to move the handle easily at the limits. */
			var _startLimit = this.getPixelValue(0) ;
			var _endLimit = this.getPixelValue(this.totalRange) ;
			var ralativePosition = [];
			
			if(node.name == "first"){
				_endLimit = dojo.coords(this.secondHandle, true).x;
				/* Move current handle over the other. */
				this.secondHandle.style.zIndex = 10;
				this.secondTooltip.style.zIndex = 30;
				this.firstHandle.style.zIndex = 20;
				this.firstTooltip.style.zIndex = 40;

				
				ralativePosition[0] = (this.ralativePositionFirst[0] < 0)? (this.ralativePositionFirst[0]) : (this.ralativePositionFirst[0] + dojo.contentBox(this.firstHandle).w);
				ralativePosition[1] = (this.ralativePositionFirst[1] < 0)? (this.ralativePositionFirst[1] + 25) : (this.ralativePositionFirst[0] + dojo.contentBox(this.firstHandle).h);
			}
			else if(node.name == "second"){
				_startLimit = dojo.coords(this.firstHandle, true).x;
				/* Move current handle over the other. */
				this.firstHandle.style.zIndex = 10;
				this.firstTooltip.style.zIndex = 30;
				this.secondHandle.style.zIndex = 20;
				this.secondTooltip.style.zIndex = 40;

				ralativePosition[0] = (this.ralativePositionSecond[0] < 0)? (this.ralativePositionSecond[0] - 30) : (this.ralativePositionSecond[0] + dojo.contentBox(this.secondHandle).w);
				ralativePosition[1] = (this.ralativePositionSecond[1] < 0)? (this.ralativePositionSecond[1] + 25) : (this.ralativePositionSecond[0] + dojo.contentBox(this.secondHandle).h);
			}
			else{
				console.debug("RangeSlider Widget: Something is wrong with node.name:" + name + " in this.setPixelPosition(node,pixelValue)");
			}
			
			if(_startLimit + 4 <= currentValue && currentValue <= _endLimit)
			{	
				if(this.snapToGrid && !this.activeDrag)
					pixelValue = this.getPixelValue(this.getUnitValue(pixelValue));
				if(node.name == "first")
				  node.style.left = pixelValue + "px";
				else
					node.style.left = pixelValue - 4 + "px";
	
				/* Whenever the handle moves we need to move the tooltip along with it. */
				if(this.showTooltip){
					node.tempHandle.style.position = "absolute";
					if(node.name == "first")
					{
						if(pixelValue + parseInt(ralativePosition[0]) + dojo.contentBox(node.tempHandle).w <= this.getPixelValue(this.endRange) + 7)
						{
							node.tempHandle.style.left =  pixelValue  + parseInt(ralativePosition[0]) + "px";
						}
						node.tempHandle.style.top =  dojo.coords(node, true).y  + parseInt(ralativePosition[1])+ "px";
						if(dojo.isOpera!=0)
						{
							node.tempHandle.style.top = parseInt(node.tempHandle.style.top) + window.pageYOffset +"px";
						}
					}
					else
					{	
						var secondHandleLeft = pixelValue - 4 + parseInt(ralativePosition[0]) - dojo.contentBox(node.tempHandle).w;
						if(secondHandleLeft >= this.getPixelValue(0) - 1)
						{
							node.tempHandle.style.left =  secondHandleLeft + "px";
						}
						node.tempHandle.style.top =  dojo.coords(node, true).y  + parseInt(ralativePosition[1])-40 + "px";
						if(dojo.isOpera!=0)
						{
							node.tempHandle.style.top = parseInt(node.tempHandle.style.top) + window.pageYOffset +"px";
						}
					}
				}
				this.valueChanged(node.name);
			}
			else if(node.name == "second" && _startLimit + 4 <= currentValue && currentValue <= _endLimit + 4)
			{
				if(this.snapToGrid && !this.activeDrag)
					pixelValue = this.getPixelValue(this.getUnitValue(pixelValue));
				node.style.left = pixelValue - 4 + "px";
				if(this.showTooltip){
					node.tempHandle.style.position = "absolute";
					var secondHandleLeft = pixelValue - 4 + parseInt(ralativePosition[0]) - dojo.contentBox(node.tempHandle).w;
					if(secondHandleLeft >= this.getPixelValue(0) - 1)
					{
						node.tempHandle.style.left = secondHandleLeft + "px";
					}
					node.tempHandle.style.top =  dojo.coords(node, true).y  + parseInt(ralativePosition[1])-40 + "px";
				}
				if(dojo.isOpera!=0)
				{
					node.tempHandle.style.top = parseInt(node.tempHandle.style.top) + window.pageYOffset +"px";
				}
				this.valueChanged(node.name);
			}
			else if(node.name == "first" && _startLimit <= currentValue && currentValue <= _endLimit)
			{
				if(this.snapToGrid && !this.activeDrag)
					pixelValue = this.getPixelValue(this.getUnitValue(pixelValue));
				node.style.left = pixelValue + "px";
				if(this.showTooltip){
					node.tempHandle.style.position = "absolute";
					node.tempHandle.style.left =  pixelValue  + parseInt(ralativePosition[0]) + "px";
					node.tempHandle.style.top =  dojo.coords(node, true).y  + parseInt(ralativePosition[1])+ "px";
				}
				if(dojo.isOpera!=0)
				{
					node.tempHandle.style.top = parseInt(node.tempHandle.style.top) + window.pageYOffset +"px";
				}
				this.valueChanged(node.name);
			}
		},

/**
     * This function returns the unitValue for specified pixelValue.
		 *
		 * It basically converts pixelValue to unitValue. It uses the startLimit, pixelsPerUnit and incrementValue parameter while
		 * calculating the unit value from given pixel value.
		 * For example if there are 10 pixels for one unit (as per pixelsPerUnit parameter), and the startLimit for widget is 0
		 * then for 100 pixel value it will return 10, and if the startLimit for widget is 3 it will return 13 for 100 pixel value.
		 * 
		 * @param pixelValue		The pixel value on the screen (X axis) for which you want to get unit value.
		 *							Represents actual pixel on the sliderBar.
		 *
		 * @return unitValue		The unit value. A logical representation for pixels on the sliderBar.
		 **/
		getUnitValue : function(pixelValue){
			var first = this.startLimit;
			var last = pixelValue;
			var res = this.startRange + (last-first)/this.pixelsPerUnit;
			var res = this.round(res/this.incrementValue) * this.incrementValue;
		
			if(res < this.startRange){
				res = this.startRange;
			}
			if(res > this.endRange){
				res = this.endRange;
			}
	
			return res;
		},
		/**
		 * This function returns the pixelValue for specified unitValue.
		 *
		 * It basically converts unitValue to pixelValue. It uses the startLimit, pixelsPerUnit and incrementValue parameter while
		 * calculating the unit value from given pixel value.
		 * For example if there are 10 pixels for one unit (as per pixelsPerUnit parameter), and the startLimit for widget is 0
		 * then for 10 unit value it will return 100, and if the startLimit for widget is 3 it will return 70 for 10 unit value.
		 * 
		 * @param unitValue			The unit value for which you want to get pixel value.
		 *							A logocal representation for pixels on the sliderBar.
		 *
		 * @return pixelValue		The pixel value on the screen (X axis).
		 *							Represents actual pixel on the sliderBar.
		 **/
		getPixelValue : function(unitValue){
			unitValue = this.round(unitValue/this.incrementValue) * this.incrementValue;
			var first = this.startLimit;
			var last = unitValue * this.pixelsPerUnit;
			var res = (first + last);
			
			if(res < this.startLimit)
				res = this.startLimit;
			if(res > this.endLimit)
				res = this.endLimit;
			
			return res;
		},
		
		/**
		 * This function returns the nearer sliding button DOM node.
		 *
		 * It accepts pixelValue and based on that it calculates the distance from that point to both sliding buttons, 
		 * and returns the nearest sliding button.
		 *
		 * @param pixelValue		The pixel value on the screen (X axis).
		 *							Represents actual pixel on the sliderBar.
		 * @return DOM node			The DOM node for one of the slider buttons, Which is nearer to the given pixelValue.
		 **/
	getCloserSliderHandle : function(pixelValue){
			var firstValue = dojo.coords(this.firstHandle, true).x + dojo.contentBox(this.firstHandle).w/2;
			var SecondValue = dojo.coords(this.secondHandle, true).x + dojo.contentBox(this.secondHandle).w/2;
			var diffFirst = pixelValue - firstValue;
			var diffSecond = SecondValue - pixelValue;
			
			if(diffFirst<=diffSecond)
			{
					return this.firstHandle;
			}
			else
			{
					return this.secondHandle;
			}
			
		},
		
		/**
		 * This function returns a current range for the widget.
		 *
		 * This function returns a JavaScript object having 'lower' and 'upper' properties. These two value represent the 
		 * current range for the widget.
		 *
		 * @return Object			This object has 'lower' and 'upper' properties. These two value represent the 
		 *							current range for the widget.
		 **/
		getCurrentValues : function(){
			return this.currentValue;
		},
/**
		 * This function calculates the upper and lower range for the widget and then set them to an internal variable
		 * currentValue. So later we can use this variabale for getting the lower and upper limits.
		 **/
		valueChanged : function(sliderName){
		
			if(sliderName == "first" || sliderName == "*"){
				this.currentValue.lower = this.round(this.getUnitValue(dojo.coords(this.firstHandle, true).x));
				
			
			}
			if(sliderName == "second" || sliderName == "*"){
				this.currentValue.upper = this.round(this.getUnitValue(dojo.coords(this.secondHandle, true).x));
				
			}
			
			var formattedLowerValue = this.currentValue.lower;
			var formattedUpperValue = this.currentValue.upper;
			
			if(this.currencyCode != null && this.currencyCode != ""){
				formattedLowerValue = dojo.currency.parse(formattedLowerValue,this.currencyCode,{places:0});
				formattedUpperValue = dojo.currency.parse(formattedUpperValue,this.currencyCode,{places:0});
			}

			/* Change the caption of the tooltip. */			
			if(this.showTooltip){
				this.firstTooltip.innerHTML = this.prefix + formattedLowerValue + this.suffix;
				this.secondTooltip.innerHTML = this.prefix + formattedUpperValue + this.suffix;
				
			}

			this.onChange(this);
		},

		getFormattedValues : function(){
			var formattedLowerValue = this.currentValue.lower;
			var formattedUpperValue = this.currentValue.upper;
			
			if(this.currencyCode != null && this.currencyCode != ""){
				formattedLowerValue = dojo.currency.format(formattedLowerValue,this.currencyCode,{places:0});
				formattedUpperValue =dojo.currency.format(formattedUpperValue,this.currencyCode,{places:0});
			}
			
			return {lower:formattedLowerValue, upper:formattedUpperValue};
		},

		/**
		 * This function gets the nearer sliding button and positions it, where user has clicked on the 'Slider Bar'.
		 * It is called when user click on the 'Slider Bar'.
		 *
		 * @param	e				The JavaScript event object.
		 **/
		onSliderBarClick : function(e){
		  var pixelValue =  e.clientX;
			var node = this.getCloserSliderHandle(pixelValue);
			pixelValue = pixelValue - dojo.contentBox(node).w/2;
			if(node.name == "second")
				pixelValue= pixelValue + 4;
			this.setPixelPosition(node,pixelValue);
			this.onChangeMade(this);
		},
		
   setPosition: function(pixelValue){
   	var node = this.getCloserSliderHandle(pixelValue);
   	pixelValue = pixelValue - dojo.contentBox(node).w/2;
   	this.setPixelPosition(node,pixelValue);
   	this.onChangeMade(this);
  },
  
    /**
		 * This function shows the tooltip for first sliding button, if showTooltip parameter is set to 'true'.
		 * It is called when user move the mouse cursor over the first sliding button.
		 **/
		onFirstMouseOver : function(){
		
			if(!this.showTooltipAllTime){
			 dojo.style(this.firstTooltip,"display","");
			}
			dojo.addClass(this.firstHandle, 'rangeSelectorHandleHover');
		},
		
		/**
		 * This function hides the tooltip for first sliding button, if showTooltip parameter is set to 'true'.
		 * It is called when user move the mouse cursor out from the first sliding button.
		 **/
		onFirstMouseOut : function(){
			
			if(!this.showTooltipAllTime){
				dojo.style(this.firstTooltip,"display","none");
			}
			dojo.removeClass(this.firstHandle, 'rangeSelectorHandleHover');
		},
		
		/**
		 * This function shows the tooltip for second sliding button, if showTooltip parameter is set to 'true'.
		 * It is called when user move the mouse cursor over the second sliding button.
		 **/
		onSecondMouseOver : function(){
			if(!this.showTooltipAllTime){
				dojo.style(this.secondTooltip,"display","");
			}
			dojo.addClass(this.secondHandle, 'rangeSelectorHandleHover');
		},
		
		/**
		 * This function hides the tooltip for second sliding button, if showTooltip parameter is set to 'true'.
		 * It is called when user move the mouse cursor out from the second sliding button.
		 **/
		onSecondMouseOut : function(){
			if(!this.showTooltipAllTime){
				dojo.style(this.secondTooltip,"display","none");
		}
			dojo.removeClass(this.secondHandle, 'rangeSelectorHandleHover');
		},
		
		
		/**
		 * This function is called when user clicks on the widget.
		 * User can set his userdefined function as a callback function for this event.
		 * e.g. this.onClick = myFunction();
		 * In this case myFunction() will be called whenever user clicks on the widget.
		 **/
		onclick : function(e){
			
		},
		
		/**
		 * This function enables keyboard access to the Price Range Slider through Alt Enter and Shift Enter.
		 **/
		onKeyPress : function(e){
			var handleFlag="";
			var relativePosition = (this.ralativePositionSecond[0] < 0)? (this.ralativePositionSecond[0] - 30) : (this.ralativePositionSecond[0] + dojo.contentBox(this.secondHandle).w - 20);
			for(var i=0; i<e.target.childNodes.length; i++)
			{
				if(e.target.childNodes[i].id==this.id+"_firstHandle")
				{
					handleFlag="first";
				}
				else if(e.target.childNodes[i].id==this.id+"_secondHandle")
				{
					handleFlag="second";
				}
			}
			if(handleFlag=="first")
			{
				var pixelValue;
				if(e.shiftKey && e.keyCode == dojo.keys.ENTER)
				{
					if(this.currentValue.lower - 1 >= this.defaultStart)
					{
						this.currentValue.lower = this.currentValue.lower - 1;
						this.firstHandle.style.left = this.getPixelValue(this.currentValue.lower + relativePosition) + "px";
						if(this.getPixelValue(this.currentValue.lower + relativePosition) + dojo.contentBox(this.firstTooltip).w <= this.getPixelValue(this.endRange) + 5)
						{
							this.firstTooltip.style.left = this.getPixelValue(this.currentValue.lower + relativePosition) + "px";
						}
						if(this.showTooltip)
						{
							this.firstTooltip.innerHTML = "";
							this.firstTooltip.innerHTML = this.prefix + this.currentValue.lower + this.suffix;								
						}
						this.onChangeMade(this);
						setTimeout(dojo.hitch(this,"gotoToolTip1",""),1000);
					}	
				}
				else if(e.altKey && e.keyCode == dojo.keys.ENTER)
				{
					if(this.currentValue.lower + 1 <= this.currentValue.upper)
					{
						this.currentValue.lower = this.currentValue.lower + 1;	
						this.firstHandle.style.left = this.getPixelValue(this.currentValue.lower + relativePosition) + "px";
						if(this.getPixelValue(this.currentValue.lower + relativePosition) + dojo.contentBox(this.firstTooltip).w <= this.getPixelValue(this.endRange) + 5)
						{
							this.firstTooltip.style.left = this.getPixelValue(this.currentValue.lower + relativePosition) + "px";
						}
						if(this.showTooltip)
						{
								this.firstTooltip.innerHTML = "";
								this.firstTooltip.innerHTML = this.prefix + this.currentValue.lower + this.suffix;
						}
						this.onChangeMade(this);
						setTimeout(dojo.hitch(this,"gotoToolTip1",""),1000);
					}
				}
			}
			else if(handleFlag=="second")
			{	
				var pixelValue;
				if(e.shiftKey && e.keyCode == dojo.keys.ENTER)
				{
					if(this.currentValue.upper - 1 >= this.currentValue.lower)
					{
						this.currentValue.upper = this.currentValue.upper - 1;
						this.secondHandle.style.left = this.getPixelValue(this.currentValue.upper + relativePosition) + "px";
						if(this.getPixelValue(this.currentValue.upper + relativePosition) - dojo.contentBox(this.secondTooltip).w + 7 >= this.getPixelValue(0))
						{
							this.secondTooltip.style.left = this.getPixelValue(this.currentValue.upper + relativePosition) - dojo.contentBox(this.secondTooltip).w + 7 + "px";
						}
						if(this.showTooltip)
						{
							this.secondTooltip.innerHTML = "";
							this.secondTooltip.innerHTML = this.prefix + this.currentValue.upper + this.suffix;
						}
						this.onChangeMade(this);
						setTimeout(dojo.hitch(this,"gotoToolTip2",""),1000);
					}
				}
				else if(e.altKey && e.keyCode == dojo.keys.ENTER) 
				{
					if(this.currentValue.upper + 1 <= this.round(this.defaultEnd))
					{
						this.currentValue.upper = this.currentValue.upper + 1;
						this.secondHandle.style.left = this.getPixelValue(this.currentValue.upper + relativePosition) + "px";
						if(this.getPixelValue(this.currentValue.upper + relativePosition) - dojo.contentBox(this.secondTooltip).w + 7 >= this.getPixelValue(0))
						{
							this.secondTooltip.style.left = this.getPixelValue(this.currentValue.upper + relativePosition) - dojo.contentBox(this.secondTooltip).w + 7 + "px";
						}
						if(this.showTooltip)
						{
							this.secondTooltip.innerHTML = "";
							this.secondTooltip.innerHTML = this.prefix + this.currentValue.upper + this.suffix;	
						}
						this.onChangeMade(this);
						setTimeout(dojo.hitch(this,"gotoToolTip2",""),1000);
					}
				}
			}
		},
		/**
		 * This function gives focus to the tooltip of the first handle (so that the new value is read out) and 
		 * returns back focus to the first handle (so that the handle is ready to be moved again).
		 **/
		gotoToolTip1 : function(){
			this.firstHandleTooltip.focus();
			this.firstHandleContent.focus();
		},

		/**
		 * This function gives focus to the tooltip of the second handle (so that the new value is read out) and 
		 * returns back focus to the second handle (so that the handle is ready to be moved again).
		 **/
		gotoToolTip2 : function(){
			this.secondHandleTooltip.focus();
			this.secondHandleContent.focus();
		},
		/**
		 * This function is called whenever there is a change 'upper' and 'lower' limit of the widget.
		 * When user drags one of the sliding button OR clicks on the 'Slider Bar' (to move the buttons) 
		 * this function gets called.
		 *
		 * Use this function carefully as a callback, bacause a samll movement can cause many calls to this function
		 *
		 * User can set his userdefined function as a callback function for this event.
		 * e.g. this.onChange = myFunction();
		 **/
		onchange : function(e){
			
		},
		

	/**
		 * This function is called whenever there is a change 'upper' and 'lower' limit of the widget.
		 * When user drags one of the sliding button and stops OR clicks on the 'Slider Bar' (to move the buttons) 
		 * this function gets called.
		 *
		 * In case of draging, this function gets called only once when user stops dragging the sliding button.
		 * It won't get called when user is still draging the sliding button, only when he stops dragging it is called.
		 *
		 * User can set his userdefined function as a callback function for this event.
		 * e.g. this.onChangeMade = myFunction();
		 **/
		onChangeMade : function(e){
		
		},


		/**
		 * This function is called when the browser window get resized
		 **/
		onWindowResized : function(){
			/* Setup the two sliders. */
			this.setupSlider(this.firstHandle, "first");
			this.setupSlider(this.secondHandle, "second");
		},
		
  	onMoveStop: function(e,count){
			if(this.snapToGrid && this.activeDrag && this.currentHandle !== null){
				var pixelValue = this.getPixelValue(this.getUnitValue(dojo.coords(this.currentHandle, true).x));
				this.currentHandle.style.left = pixelValue + "px";
			}
			if(!this.showTooltipAllTime){
				dojo.style(this.firstTooltip,"display","none");
				dojo.style(this.secondTooltip,"display","none");
			}
			if(count != this.stopCount){return;}
			else{
			this.onChangeMade(this);
			}
	}
	
});

/**
 * This class extends the dojo.dnd.Mover class to provide
 * features for the slider handle.
 */
dojo.declare (
	"wc.widget.SliderDragMove",
	[dojo.dnd.Mover],
{


	/** 
	 * Moves the node to follow the mouse.
	 * Extends functon dojo.dnd.Mover by adding functionality to snap handle
	 * to a discrete value.
	 **/
	onMouseMove: function (e) {

	  var widget = this.widget;
    var c = this.constraintBox;
    
    if(!c){
          var container = widget.rangeSelectorBar;
          var s = dojo.getComputedStyle(container);
          var c = dojo._getContentBox(container, s);
              c[widget. _startingPixelCount] = 0;
              this.constraintBox = c;
          }
    var m = this.marginBox;
             
    var pixelValue =  e[widget._mousePixelCoord];
               
    var node = widget.getCloserSliderHandle(pixelValue);
     
    widget.setPixelPosition(node, pixelValue);
              
	},
	/**
	 *  This function stops the slider movement, deletes all references, so the object can be garbage-collected.
	 */
	destroy: function(){
		dojo.forEach(this.events, dojo.disconnect);
		/* undo global settings. */
		var h = this.host;
		if(h && h.onMoveStop){
			h.onMoveStop(this);
			var widget = this.widget;
		    widget.stopCount = widget.stopCount + 1;
			setTimeout(dojo.hitch(widget,"onMoveStop",widget,widget.stopCount),10);
		}
		/* destroy objects. */
		this.events = this.node = null;
	}

});

}

//*******************************************************************
