<commit_before>/* **************************************************************

   Copyright 2011 Zoovy, Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.



************************************************************** */



var zController = function(params) {
	this.u.dump('zController has been instantiated');
	if(typeof Prototype == 'object')	{
		alert("Oh No! you appear to have the prototype ajax library installed. This library is not compatible. Please change to a non-prototype theme (2011 series).");
		}
//zglobals is not required in the UI, but is for any
	else if(typeof zGlobals != 'object' && !app.vars.thisSessionIsAdmin)	{
//zGlobals not required in an admin session.
		alert("Uh Oh! A  required include (config.js) is not present. This document is required.");
		}
	else	{
		this.initialize(params);
		}
	}


jQuery.extend(zController.prototype, {


	
	initialize: function(P) {
//		this.u.dump(" -> initialize executed.");

//		app = this;
//		this.u.dump(P);
		app = $.extend(true,P,this); //deep extend to make sure nexted functions are preserved. If duplicates, 'this' will override P.
		app.model = zoovyModel(); // will return model as object. so references are app.model.dispatchThis et all.

		app.vars = app.vars || {};
		app.vars.platform = P.platform ? P.platform : 'webapp'; //webapp, ios, android
		app.vars.cid = null; //gets set on login. ??? I'm sure there's a reason why this is being saved outside the normal  object. Figure it out and document it.
		app.vars.fbUser = {};
		app.vars.protocol = document.location.protocol == 'https:' ? 'https:' : 'http:';

//used in conjunction with support/admin login. nukes entire local cache.
		if(app.u.getParameterByName('flush'))	{
			app.u.dump("uri param flush is true. CLEAR LOCAL STORAGE");
			localStorage.clear();
			}
		

		
//in some cases, such as the zoovy UI, zglobals may not be defined. If that's the case, certain vars, such as jqurl, must be passed in via P in initialize:
		if(typeof zGlobals == 'object')	{
			app.vars.profile = zGlobals.appSettings.profile;
			app.vars.username = zGlobals.appSettings.username;
//need to make sure the secureURL ends in a / always. doesn't seem to always come in that way via zGlobals
			app.vars.secureURL = zGlobals.appSettings.https_app_url;
			app.vars.sdomain = zGlobals.appSettings.sdomain;
			if('https:' == app.vars.protocol)	{app.vars.jqurl = zGlobals.appSettings.https_api_url;}
			else	{app.vars.jqurl = zGlobals.appSettings.http_api_url}
			}
		
// can be used to pass additional variables on all request and that get logged for certain requests (like createOrder). 
// default to blank, not 'null', or += below will start with 'undefined'.
//vars should be passed as key:value;  _v will start with zmvc:version.release.
		app.vars.passInDispatchV = '';  
		app.vars.release = app.vars.release || 'unspecified'; //will get overridden if set in P. this is default.

// += is used so that this is appended to anything passed in P.
		app.vars.passInDispatchV += 'browser:'+app.u.getBrowserInfo()+";OS:"+app.u.getOSInfo()+';'; //passed in model as part of dispatch Version. can be app specific.
		
		app.ext = app.ext || {}; //for holding extensions, including checkout.
		app.data = {}; //used to hold all data retrieved from ajax requests.
		
/*
app.templates holds a copy of each of the templates declared in an extension but defined in the view.
copying the template into memory was done for two reasons:
1. faster reference when template is needed.
2. solve any duplicate 'id' issues within the spec itself when original spec and cloned template are present.
   -> this solution was selected over adding a var for subbing in the templates because the interpolation was thought to be too heavy.
*/
		app.templates = {};

		app.q = {};
//queues are arrays, not objects, because order matters here. the model.js file outlines what each of these is used for.
		app.q = {mutable : new Array(), passive: new Array(), immutable : new Array()};
		
		app.globalAjax = {
			dataType : 'json',
			overrideAttempts : 0, //incremented when an override occurs. allows for a cease after X attempts.
			lastDispatch : null, //timestamp.
			passiveInterval : setInterval(function(){app.model.dispatchThis('passive')},5000), //auto-dispatch the passive q every five seconds.
			numRequestsPerPipe : 50,
			requests : {"mutable":{},"immutable":{},"passive":{}} //'holds' each ajax request. completed requests are removed.
			}; //holds ajax related vars.
		app.sessionId = null;
		app.vars.extensions = app.vars.extensions || [];
		
		app.handleAdminVars(); //needs to be late because it'll use some vars set above.
		app.onReady();

		}, //initialize

	handleAdminVars : function(){
//		app.u.dump("BEGIN handleAdminVars");
		var localVars = {}
		
		if(app.model.fetchData('authAdminLogin'))	{localVars = app.data.authAdminLogin}

//		app.u.dump(" -> localVars: "); app.u.dump(localVars);
		
		function setVars(id){
//			app.u.dump("GOT HERE!");
//			app.u.dump("-> "+id+": "+app.u.getParameterByName(id));
			if(app.vars[id])	{} //already set, do nothing.
//check url. these get priority of local so admin/support can overwrite.
			else if(app.u.getParameterByName(id))	{app.vars[id] = app.u.getParameterByName(id);} 
			else if(localVars[id])	{app.vars[id] = localVars[id]}
			else	{app.vars[id] = ''}//set to blank by default.
			}
		
		setVars('deviceid');
		setVars('userid');
		setVars('authtoken');
		setVars('domain');

//		app.u.dump(" -> app.vars: "); app.u.dump(app.vars);
		
		if(!app.vars.username && app.vars.userid && app.vars.userid.indexOf('@') > 0)	{
			app.u.dump("REMINDER!!! this is a temporary solution till username is returned in authAdminLogin");
			app.vars.username = app.vars.userid.split('@')[1];
			}
		
		},

	onReady : function()	{
		this.u.dump(" -> onReady executed. V: "+app.model.version+"|"+app.vars.release);
/*

session ID can be passed in via the params (for use in one page checkout on a non-ajax storefront). If one is passed, it must be validated as active session.
if no session id is passed, the getValidSessionID function will look to see if one is in local storage and use it or request a new one.
Exception - the controller is used for admin sessions too. if an admin session is being instantiated, forget about session id (cartid) for now.

A session ID could be passed in through vars, but app.sessionId isn't set until the session id has been verified OR the app is explicitly told to not validate the session.
*/
		if(app.vars.thisSessionIsAdmin && app.vars.sessionId)	{
//you'd get here in the UI.
			app.sessionId = app.vars.sessionId
			app.model.addExtensions(app.vars.extensions);
			}
		else if(app.vars.thisSessionIsAdmin)	{
			//for now, do nothing.  this may change later.
			app.model.addExtensions(app.vars.extensions);
			}
		else if(app.vars.sessionId)	{
			app.calls.appCartExists.init(app.vars.sessionId,{'callback':'handleTrySession','datapointer':'appCartExists'});
			app.model.dispatchThis('immutable');
			}
//if sessionId is set on URI, there's a good chance a redir just occured from non secure to secure.
		else if(app.u.isSet(app.u.getParameterByName('sessionId')))	{
			app.calls.appCartExists.init(app.u.getParameterByName('sessionId'),{'callback':'handleTrySession','datapointer':'appCartExists'});
			app.model.dispatchThis('immutable');
			}
//check localStorage
		else if(app.model.fetchSessionId())	{
			app.calls.appCartExists.init(app.model.fetchSessionId(),{'callback':'handleTrySession','datapointer':'appCartExists'});
			app.model.dispatchThis('immutable');			
			}
		else	{
			app.calls.getValidSessionID.init('handleNewSession');
			app.model.dispatchThis('immutable');
			}
		
//if third party inits are not done before extensions, the extensions can't use any vars loaded by third parties. yuck. would rather load our code first.
// -> EX: username from FB and OPC.
		app.u.handleThirdPartyInits();
		
		}, //onReady
					// //////////////////////////////////   CALLS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\		


/*
calls all have an 'init' as well as a 'dispatch'.
the init allows for the call to check if the data being retrieved is already in the session or local storage and, if so, avoid a request.
If the data is not there, or there's no data to be retrieved (a Set, for instance) the init will execute the dispatch.
*/
	calls : {

//all authentication calls use immutable Q
		authentication : {
//the authentication through FB sdk has already taken place and this is an internal server check to verify integrity.	
//the getFacebookUserData function also updates bill_email and adds the fb.user info into memory in a place quickly accessed
//the obj passed in is passed into the request as the _tag
			facebook : {
				init : function(tagObj)	{
					app.u.dump('BEGIN app.calls.authentication.facebook.init');
//Sanity - this call occurs AFTER a user has already logged in to facebook. So though server authentication may fail, the login still occured.
_gaq.push(['_trackEvent','Authentication','User Event','Logged in through Facebook']);
					this.dispatch(tagObj);
					return 1;
					},
				dispatch : function(tagObj)	{
//note - was using FB['_session'].access_token pre v-1202. don't know how long it wasn't working, but now using _authRepsonse.accessToken
					app.model.addDispatchToQ({'_cmd':'appVerifyTrustedPartner','partner':'facebook','appid':zGlobals.thirdParty.facebook.appId,'token':FB['_authResponse'].accessToken,'state':app.sessionID,"_tag":tagObj},'immutable');
					}
				}, //facebook
//obj is login/password.
//tagObj is everything that needs to be put into the tag node, including callback, datapointer and/or extension.
			zoovy : {
				init : function(obj,tagObj)	{
//					app.u.dump('BEGIN app.calls.authentication.zoovy.init ');
//					app.u.dump(' -> username: '+obj.login);
//email should be validated prior to call.  allows for more custom error handling based on use case (login form vs checkout login)
					app.calls.cartSet.init({"bill/email":obj.login}) //whether the login succeeds or not, set bill/email in /session
					this.dispatch(obj,tagObj);
					return 1;
					},
				dispatch : function(obj,tagObj)	{
					obj["_cmd"] = "appBuyerLogin";
					obj['method'] = "unsecure";
					obj["_tag"] = tagObj;
					obj["_tag"]["datapointer"] = "appBuyerLogin";
					
					app.model.addDispatchToQ(obj,'immutable');
					}
				}, //zoovy
			authAdminLogout : {
				init : function(tagObj)	{
					this.dispatch(tagObj);
					return 1;
					},
				dispatch : function(tagObj){
					app.model.addDispatchToQ({'_cmd':'authAdminLogout',"_tag":tagObj},'immutable');
					}
				}, //authAdminLogout
			accountLogout : {
				init : function(obj,tagObj)	{
					this.dispatch(obj,tagObj);
					return 1;
					},
				dispatch : function(obj,tagObj){

					}				
				},
			accountLogin : {
				init : function(obj,tagObj)	{
					this.dispatch(obj,tagObj);
					return 1;
					},
				dispatch : function(obj,tagObj){
					app.u.dump("Attempting to log in");
					obj._cmd = 'authAdminLogin';
					app.vars.userid = obj.userid;
					obj.authtype = "md5";
					obj.ts = app.u.ymdNow();
					obj.authid = Crypto.MD5(obj.password+obj.ts);
					obj._tag = tagObj || {};
					obj.device_notes = "";
					if(obj.persitentAuth)	{obj._tag.datapointer = "authAdminLogin"} //this is only saved locally IF 'keep me logged in' is true.
					delete obj.password;
					app.model.addDispatchToQ(obj,'immutable');
					}
				}

			}, //authentication

		authAccountCreate : {
			init : function(obj,tagObj){
				this.dispatch(obj,tagObj);
				},
			dispatch : function(obj,tagObj){
				obj._cmd = 'authUserRegister';
				tagObj = tagObj || {};
				obj['tag'] = tagObj;
				app.model.addDispatchToQ(obj,'immutable');
				}
			},


//always uses immutable Q
		getValidSessionID : {
			init : function(callback)	{
				this.dispatch(callback); 
				return 1;
				},
			dispatch : function(callback)	{
				app.model.addDispatchToQ({"_cmd":"appCartCreate","_tag":{"callback":callback}},'immutable');
				}
			},//getValidSessionID

//always uses immutable Q
//formerly canIHaveSession
		appCartExists : {
			init : function(cartid,tagObj)	{
//					app.u.dump('BEGIN app.calls.appCartExists');
				app.sessionId = cartid; //needed for the request. may get overwritten if not valid.
				this.dispatch(cartid,tagObj);
				return 1;
				},
			dispatch : function(cartid,tagObj)	{
				var obj = {};
				obj["_cmd"] = "appCartExists"; 
				obj["_tag"] = tagObj;
				app.model.addDispatchToQ(obj,'immutable');
				}
			}, //appCartExists

//for now, no fetch is done here. it's assumed if you execute this, you don't know who you are dealing with.
		whoAmI : {
			init : function(tagObj,Q)	{
				this.dispatch(tagObj,Q);
				return 1;
				},
			dispatch : function(tagObj,Q)	{
				tagObj = $.isEmptyObject(tagObj) ? {} : tagObj; 
				tagObj.datapointer = "whoAmI"
				app.model.addDispatchToQ({"_cmd":"whoAmI","_tag" : tagObj},Q);	
				}
			},//whoAmI

		canIUse : {
			init : function(flag,Q)	{
				this.dispatch(flag,Q);
				return 1;
				},
			dispatch : function(flag,Q)	{
				app.model.addDispatchToQ({"_cmd":"canIUse","flag":flag,"_tag":{"datapointer":"canIUse|"+flag}},Q);
				}
			}, //canIUse

//default immutable Q
//formerly setSessionVars
		cartSet : {
			init : function(obj,tagObj,Q)	{
				this.dispatch(obj,tagObj,Q);
				return 1;
				},
			dispatch : function(obj,tagObj,Q)	{
				if(!Q)	{Q = 'immutable'}
				obj["_cmd"] = "cartSet";
				if(tagObj)	{obj["_tag"] = tagObj;}
				app.model.addDispatchToQ(obj,Q);
				}
			}, //cartSet

		ping : {
			init : function(tagObj,Q)	{
				this.dispatch(tagObj,Q);
				return 1;
				},
			dispatch : function(tagObj,Q)	{
				app.model.addDispatchToQ({"_cmd":"ping","_tag":tagObj},Q); //get new session id.
				}
			}, //ping

//always uses immutable Q.
// used when a new session id must be generated, such as post-checkout.
		appCartCreate : {
			init : function(callback)	{
				this.dispatch(callback);
				return 1;
				},
			dispatch : function(callback)	{
				app.model.addDispatchToQ({"_cmd":"appCartCreate","_tag":{"callback":callback}},'immutable'); //get new session id.
				}
			}, //appCartCreate
			
		appProfileInfo : {
			init : function(profileID,tagObj,Q)	{
//				app.u.dump("BEGIN app.calls.appProfileInfo.init");
				var r = 0; //will return 1 if a request is needed. if zero is returned, all data needed was in local.
				tagObj = typeof tagObj == 'object' ? tagObj : {};
				tagObj.datapointer = 'appProfileInfo|'+profileID; //for now, override datapointer for consistency's sake.

				if(app.model.fetchData(tagObj.datapointer) == false)	{
					r = 1;
					this.dispatch(profileID,tagObj,Q);
					}
				else 	{
					app.u.handleCallback(tagObj)
					}

				return r;
				}, // init
			dispatch : function(profileID,tagObj,Q)	{
				obj = {};
				obj['_cmd'] = "appProfileInfo";
				obj['profile'] = profileID;
				obj['_tag'] = tagObj;
				app.model.addDispatchToQ(obj,Q);
				} // dispatch
			}, //appProfileInfo

//used to get a clean copy of the cart. ignores local/memory. used for logout.
		refreshCart : {
			init : function(tagObj,Q)	{
//				app.u.dump("BEGIN app.calls.refreshCart");
				var r = 1;
//if datapointer is fixed (set within call) it needs to be added prior to executing handleCallback (which will likely need datapointer to be set).
				tagObj = jQuery.isEmptyObject(tagObj) ? {} : tagObj;
				tagObj.datapointer = "cartDetail";
				this.dispatch(tagObj,Q);
				return r;
				},
			dispatch : function(tagObj,Q)	{
//				app.u.dump('BEGIN app.ext.store_cart.calls.cartDetail.dispatch');
				app.model.addDispatchToQ({"_cmd":"cartDetail","_tag": tagObj},Q);
				} 
			} // refreshCart removed comma from here line 383
		}, // calls

					// //////////////////////////////////   CALLBACKS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\
/*
Callbacks require should have an onSuccess.
Optionally, callbacks can have on onError. if you have a custom onError, no error messaging is displayed. This give the developer the opportunity to easily suppress errors for a given request/callback.
app.u.throwMessage(responseData); is the default error handler.
*/
	callbacks : {
		

		handleSessionStartResponse : {
			onSuccess : function(tagObj)	{
//				app.data[tagObj.datapointer]				
				},
			onError : function(responseData)	{
				app.u.throwMessage(responseData);
				}
			},//handleSessionStartResponse


		handleNewSession : {
//app.sessionID is set in the method. no need to set it here.
//use app.sessionID if you need it in the onSuccess.
//having a callback does allow for behavioral changes (update new session with old cart contents which may still be available.
			onSuccess : function(tagObj)	{
//				app.u.dump('BEGIN app.callbacks.handleNewSession.onSuccess');
// if there are any  extensions(and most likely there will be) add then to the controller.
// This is done here because a valid cart id is required.
				app.model.addExtensions(app.vars.extensions);
				}
			},//convertSessionToOrder

//executed when appCartExists is requested.
//app.sessionID is already set by this point. need to reset it onError.
// onError does NOT need to nuke app.sessionId because it's handled in handleResponse_appCartExists 
		handleTrySession : {
			onSuccess : function(tagObj)	{
//				app.u.dump('BEGIN app.callbacks.handleTrySession.onSuccess');
//				app.u.dump(" -> exists: "+app.data.appCartExists.exists);
				if(app.data.appCartExists.exists >= 1)	{
					app.u.dump(' -> valid session id.  Proceed.');
// if there are any  extensions(and most likely there will be) add then to the controller.
// This is done here because a valid cart id is required.
					app.model.addExtensions(app.vars.extensions);
//
					app.calls.whoAmI.init({'callback':'suppressErrors'},'passive'); //get this info when convenient.
					}
				else	{
					app.u.dump(' -> UH OH! invalid session ID. Generate a new session. nuke localStorage if domain is ssl.zoovy.com.');
					app.calls.appCartCreate.init('handleNewSession');
					app.model.dispatchThis('immutable');
					}
				}
			}, //handleTrySession
		
		handleAdminSession : {
			onSuccess : function(tagObj)	{
				app.u.dump('BEGIN app.callbacks.handleAdminSession.onSuccess');
//in DEV still. do not use this callback.
//				app.vars['_admin'] is set in the model.
				}
			},
			
		translateSelector : {
			onSuccess : function(tagObj)	{
//				app.u.dump("BEGIN callbacks.translateSelector");
				app.renderFunctions.translateSelector(tagObj.selector,app.data[tagObj.datapointer]);
				}
			},
	
		
//pass the following on _tag:
// parentID is the container id that the template instance is already in (should be created before call)
// templateID is the template that will get translated.
// the app.data.datapointer is what'll get passed in to the translate function as the data src. (ex: getProduct|PID)
		translateTemplate : 	{
			onSuccess : function(tagObj)	{
//				app.u.dump("BEGIN callbacks.translateTemplate");
				app.renderFunctions.translateTemplate(app.data[tagObj.datapointer],tagObj.parentID);
				}
			
			}, //translateTemplate
// a generic callback to allow for success messaging to be added. 
// pass message for what will be displayed.  For error messages, the system messaging is used.
		showMessaging : {
			onSuccess : function(tagObj)	{
//				app.u.dump("BEGIN app.callbacks.showMessaging");
				var msg = app.u.successMsgObject(tagObj.message);
				msg['_rtag'] = tagObj; //pass in tagObj as well, as that contains info for parentID.
				app.u.throwMessage(msg);
				}
			}, //showMessaging
/*
By default, error messaging is thrown to the appMessaging class. Sometimes, this needs to be suppressed. Add this callback and no errors will show.
ex: whoAmI call executed during app init. Don't want "we have no idea who you are" displayed as an error.
*/

		suppressErrors : {
			onSuccess : function(tagObj)	{
//dummy callback. do nothing.
				},
			onError : function(responseData,uuid)	{
//dummy callback. do nothing.
				app.u.dump("CAUTION! response for uuid ["+uuid+"] contained errors but they were suppresed. This may be perfectly normal (passive requests) but should be investigated.");
				}
			} //suppressErrors
			
			
		}, //callbacks




////////////////////////////////////   UTIL [u]    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\






	u : {


/*
when a call is requested but the data is local, execute this function.
it will check to see if a callback is defined and if it is, execute it.
smart enough to determine if an extension is involved and execute it from there.

-> this is a function because what is in the 'if' was being duplicated in almost every call 
that dealt with a fetch. So it was made into a function to make the calls tighter and also 
allow for global manipulation if needed later.

app.u.handleCallback(tagObj);
*/

		handleCallback : function(tagObj)	{
//			app.u.dump("BEGIN u.handleCallback");
			var callback;
			if(tagObj && tagObj.datapointer){app.data[tagObj.datapointer]['_rtag'] = tagObj} //updates obj in memory to have latest callback.
			if(tagObj && tagObj.callback){
//				app.u.dump(" -> executing callback ("+tagObj.callback+") in extension ("+tagObj.extension+")");
//most callbacks are likely in an extension, but support for 'root' callbacks is necessary.
//save path to callback so that we can verify the onSuccess is a function before executing (reduce JS errors with this check)
				callback = tagObj.extension ? app.ext[tagObj.extension].callbacks[tagObj.callback] : app.callbacks[tagObj.callback];
				if(typeof callback.onSuccess == 'function')
					callback.onSuccess(tagObj);
				}
			else	{
//				app.u.dump(" -> no callback was defined.");
				}
			},


/*
when quickstart is added, it will go through app.rq and use this function to add all resources as needed.
it'll then set app.rq.push to mirror this function.
*/

			handleResourceQ : function(arr)	{
				if(arr[0] == 'script')	{
					app.u.loadScript(arr[2],arr[3]);
					}
				else if(arr[0] == 'extension')	{
//					app.u.dump(" -> extension loading: "+arr[2]+" callback: "+arr[4]);
					var tmpObj = {"namespace":arr[2],"filename":arr[3],"callback":arr[4]}; //
					app.vars.extensions.push(tmpObj); // keep the full list just in case.
					app.u.loadScript(arr[3],function(){
						app.model.fetchExtension(tmpObj); 
						});
					app.model.executeCallbacksWhenExtensionsAreReady([tmpObj]); //function wants an array of objects.
					}
				else if(arr[0] == 'templateFunction')	{
					app.ext.myRIA.template[arr[1]][arr[2]].push(arr[3]);
					}
				else if(arr[0] == 'css')	{
					app.u.loadCSSFile(arr[2],arr[3] || null);
					}
				else	{
		//currently, this function is intended for pass 0 only, so if an item isn't pass 0,do nothing with it.
					}

				
				},


//filename is full path of .css file (or valid relative path)
//domID is optional id to add to <link> allows for removal or changing later.
//if you pass a domID that already exists, that file is 'saved over'.
		loadCSSFile : function(filename,domID){
			if(filename && domID && document.getElementById(domID))	{
				document.getElementById(domID).href = filename; //overwrite existing .css file. effectively removes old in favor of new.
				}
			else if(filename)	{
// Create element node - link object
				var fileref=document.createElement('link');
	
// Set link object attributes like
// <link rel="stylesheet" type="text/css" href="filename" />
				fileref.setAttribute('rel', 'stylesheet');
				fileref.setAttribute('type', 'text/css');
				fileref.setAttribute('href', filename);
				if(domID)	{fileref.setAttribute('id', domID);}
// Append link object inside html's head
				document.getElementsByTagName("head")[0].appendChild(fileref);
				}
			else	{
				//doh! no filename.
				}
			},



		printByElementID : function(id)	{
//				app.u.dump("BEGIN myRIA.a.printByElementID");
			if(id && $('#'+id).length)	{
				var html="<html><body style='font-family:sans-serif;'>";
				html+= document.getElementById(id).innerHTML;
				html+="</body></html>";
				
				var printWin = window.open('','','left=0,top=0,width=600,height=600,toolbar=0,scrollbars=0,status=0');
				printWin.document.write(html);
				printWin.document.close();
				printWin.focus();
				printWin.print();
				printWin.close();
				}
			else	{
				app.u.dump("WARNING! - myRIA.a.printByElementID executed but not ID was passed ["+id+"] or was not found on DOM [$('#'+"+id+").length"+$('#'+id).length+"].");
				}
			},

//pass in a string (my.string.has.dots) and a nested data object, and the dots in the string will map to the object and return the value.
//ex:  ('a.b',obj) where obj = {a:{b:'go pack go'}} -> this would return 'go pack go'
//will be used in updates to translator.

//http://stackoverflow.com/questions/5240785/split-abc/5240797#5240797
		getObjValFromString : function (s,obj,char)	{
			cart = char || '.';
			var o=obj, attrs=s.split(char);
			while (attrs.length > 0) {
				o = o[attrs.shift()];
				if (!o) {o= null; break;}
				}
			return o;

			},



//http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
		guidGenerator : function() {
			var S4 = function() {
				return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
				};
			return (S4()+S4()+S4()+S4()+S4()+S4()+S4()+S4());
			},

//when sending the user into a waiting pattern, pass true.  This changes the cursor into the browser 'waiting' state
//don't forget to turn this off when done.
		handleWait : function (tfu){
			if(tfu == true)	{
				$('body').css({'cursor':'wait'})
				}
			else	{
				$('body').css({'cursor':'auto'})
				}
			},
			

//jump to an anchor. can use a name='' or id=''.  anchor is used in function name because that's the common name for this type of action. do not need to pass # sign.
		jumpToAnchor : function(id)	{
			window.location.hash=id;
			},
//uses throw message, but always adds the same generic message. value of 'err' is output w/ dump.
//this should only be used for app errors (errors thrown from within the MVC, not as a result of an API call, in which case throwMessage should be used (handles request errors nicely)
		throwGMessage : function(err,parentID){
			var msg = this.errMsgObject("Well this is embarrassing. Something bad happened. Please try that again. If this error persists, please contact the site administrator.","#");
			if(parentID)	{msg.parentID = parentID}
			this.throwMessage(msg);
			app.u.dump(err);
			},
/*
msg could be a string or an object.
if an object, could be: {errid,errmsg,errtype}   OR   {msg_X_txt,msg_X_type,msg_X_id}
 -> if msg_X format, X will be an integer and _msgs will be set to indicate the # of messages.

$target - a jquery object of the target/destination for the message itself. Will check err for parentID, targetID and if not present, check to see if globalMessaging is present AND visible.  If not visible, will open modal.
returns the id of the message, so that an action can be easily added if needed (onclick or timeout w/ a hide, etc)

persistant - this can be passed in as part of the msg object or a separate param. This was done because repeatedly, error messaging in the control
and model that needed to be permanently displayed had to be converted into an object just for that and one line of code was turning into three.
*/
		throwMessage : function(msg,persistant){
//			app.u.dump("BEGIN app.u.throwMessage");
//			app.u.dump(" -> msg follows: "); app.u.dump(msg);
			var $target; //where the app message will be appended.
			var messageClass = "appMessage_"+this.guidGenerator(); //the class added to the container of the message. message 'may' appear in multiple locations, so a class is used instead of an id.
			var r = messageClass; //what is returned. set to false if no good error message found. set to htmlID is error found. 
			var $container = $("<div \/>").addClass('appMessage').addClass(messageClass).append("<button onClick='$(\"."+messageClass+"\").toggle(); return false;' class='ui-state-default ui-corner-all floatRight stdMargin'><span class='ui-button ui-icon-circle-close'>X</span><\/button>");
//make sure the good-ole fallback destination for errors exists and is a modal.
			var $globalDefault = $('#globalErrorMessaging')
			if	($globalDefault.length == 0)	{
				$globalDefault = $("<div \/>").attr({'id':'globalErrorMessaging'}).appendTo('body');
				$globalDefault.dialog({autoOpen:false,modal:true})
				}

			if(typeof msg === 'string')	{
				if($('.appMessaging:visible').length > 0)	{
//					app.u.dump(" -> target is appMessaging.");
					$target = $('.appMessaging');
					}
				else	{
//					app.u.dump(" -> target is globalDefault.");
					$target = $globalDefault;
					$target.dialog('open');
					}
				$container.append(this.formatMessage(msg)).prependTo($target); //always put new messages at the top.
				}
			else if(typeof msg === 'object')	{
				persistant = msg.persistant || false;
				msg.messageClass = messageClass;
				if(msg.parentID){$target = $('#'+msg.parentID);}
				else if(typeof msg['_rtag'] == 'object' && msg['_rtag'].parentID && $('#'+msg['_rtag'].parentID).length)	{$target = $('#'+msg['_rtag'].parentID);}
				else if(typeof msg['_rtag'] == 'object' && msg['_rtag'].targetID && $('#'+msg['_rtag'].targetID).length)	{app.u.dump("USING targetID for errors"); $target = $('#'+msg['_rtag'].targetID)}
				else if($('.appMessaging:visible').length > 0)	{$target = $('.appMessaging');}
				else	{
					$target = $globalDefault;
					$target.dialog('open');
					}
				$container.append(this.formatResponseErrors(msg)).prependTo($target);
				}
			else	{
				app.u.dump("WARNING! - unknown type ["+typeof err+"] set on parameter passed into app.u.throwMessage");
				r = false; //don't return an html id.
				}
			if(persistant !== true)	{
//the message could be removed manually prior to the callback being executed, so don't animate if that's the case. (avoids popping issue)
				setTimeout(function(){
					if($('.'+messageClass).is(':visible'))	{
						$('.'+messageClass).slideUp(2000);
						}
					},10000); //shrink message after a short display period
				}
//get rid of all the loading gfx in the target so users know the process has stopped.
			$target.removeClass('loadingBG');
			if(typeof $.hideLoading == 'function'){$target.hideLoading()} //used in UI. plan on switching everything applicable to this.
 
			return r;
			},



// The next functions are simple ways to create success or error objects.
// pass in a message and the entire success object is returned.
// keep this simple. don't add support for icons or message type. If that degree of control is needed, build your own object and pass that in.
// function used in store_product (and probably more)
		successMsgObject : function(msg)	{
			return {'errid':'#','errmsg':msg,'errtype':'success','uiIcon':'check','uiClass':'success'}
			},

		uiMsgObject : function(msg)	{
			var obj; //what is returned.
			if(msg.indexOf('|') == -1)	{
				obj = {'errid':'#','errmsg':msg,'errtype':'unknown','uiIcon':'ui-icon-z-ise','uiClass':'z-hint'} //some legacy messaging is pipeless (edit order, tracking, for instance).
				}
			else	{
				var eType = msg.split('|')[0].toLowerCase();
				obj = {'errid':'#','errmsg':msg.split('|')[1],'errtype':msg.split('|')[0],'uiIcon':'z-'+eType,'uiClass':'z-'+eType}
				}
			return obj;
			},

		errMsgObject : function(msg,errid)	{
			return {'errid':errid,'errmsg':msg,'errtype':'apperr','uiIcon':'alert','uiClass':'error'}
			},
		statusMsgObject : function(msg)	{
			return {'errid':'#','errmsg':msg,'errtype':'statusupdate','uiIcon':'transferthick-e-w','uiClass':'statusupdate'}
			},
		youErrObject : function(errmsg,errid)	{
			return {'errid':errid,'errmsg':errmsg,'errtype':'youerr','uiIcon':'alert','uiClass':'highlight'}
			},

/*
pass in the responseData from the api request and this will return all the errors as their own lineitems.
Request that can have multiple errors come back have _msg... in response (like adding to cart)
Other requests will have errid set
This function will have both cases.
*/

		formatResponseErrors : function(d)	{
			var r;
			if(!d)	{
				r = 'unknown error has occured';
				}
			else if(typeof d == 'string')	{
				r = app.u.formatMessage(d);
				}
			else if(typeof d == 'object')	{
				r = "";
				if(d['_msgs'])	{
					for(var i = 1; i <= d['_msgs']; i += 1)	{
//						app.u.dump(d['_msg_'+i+'_type']+": "+d['_msg_'+i+'_id']);
						r += "<div class='"+d['_msg_'+i+'_type']+"'>"+d['_msg_'+i+'_txt']+" [id: "+d['_msg_'+i+'_id']+"]<\/div>";
						}
					}
				else if(d['errid'])	{
					r += "<div class='"+d.errtype+" appMessageTxt'>"+d.errmsg+"<br \/>Error ID: "+d.errid+"<\/div>";
//					app.u.dump("WARNGING! error occured. id: "+d.errid+" and type: "+d.errtype+" and msg: "+errmsg);
					}
//the validate order request returns a list of issues.
				else if(d['@issues'])	{
					var L = d['@issues'].length;
					for(var i = 0; i < L; i += 1)	{
						r += "<div>"+d['@issues'][i][3]+"<\/div>";
						}
					}
				d.message = r; //pass in entire original object as it may contain uiClass and/or uiIcon or other params<commit_msg>throwMessage now only shows errid if it has an error<commit_after>/* **************************************************************

   Copyright 2011 Zoovy, Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.



************************************************************** */



var zController = function(params) {
	this.u.dump('zController has been instantiated');
	if(typeof Prototype == 'object')	{
		alert("Oh No! you appear to have the prototype ajax library installed. This library is not compatible. Please change to a non-prototype theme (2011 series).");
		}
//zglobals is not required in the UI, but is for any
	else if(typeof zGlobals != 'object' && !app.vars.thisSessionIsAdmin)	{
//zGlobals not required in an admin session.
		alert("Uh Oh! A  required include (config.js) is not present. This document is required.");
		}
	else	{
		this.initialize(params);
		}
	}


jQuery.extend(zController.prototype, {


	
	initialize: function(P) {
//		this.u.dump(" -> initialize executed.");

//		app = this;
//		this.u.dump(P);
		app = $.extend(true,P,this); //deep extend to make sure nexted functions are preserved. If duplicates, 'this' will override P.
		app.model = zoovyModel(); // will return model as object. so references are app.model.dispatchThis et all.

		app.vars = app.vars || {};
		app.vars.platform = P.platform ? P.platform : 'webapp'; //webapp, ios, android
		app.vars.cid = null; //gets set on login. ??? I'm sure there's a reason why this is being saved outside the normal  object. Figure it out and document it.
		app.vars.fbUser = {};
		app.vars.protocol = document.location.protocol == 'https:' ? 'https:' : 'http:';

//used in conjunction with support/admin login. nukes entire local cache.
		if(app.u.getParameterByName('flush'))	{
			app.u.dump("uri param flush is true. CLEAR LOCAL STORAGE");
			localStorage.clear();
			}
		

		
//in some cases, such as the zoovy UI, zglobals may not be defined. If that's the case, certain vars, such as jqurl, must be passed in via P in initialize:
		if(typeof zGlobals == 'object')	{
			app.vars.profile = zGlobals.appSettings.profile;
			app.vars.username = zGlobals.appSettings.username;
//need to make sure the secureURL ends in a / always. doesn't seem to always come in that way via zGlobals
			app.vars.secureURL = zGlobals.appSettings.https_app_url;
			app.vars.sdomain = zGlobals.appSettings.sdomain;
			if('https:' == app.vars.protocol)	{app.vars.jqurl = zGlobals.appSettings.https_api_url;}
			else	{app.vars.jqurl = zGlobals.appSettings.http_api_url}
			}
		
// can be used to pass additional variables on all request and that get logged for certain requests (like createOrder). 
// default to blank, not 'null', or += below will start with 'undefined'.
//vars should be passed as key:value;  _v will start with zmvc:version.release.
		app.vars.passInDispatchV = '';  
		app.vars.release = app.vars.release || 'unspecified'; //will get overridden if set in P. this is default.

// += is used so that this is appended to anything passed in P.
		app.vars.passInDispatchV += 'browser:'+app.u.getBrowserInfo()+";OS:"+app.u.getOSInfo()+';'; //passed in model as part of dispatch Version. can be app specific.
		
		app.ext = app.ext || {}; //for holding extensions, including checkout.
		app.data = {}; //used to hold all data retrieved from ajax requests.
		
/*
app.templates holds a copy of each of the templates declared in an extension but defined in the view.
copying the template into memory was done for two reasons:
1. faster reference when template is needed.
2. solve any duplicate 'id' issues within the spec itself when original spec and cloned template are present.
   -> this solution was selected over adding a var for subbing in the templates because the interpolation was thought to be too heavy.
*/
		app.templates = {};

		app.q = {};
//queues are arrays, not objects, because order matters here. the model.js file outlines what each of these is used for.
		app.q = {mutable : new Array(), passive: new Array(), immutable : new Array()};
		
		app.globalAjax = {
			dataType : 'json',
			overrideAttempts : 0, //incremented when an override occurs. allows for a cease after X attempts.
			lastDispatch : null, //timestamp.
			passiveInterval : setInterval(function(){app.model.dispatchThis('passive')},5000), //auto-dispatch the passive q every five seconds.
			numRequestsPerPipe : 50,
			requests : {"mutable":{},"immutable":{},"passive":{}} //'holds' each ajax request. completed requests are removed.
			}; //holds ajax related vars.
		app.sessionId = null;
		app.vars.extensions = app.vars.extensions || [];
		
		app.handleAdminVars(); //needs to be late because it'll use some vars set above.
		app.onReady();

		}, //initialize

	handleAdminVars : function(){
//		app.u.dump("BEGIN handleAdminVars");
		var localVars = {}
		
		if(app.model.fetchData('authAdminLogin'))	{localVars = app.data.authAdminLogin}

//		app.u.dump(" -> localVars: "); app.u.dump(localVars);
		
		function setVars(id){
//			app.u.dump("GOT HERE!");
//			app.u.dump("-> "+id+": "+app.u.getParameterByName(id));
			if(app.vars[id])	{} //already set, do nothing.
//check url. these get priority of local so admin/support can overwrite.
			else if(app.u.getParameterByName(id))	{app.vars[id] = app.u.getParameterByName(id);} 
			else if(localVars[id])	{app.vars[id] = localVars[id]}
			else	{app.vars[id] = ''}//set to blank by default.
			}
		
		setVars('deviceid');
		setVars('userid');
		setVars('authtoken');
		setVars('domain');

//		app.u.dump(" -> app.vars: "); app.u.dump(app.vars);
		
		if(!app.vars.username && app.vars.userid && app.vars.userid.indexOf('@') > 0)	{
			app.u.dump("REMINDER!!! this is a temporary solution till username is returned in authAdminLogin");
			app.vars.username = app.vars.userid.split('@')[1];
			}
		
		},

	onReady : function()	{
		this.u.dump(" -> onReady executed. V: "+app.model.version+"|"+app.vars.release);
/*

session ID can be passed in via the params (for use in one page checkout on a non-ajax storefront). If one is passed, it must be validated as active session.
if no session id is passed, the getValidSessionID function will look to see if one is in local storage and use it or request a new one.
Exception - the controller is used for admin sessions too. if an admin session is being instantiated, forget about session id (cartid) for now.

A session ID could be passed in through vars, but app.sessionId isn't set until the session id has been verified OR the app is explicitly told to not validate the session.
*/
		if(app.vars.thisSessionIsAdmin && app.vars.sessionId)	{
//you'd get here in the UI.
			app.sessionId = app.vars.sessionId
			app.model.addExtensions(app.vars.extensions);
			}
		else if(app.vars.thisSessionIsAdmin)	{
			//for now, do nothing.  this may change later.
			app.model.addExtensions(app.vars.extensions);
			}
		else if(app.vars.sessionId)	{
			app.calls.appCartExists.init(app.vars.sessionId,{'callback':'handleTrySession','datapointer':'appCartExists'});
			app.model.dispatchThis('immutable');
			}
//if sessionId is set on URI, there's a good chance a redir just occured from non secure to secure.
		else if(app.u.isSet(app.u.getParameterByName('sessionId')))	{
			app.calls.appCartExists.init(app.u.getParameterByName('sessionId'),{'callback':'handleTrySession','datapointer':'appCartExists'});
			app.model.dispatchThis('immutable');
			}
//check localStorage
		else if(app.model.fetchSessionId())	{
			app.calls.appCartExists.init(app.model.fetchSessionId(),{'callback':'handleTrySession','datapointer':'appCartExists'});
			app.model.dispatchThis('immutable');			
			}
		else	{
			app.calls.getValidSessionID.init('handleNewSession');
			app.model.dispatchThis('immutable');
			}
		
//if third party inits are not done before extensions, the extensions can't use any vars loaded by third parties. yuck. would rather load our code first.
// -> EX: username from FB and OPC.
		app.u.handleThirdPartyInits();
		
		}, //onReady
					// //////////////////////////////////   CALLS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\		


/*
calls all have an 'init' as well as a 'dispatch'.
the init allows for the call to check if the data being retrieved is already in the session or local storage and, if so, avoid a request.
If the data is not there, or there's no data to be retrieved (a Set, for instance) the init will execute the dispatch.
*/
	calls : {

//all authentication calls use immutable Q
		authentication : {
//the authentication through FB sdk has already taken place and this is an internal server check to verify integrity.	
//the getFacebookUserData function also updates bill_email and adds the fb.user info into memory in a place quickly accessed
//the obj passed in is passed into the request as the _tag
			facebook : {
				init : function(tagObj)	{
					app.u.dump('BEGIN app.calls.authentication.facebook.init');
//Sanity - this call occurs AFTER a user has already logged in to facebook. So though server authentication may fail, the login still occured.
_gaq.push(['_trackEvent','Authentication','User Event','Logged in through Facebook']);
					this.dispatch(tagObj);
					return 1;
					},
				dispatch : function(tagObj)	{
//note - was using FB['_session'].access_token pre v-1202. don't know how long it wasn't working, but now using _authRepsonse.accessToken
					app.model.addDispatchToQ({'_cmd':'appVerifyTrustedPartner','partner':'facebook','appid':zGlobals.thirdParty.facebook.appId,'token':FB['_authResponse'].accessToken,'state':app.sessionID,"_tag":tagObj},'immutable');
					}
				}, //facebook
//obj is login/password.
//tagObj is everything that needs to be put into the tag node, including callback, datapointer and/or extension.
			zoovy : {
				init : function(obj,tagObj)	{
//					app.u.dump('BEGIN app.calls.authentication.zoovy.init ');
//					app.u.dump(' -> username: '+obj.login);
//email should be validated prior to call.  allows for more custom error handling based on use case (login form vs checkout login)
					app.calls.cartSet.init({"bill/email":obj.login}) //whether the login succeeds or not, set bill/email in /session
					this.dispatch(obj,tagObj);
					return 1;
					},
				dispatch : function(obj,tagObj)	{
					obj["_cmd"] = "appBuyerLogin";
					obj['method'] = "unsecure";
					obj["_tag"] = tagObj;
					obj["_tag"]["datapointer"] = "appBuyerLogin";
					
					app.model.addDispatchToQ(obj,'immutable');
					}
				}, //zoovy
			authAdminLogout : {
				init : function(tagObj)	{
					this.dispatch(tagObj);
					return 1;
					},
				dispatch : function(tagObj){
					app.model.addDispatchToQ({'_cmd':'authAdminLogout',"_tag":tagObj},'immutable');
					}
				}, //authAdminLogout
			accountLogout : {
				init : function(obj,tagObj)	{
					this.dispatch(obj,tagObj);
					return 1;
					},
				dispatch : function(obj,tagObj){

					}				
				},
			accountLogin : {
				init : function(obj,tagObj)	{
					this.dispatch(obj,tagObj);
					return 1;
					},
				dispatch : function(obj,tagObj){
					app.u.dump("Attempting to log in");
					obj._cmd = 'authAdminLogin';
					app.vars.userid = obj.userid;
					obj.authtype = "md5";
					obj.ts = app.u.ymdNow();
					obj.authid = Crypto.MD5(obj.password+obj.ts);
					obj._tag = tagObj || {};
					obj.device_notes = "";
					if(obj.persitentAuth)	{obj._tag.datapointer = "authAdminLogin"} //this is only saved locally IF 'keep me logged in' is true.
					delete obj.password;
					app.model.addDispatchToQ(obj,'immutable');
					}
				}

			}, //authentication

		authAccountCreate : {
			init : function(obj,tagObj){
				this.dispatch(obj,tagObj);
				},
			dispatch : function(obj,tagObj){
				obj._cmd = 'authUserRegister';
				tagObj = tagObj || {};
				obj['tag'] = tagObj;
				app.model.addDispatchToQ(obj,'immutable');
				}
			},


//always uses immutable Q
		getValidSessionID : {
			init : function(callback)	{
				this.dispatch(callback); 
				return 1;
				},
			dispatch : function(callback)	{
				app.model.addDispatchToQ({"_cmd":"appCartCreate","_tag":{"callback":callback}},'immutable');
				}
			},//getValidSessionID

//always uses immutable Q
//formerly canIHaveSession
		appCartExists : {
			init : function(cartid,tagObj)	{
//					app.u.dump('BEGIN app.calls.appCartExists');
				app.sessionId = cartid; //needed for the request. may get overwritten if not valid.
				this.dispatch(cartid,tagObj);
				return 1;
				},
			dispatch : function(cartid,tagObj)	{
				var obj = {};
				obj["_cmd"] = "appCartExists"; 
				obj["_tag"] = tagObj;
				app.model.addDispatchToQ(obj,'immutable');
				}
			}, //appCartExists

//for now, no fetch is done here. it's assumed if you execute this, you don't know who you are dealing with.
		whoAmI : {
			init : function(tagObj,Q)	{
				this.dispatch(tagObj,Q);
				return 1;
				},
			dispatch : function(tagObj,Q)	{
				tagObj = $.isEmptyObject(tagObj) ? {} : tagObj; 
				tagObj.datapointer = "whoAmI"
				app.model.addDispatchToQ({"_cmd":"whoAmI","_tag" : tagObj},Q);	
				}
			},//whoAmI

		canIUse : {
			init : function(flag,Q)	{
				this.dispatch(flag,Q);
				return 1;
				},
			dispatch : function(flag,Q)	{
				app.model.addDispatchToQ({"_cmd":"canIUse","flag":flag,"_tag":{"datapointer":"canIUse|"+flag}},Q);
				}
			}, //canIUse

//default immutable Q
//formerly setSessionVars
		cartSet : {
			init : function(obj,tagObj,Q)	{
				this.dispatch(obj,tagObj,Q);
				return 1;
				},
			dispatch : function(obj,tagObj,Q)	{
				if(!Q)	{Q = 'immutable'}
				obj["_cmd"] = "cartSet";
				if(tagObj)	{obj["_tag"] = tagObj;}
				app.model.addDispatchToQ(obj,Q);
				}
			}, //cartSet

		ping : {
			init : function(tagObj,Q)	{
				this.dispatch(tagObj,Q);
				return 1;
				},
			dispatch : function(tagObj,Q)	{
				app.model.addDispatchToQ({"_cmd":"ping","_tag":tagObj},Q); //get new session id.
				}
			}, //ping

//always uses immutable Q.
// used when a new session id must be generated, such as post-checkout.
		appCartCreate : {
			init : function(callback)	{
				this.dispatch(callback);
				return 1;
				},
			dispatch : function(callback)	{
				app.model.addDispatchToQ({"_cmd":"appCartCreate","_tag":{"callback":callback}},'immutable'); //get new session id.
				}
			}, //appCartCreate
			
		appProfileInfo : {
			init : function(profileID,tagObj,Q)	{
//				app.u.dump("BEGIN app.calls.appProfileInfo.init");
				var r = 0; //will return 1 if a request is needed. if zero is returned, all data needed was in local.
				tagObj = typeof tagObj == 'object' ? tagObj : {};
				tagObj.datapointer = 'appProfileInfo|'+profileID; //for now, override datapointer for consistency's sake.

				if(app.model.fetchData(tagObj.datapointer) == false)	{
					r = 1;
					this.dispatch(profileID,tagObj,Q);
					}
				else 	{
					app.u.handleCallback(tagObj)
					}

				return r;
				}, // init
			dispatch : function(profileID,tagObj,Q)	{
				obj = {};
				obj['_cmd'] = "appProfileInfo";
				obj['profile'] = profileID;
				obj['_tag'] = tagObj;
				app.model.addDispatchToQ(obj,Q);
				} // dispatch
			}, //appProfileInfo

//used to get a clean copy of the cart. ignores local/memory. used for logout.
		refreshCart : {
			init : function(tagObj,Q)	{
//				app.u.dump("BEGIN app.calls.refreshCart");
				var r = 1;
//if datapointer is fixed (set within call) it needs to be added prior to executing handleCallback (which will likely need datapointer to be set).
				tagObj = jQuery.isEmptyObject(tagObj) ? {} : tagObj;
				tagObj.datapointer = "cartDetail";
				this.dispatch(tagObj,Q);
				return r;
				},
			dispatch : function(tagObj,Q)	{
//				app.u.dump('BEGIN app.ext.store_cart.calls.cartDetail.dispatch');
				app.model.addDispatchToQ({"_cmd":"cartDetail","_tag": tagObj},Q);
				} 
			} // refreshCart removed comma from here line 383
		}, // calls

					// //////////////////////////////////   CALLBACKS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\
/*
Callbacks require should have an onSuccess.
Optionally, callbacks can have on onError. if you have a custom onError, no error messaging is displayed. This give the developer the opportunity to easily suppress errors for a given request/callback.
app.u.throwMessage(responseData); is the default error handler.
*/
	callbacks : {
		

		handleSessionStartResponse : {
			onSuccess : function(tagObj)	{
//				app.data[tagObj.datapointer]				
				},
			onError : function(responseData)	{
				app.u.throwMessage(responseData);
				}
			},//handleSessionStartResponse


		handleNewSession : {
//app.sessionID is set in the method. no need to set it here.
//use app.sessionID if you need it in the onSuccess.
//having a callback does allow for behavioral changes (update new session with old cart contents which may still be available.
			onSuccess : function(tagObj)	{
//				app.u.dump('BEGIN app.callbacks.handleNewSession.onSuccess');
// if there are any  extensions(and most likely there will be) add then to the controller.
// This is done here because a valid cart id is required.
				app.model.addExtensions(app.vars.extensions);
				}
			},//convertSessionToOrder

//executed when appCartExists is requested.
//app.sessionID is already set by this point. need to reset it onError.
// onError does NOT need to nuke app.sessionId because it's handled in handleResponse_appCartExists 
		handleTrySession : {
			onSuccess : function(tagObj)	{
//				app.u.dump('BEGIN app.callbacks.handleTrySession.onSuccess');
//				app.u.dump(" -> exists: "+app.data.appCartExists.exists);
				if(app.data.appCartExists.exists >= 1)	{
					app.u.dump(' -> valid session id.  Proceed.');
// if there are any  extensions(and most likely there will be) add then to the controller.
// This is done here because a valid cart id is required.
					app.model.addExtensions(app.vars.extensions);
//
					app.calls.whoAmI.init({'callback':'suppressErrors'},'passive'); //get this info when convenient.
					}
				else	{
					app.u.dump(' -> UH OH! invalid session ID. Generate a new session. nuke localStorage if domain is ssl.zoovy.com.');
					app.calls.appCartCreate.init('handleNewSession');
					app.model.dispatchThis('immutable');
					}
				}
			}, //handleTrySession
		
		handleAdminSession : {
			onSuccess : function(tagObj)	{
				app.u.dump('BEGIN app.callbacks.handleAdminSession.onSuccess');
//in DEV still. do not use this callback.
//				app.vars['_admin'] is set in the model.
				}
			},
			
		translateSelector : {
			onSuccess : function(tagObj)	{
//				app.u.dump("BEGIN callbacks.translateSelector");
				app.renderFunctions.translateSelector(tagObj.selector,app.data[tagObj.datapointer]);
				}
			},
	
		
//pass the following on _tag:
// parentID is the container id that the template instance is already in (should be created before call)
// templateID is the template that will get translated.
// the app.data.datapointer is what'll get passed in to the translate function as the data src. (ex: getProduct|PID)
		translateTemplate : 	{
			onSuccess : function(tagObj)	{
//				app.u.dump("BEGIN callbacks.translateTemplate");
				app.renderFunctions.translateTemplate(app.data[tagObj.datapointer],tagObj.parentID);
				}
			
			}, //translateTemplate
// a generic callback to allow for success messaging to be added. 
// pass message for what will be displayed.  For error messages, the system messaging is used.
		showMessaging : {
			onSuccess : function(tagObj)	{
//				app.u.dump("BEGIN app.callbacks.showMessaging");
				var msg = app.u.successMsgObject(tagObj.message);
				msg['_rtag'] = tagObj; //pass in tagObj as well, as that contains info for parentID.
				app.u.throwMessage(msg);
				}
			}, //showMessaging
/*
By default, error messaging is thrown to the appMessaging class. Sometimes, this needs to be suppressed. Add this callback and no errors will show.
ex: whoAmI call executed during app init. Don't want "we have no idea who you are" displayed as an error.
*/

		suppressErrors : {
			onSuccess : function(tagObj)	{
//dummy callback. do nothing.
				},
			onError : function(responseData,uuid)	{
//dummy callback. do nothing.
				app.u.dump("CAUTION! response for uuid ["+uuid+"] contained errors but they were suppresed. This may be perfectly normal (passive requests) but should be investigated.");
				}
			} //suppressErrors
			
			
		}, //callbacks




////////////////////////////////////   UTIL [u]    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\






	u : {


/*
when a call is requested but the data is local, execute this function.
it will check to see if a callback is defined and if it is, execute it.
smart enough to determine if an extension is involved and execute it from there.

-> this is a function because what is in the 'if' was being duplicated in almost every call 
that dealt with a fetch. So it was made into a function to make the calls tighter and also 
allow for global manipulation if needed later.

app.u.handleCallback(tagObj);
*/

		handleCallback : function(tagObj)	{
//			app.u.dump("BEGIN u.handleCallback");
			var callback;
			if(tagObj && tagObj.datapointer){app.data[tagObj.datapointer]['_rtag'] = tagObj} //updates obj in memory to have latest callback.
			if(tagObj && tagObj.callback){
//				app.u.dump(" -> executing callback ("+tagObj.callback+") in extension ("+tagObj.extension+")");
//most callbacks are likely in an extension, but support for 'root' callbacks is necessary.
//save path to callback so that we can verify the onSuccess is a function before executing (reduce JS errors with this check)
				callback = tagObj.extension ? app.ext[tagObj.extension].callbacks[tagObj.callback] : app.callbacks[tagObj.callback];
				if(typeof callback.onSuccess == 'function')
					callback.onSuccess(tagObj);
				}
			else	{
//				app.u.dump(" -> no callback was defined.");
				}
			},


/*
when quickstart is added, it will go through app.rq and use this function to add all resources as needed.
it'll then set app.rq.push to mirror this function.
*/

			handleResourceQ : function(arr)	{
				if(arr[0] == 'script')	{
					app.u.loadScript(arr[2],arr[3]);
					}
				else if(arr[0] == 'extension')	{
//					app.u.dump(" -> extension loading: "+arr[2]+" callback: "+arr[4]);
					var tmpObj = {"namespace":arr[2],"filename":arr[3],"callback":arr[4]}; //
					app.vars.extensions.push(tmpObj); // keep the full list just in case.
					app.u.loadScript(arr[3],function(){
						app.model.fetchExtension(tmpObj); 
						});
					app.model.executeCallbacksWhenExtensionsAreReady([tmpObj]); //function wants an array of objects.
					}
				else if(arr[0] == 'templateFunction')	{
					app.ext.myRIA.template[arr[1]][arr[2]].push(arr[3]);
					}
				else if(arr[0] == 'css')	{
					app.u.loadCSSFile(arr[2],arr[3] || null);
					}
				else	{
		//currently, this function is intended for pass 0 only, so if an item isn't pass 0,do nothing with it.
					}

				
				},


//filename is full path of .css file (or valid relative path)
//domID is optional id to add to <link> allows for removal or changing later.
//if you pass a domID that already exists, that file is 'saved over'.
		loadCSSFile : function(filename,domID){
			if(filename && domID && document.getElementById(domID))	{
				document.getElementById(domID).href = filename; //overwrite existing .css file. effectively removes old in favor of new.
				}
			else if(filename)	{
// Create element node - link object
				var fileref=document.createElement('link');
	
// Set link object attributes like
// <link rel="stylesheet" type="text/css" href="filename" />
				fileref.setAttribute('rel', 'stylesheet');
				fileref.setAttribute('type', 'text/css');
				fileref.setAttribute('href', filename);
				if(domID)	{fileref.setAttribute('id', domID);}
// Append link object inside html's head
				document.getElementsByTagName("head")[0].appendChild(fileref);
				}
			else	{
				//doh! no filename.
				}
			},



		printByElementID : function(id)	{
//				app.u.dump("BEGIN myRIA.a.printByElementID");
			if(id && $('#'+id).length)	{
				var html="<html><body style='font-family:sans-serif;'>";
				html+= document.getElementById(id).innerHTML;
				html+="</body></html>";
				
				var printWin = window.open('','','left=0,top=0,width=600,height=600,toolbar=0,scrollbars=0,status=0');
				printWin.document.write(html);
				printWin.document.close();
				printWin.focus();
				printWin.print();
				printWin.close();
				}
			else	{
				app.u.dump("WARNING! - myRIA.a.printByElementID executed but not ID was passed ["+id+"] or was not found on DOM [$('#'+"+id+").length"+$('#'+id).length+"].");
				}
			},

//pass in a string (my.string.has.dots) and a nested data object, and the dots in the string will map to the object and return the value.
//ex:  ('a.b',obj) where obj = {a:{b:'go pack go'}} -> this would return 'go pack go'
//will be used in updates to translator.

//http://stackoverflow.com/questions/5240785/split-abc/5240797#5240797
		getObjValFromString : function (s,obj,char)	{
			cart = char || '.';
			var o=obj, attrs=s.split(char);
			while (attrs.length > 0) {
				o = o[attrs.shift()];
				if (!o) {o= null; break;}
				}
			return o;

			},



//http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
		guidGenerator : function() {
			var S4 = function() {
				return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
				};
			return (S4()+S4()+S4()+S4()+S4()+S4()+S4()+S4());
			},

//when sending the user into a waiting pattern, pass true.  This changes the cursor into the browser 'waiting' state
//don't forget to turn this off when done.
		handleWait : function (tfu){
			if(tfu == true)	{
				$('body').css({'cursor':'wait'})
				}
			else	{
				$('body').css({'cursor':'auto'})
				}
			},
			

//jump to an anchor. can use a name='' or id=''.  anchor is used in function name because that's the common name for this type of action. do not need to pass # sign.
		jumpToAnchor : function(id)	{
			window.location.hash=id;
			},
//uses throw message, but always adds the same generic message. value of 'err' is output w/ dump.
//this should only be used for app errors (errors thrown from within the MVC, not as a result of an API call, in which case throwMessage should be used (handles request errors nicely)
		throwGMessage : function(err,parentID){
			var msg = this.errMsgObject("Well this is embarrassing. Something bad happened. Please try that again. If this error persists, please contact the site administrator.","#");
			if(parentID)	{msg.parentID = parentID}
			this.throwMessage(msg);
			app.u.dump(err);
			},
/*
msg could be a string or an object.
if an object, could be: {errid,errmsg,errtype}   OR   {msg_X_txt,msg_X_type,msg_X_id}
 -> if msg_X format, X will be an integer and _msgs will be set to indicate the # of messages.

$target - a jquery object of the target/destination for the message itself. Will check err for parentID, targetID and if not present, check to see if globalMessaging is present AND visible.  If not visible, will open modal.
returns the id of the message, so that an action can be easily added if needed (onclick or timeout w/ a hide, etc)

persistant - this can be passed in as part of the msg object or a separate param. This was done because repeatedly, error messaging in the control
and model that needed to be permanently displayed had to be converted into an object just for that and one line of code was turning into three.
*/
		throwMessage : function(msg,persistant){
//			app.u.dump("BEGIN app.u.throwMessage");
//			app.u.dump(" -> msg follows: "); app.u.dump(msg);
			var $target; //where the app message will be appended.
			var messageClass = "appMessage_"+this.guidGenerator(); //the class added to the container of the message. message 'may' appear in multiple locations, so a class is used instead of an id.
			var r = messageClass; //what is returned. set to false if no good error message found. set to htmlID is error found. 
			var $container = $("<div \/>").addClass('appMessage').addClass(messageClass).append("<button onClick='$(\"."+messageClass+"\").toggle(); return false;' class='ui-state-default ui-corner-all floatRight stdMargin'><span class='ui-button ui-icon-circle-close'>X</span><\/button>");
//make sure the good-ole fallback destination for errors exists and is a modal.
			var $globalDefault = $('#globalErrorMessaging')
			if	($globalDefault.length == 0)	{
				$globalDefault = $("<div \/>").attr({'id':'globalErrorMessaging'}).appendTo('body');
				$globalDefault.dialog({autoOpen:false,modal:true})
				}

			if(typeof msg === 'string')	{
				if($('.appMessaging:visible').length > 0)	{
//					app.u.dump(" -> target is appMessaging.");
					$target = $('.appMessaging');
					}
				else	{
//					app.u.dump(" -> target is globalDefault.");
					$target = $globalDefault;
					$target.dialog('open');
					}
				$container.append(this.formatMessage(msg)).prependTo($target); //always put new messages at the top.
				}
			else if(typeof msg === 'object')	{
				persistant = msg.persistant || false;
				msg.messageClass = messageClass;
				if(msg.parentID){$target = $('#'+msg.parentID);}
				else if(typeof msg['_rtag'] == 'object' && msg['_rtag'].parentID && $('#'+msg['_rtag'].parentID).length)	{$target = $('#'+msg['_rtag'].parentID);}
				else if(typeof msg['_rtag'] == 'object' && msg['_rtag'].targetID && $('#'+msg['_rtag'].targetID).length)	{app.u.dump("USING targetID for errors"); $target = $('#'+msg['_rtag'].targetID)}
				else if($('.appMessaging:visible').length > 0)	{$target = $('.appMessaging');}
				else	{
					$target = $globalDefault;
					$target.dialog('open');
					}
				$container.append(this.formatResponseErrors(msg)).prependTo($target);
				}
			else	{
				app.u.dump("WARNING! - unknown type ["+typeof err+"] set on parameter passed into app.u.throwMessage");
				r = false; //don't return an html id.
				}
			if(persistant !== true)	{
//the message could be removed manually prior to the callback being executed, so don't animate if that's the case. (avoids popping issue)
				setTimeout(function(){
					if($('.'+messageClass).is(':visible'))	{
						$('.'+messageClass).slideUp(2000);
						}
					},10000); //shrink message after a short display period
				}
//get rid of all the loading gfx in the target so users know the process has stopped.
			$target.removeClass('loadingBG');
			if(typeof $.hideLoading == 'function'){$target.hideLoading()} //used in UI. plan on switching everything applicable to this.
 
			return r;
			},



// The next functions are simple ways to create success or error objects.
// pass in a message and the entire success object is returned.
// keep this simple. don't add support for icons or message type. If that degree of control is needed, build your own object and pass that in.
// function used in store_product (and probably more)
		successMsgObject : function(msg)	{
			return {'errid':'#','errmsg':msg,'errtype':'success','uiIcon':'check','uiClass':'success'}
			},

		uiMsgObject : function(msg)	{
			var obj; //what is returned.
			if(msg.indexOf('|') == -1)	{
				obj = {'errid':'#','errmsg':msg,'errtype':'unknown','uiIcon':'ui-icon-z-ise','uiClass':'z-hint'} //some legacy messaging is pipeless (edit order, tracking, for instance).
				}
			else	{
				var eType = msg.split('|')[0].toLowerCase();
				obj = {'errid':'#','errmsg':msg.split('|')[1],'errtype':msg.split('|')[0],'uiIcon':'z-'+eType,'uiClass':'z-'+eType}
				}
			return obj;
			},

		errMsgObject : function(msg,errid)	{
			return {'errid':errid,'errmsg':msg,'errtype':'apperr','uiIcon':'alert','uiClass':'error'}
			},
		statusMsgObject : function(msg)	{
			return {'errid':'#','errmsg':msg,'errtype':'statusupdate','uiIcon':'transferthick-e-w','uiClass':'statusupdate'}
			},
		youErrObject : function(errmsg,errid)	{
			return {'errid':errid,'errmsg':errmsg,'errtype':'youerr','uiIcon':'alert','uiClass':'highlight'}
			},

/*
pass in the responseData from the api request and this will return all the errors as their own lineitems.
Request that can have multiple errors come back have _msg... in response (like adding to cart)
Other requests will have errid set
This function will have both cases.
*/

		formatResponseErrors : function(d)	{
			var r;
			if(!d)	{
				r = 'unknown error has occured';
				}
			else if(typeof d == 'string')	{
				r = app.u.formatMessage(d);
				}
			else if(typeof d == 'object')	{
				r = "";
				if(d['_msgs'])	{
					for(var i = 1; i <= d['_msgs']; i += 1)	{
//						app.u.dump(d['_msg_'+i+'_type']+": "+d['_msg_'+i+'_id']);
						r += "<div class='"+d['_msg_'+i+'_type']+"'>"+d['_msg_'+i+'_txt'];
						if(d['_msg_'+i+'_id']) { r += "<br \/>Error ID: "+d['_msg_'+i+'_id']}
						r += "<\/div>";
						}
					}
				else if(d['errid'])	{
					r += "<div class='"+d.errtype+" appMessageTxt'>"+d.errmsg
					if(d.errid && d.errid != "#") { r += "<br \/>Error ID: "+d.errid}
					r += "<\/div>";
//					app.u.dump("WARNGING! error occured. id: "+d.errid+" and type: "+d.errtype+" and msg: "+errmsg);
					}
//the validate order request returns a list of issues.
				else if(d['@issues'])	{
					var L = d['@issues'].length;
					for(var i = 0; i < L; i += 1)	{
						r += "<div>"+d['@issues'][i][3]+"<\/div>";
						}
					}
				d.message = r; //pass in entire original object as it may contain uiClass and/or uiIcon or other params
{'fraction_non_alphanumeric': 0.15016700066800268, 'fraction_numerical': 0.004423662139093001, 'mean_word_length': 3.402143370580932, 'pattern_counts': {'":': 52, '<': 46, '<?xml version=': 0, '>': 92, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '291690', 'n_tokens_mistral': 24484, 'n_tokens_neox': 20899, 'n_words': 6394}