﻿;
(function($) {
	//Namespace for browser cookies.
	$.cookie={
		getCookie: function(name) {
			var n=name+"=", l=document.cookie.replace(/; +/g, ";").split(";");
			for (var i=l.length-1; i>=0; i--) {
				if (l[i].indexOf(n)==0) return unescape(l[i].substring(n.length, l[i].length));
			}
			return null;
		},
		setCookie: function(n, v, d) {
			if (n) {
				var expDate=new Date((new Date()).getTime()+((d||0)*3600*24000));
				document.cookie=n+"="+escape(v)+";path=/"+(d?";expires="+expDate.toUTCString():"");
			}
		},
		deleteCookie: function(n) {
			$.cookie.setCookie(n, "", -1);
		},
		disableFeature: function(n, d) {
			if (n) { $.cookie.setCookie("Feature_"+n, "0", d||1); }
		},
		enableFeature: function(n) {
			if (n) { $.cookie.deleteCookie("Feature_"+n); }
		},
		isFeatureEnabled: function(n) {
			return !n||$.cookie.getCookie("Feature_"+n)!="0";
		}
	};

	//Namespace for deferred-execution (e.g. defering load of tracking-tags).
	$.deferExecute=(function() {
		//Private memebers.
		var q=[];

		//Return public object.
		return {
			defaults: {
				queueInterval: 50
			},
			queueJs: function(aF, n, d) {
				if ($.cookie.isFeatureEnabled(n)) {
					q.push({ actionFunc: aF, name: n, days: d });
					if (q.length==1) {
						$(document).ready(function() {
							setTimeout($.deferExecute.processQueue, $.deferExecute.defaults.queueInterval);
						});
					}
				}
			},
			processQueue: function() {
				var x=q.shift();
				while (x) {
					try {
						if ($.cookie.isFeatureEnabled(x.name)) { x.actionFunc.call(window); }
					} catch (err) { $.cookie.disableFeature(x.name, x.days); }
					x=q.shift();
				}
			},
			loadImgBeacon: function(u, n, d) {
				$.deferExecute.queueJs(function() {
					$('<img />')
                    .appendTo($("body"))
                    .attr({ src: u, width: 1, height: 1, border: 0, alt: "" })
                    .hide()
                    .bind("error", function() { $.cookie.disableFeature(n, d); });
				}, n, d);
			},
			loadJsFile: function(u, n, d) {
				$.deferExecute.queueJs(function() {
					var x=document.createElement("SCRIPT");
					x.setAttribute("type", "text/javascript");
					x.setAttribute("src", u);
					x.setAttribute("onerror", "$.cookie.disableFeature('"+n+"'"+(d?","+d:"")+");");
					document.getElementsByTagName("head")[0].appendChild(x);
				}, n, d);
			},
			loadIframe: function(u, n, d) {
				$.deferExecute.queueJs(function() {
					$('<iframe />')
                    .appendTo($("body"))
                    .attr({ src: u, width: 1, height: 1, frameBorder: 0 })
                    .bind("error", function() { $.cookie.disableFeature(n, d); });
				}, n, d);
			}
		};
	})();

	//Namespace for Google Analytics.
	$.ga=(function() {
		//Private members.
		var gaOrderId="Unknown";
		var _gaq=window['_gaq']||[];
		//Return public object.
		return {
			//Register custom variable (call before sendPageView).
			addCustomVar: function(index, name, value, opt_scope) {
				var _gaq=window['_gaq']||[];
				_gaq.push(["_setCustomVar", index, name, value, opt_scope||2]);
			},
			//Set custom variable
			setCustomVar: function(value) {
				var _gaq=window['_gaq']||[];
				if (value!=undefined) {
					_gaq.push(["_setVar", value]);
				}
			},
			//Register transaction (call before sendPageView).
			addTrans: function(orderId, productName, price) {
				var _gaq=window['_gaq']||[];
				gaOrderId=orderId||gaOrderId;
				_gaq.push(['_addTrans', gaOrderId, productName, price, 0, 0, "", "", ""]);
			},
			//Register transaction item (call after addTrans).
			addAddonItem: function(orderId, addOn, AddonName) {
				var _gaq=window['_gaq']||[];
				gaOrderId=orderId||gaOrderId;
				if (addOn!=undefined&&parseInt(addOn)>0) {
					_gaq.push(["_addItem", gaOrderId, addOn, AddonName, "Add-on", 0, 1]);
				}
			},
			//Register an addon item (call after addTrans).
			addItem: function(orderId, variation, method, period, price) {
				var _gaq=window['_gaq']||[];
				gaOrderId=orderId||gaOrderId;
				_gaq.push(["_addItem", gaOrderId, variation, method, period, price, 1]);
			},
			// Register a Virtual Page
			addVirtualPage: function(processName, pageName) {
				var _gaq=window['_gaq']||[];
				gaProcessName=processName||"Unknown";
				gaPageName=pageName||"Unknown";
				gaProductName=$("[id$='_hidProductId']").val()||"Unknown";
				trackingPage='/'+gaProcessName+'/'+gaProductName+'/'+gaPageName;
				_gaq.push(["_trackPageview", trackingPage]);
			}
		};
	})();

	//Namespace for MVT experiments.
	$.mvt=(function() {
		//Private members.
		var expList=[], expMap={}, allVariationMap={}, cookieName="MvtHash", setterCallback;

		var registerVariation=function(expName, varName, opts) {
			var varRef=expName+"."+varName, exp=expMap[expName];
			var x=$.extend(allVariationMap[varRef]||{ expName: expName, varName: varName, varRef: varRef }, opts);
			if (!exp) {
				expMap[expName]=exp={ expName: expName, ruleList: [] };
				expList.push(exp);
			}
			if (!allVariationMap[varRef]) {
				exp.ruleList.push(x);
				allVariationMap[varRef]=x;
			}
		};

		//Return public object.
		return {
			//Register an experiment variation function.
			registerVariationScript: function(expName, varName, func) {
				registerVariation(expName, varName, { actionFunc: func });
			},
			//Register weighting rule to decide how many visitors are served with this variation.
			registerVariationWeight: function(expName, varName, weight, enabled) {
				registerVariation(expName, varName, { weight: weight, enabled: typeof enabled=="undefined"?true:enabled });
			},
			//Specify name of cookie for storing experiment hash string.
			setCookieName: function(newName) {
				cookieName=newName;
			},
			//Specify function to call if hash string is changed.
			setSetterCallback: function(setterFunc) {
				setterCallback=setterFunc;
			},
			//Store hash-string representing current session's experiment variations.
			setHash: function(newHash) {
				if (newHash!=$.cookie.getCookie(cookieName)&&setterCallback) { setterCallback(newHash); }
				$.cookie.setCookie(cookieName, newHash);
			},
			//Get hash-string that represents all of current session's experiment variations.
			getHash: function() {
				var mvtHash=$.cookie.getCookie(cookieName);
				if (!mvtHash) {
					mvtHash=$.mvt.createHash();
					$.mvt.setHash(mvtHash);
				}
				return mvtHash;
			},
			//Return name of random variation.
			chooseRandomVariation: function(expName) {
				var varName, exp=expMap[expName], rand=Math.random()*100;
				if (exp) {
					$.each(exp.ruleList, function() {
						if (!varName&&this.enabled&&(rand-=(this.weight||999))<0) {
							varName=this.varName;
						}
					});
				}
				return varName;
			},
			//Create a brand new experiment hash string containing random variations.
			createHash: function() {
				var list=[];
				$.each(expList, function() {
					var varName=$.mvt.chooseRandomVariation(this.expName);
					if (varName) { list.push(this.expName+"."+varName); }
				});
				if (!list.length) { list.push("none"); }
				return list.join(";");
			},
			//Get name of variation running for specified session experiment.
			getVariation: function(hash, expName) {
				var re=new RegExp(";"+expName+"\.([^;]*);"), varName="";
				var x=(";"+hash+";").match(re);
				if (x&&x.length>1) {
					varName=x[1];
				}
				return varName;
			},
			//Set name of variation to run for specified session experiment.
			setVariation: function(hash, expName, varName) {
				var list=[];
				$.each(expList, function() {
					if (this.expName==expName) {
						list.push(expName+"."+varName);
					} else {
						var other=$.mvt.getVariation(hash, this.expName);
						if (other) { list.push(this.expName+"."+other); }
					}
				});
				if (!list.length) { list.push("none"); }
				return list.join(";");
			},
			//Run the relevent variation scripts for current page.
			runScripts: function() {
				var list=$.mvt.getHash().split(";");
				for (var i=0, len=list.length; i<len; i++) {
					var x=allVariationMap[list[i]];
					if (x&&$.isFunction(x.actionFunc)) { x.actionFunc(); }
				}
			}
		};
	})();

	//Class definition for generic page.  Contains a number of extension points that can be overridden by
	//later-loading JavaScript code (i.e. brand-specific or MVT).
	var Page=function(obj) {
		//If no object passed in then create new one.  Otherwise extend object with our page goodness.
		var This=obj||{};
		This.data=This.data||{};

		//==============================
		//Page stuff.
		//==============================
		(function() {
			var blockCount=0;

			//Called when we need to block browser interaction (normally for page-start, form-posts and AJAX).
			This.block=function(opacity) {
				if (This.onBlock&& ++blockCount==1&&top==self) { This.onBlock(opacity); }
			};
			This.unblock=function() {
				if (This.onUnblock&& --blockCount==0&&top==self) { This.onUnblock(); }
			};
		})();

		//==============================
		//AJAX stuff.
		//==============================
		This.createAjaxMap=function() {
			var mapData={};
			var t={
				addMapping: function(ajaxName, targetContext, targetName, initialValue) {
					mapData[ajaxName]={ context: targetContext, name: targetContext, initial: initialValue };
				},
				decode: function(data) {
					data=data||t;
					for (var n in data) {
						if (data.hasOwnProperty(n)) {
							var d=data[n], m=mapData[n];
							if ($.isPlainObject(d)&&typeof d.initial!="undefined") { d=d.initial };
							if (m&&m.context) { m.context[m.name||n]=d; }
						}
					}
				},
				getMappings: function() {
					return mapData;
				}
			};
			return t;
		};
		This.sendAjaxRequest=function(settings) {
			var responseFunc=function(data, textStatus, xmlHttpRequest) {
				var r={ Error: "No data returned from server" };
				if (data&&data.d) { r=JSON.parse(data.d); }
				if (r.Error!="") { showAjaxFailedAlert(r.Error); }
				if (r.Session=="") { redirectAjaxTimeout(); }
				if (settings.ajaxMap) { settings.ajaxMap.decode(r); }
				if (settings.callback) { settings.callback(r); }
				This.refresh();
				This.unblock();
			};

			var obj=$.extend({
				url: "NotSpecified",
				type: "POST",
				contentType: "application/json; charset=utf-8",
				data: settings.data||"",
				dataType: "json",
				success: responseFunc,
				error: responseFunc
			}, settings);

			if ($.isFunction(obj.data)) { obj.data=obj.data(); }
			if ($.isPlainObject(obj.data)) { obj.data=JSON.stringify(obj.data); }

			This.block(0);
			$.ajax(obj);
		};

		//Arrays that can be used for assigning jQuery treatments to page.
		var Treatments=function() {
			this.beforeRender=[];
			this.afterRender=[];
			this.onRefresh=[];

			this.process=function(treatmentsList) {
				for (var i=0; i<treatmentsList.length; i++) {
					treatmentsList[i]();
				}
			};
		};
		This.appTreatments=new Treatments();
		This.prodTreatments=new Treatments();
		This.brandTreatments=new Treatments();

		This.render=function() {
			This.appTreatments.process(This.appTreatments.beforeRender);
			This.prodTreatments.process(This.prodTreatments.beforeRender);
			This.brandTreatments.process(This.appTreatments.beforeRender);

			setTimeout(function() {
				This.appTreatments.process(This.appTreatments.afterRender);
				This.prodTreatments.process(This.prodTreatments.afterRender);
				This.brandTreatments.process(This.brandTreatments.afterRender);
				This.refresh();
			}, 30);
		}

		This.refresh=function() {
			This.appTreatments.process(This.appTreatments.onRefresh);
			This.prodTreatments.process(This.prodTreatments.onRefresh);
			This.brandTreatments.process(This.brandTreatments.onRefresh);
		};

		return This;
	};

	//Default page instance.  Separate from above so that we can unit-text the Page class easier.
	$.page=new Page();
})(jQuery);

