/* START: jquery.endless-scroll-1.3.js */

/**ad
 * Endless Scroll plugin for jQuery
 *
 * v1.3
 *
 * Copyright (c) 2008 Fred Wu
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 */

/**
 * Usage:
 * 
 * // using default options
 * $(document).endlessScroll();
 *
 * // using some custom options
 * $(document).endlessScroll({
 *   fireOnce: false,
 *   fireDelay: false,
 *   loader: "<div class=\"loading\"><div>",
 *   callback: function(){
 *     alert("test");
 *   }
 * });
 *
 * Configuration options:
 *
 * bottomPixels  integer          the number of pixels from the bottom of the page that triggers the event
 * fireOnce      boolean          only fire once until the execution of the current event is completed
 * fireDelay     integer          delay the subsequent firing, in milliseconds. 0 or false to disable delay.
 * loader        string           the HTML to be displayed during loading
 * data          string|function  plain HTML data, can be either a string or a function that returns a string
 * insertAfter   string           jQuery selector syntax: where to put the loader as well as the plain HTML data
 * callback      function         callback function, accepets one argument: fire sequence (the number of times 
 *                                the event triggered during the current page session)
 * resetCounter  function         resets the fire sequence counter if the function returns true, this function
 *                                could also perform hook actions since it is applied at the start of the event
 * ceaseFire     function         stops the event (no more endless scrolling) if the function returns true
 *
 * Usage tips:
 *
 * The plugin is more useful when used with the callback function, which can then make AJAX calls to retrieve content.
 * The fire sequence argument (for the callback function) is useful for 'pagination'-like features.
 */

(function(jQuery){
	
	jQuery.fn.endlessScroll = function(options){
		
		var defaults = {
			bottomPixels: 50,
			fireOnce: true,
			fireDelay: 150,
			loader: "<br />Loading...<br />",
			data: "",
			insertAfter: "div:last",
			resetCounter: function(){ return false; },
			callback: function(){ return true; },
			ceaseFire: function(){ return false; }
		};
		
		var options = jQuery.extend(defaults, options);
		
		var firing       = true;
		var fired        = false;
		var fireSequence = 0;
		
		if(options.ceaseFire.apply(this) === true)
		{
			firing = false;
			jQuery(window).unbind("scroll");
		}
		
		if (firing === true)
		{
			jQuery(window).scroll(function(){
				if (jQuery(document).height() - jQuery(window).height() <= jQuery(window).scrollTop() + options.bottomPixels)
				{
					if ((options.fireOnce == false || (options.fireOnce == true && fired != true)))
					{
						//console.log("endless scroll");
						if(options.resetCounter.apply(this) === true)
						{
							fireSequence = 0;
						}
						
						fired = true;
						fireSequence++;
						
						if (jQuery("div#endless_scroll_loader").length < 1)
							jQuery(options.insertAfter).after("<div id=\"endless_scroll_loader\">" + options.loader + "</div>");
						
						if (typeof options.data == 'function')
						{
							//console.log("data == 'function'");
							data = options.data.apply(this);
						}
						else
						{
							data = options.data;
							//console.log("data " + data);
						}

						if (data !== false)
						{
							//console.log("endless scroll data !== false");
							//jQuery("div#endless_scroll_loader").remove();
							
							if (data.length > 0)
							{
								jQuery(options.insertAfter).after("<div id=\"endless_scroll_data\">" + data + "</div>");
								jQuery("div#endless_scroll_data").hide().fadeIn();
								jQuery("div#endless_scroll_data").removeAttr("id");
							}

							var args = new Array();
							args[0] = fireSequence;
							options.callback.apply(this, args);							
							
							if (options.fireDelay !== false || options.fireDelay !== 0)
							{
								// slight delay for preventing event firing twice
								jQuery(document.body).after("<div id=\"endless_scroll_marker\"></div>");
								jQuery("div#endless_scroll_marker").fadeTo(options.fireDelay, 1, function(){
									jQuery(this).remove();
									fired = false;
								});
							}
							else
							{
								fired = false;
							}
						}
					}
				}
			});
		}
	};
	
})(jQuery);

/* END: jquery.endless-scroll-1.3.js */
(function(jQuery) {
	var supportedCSS,styles=document.getElementsByTagName("head")[0].style,toCheck="transformProperty WebkitTransform OTransform msTransform".split(" "); //MozTransform <- firefox works slower with css!!!
	for (var a=0;a<toCheck.length;a++) if (styles[toCheck[a]] !== undefined) supportedCSS = toCheck[a];
	// Bad eval to preven google closure to remove it from code o_O
	// After compresion replace it back to var IE = 'v' == '\v'
	var IE = eval('"v"=="\v"');

	jQuery.fn.extend({
	ImageRotate:function(parameters)
	{
		// If this element is already a Wilq32.PhotoEffect object, skip creation
		if (this.Wilq32&&this.Wilq32.PhotoEffect) return;
		// parameters might be applied to many objects - so because we use them later - a fresh instance is needed 
		var paramClone = jQuery.extend(true, {}, parameters); 
		return (new Wilq32.PhotoEffect(this.get(0),paramClone))._temp;
	},
	rotate:function(parameters)
	{
		if (this.length===0||typeof parameters=="undefined") return;
		if (typeof parameters=="number") parameters={angle:parameters};
		var returned=[];
		for (var i=0,i0=this.length;i<i0;i++)
		{
		    var element=this.get(i);	
			if (typeof element.Wilq32 == "undefined") 
				returned.push(jQuery(jQuery(element).ImageRotate(parameters)));
			else 
			{
				element.Wilq32.PhotoEffect._rotate(parameters.angle);
			}
		}
		return returned;
	},

	rotateAnimation:function(parameters)
	{
		if (this.length===0||typeof parameters=="undefined") return;
		if (typeof parameters=="number") parameters={animateAngle:parameters};
		var returned=[];
		for (var i=0,i0=this.length;i<i0;i++)
		{	
		    var element=this.get(i);
			if (typeof element.Wilq32 == "undefined") 
				returned.push(jQuery(jQuery(element).ImageRotate(parameters)));
			else 
			{
				element.Wilq32.PhotoEffect.rotateAnimation(parameters);
			}
		}
		return returned;
	}

	});

	// Library agnostic interface

	Wilq32=window.Wilq32||{};
	Wilq32.PhotoEffect=(function(){
		function setupParameters(img,parameters){
			this._img = img;
			this._parameters = parameters || {};
			this._parameters.angle = this._angle = parameters.angle || 0;
			this._parameters.animateAngle = typeof parameters.animateAngle=="number" ? parameters.animateAngle : this._angle;
			this._parameters.easing = parameters.easing || function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }
			this._parameters.duration = parameters.duration || 1000;
		}
		if (supportedCSS) {
			return function(img,parameters){
				setupParameters.call(this,img,parameters);
				img.Wilq32 = {
					PhotoEffect: this
				};
				// TODO: needed to have a _temp variable accessible outside - used for object retrieval, 
				//        needs refactor + change name (temp is not self descriptive)
				// also need better passing values between functions - to FIX (remove _temp and _img at all)
				this._temp = this._img;
				this._BindEvents(img,this._parameters.bind);
				this._rotate(this._parameters.angle);
				if (this._parameters.angle!=this._parameters.animateAngle) this.rotateAnimation(this._parameters);
			}
		} else {
			return function(img,parameters) {
				setupParameters.call(this,img,parameters);			
				// Make sure that class and id are also copied - just in case you would like to refeer to an newly created object
				this._parameters.className=img.className;
				this._parameters.id=img.getAttribute('id');

				this._temp=document.createElement('span');
				this._temp.style.display="inline-block";
				this._temp.Wilq32 = 
					{
						PhotoEffect: this
					};
				img.parentNode.insertBefore(this._temp,img);
				
				if (img.complete) {
					this._Loader();
				} else {
					var self=this;
					// TODO: Remove jQuery dependency
					jQuery(this._img).bind("load", function()
					{
						self._Loader();
					});
				}
			}
		}
	})();

	Wilq32.PhotoEffect.prototype={

		rotateAnimation : function(parameters){
			this._parameters.animateAngle = parameters.animateAngle;
			this._parameters.callback = parameters.callback || this._parameters.callback || function(){};	
			this._animateStart();
		},

		_BindEvents:function(element,events){
			if (events) 
			{
				for (var a in events) if (events.hasOwnProperty(a)) 
					for (var b in events[a]) if (events[a].hasOwnProperty(b)) 
					// TODO: Remove jQuery dependency
						jQuery(element).bind(b,events[a][b]);
			}
		},

		_Loader:(function()
		{
			if (IE)
			return function()
			{
				var width=this._img.width;
				var height=this._img.height;
				this._img.parentNode.removeChild(this._img);
								
				this._vimage = this.createVMLNode('image');
				this._vimage.src=this._img.src;
				this._vimage.style.height=height+"px";
				this._vimage.style.width=width+"px";
				this._vimage.style.position="absolute"; // FIXES IE PROBLEM - its only rendered if its on absolute position!
				this._vimage.style.top = "0px";
				this._vimage.style.left = "0px";

				/* Group minifying a small 1px precision problem when rotating object */
				this._container =  this.createVMLNode('group');
				this._container.style.width=width;
				this._container.style.height=height;
				this._container.style.position="absolute";
				this._container.setAttribute('coordsize',width-1+','+(height-1)); // This -1, -1 trying to fix that ugly problem
				this._container.appendChild(this._vimage);
				
				this._temp.appendChild(this._container);
				this._temp.style.position="relative"; // FIXES IE PROBLEM
				this._temp.style.width=width+"px";
				this._temp.style.height=height+"px";
				this._temp.setAttribute('id',this._parameters.id);
				this._temp.className=this._parameters.className;			
				
				this._BindEvents(this._temp,this._parameters.bind);
				_finally.call(this);
				
			}
			else
			return function ()
			{
				this._temp.setAttribute('id',this._parameters.id);
				this._temp.className=this._parameters.className;
				
				this._width=this._img.width;
				this._height=this._img.height;
				this._widthHalf=this._width/2; // used for optimisation
				this._heightHalf=this._height/2;// used for optimisation
				
				//var _widthMax=Math.sqrt((this._height)*(this._height) + (this._width) * (this._width));
				var radianAngle = 5 * Math.PI / 180.0;
				var sinRads = Math.sin(radianAngle);
				var cosRads = Math.cos(radianAngle);

				var _widthMax=this._height*sinRads + this._width*cosRads;
				var _heightMax=this._height*cosRads + this._width*sinRads;

				this._widthAdd = _widthMax - this._width;
				this._heightAdd = _heightMax - this._height;	// widthMax because maxWidth=maxHeight
				this._widthAddHalf=this._widthAdd/2; // used for optimisation
				this._heightAddHalf=this._heightAdd/2;// used for optimisation
				
				this._img.parentNode.removeChild(this._img);	
				
				this._aspectW = ((parseInt(this._img.style.width,10)) || this._width)/this._img.width;
				this._aspectH = ((parseInt(this._img.style.height,10)) || this._height)/this._img.height;
				
				this._canvas=document.createElement('canvas');
				this._canvas.setAttribute('width',this._width);
				this._canvas.style.position="relative";
				this._canvas.style.left = -this._widthAddHalf + "px";
				this._canvas.style.top = -this._heightAddHalf + "px";
				this._canvas.Wilq32 = this._temp.Wilq32;
				
				this._temp.appendChild(this._canvas);
				this._temp.style.width=this._width+"px";
				this._temp.style.height=this._height+"px";
				
				this._BindEvents(this._canvas,this._parameters.bind);
				this._cnv=this._canvas.getContext('2d');
				_finally.call(this);
			}
			function _finally(){
				this._rotate(this._parameters.angle);
				if (this._parameters.angle!=this._parameters.animateAngle) this.rotateAnimation(this._parameters);		
			}

		})(),

		_animateStart:function()
		{	
			if (this._timer) {
				clearTimeout(this._timer);
			}
			this._animateStartTime = +new Date;
			this._animateStartAngle = this._angle;
			this._animate();
		},
		_animate:function()
		{
			var actualTime = +new Date;
			//var checkEnd = !!(Math.round(this._angle * 100 - this._parameters.animateAngle * 100)) == 0 && !!this._timer;
			var checkEnd = actualTime - this._animateStartTime > this._parameters.duration;
			if (this._parameters.callback && checkEnd){
				this._parameters.callback();
			}

			// TODO: Bug for animatedGif for static rotation ? (to test)
			if (checkEnd && !this._parameters.animatedGif) 
				{
					clearTimeout(this._timer);
				}
				else 
				{
					if (this._canvas||this._vimage||this._img) {
						// TODO: implement easing and speed of animation
						this._angle = this._parameters.easing(0, actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateAngle - this._animateStartAngle, this._parameters.duration);
						//this._angle-=(this._angle-this._parameters.animateAngle)*0.1;
						//if (typeof this._parameters.minAngle!="undefined") this._angle=Math.max(this._angle,this._parameters.minAngle);
						//if (typeof this._parameters.maxAngle!="undefined") this._angle=Math.min(this._angle,this._parameters.maxAngle);
						this._rotate((~~(this._angle*10))/10);
					}
					var self = this;
					this._timer = setTimeout(function()
					{
						self._animate.call(self);
					}, 10);
				}
		},

		_rotate : (function()
		{
			var rad = Math.PI/180;
			if (IE)
			return function(angle)
			{
				this._container.style.rotation=angle+"deg";
			}
			else if (supportedCSS)
			return function(angle){
				this._img.style[supportedCSS]="rotate("+angle+"deg)";
			}
			else 
			return function(angle)
			{

				if (!this._img.width||typeof angle!="number") return;
				angle=(angle%360)* rad;
				// clear canvas	
				this._canvas.width = this._width+this._widthAdd;
				this._canvas.height = this._height+this._heightAdd;
							
				// REMEMBER: all drawings are read from backwards.. so first function is translate, then rotate, then translate, translate..
				this._cnv.translate(this._widthAddHalf,this._heightAddHalf);	// at least center image on screen
				this._cnv.translate(this._widthHalf,this._heightHalf);			// we move image back to its orginal 
				this._cnv.rotate(angle);										// rotate image
				this._cnv.translate(-this._widthHalf,-this._heightHalf);		// move image to its center, so we can rotate around its center
				this._cnv.scale(this._aspectW,this._aspectH); // SCALE - if needed ;)
				this._cnv.drawImage(this._img, 0, 0);							// First - we draw image
			}

		})()
	}

	if (IE)
	{
	Wilq32.PhotoEffect.prototype.createVMLNode=(function(){
	document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
			try {
				!document.namespaces.rvml && document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
				return function (tagName) {
					return document.createElement('<rvml:' + tagName + ' class="rvml">');
				};
			} catch (e) {
				return function (tagName) {
					return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
				};
			}		
	})();
	}

	})(jQuery);
/*
 * All java script logic for the application.
 *
 * The code relies on the jQuery JS library to
 * be also loaded.
 */

var app = (function(jQuery){

	if (!jQuery) {
		alert(app.resources["MISSING_LIB"]);
		return null;
	}
	
	// Global dw private data goes here	

	// dw scope public
	return {
		URLs			: {}, // holds dw specific urls, check htmlhead.isml for some examples
		resources		: {},  // resource strings used in js
		constants		: {}, // platform constants, initialized in htmlhead.isml
		containerId		: "content",
		ProductCache	: null,  // app.Product object ref to the current/main product
		clearDivHtml	: "<div class=\"clear\"><!-- W3C Clearing --></div>",
		currencyCodes	: {}, // holds currency code/symbol for the site
		currentPage		: {}, // holds data about the current page, initialized in htmlhead.isml

		// default dialog box settings
		dialogSettings: {
				bgiframe: true, // this is required mainly for IE6 where drop downs bleed into dialogs!!! it depends on 
				autoOpen: false,
				buttons: {},
				modal: true,
				overlay: {
		    		opacity: 0.5,
		     		background: "black"
				},
		    	height: 530,
		    	width: 800,
		    	title: '',
		    	// show: "slow", This is causing dialog to break in jquery 1.3.2 rel, show: "slide" works but not desired
		    	//hide: "normal",
				//show: "slide",
				//hide: "fast",
		    	resizable: false,
		    	create: function(e, ui) {
					//console.log("dialogSettings dialog created");
					jQuery('.ui-dialog-titlebar').each(function () {
						if (jQuery('.ui-dialog-titlebar-pin', this).length == 0)
							jQuery('.ui-dialog-title', this).after('<div class="ui-dialog-titlebar-pin"></div>');
					 });
				},
		    	open: function(e, ui) {
					//console.log("dialogSettings dialog opened");
					jQuery('.ui-dialog-titlebar').each(function () {
						if (jQuery('.ui-dialog-titlebar-pin', this).length == 0)
							jQuery('.ui-dialog-title', this).after('<div class="ui-dialog-titlebar-pin"></div>');
					 });
				}
		},

		// default tooltip settings
		tooltipSettings: {
				delay: 0,
				showURL: false,
				extraClass: "tooltipshadow tooltipshadow02",
				top: 15,
				left: 5
		},

		// global form validator settings
		validatorSettings: {
			errorClass : 'errorclient',
			errorElement: 'span',
			
		    onfocusout: function(element) {
				if ( !this.checkable(element) ) {
					this.element(element);
				}				
			}
		},

		// app initializations called from jQuery(document).ready at the end of the file
		init: function() {
			// register initializations here
			
			// quick view dialog div
			jQuery("<div/>").attr("id", "QuickViewDialog").html(" ").appendTo(document.body);
						
			// minicart object initialization
			this.minicart.init();
			
			// execute unobtrusive js code
			this.execUjs();
			
			if (jQuery.fn.jcarousel) {
				// renders horizontal/vertical carousels for product slots
				jQuery('#horicarousel').jcarousel({
		        	scroll: 1,
					itemVisibleInCallback: app.captureCarouselRecommendations
			    });
	
			    jQuery('#vertcarousel').jcarousel({
			        scroll: 1,
					vertical: true,
					itemVisibleInCallback: app.captureCarouselRecommendations
			    });	
			    
			    // banner carousels
			    jQuery(".jcarousel-skin-bannerSlider").each(function() {
			    	
			    	if (jQuery(".jcarousel-container", this).length > 0) { 
			    		return;
			    	}
			    	
			    	var jthis = jQuery(this);
			    	var wrap = jthis.attr('wrap') || null;
			    	var auto = jthis.attr('auto') || 4;
			    	var scroll = jthis.attr('scroll') || 1;
			    	var anim = jthis.attr('animation') ? parseInt(jthis.attr('animation')) : 'normal';
			    	var pauseOnHover = jthis.attr('pauseOnHover') || true;
			    	var stopOnClick = jthis.attr('stopOnClick') || true;			    	

                	if (this.id.length == 0)
                		this.id = 'banner_' + Math.ceil(Math.random() * 999999).toString();
			    	
			    	jthis.jcarousel({ 
				    	auto: parseInt(auto),
				    	wrap: wrap,
				    	scroll: parseInt(scroll), 
				    	animation: anim, 
				    	initCallback: function (carousel) {

			    			if (stopOnClick == true || stopOnClick.toString().toLowerCase() == 'true') {
			    				
						    	// Disable autoscrolling if the user clicks the prev or next button.
						    	carousel.buttonNext.bind('click', function() {
						    		carousel.startAuto(0);
						    	});
			
						    	carousel.buttonPrev.bind('click', function() {
						    		carousel.startAuto(0);
						    	});
			    			}
			    			
					    	if (pauseOnHover == true || pauseOnHover.toString().toLowerCase() == 'true') {
					    		
						    	// Pause autoscrolling if the user moves with the cursor over the clip.
						    	carousel.clip.hover(function() {
						    		carousel.stopAuto();
						    	}, function() {
						    		carousel.startAuto();
						    	});
					    	}
					    }
				    });			    	
			    });
			}
		    
		    // open blog posts in a modal
			jQuery('#blogPost, .blogModal').click( function(e) {
				//console.log("blogModal click");
				var bpurl, bpid;
				
				try {
					if (jQuery(this).attr('bpurl')) {
						bpurl = jQuery(this).attr('bpurl');
					} else {
						bpid = jQuery.getUrlVar('bpid', this.href);
						//console.log('bpid=' + bpid);
						if (bpid)
							bpurl = app.URLs.blogPostModalUrl + '?bpid=' + bpid;
					}
				} catch (ex) { 
					bpurl = undefined;
				}
				
				if (bpurl) {
					e.preventDefault();		
					//console.log("blogModal go to dialog.open");			
					app.dialog.open(bpurl, ' ' );
					return false;				
				}
			});
		    
		    // generic open link in a modal
			jQuery('.contentOpenModal').click( function(e) {
				var curl;
				try {					
					if(this.tagName == 'A' || this.tagName == 'AREA') {
						curl = this.href;
					} else {
						if (jQuery(this).attr('curl')) 
							curl = jQuery(this).attr('curl');
						else
							curl = undefined;
					}
				} catch (ex) { 
					curl = undefined;
				}
				
				if (curl) {
					e.preventDefault();		
					//console.log(curl);
					app.dialog.open(curl, ' ' );
					return false;				
				}
			});
			
			// Close all dialogs when overlay is clicked
			jQuery('.ui-widget-overlay').live("click", function() {
				jQuery(".ui-dialog-content").dialog("close");
			}); 
		},
	
		// sub namespace app.ajax.* contains application specific ajax components
		ajax: {
			Success: "success",
			currentRequests: {}, // request cache

			// ajax request to get json response
			// @param - reqName - String - name of the request
			// @param - async - boolean - asynchronous or not
			// @param - url - String - uri for the request
			// @param - data - name/value pair data request
			// @param - callback - function - callback function to be called
			getJson: function(options) {
				var thisAjax = this;

				// do not bother if the request is already in progress
				// and let go null reqName
				if (!options.reqName || !this.currentRequests[options.reqName]) {
					this.currentRequests[options.reqName] = true;
					if(options.async == "undefined") options.async = true;
					// make the server call
					jQuery.ajax({
						///contentType: "application/json; charset=utf-8",
						//dataType: "json",
						url		: options.url,
						cache	: true,
						async	: options.async,
						data	: options.data,

						success: function(response, textStatus) {
							thisAjax.currentRequests[options.reqName] = false;

							if (!response.Success) {
								// handle failure
							}

							options.callback(response, textStatus);
						},

						error: function(request, textStatus, error) {
							if (textStatus === "parsererror") {								
								alert(app.resources["BAD_RESPONSE"] + '\n\n' + options.url);
							}
							
							options.callback({Success: false, data:{}});
						}
					});
				}
			},

			// ajax request to load html response in a given container
			// @param - reqName - String - name of the request
			// @param - url - String - uri for the request
			// @param - data - name/value pair data request
			// @param - callback - function - callback function to be called
			// @param - selector - string - id of the container div/span (#mycontainer) - it must start with '#'
			load: function(options) {
				//console.log("app.ajax.load");

				var thisAjax = this;

				// do not bother if the request is already in progress
				// and let go null reqname
				if (!options.reqName || !this.currentRequests[options.reqName]) {
					this.currentRequests[options.reqName] = true;
					// make the server call
					jQuery.ajax({
						dataType: "html",
						url		: options.url,
						cache	: true,
						data	: options.data,

						success: function(response, textStatus) {
							//console.log("app.ajax.load success");
							thisAjax.currentRequests[options.reqName] = false;
							
							if (options.selector) {
								//console.log("html into " + options.selector);
								jQuery(options.selector).html(response);
								//console.log("html loaded");
							}

							(options.callback != undefined ? options.callback(response, textStatus): null)
						},

						error: function(request, textStatus, error) {
							if (textStatus === "parsererror") {								
								alert(app.resources["BAD_RESPONSE"]);
							}

							options.callback(null, textStatus);
						}
					});
				}
			}
		},

		// loads a product into a given container div
		// params
		// 		containerId - id of the container div, if empty then global app.containerId is used
		//		source - source string e.g. search, cart etc.
		//		label - label for the add to cart button, default is Add to Cart
		//		url - url to get the product
		//		id - id of the product to get, is optional only used when url is empty
		getProduct: function(options) { // id, source, start
			var cId 		= options.containerId || app.containerId;
			var source 		= options.source || "";
			var a2cBtnLabel = options.label || null;

			// show small loading image
			jQuery("#"+cId).html(app.showProgress("productloader"));

			var productUrl = options.url ? options.url : app.util.appendParamToURL(app.URLs.getProductUrl, "pid", options.id);
						
			productUrl = app.util.appendParamToURL(productUrl, "source", source);

			app.ajax.load({selector: "#"+cId, url: productUrl, callback: function(responseText, textStatus){
				// update the Add to cart button label if one provided
				
					(a2cBtnLabel != null ? jQuery("#"+cId+" #addToBagBtn").removeClass('sendBtnImage').addClass('updateBagBtn') : '');
					setTimeout("app.metrics.trackQuickView()", 500);
			}});
		},

		// sub name space app.minicart.* provides functionality around the mini cart
		minicart: {
			url   : '',  // during page loading, the Demandware URL is stored here
			timer : null, // timer for automatic close of cart item view

			// initializations
			init: function() {
				// reset all the existing event bindings
				app.minicart.reset();

				// bind hover event to the cart total link at the top right corner
				jQuery(".minicarttotal").hover(function(e){(app.minicart.isShow() ? '': app.minicart.slide());});
			
				jQuery('.minicartcontent')
				.mouseenter(function(e) {
					clearTimeout(app.minicart.timer);
					app.minicart.timer = null;
				})
				.mouseleave(function(e) {
					clearTimeout(app.minicart.timer);
					app.minicart.timer = null;
					// after a time out automatically close it
					app.minicart.timer = setTimeout( 'app.minicart.close()', 30 );
				});

				// register close button event
				jQuery('.minicartcontent .minicartclose').click(function() {
					// reset all the events bindings
					app.minicart.reset();
					app.minicart.close(0);
				});
			},
			
			// returns a boolean if a minicart is visible/shown or hidden
			isShow: function() {
				return jQuery('.minicartcontent').css('display') == 'none' ? false : true;
			},
			
			// reset minicart
			reset: function() {
				jQuery(".minicarttotal").unbind("hover");
				jQuery('.minicartcontent').unbind("mouseenter").unbind("mouseleave");
				jQuery('.minicartcontent .minicartclose').unbind("click");
			},

			// shows the given content in the mini cart
			show: function(html) {
				jQuery('#minicart').html(html);
				
				// bind all the events
				app.minicart.init();
				
				if(app.minicart.suppressSlideDown && app.minicart.suppressSlideDown()) {
					// do nothing
					// the hook 'MiniCart.suppressSlideDown()' should have done the refresh
				}
				else {
					app.minicart.slide();
				}
			},
			
			// slide down and show the contents of the mini cart
			slide: function() {
				if(app.minicart.suppressSlideDown && app.minicart.suppressSlideDown()) {
					return;
				}
					
				// show the item
				jQuery('.minicartcontent').slideDown('slow');//show("slide", { direction: "up" }, 1000);

				clearTimeout(app.minicart.timer);
				app.minicart.timer = null;
					
				// after a time out automatically close it
				app.minicart.timer = setTimeout( 'app.minicart.close()', 6000 );
			},

			// adds a product to the mini cart
			// @params
			// progressImageSrc - source/url of the image to show when the item is being added to the cart
			// postdata - form data containing the product information to be added to mini-cart
			// callback - call back function/handler
			add: function(progressImageSrc, postdata, callback)
			{
				// get the data of the form as serialized string
				var postdata = postdata;

				// get button reference
				var addButtons = [];

				// the button to update
				var addButton = null;
				
				// it is an array of buttons, but we need only one all
				// other combinations are strange so far
				if (addButtons.length == 1)	{
					addButton = addButtons[0];
				}

				var previousImageSrc = null;

				// show progress indicator
				if (addButton != null) {
					previousImageSrc = addButton.src;
					addButton.src = progressImageSrc;
				}

				// handles successful add to cart
				var handlerFunc = function(req)	{
					// hide progress indicator
					if (addButton != null) {
						addButton.src = previousImageSrc;
					}

					// replace the content
					jQuery('#minicart').html(req);

					// bind all the events
					app.minicart.init();

					if(app.minicart.suppressSlideDown && app.minicart.suppressSlideDown()) {
						// do nothing
						// the hook 'MiniCart.suppressSlideDown()' should have done the refresh
					}
					else {
						app.minicart.slide();

						if (callback) callback();
					}
				}

				// handles add to cart error
				var errFunc = function(req) {
					// hide progress indicator
					if (addButton != null) {
						addButton.src = previousImageSrc;
					}				}

				// closes a previous mini cart
				app.minicart.close();

				// add the product
				jQuery.ajax({
								type	: "POST",
								url		: app.minicart.url,
								cache	: true,
								data	: postdata,
								success	: handlerFunc,
								error	: errFunc
							});
			},

			// closes the mini cart with given delay
			close: function(delay) {
				if ( app.minicart.timer != null || delay == 0) {
					clearTimeout( app.minicart.timer );
					app.minicart.timer = null;					
					jQuery('.minicartcontent').fadeOut(); // hide with "slide" causes to fire mouse enter/leave events sometimes infinitely thus changed it to fadeOut
				}
			},

			// hook which can be replaced by individual pages/page types (e.g. cart)
			suppressSlideDown: function() {
				return false;
			}
		},

		// close quick view dialog if open and refresh the page
		refreshCart: function() {
			app.quickView.close();

			// refresh without posting
			location.href = location.href;
		},

		// Product quick view object
		quickView: {
			categoryHierarchy: [],
			
			// bind browser events
			// options
			// buttonSelector - css selector for the quickview button
			// imageSelector - css selector for product image
			// buttonLinkSelector - css selector for quickview button link (a tag)
			// productNameLinkSelector - css selector for product name link (a tag)
			bindEvents: function(options) {
				//console.log('app.quickView.bindEvents');
				// hide quickview buttons
				jQuery(options.buttonSelector).hide();

				// hovering
				jQuery(options.imageSelector).unbind('hover').hover(
					function(e) {
						jQuery(this).children(options.buttonSelector).show();
						return false;
					},
					function(e) {
						jQuery(this).children(options.buttonSelector).hide();
						return false;
					}
				);

				// click binding for quick view
				jQuery(options.buttonLinkSelector).unbind('click').click(function(e) {
					app.quickView.show({url: this.href, source: "quickview"});
					return false;
				});

				/*
				To make bookmarking and browser back-button work correctly the browser URL needs 
				to change. To force that change we do a full-page load (not AJAX) when going from 
				search result page to product detail page.
				The implementation supports loading the product detail content with AJAX: just 
				uncomment this code block to bind the event handler.
				
				// click binding for name link
				if(options.productNameLinkSelector) {
					jQuery(options.productNameLinkSelector).click(function(e) {
						app.getProduct({url: this.href, source: "search"});
						return false;
					});
				}
				*/
			},

			// show quick view dialog and send request to the server to get the product
			// options.source - source of the dialog i.e. search/cart
			// options.url - product url
			show: function(options) {
				app.createDialog({id: 'QuickViewDialog', options: {
			    	height: 'auto',
			    	width: 800,
			    	dialogClass: 'quickview',
			       	resizable: false
			       	//,position:top
				}});

			    jQuery('#QuickViewDialog').dialog('open');
			    app.getProduct({containerId: "QuickViewDialog", source: options.source, url: options.url, label: options.label});
			},
			// close the quick view dialog
			close: function() {
				if (jQuery.fn.dialog)
					jQuery('#QuickViewDialog').dialog('close');
			}
		},

		// helper method to create a dialog with the given options
		// options - dialog box options along with id of the container
		createDialog: function(options) {
			//console.log("app.createDialog");
			var extOptions = jQuery.extend({}, app.dialogSettings, options.options);
			//alert(extOptions.width + ' x ' + extOptions.height);
			jQuery('#' + options.id).dialog(extOptions);
		},

		// shows tooltip popup
		// options
		// id - id of the container
		// options - tooltip popup options
		tooltip: function(options) {
			if (options.id.charAt(0) !== '#') {
				options.id = "#"+options.id;
			}
			jQuery(options.id).tooltip(jQuery.extend({}, app.tooltipSettings, options.options));
		},
		
		/**
		 * Unobtrusively build tooltips on the page.
		 * it looks for a tooltip class anchor which contains a div with tooltip-body class as the body container.
		 */
		tooltipDefault: function () {	 
			 jQuery(document).ready(function() {
				jQuery(".tooltip").tooltip(jQuery.extend({}, app.tooltipSettings, {	
						bodyHandler: function() {
							return jQuery(this).children(".tooltip-body").html();
						}
					}
				));
			 });			
		},

		// renders a progress indicator on the page; this function can be used
		// to indicate an ongoing progress to the user; the optional parameter "className"
		// can be used to attach an additional CSS class to the container
		showProgress : function(className) {
			var clazz = "loading";
			if (className) clazz += " " + className;
			return jQuery("<div class=\"" + clazz + "\"/>").append(jQuery("<img/>").attr("src", app.URLs.loadingSmallImg));
		},

		// validation plugin intialization
		validator: function() {
			// override default required field message
			jQuery.validator.messages.required = function($1, ele, $3) {
				return "";
			};
			
			/**
			 * Add phone validation method to jQuery validation plugin.
			 * Text fields must have 'phone' css class to be validated as phone
			 * phoneUS is copied from http://docs.jquery.com/Plugins/Validation/CustomMethods/phoneUS
			 */
			jQuery.validator.addMethod("phone", function(phone_number, element) {
				// find out the country code
				var data 	= jQuery(element).data("data");
				var country = (data && data.country && data.country != "") ? data.country : "US"; // default to US phone validation
				
				// preserve this instance
				var that = this;
				
				// country specific phone validation handlers
				var phoneCA,
					phoneUS = phoneCA = function() {
						phone_number = phone_number.replace(/\s+/g, ""); 
						return that.optional(element) || phone_number.length > 9 &&
							phone_number.match(/^(1-?)?(\([2-9]\d{2}\)|[2-9]\d{2})-?[2-9]\d{2}-?\d{4}$/);
					}
				
				window["eval"]("var phoneHandler = (typeof phone" + country + " != 'undefined') ? phone"+country+": null;");
				
			    // call the country specific phone validation handler
				return (phoneHandler && typeof phoneHandler == "function" ? phoneHandler() : true);
			}, app.resources["INVALID_PHONE"]);

			/**
			 * Add positive number validation method to jQuery validation plugin.
			 * Text fields must have 'positivenumber' css class to be validated as positivenumber
			 * it validates a number and throws error if it is below 0 or if it is not a number.
			 */
			jQuery.validator.addMethod("positivenumber", function(value, element) {
				if (value == '') return true;				
				return /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value) && Number(value) >= 0;
			}, ""); // "" should be replaced with error message if needed
			
			// register form validator for form elements
			// except for those which are marked "suppress"
			jQuery.each(jQuery("form:not(.suppress)"), function() {
				jQuery(this).validate(app.validatorSettings);
			});
		},

		/**
		 * grab anything inside a hidden dom element and append it to its immediate previous sibling
		 * as data attribute i.e. jQuery().data("data", hiddenStr)
		 * if the hidden data specifies json in the class then this routine would attempt to 
		 * convert the hidden data into json object before adding it as data attribute.
		 * after adding the data, the hidden span/element is removed from the DOM.
		 */
		hiddenData : function() {
			jQuery.each(jQuery(".hidden"), function() {
				var hiddenStr = jQuery(this).html();
				
				if (hiddenStr === "") {
					return;
				}
				
				// see if its a json string
				if (jQuery(this).hasClass("json")) {
					// try to parse it as a json
					try {
						hiddenStr = window["eval"]("(" + hiddenStr + ")");
					}
					catch(e) {}				
				}
				
				jQuery(this).prev().data("data", hiddenStr);
				
				jQuery(this).remove();
			});
		},
		
		/**
		 * Process country drop downs and attach a change listener so that phone field 
		 * can be validated properly based on the currently selected country.
		 */
		addCountryListener: function() {
			var countryHandler = function(e) {
				var selectedCountry = this.options[this.selectedIndex].value;
				// for each field of type phone in the current form, set its country as a data attribute
				// to be used while doing phone field validatiaon see app.validator addMethod.
				jQuery(this).parents("form:first").find("input.phone").each(function() {
					var data = jQuery(this).data("data");
					var currentData = (data && typeof data == 'object') ? data : {};
					currentData.country = selectedCountry;
					jQuery(this).data("data", currentData);
				});						
			}
			jQuery("select.country").change(countryHandler).each(countryHandler);
		},
		
		/**
		 * Unobtrusive js api calls go here.
		 */
		 execUjs: function() {
			// make global nav drop downs with superfish jquery plugin
			if (jQuery.fn.superfish) {
				jQuery('.categorymenu ul').addClass('sf-menu');			
				jQuery('ul.sf-menu').superfish({autoArrows	: false, dropShadows : false,speed:1  });
			}
			
			// process hidden data in the html markup and cnnvert it into data object(s)
			this.hiddenData();
			
			// initialize form validator plugin
			if (jQuery.fn.validate) {
				this.validator();
			}
			
			// process country form fields and attach listeners
			this.addCountryListener();
			
			// process tooltips on the page
			if (jQuery.fn.tooltip) {
				this.tooltipDefault();
			}
		},
		
		// capture recommendation of each product when it becomes visible in the carousel
		captureCarouselRecommendations : function(c, li, index, state) {
			jQuery(li).find(".captureproductid").each(function() {
				dw.ac.capture({id:this.innerHTML, type:dw.ac.EV_PRD_RECOMMENDATION});
			});
		},

		// sub namespace app.producttile.* contains utility functions for product tiles
		producttile : {
			// initializes all product tiles contained in the current page
			initAll: function() {
				// bind quick view button toggling and click
				var quickViewOptions = {
					buttonSelector: "div.producttile div.quickviewbutton",
					imageSelector: "div.producttile div.image",
					buttonLinkSelector: "div.producttile div.quickviewbutton a"
				};
				app.quickView.bindEvents(quickViewOptions);
				
				// prepare swatch palettes and thumbnails
				jQuery("div.producttile div.swatches div.invisible").hide();
				jQuery("div.producttile div.swatches a.swatch img.hiddenthumbnail").hide();
				
				// show the palette
				jQuery("div.producttile div.swatches > a").click(function(e) {
					var cont = jQuery(this).parent().find("div.palette");
					cont.show().focus();
					return false;
				});
				
				// hide the palette
				jQuery("div.producttile div.swatches div.invisible").mouseout(function(e) {
					// fix for event bubbling (http://www.quirksmode.org/js/events_mouse.html)
					if(!e) var e = window.event;
					var tg = (window.event) ? e.srcElement : e.target;
					if(tg.nodeName != 'DIV') return;
					var reltg = (e.relatedTarget) ? e.relatedTarget : e.toElement;
					while(reltg != tg && reltg.nodeName != 'BODY')
						reltg = reltg.parentNode
					if (reltg == tg) return;
					
					// mouseout took place when mouse actually left layer
					// handle event now
					jQuery(this).hide();
					return false;
				});
				
				// thumb nail toggling
				jQuery("div.producttile div.swatches div.palette a.swatch").bind("mouseover mouseout", function(e) {
					var swatch = jQuery(this);
					app.producttile.toggleVariationThumbnail(swatch);
				});
				
				// color swatch selection
				jQuery("div.producttile div.swatches div.palette a.swatch").click(function(e) {
					var swatch = jQuery(this);
					app.producttile.selectVariation(swatch);
					// omit following the swatch link
					return false;
				});
			},

			// selects a certain variation in a product tile, replaces the current image with
			// the correct variation image, changes the link to the detail
			// page and the quick view
			selectVariation : function(swatch) {
				// get the new and the original image
				var currentImg = jQuery(swatch.parents()[3]).find(".productimage img.product-image-primary");
				var newImg = swatch.children("img.swatchimage.hiddenthumbnail");
				
				// get new and original alternate images
				var currentAltImg = jQuery(swatch.parents()[3]).find(".productimage img.alternateimage");
				var newAltImg = swatch.children("img.swatchalternateimage.hiddenthumbnail");
				if(!currentImg || !newImg) return;
				
				// get the anchors
				var nameAnchor = swatch.parents(".producttile").find(".name a");
				var quickViewAnchor = swatch.parents(".producttile").find(".quickviewbutton a");
				var imageAnchor = swatch.parents(".producttile").find(".productimage a");
				
				// change the link url to the detail page and quick view
				var newUrl = swatch.attr("href");
				nameAnchor.attr("href", newUrl);
				quickViewAnchor.attr("href", newUrl);
				imageAnchor.attr("href", newUrl);
				
				// remove all current markers
				jQuery(swatch.parents()[0]).find("a.swatch").removeClass("selected");
				
				// mark swatch as selected
				swatch.addClass("selected");
				// we just remove the markers at the images; the actual elements
				// are correct, since they were already swapped by mouse over
				//currentImg.removeClass("temp original");
				//newImg.removeClass("temp original");
				
				var newCopy = newImg.clone().show().addClass('product-image-primary').removeClass("hiddenthumbnail");
				currentImg.replaceWith(newCopy[0]);
				
				var newAltCopy = newAltImg.clone().show().addClass('alternateimage').removeClass("hiddenthumbnail");
				currentAltImg.replaceWith(newAltCopy[0]);
			},

			// shows the thumb nail of a product; this function is used to
			// temporally display a new image and restore the original one
			toggleVariationThumbnail : function(swatch) {
				// get the new and the original image
				var currentImg = jQuery(swatch.parents()[3]).find(".productimage img");
				var newImg = swatch.children("img.hiddenthumbnail");
				if(!newImg || !currentImg) return;
				
				var selectedSwatch = jQuery(swatch.parents()[0]).find("a.selected");
				var selectedImg = selectedSwatch.children("img.hiddenthumbnail");

				// we do nothing in case the swatch is already selected
				if(swatch.hasClass("selected")) return;
				
				if(currentImg.hasClass("temp")) {
					// current image is just a temp image, restore original
					var newCopy = selectedImg.clone().show().removeClass("original hiddenthumbnail");
					currentImg.replaceWith(newCopy[0]);
				} else {
					// we create a copy of the swatch image, replace
					// the current and mark it with classes
					var newCopy = newImg.clone().show().addClass("temp").removeClass("hiddenthumbnail");
					currentImg.replaceWith(newCopy[0]);
				}
			}
		},

		// sub namespace app.util.* contains utility functions
		util : {
			// disables browser auto completion for the given element
			disableAutoComplete : function(elemId) {
				jQuery("#"+elemId).attr("autocomplete", "off");
			},

			// trims a prefix from a given string, this can be used to trim
			// a certain prefix from DOM element IDs for further processing on the ID
			trimPrefix : function(str, prefix) {
				return str.substring(prefix.length);
			},

			// appends the parameter with the given name and
			// value to the given url and returns the changed url
			appendParamToURL : function(url, name, value) {
				var c = "?";
				if(url.indexOf(c) != -1) {
					c = "&";
				}
				return url + c + name + "=" + encodeURIComponent(value);
			},

			// dynamically loads a CSS file
			loadCSSFile : function(url) {
				var elem = document.createElement("link");
				elem.setAttribute("rel", "stylesheet");
				elem.setAttribute("type", "text/css");
				elem.setAttribute("href", url);

				if(typeof elem != "undefined") {
					document.getElementsByTagName("head")[0].appendChild(elem);
					app.util.loadedCSSFiles.push(url);
				}
			},

			// array to keep track of the dynamically loaded CSS files
			loadedCSSFiles : [],

			// removes all dynamically loaded CSS files
			clearDynamicCSS : function() {
				for(var i=0; i<app.util.loadedCSSFiles.length; i++) {
					app.util.unloadCSSFile(app.util.loadedCSSFiles[i]);
				}
			},

			// dynamically unloads a CSS file
			unloadCSSFile : function(url) {
				var candidates = document.getElementsByTagName("link");
				for(var i=candidates.length; i>=0; i--) {
					if(candidates[i] && candidates[i].getAttribute("href") != null && candidates[i].getAttribute("href").indexOf(url) != -1) {
						candidates[i].parentNode.removeChild(candidates[i]);
					}
				}
			},

			// checks if cookies are enabled
			cookiesEnabled : function() {
				// first we'll split this cookie up into name/value pairs
				// note: document.cookie only returns name=value, not the other components
				var all_cookies = document.cookie.split( ';' );
				var temp_cookie = '';
				var cookie_name = '';
				var cookie_value = '';
				var cookie_found = false; // set boolean t/f default f

				for ( i = 0; i < all_cookies.length; i++ )
				{
					// now we'll split apart each name=value pair
					temp_cookie = all_cookies[i].split( '=' );

					// and trim left/right whitespace while we're at it
					cookie_name = temp_cookie[0].replace(/^\s+|\s+$/g, '');

					// if the extracted name matches the session cookie name 
					if ( cookie_name == 'sid' )
					{
						// we need to handle case where cookie has no value but exists (no = sign, that is):
						if ( temp_cookie.length > 1 )
						{
							cookie_value = unescape( temp_cookie[1].replace(/^\s+|\s+$/g, '') );
						}

						if (cookie_value.length > 0)
						{
							cookie_found = true;
							break;
						}
					}
					temp_cookie = null;
					cookie_name = '';
				}
				return cookie_found;
			},
			
			/**
			 * IE 6 multiple button submit issue work around.
			 * when a form has multiple buttons of submit type, then IE 6 submits all of them
			 * whenever form is submitted. e.g. Remove on cart page would remove the wrong item
			 * (always first item in the cart) because IE 6 submits all form data which includes all 
			 * remove links!!!
			 * the workaorund is to disable all buttons except the one which is being clicked.
			 * it should only be called for IE 6 (check out htmlhead.isml for usage)
			 */
			ie6ButtonFix: function() {
				jQuery('button').click(function(){
		        	// disable all buttons
		        	jQuery(this.form).find('button').attr("disabled", true);
		        	// enable the one being clicked
		            jQuery(this).attr("disabled", false);
			    });
			},
			
			isTouchDevice: function() {
			    return "ontouchstart" in window;
			},
			isAppleDevice: function() {
			    return (navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) || (navigator.userAgent.match(/iPad/i));
			}
		},

		// sub namespace app.dialog.* provides convenient functions to handle dialogs
		// note, that this code relies on single dialog modals (multi dialog, e.g. modal in modal is not supported)
		dialog : {
			// opens a dialog using the given url
			open : function(url, title, width, height) {
				//console.log("app.dialog.open");	
				// create the dialog container if not present already
				if(jQuery("#dialogcontainer").length == 0) {
					//console.log("create the dialog container");	
					jQuery(document.body).append('<div id="dialogcontainer"></div>');
				}

				// set a default title
				title = title || "&nbsp;";

				//console.log("go to ajax.load");	
				// finally load the dialog, set the dialog title
				app.ajax.load({
					selector: "#dialogcontainer",
					url: url,
					callback: function() {
						app.dialog.checkOpen(width, height);
						app.dialog.setTitle(title);

						setTimeout(function() {
							if (jQuery.browser.msie && jQuery.browser.version < 8) {
								var modalcontent = jQuery("#dialogcontainer :first-child");
								app.dialog.checkSize(modalcontent.outerWidth());
							}
							jQuery("#dialogcontainer").addClass('S7ZoomViewerDialog');
						}, 500);
					}
				});
			},

			// initializes the dialog with common dialog actions, like closing upon canceling
			// use this function in the dialog rendering template to re-bind common actions
			// upon dialog reload
			init : function() {
				jQuery(document).ready(function() {
					// binds the action to all buttons defining an action through the "name" attribute
					jQuery("#dialogcontainer button").each(function() {
						jQuery(this).click(function() {
							var action = jQuery(this).attr("name");
							if(action) {
								app.dialog.submit(action);
							}
							return false;
						});
					});

					// cancel button binding
					jQuery("#dialogCancelBtn").click(function() {
						app.dialog.close();
						return false;
					});
				});
			},

			// sets the title of the dialog
			setTitle : function(title) {
				jQuery("#dialogcontainer").dialog("option", "title", title);
			},

			// checks, if the dialog is in the state "open" and sets the state if not presently set
			// this function is implicitly called by app.dialog.open(url, title) in order to initialize
			// the dialog properly; use this function to recover the "open" state of a dialog
			checkOpen : function(width, height) {					
				if(jQuery("#dialogcontainer").dialog("isOpen") == false || jQuery("#dialogcontainer").hasClass('ui-dialog-content') == false)
				{
					//console.log("app.dialog.checkOpen");
					app.dialog.checkSize(width, height);
				}
			},

			checkSize : function(width, height) {	
				//console.log("app.dialog.checkSize");			
				if (!width) width = 'auto';
				if (!height) height = 'auto';
				//var currentwidth = jQuery( "#dialogcontainer" ).width();
				//var currentsetting = jQuery( "#dialogcontainer" ).dialog("option", "width");
				//alert('checkSize ' + width + ' x ' + height + ' current = ' + currentwidth);
				//if (width != currentwidth || width != currentsetting) {
					//alert('resize');
					app.createDialog({id: 'dialogcontainer', options: {
						width: width,
						height: height
					}});						
					jQuery("#dialogcontainer").dialog("open");
				//}
			},

			// closes the dialog and triggers the "close" event for the dialog
			close : function() {
				//console.log("app.dialog.close");
				jQuery("#dialogcontainer").dialog("close");
				jQuery(document.body).trigger("dialogClosed");
			},

			// attaches the given callback function upon dialog "close" event
			onClose : function(callback) {
				//console.log("app.dialog.onClose");
				if(callback != undefined) {
					jQuery(document.body).bind("dialogClosed", callback);
				}
			},

			// triggers the "apply" event for the dialog
			triggerApply : function() {
				jQuery(document.body).trigger("dialogApplied");
			},

			// attaches the given callback function upon dialog "apply" event
			onApply : function(callback) {
				if(callback != undefined) {
					jQuery(document.body).bind("dialogApplied", callback);
				}
			},

			// triggers the "delete" event for the dialog
			triggerDelete : function() {
				jQuery(document.body).trigger("dialogDeleted");
			},

			// attaches the given callback function upon dialog "delete" event
			onDelete : function(callback) {
				if(callback != undefined) {
					jQuery(document.body).bind("dialogDeleted", callback);
				}
			},

			// submits the dialog form with the given action
			submit : function(action) {
				// set the action
				jQuery("#dialogcontainer form").append("<input name=\"" + action + "\" type=\"hidden\" />");

				// serialize the form and get the post url
				var post = jQuery("#dialogcontainer form").serialize();
				var url = jQuery("#dialogcontainer form").attr("action");

				// post the data and replace current content with response content
		  		jQuery.ajax({
				   type: "POST",
				   url: url,
				   data: post,
				   dataType: "html",
				   success: function(data){
		  				jQuery("#dialogcontainer").empty().html(data);
				   },
				   failure: function(data) {
					   alert(app.resources["SERVER_ERROR"]);
				   }
				});
			}
		}
	}
})(jQuery);

//Other custom plugins
jQuery.fn.clearSelect = function() {
	/// <summary>Clear the options in a dropdownlist</summary>
	return this.each(function() {
		if (this.tagName == 'SELECT')
			this.options.length = 0;
	});
};
jQuery.fn.fillSelect = function(data, textProperty, valueProperty) {
	/// <summary>Fill a dropdownlist with json data</summary>
	/// <param name="data" type="Array">The JSON data used to populate the dropdownlist</param>
	/// <param name="textProperty" type="String">The name of the property of the JSON objects to use for the Option Text</param>
	/// <param name="valueProperty" type="String">The name of the property of the JSON objects to use for the Option Value</param>
	return this.clearSelect().each(function() {
		if (this.tagName == 'SELECT') {
			var dropdownList = this;
			if (!textProperty) textProperty = "text";
			if (!valueProperty) valueProperty = "value";
			jQuery.each(data, function(index, optionData) {
				var option = new Option(optionData[textProperty], optionData[valueProperty]);

				if (jQuery.browser.msie) {
					dropdownList.add(option);
				}
				else {
					dropdownList.add(option, null);
				}
			});
		}
	});
};
jQuery.fn.fadeSwap = function(imgUrl) {
	/// <summary>Fade image out, change src, fade in</summary>
	/// <param name="imgUrl" type="String">The url of the image to swap to after fading out</param>
	return this.each(function() {
		if (this.tagName == 'IMG') {
			if (this.src != imgUrl) {
				var imgTag = jQuery(this);
				var img = new Image();
				imgTag.fadeOut('fast', function() {
					jQuery(img).load(function() {
						imgTag.css('display', 'none').attr('src', this.src).fadeIn();
					}).attr('src', imgUrl);
				});
			}
		}
	});
};
jQuery.fn.watermark = function(options) {
    var method = typeof arguments[0] == "string" && arguments[0];
    return this.each(function() {
        if (this.tagName == 'INPUT') {
            var input = jQuery(this);
            var watermarkText = input.attr('watermark') ? input.attr('watermark') : input.attr('title');

            if (method == 'clear') {
                if (input.val() == watermarkText)
                    input.val('');
            } else {

                if (input.val() == '')
                    input.val(watermarkText);

                input.focus(function() {
                    var inputVal = input.val();
                    if (inputVal == '' || inputVal == watermarkText)
                        input.val('');
                    else
                        input.select();
                }).blur(function() {
                    if (input.val() == '')
                        input.val(watermarkText);
                });

            }
        }
    });
};
jQuery.fn.commaSeparatedText = function() {
	var texts = [];
	this.each(function() {
		texts.push( jQuery(this).text() );
	});
	return texts.join(',');
};

jQuery.fn.textAreaMaxLength = function () {
	/// <summary>Adds support for the maxlength attribute to textarea</summary>
	return this.each(function () {
		if (this.tagName.toLowerCase() == 'textarea') {
			var max = parseInt($(this).attr('maxlength'));

			//Add the key press event
			this.onkeypress = function (e) {
				//Get the event object (for IE)
				var ob = e || event;
				//Get the code of key pressed
				var keyCode = ob.keyCode;
				//Check if it has a selected text
				var hasSelection = document.selection ? document.selection.createRange().text.length > 0 : this.selectionStart != this.selectionEnd;
				//return false if can't write more
				return !(this.value.length >= max && (keyCode > 50 || keyCode == 32 || keyCode == 0 || keyCode == 13) && !ob.ctrlKey && !ob.altKey && !hasSelection);
			};

			//Add the key up event
			this.onkeyup = function () {
				//If the keypress fail and allow write more text that required, this event will remove it
				if (this.value.length > max) {
					this.value = this.value.substring(0, max);
				}
			};
		}
	});
};

jQuery.extend({
	 getUrlVars: function(url) {
	   var vars = [], hash;
	   if (!url)
		   url = window.location.href;
	   var hashes = url.slice(url.indexOf('?') + 1).split('&');
	   for(var i = 0; i < hashes.length; i++)
	   {
	     hash = hashes[i].split('=');
	     vars.push(hash[0]);
	     vars[hash[0]] = hash[1];
	   }
	   return vars;
	 },
	 getUrlVar: function(name, url){
	   return jQuery.getUrlVars(url)[name];
	 },
	 capitalizeWords: function(str){
		 return str.toLowerCase().replace( /(^|\s)([a-z])/g , function(m,p1,p2){ return p1+p2.toUpperCase(); } );
	 }
});

// application initialization on dom ready
jQuery(document).ready(function(){
	app.init();
	
	// add support for maxlength to textarea
	jQuery('textarea[maxlength]').textAreaMaxLength();
	
	// Launch email signup immediately if signup=true in querystring
	if (jQuery.getUrlVar('signup') == 'true')	{
		app.dialog.open(app.URLs.emailSignup, ' ');
	}
		
	// Launch email signup if button clicked
	jQuery('#btn_newsletter, #btn_newsletter2, #btn_newsletter3, .emailSignupModal').click(function(e) {
		e.preventDefault();
		app.dialog.open(app.URLs.emailSignup, ' ');
		return false;
	});
	
	// open music player window
	jQuery('#open-music-player').click(function(e){
		e.preventDefault();
		popwindow(app.URLs.musicPlayer);
		return false;
	});
	
	// rotated images / tooltips  (Tory's Must Haves)
	jQuery(".rotateable-image-wrapper").each(function(){
		var that = jQuery(this);
		var img = jQuery("img",this);
		var bubble = that.find(".tooltip-bubble");
		bubble.hide();
		bubble.append('<div class="tooltip-bubble-tip">&nbsp;</div>');
		var data = img.data('data');
		var angle = Math.floor(Math.random()*11)-5;
		if( data && 'rotateAngle' in data ) {
			angle = data.rotateAngle;
		} 
		img.rotate({
			angle:angle,
			bind:[{
				"mouseenter":function(){
					that.find('.tooltip-bubble').show();
				}
			}]
		});
		that.mouseleave(function(){
			that.find('.tooltip-bubble').hide();
		});
	});
	
	// enable swipe for iPad
	if (app.util.isTouchDevice()) {
		setTimeout(function () {
			jQuery('.jcarousel-list-horizontal').swipe({
				swipeLeft: function(obj) { 
					var id = jQuery(obj).attr('id');
					if (id.length > 0) {
						var carousel = jQuery('#' + id).data('jcarousel');
						if (carousel) 
							carousel.next();
					}
				},
				swipeRight: function(obj) {  
					var id = jQuery(obj).attr('id');
					if (id.length > 0) {
						var carousel = jQuery('#' + id).data('jcarousel');
						if (carousel) 
							carousel.prev();
					}		
				}
			});
		}, 1000);
	} 
});

var newwindow;
function popwindow(url)
{
	newwindow=window.open(url,newwindow,'height=400,width=400,resizable=no,scrollbars=no,toolbar=no,status=no,menubar=no,location=no,titlebar=yes');
	if (window.focus) {newwindow.focus()}
}
