/* Minification failed. Returning unminified contents.
(10315,8): run-time error JS1004: Expected ';'
(10318,28): run-time error JS1004: Expected ';'
(10486,8): run-time error JS1004: Expected ';'
(10489,28): run-time error JS1004: Expected ';'
(13834,25-28): run-time error JS1009: Expected '}': ...
(13834,25): run-time error JS1004: Expected ';'
(13871,1-2): run-time error JS1006: Expected ')': }
(13871,1-2): run-time error JS1002: Syntax error: }
(13871,2-3): run-time error JS1195: Expected expression: )
(15211,46-47): run-time error JS1195: Expected expression: .
(15211,58-59): run-time error JS1003: Expected ':': )
(15212,29-32): run-time error JS1009: Expected '}': var
(15212,29-32): run-time error JS1006: Expected ')': var
(15226,21-22): run-time error JS1006: Expected ')': }
(15225,26): run-time error JS1004: Expected ';'
(15226,22-23): run-time error JS1195: Expected expression: )
(16047,38-39): run-time error JS1195: Expected expression: .
(16047,63-64): run-time error JS1003: Expected ':': )
(16048,13-24): run-time error JS1009: Expected '}': userService
(16048,13-24): run-time error JS1006: Expected ')': userService
(16062,60-61): run-time error JS1195: Expected expression: .
(16062,85-86): run-time error JS1003: Expected ':': ;
(16405,1-2): run-time error JS1006: Expected ')': }
(16405,1-2): run-time error JS1002: Syntax error: }
(16405,2-3): run-time error JS1195: Expected expression: )
(16089,13-27): run-time error JS1300: Strict-mode does not allow assignment to undefined variables: _orderTypeRule
 */
// Main configuration file. Sets up AngularJS module and routes and any other config objects
(function() {
	'use strict';

	angular.module('main', [
		'ngAnimate',
		'ngSanitize', 
		'ngTouch',
		'ui.router',
		'common',
		'ui.bootstrap', // ui-bootstrap (ex: carousel, pagination, dialog)
        'vcRecaptcha',	// https://github.com/VividCortex/angular-recaptcha
		'ngAria'
	]);
})();;
(function () {
	'use strict';

	var app = angular.module('main');

	app.config(['$provide', function ($provide) {
		$provide.decorator('$exceptionHandler', ['$delegate', '$injector', 'notify', extendExceptionHandler]);
	}]);

	// Extend the $exceptionHandler service to also notify the user.
	function extendExceptionHandler($delegate, $injector, notify) {
		return function (exception, cause) {
			$delegate(exception, cause);
			if (exception) {
				if (exception.message) {
					if (exception.beKind) {
						notify.info(exception.message);
					} else {
						notify.error(exception.message);
					}
					if (exception.showAlert) {
						notify.dialog('Oops', exception.message);
						return;
					}
				}

				if (_.isString(exception)) {
					if (exception.indexOf("Possibly unhandled rejection:") === 0) {
						return;
					}
				}
				if (!exception.cause && cause) {
					exception.cause = cause;
				}

				if (window.navigator) {
					exception.navigator = window.navigator;
				}

				$injector.get('$http').post("api/Log", JSON.stringify(exception, replaceErrors));
			}
		};
	}

	function replaceErrors(key, value) {
		if (value instanceof Error) {
			var error = {};

			Object.getOwnPropertyNames(value).forEach(function (key) {
				error[key] = value[key];
			});

			return error;
		} else if (value instanceof Navigator) {
			var nav = {};

			nav.vendor = value.vendor;
			nav.appVersion = value.appVersion;
			nav.deviceMemory = value.deviceMemory;
			nav.userAgent = value.userAgent;
			nav.language = value.language;
			nav.platform = value.platform;
			nav.product = value.product;
			nav.oscpu = value.oscpu;

			return nav;
		}
		return value;
	}
})();;
(function () {
    'use strict';

    var app = angular.module('main');

    app.factory('templateCacheHandler', [templateCacheHandler]);

    function templateCacheHandler() {
        return {
            'request': function (config) {
                if (config.url.indexOf('app/') === 0 && config.url.indexOf('.html', config.url.length - 5) !== -1) {
                    config.url += "?_=20240401-0213";
                    config.headers["Cache-Control"] = "public, max-age=2592000";
                }
                return config;
            }
        };
    }

    app.factory('httpErrorHandler', ['$q', '$injector', 'notify', 'spinner', 'common', 'events', httpErrorHandler]);

    function httpErrorHandler($q, $injector, notify, spinner, common, events) {
        return {
            'requestError': function (rejection) {
                spinner.spinnerHide();
                notify.error('An unexpected error occurred.', 'requestError', rejection);
                return $q.reject(rejection);
            },
            'responseError': function (rejection) {
                spinner.spinnerHide();
                //rejection.data.message = msg; message is read-only 
                
                // Only do special ui behavior for the togo api
                if (rejection.config.url.startsWith(togoorder.apiUrl)) {
                    var msg = '';

                    if (rejection.status === 401) {
                        msg = 'Please Sign In to use this feature.';
                        notify.error(msg, 'responseError', rejection);

                        $injector.get('$http').post("api/Log", rejection);

                        $injector.get('$uibModal').open({
                            templateUrl: 'app/security/userFeatureAlert.html',
                            controller: 'userFeatureAlert as vm',
                            size: 'md',
                            resolve: {}
                        }).result.then(function() {
                            common.broadcast(events.securityAuthorizationRequired, {});
                        });
                    } else if (rejection && rejection.xhrStatus === 'abort') {
                        console.log('rejection', rejection);
                    } else {
                        msg = rejection &&
                            rejection.data &&
                            (rejection.data.Message || rejection.data.message || rejection.data.error_description) ||
                            'An unexpected error occurred';

                        notify.error(msg, 'responseError', rejection);
                    }
                }

                return $q.reject(rejection);
            }
        };
    }

    app.config(['$httpProvider', function ($httpProvider) {
        $httpProvider.interceptors.push('httpErrorHandler');
        $httpProvider.interceptors.push('templateCacheHandler');
    }]);
})();;
(function () {
    'use strict';

    var app = angular.module('main');

    app.config(['commonProvider', 'events',
        function (commonProvider, events) {
            // Configure Toastr
            toastr.options.timeOut = 5000;
            toastr.options.showDuration = 0;
            toastr.options.extendedTimeOut = 3000;
            toastr.options.positionClass = 'toast-bottom-right';
            toastr.options.onclick = function () {
                var success = $(event.target).closest('.toast-success');
                if (success.length) {
                    var common = commonProvider.$get();
                    common.broadcast(events.presentCart);
                }
            };
        }
    ]);

    app.config(['$logProvider', function ($logProvider) {
        if ($logProvider.debugEnabled) {
            $logProvider.debugEnabled(false);
        }
    }]);

    app.config(['$locationProvider', function ($locationProvider) {
            $locationProvider.hashPrefix('');
        }]);

    app.config(['$rootScopeProvider', function($rootScopeProvider) {
	    $rootScopeProvider.digestTtl(20); 
    }]);
})();;
(function (angular, _) {
	"use strict";

	angular.module("main").config(["$stateProvider", "$urlRouterProvider", "events", configureRouteProvider]);

	function configureRouteProvider($stateProvider, $urlRouterProvider, events) {

		$urlRouterProvider.otherwise("/");

		function ifLocationHasWebsite() {
			var tgo = togoorder || {};
			return !!tgo.locationWebsite;
		}

		function ifHasSiblingLocations() {
			var tgo = togoorder || {};
			return tgo.merchantLocation.MerchantLocationCount > 1;
		}

		function ifInLocationContext($rootScope) {
			var tgo = togoorder || {};
			return !!tgo.locationId;
		}

		function ifNotInLocationContext($rootScope) {
			return !ifInLocationContext($rootScope);
		}

		function ifLoggedIn($rootScope, userService) {
			try {
				return userService.isAuthenticated();
			} catch (e) {
				return false;
			}
		}

		function ifLoginAllowed($rootScope, userService, merchantLocation) {
            return !merchantLocation.IsOnlyGuestCheckout;
        }

		function ifNotLoggedIn($rootScope, userService) {
			return !ifLoggedIn($rootScope, userService);
		}

		function ifMerchantHasLoyalty($rootScope, userService, merchantLocation) {
			return !!merchantLocation.LoyaltyProfile;
		}

		function ifUserCreateLoyaltyAccount($rootScope, userService, merchantLocation) {
			return true;
			//return merchantLocation.LoyaltyProfile.LoyaltyProviderType != 6;
        }

		function ifMerchantHasMealPlan($rootScope, userService, merchantLocation) {
			return !!merchantLocation.MealPlanProfile;
		}

		function ifMerchantHasGiftCard($rootScope, userService, merchantLocation) {
			return !!merchantLocation.GiftCardProfile;
		}

		function ifMerchantHasCampusCard($rootScope, userService, merchantLocation) {
			try {
				return merchantLocation.GiftCardProfile && merchantLocation.GiftCardProfile.IsCampusCardEnabled;
			} catch (e) {
				return false;
			}
		}

		function ifMerchantSignUpWithLoyalty($rootScope, userService, merchantLocation) {
			return !!merchantLocation.MerchantSignUpWithLoyalty;
		}

		function ifNotMerchantSignUpWithLoyalty($rootScope, userService, merchantLocation) {
			return !merchantLocation.MerchantSignUpWithLoyalty;
		}

		function ifShowOffers($rootScope, userService, merchantLocation) {
			return !!merchantLocation.MerchantSignUpWithLoyalty ||
				(merchantLocation.LoyaltyProfile && merchantLocation.LoyaltyProfile.LoyaltyProviderType);
		}

		function all() {
			var conditions = _.toArray(arguments);
			return function($rootScope, userService, merchantLocation) {
				return _.every(conditions,
					function(func) {
						return func($rootScope, userService, merchantLocation);
					});
			};
		}

		function getItemDetailPreload() {
			var preload = ["app/menu/itemIncludedItemsRepeater.html",
				"app/menu/itemIncludedItems.html",
				"app/menu/itemIncludedItems.controls.html",
				"app/menu/itemIncludedItems.expanded.html",
				"app/menu/itemIncludedGroupHeader.html"];
			if (togoorder && togoorder.merchantLocation && togoorder.merchantLocation.MenuItemUiType === 1) {
				preload.push("app/menu/itemIncludedItems.collapsed.html");
			}
			return preload;
		}

		function getMenuSectionViewUrl() {
			var urlMap = {
				0: "app/menu/menu.sections.html",
				1: "app/menu/menu.sections.expanded.html",
                2: "app/menu/menu.sections.horizontal.html",
                3: "app/menu/menu.sections.expandedWithHorizontalNav.html"
			};

			return urlMap[togoorder && togoorder.merchantLocation && togoorder.merchantLocation.MenuSectionUiType] || urlMap[0];
		}
		
		var isOrderFlow = !togoorder.merchantLocation.MerchantSignUpWithLoyalty;
		var accountMenuItem = { title: "Account", icon: "glyphicon-user" };
		var merchantMenuItem = { title: togoorder.merchantName, icon: "glyphicon-home" };

		var states = [
			{
				name: "setTableNumber",
				url: "/setTable/:tableNumber",
				controller: "setTableNumber",
				templateUrl: "app/tableNumber/setTableNumber.html",
				data: {
					title: "Got your table...",
					showInMenu: false
				}
			},
			{
				name: "showLogin",
				url: "/showLogin/:returnStateName",
				controller: "showLogin",
				data: {
					title: "Sign In...",
					showInMenu: false
				}
			},
			{
				name: "samlLogin",
				url: "/samlLogin/:returnUrlHash",
				controller: "samlLogin",
				data: {
					showInMenu: false
				}
			},
			{
				name: "locationHome",
				url: "/",
				templateUrl: isOrderFlow ? "app/chooseOrderType/chooseOrderType.html" : "app/loyalty/loyaltyHome.html",
				data: {
					title: isOrderFlow ? "Choose Order Type" : "Welcome",
					linkText: isOrderFlow ? "Start New Order" : "Main Menu",
					isOrderAccessControlled: true,
					showInMenu: all(ifInLocationContext),
					parentMenuItem: merchantMenuItem,
					isLoyaltyControlled: true,
					onBeforeNavigate: ['$uibModal', 'cartService', function($modal, cartService) {
                        // If items are in cart, prompt if user wishes to start a new order or continue current order
						cartService.loadCurrent().then(function(cart) {
                            if (cart && cart.hasOrderItems()) {
                                $modal.open({
                                    templateUrl: 'shared/common/alert.html',
                                    controller: 'alert as vm',
                                    size: 'md',
                                    resolve: {
                                        alertOptions: function () {
                                            return {
                                                dialogTitle: 'Start a New Order?',
                                                dialogMessage: 'You have items in your cart. Do you want to continue your order or start a new order?',
                                                closeButtonText: 'New Order',
                                                dismissButtonText: 'Continue'
                                            };
                                        }
                                    }
                                }).result.then(function () {
                                    cartService.destroy();
                                });
                            }
                        });
                    }]
                }
			},
			{
				name: "merchantHome",
				data: {
					title: "Find Another Location",
					justRaiseEvent: events.navigateToMerchantHome,
					showInMenu: ifHasSiblingLocations,
					parentMenuItem: merchantMenuItem
				}
			},
			{
				name: "merchantWebsite",
				data: {
					title: "Go To " + togoorder.merchantName + " Home",
					justRaiseEvent: events.navigateToMerchantWebsite,
					showInMenu: all(ifLocationHasWebsite),
					parentMenuItem: merchantMenuItem
				}
			},
			{
				name: "chooseUnlistedMenu",
				url: "/o/:orderType/f/:fulfillmentType/menus/:unlistedOnly",
				templateUrl: "app/chooseMenu/chooseMenu.html",
				data: {
					validateOrderTypeRuleMenu: false,
					requiresOrderTypeWorkflow: true,
					isOrderAccessControlled: false,
					title: "Choose Unlisted Menu"
				}
			},
			{
				name: "chooseMenu",
				url: "/o/:orderType/f/:fulfillmentType/menus",
				templateUrl: "app/chooseMenu/chooseMenu.html",
				data: {
					requiresOrderTypeWorkflow: true,
					validateOrderTypeRule: true,
					isOrderAccessControlled: true,
					title: "Choose Menu"
				}
			}, {
				name: "getTableNumber",
				url: "/o/:orderType/f/:fulfillmentType/table/:menuId",
				templateUrl: "app/tableNumber/getTableNumber.html",
				params: {
					menuId: null
				},
				data: {
					validateOrderTypeRule: true,
					isOrderAccessControlled: true,
					title: "Enter Table Number"
				}
			}, {
				name: "getDeliveryAddress",
				url: "/o/:orderType/f/:fulfillmentType/address/:menuId",
				templateUrl: "app/deliveryAddress/getDeliveryAddress.html",
				params: {
					menuId: null
				},
				data: {
					validateOrderTypeRule: true,
					isOrderAccessControlled: true,
					title: "Enter Delivery Address"
				}
			}, {
				name: "days",
				url: "/o/:orderType/f/:fulfillmentType/days",
				templateUrl: "app/chooseDay/days.html",

				data: {
					requiresOrderTypeWorkflow: true,
					validateOrderTypeRule: true,
					isOrderAccessControlled: true,
					title: "Daily Menu - Select Day"
				}
			}, {
				name: "days.menus",
				url: "/:day/menus",
				views: {
					daysMenusView: { templateUrl: "app/chooseDay/days.menus.html" }
				},
				data: {
					title: "Daily Menu - Select Menu"
				}
			}, {
				name: "days.menus.menu",
				'abstract': true,
				url: "/m/:menuId/:isPreview",
				views: {
					daysMenusMenuView: {
						templateUrl: "app/menu/menu.html"
					}
				},
				data: {
					preload: ["app/cart/cartItem.html", "shared/common/alert.html"],
					// validateOrderTypeRuleMenu: true,
					// requiresOrderTypeWorkflow: true,
					isOrderAccessControlled: true,
					title: "Menu"
				}

			}, {
				name: "days.menus.menu.sections",
				url: "/s",
				views: {
					menuView: {
						templateUrl: getMenuSectionViewUrl
					}
				},
				data: {
					title: "Menu"
				}
			}, {
				name: "days.menus.menu.sections.items",
				url: "/:sectionId/items",
				views: {
					menuSectionView: { templateUrl: "app/menu/menu.sections.items.html" }
				},
				data: {
					requiresActivation: false,
					title: "Menu"
				}
			}, {
				name: "days.menus.menu.itemDetail",
				url: "/item",
				views: {
					menuView: { templateUrl: "app/menu/menu.itemDetail.html" }
				},
				data: {
					preload: getItemDetailPreload(),
					requiresActivation: false,
					title: "Menu"
				}
			}, {
				name: "menu",
				'abstract': true,
				url: "/o/:orderType/f/:fulfillmentType/m/:menuId/:isPreview",
				templateUrl: "app/menu/menu.html",
				data: {
					preload: ["app/cart/cartItem.html", "shared/common/alert.html"],
					// validateOrderTypeRuleMenu: true,
					// requiresOrderTypeWorkflow: true,
					isOrderAccessControlled: true,
					title: "Menu"
				}
			}, {
				name: "menu.sections",
				url: "/s",
				views: {
					menuView: {
						templateUrl: getMenuSectionViewUrl
					}
				},
				data: {
					title: "Menu"
				}
			}, {
				name: "menu.sections.items",
				url: "/:sectionId/items",
				views: {
					menuSectionView: { templateUrl: "app/menu/menu.sections.items.html" }
				},
				data: {
					requiresActivation: false,
					title: "Menu"
				}
			}, {
				name: "menu.itemDetail",
				url: "/item",
				views: {
					menuView: { templateUrl: "app/menu/menu.itemDetail.html" }
				},
				data: {
					preload: getItemDetailPreload(),
					requiresActivation: false,
					title: "Menu"
				}
			}, {
				name: "loyaltyAdmin",
				url: "/rewards/:updating",
				templateUrl: "app/admin/loyaltyAdmin.html",
				data: {
					showInMenu: all(ifLoggedIn, ifMerchantHasLoyalty, ifUserCreateLoyaltyAccount),
					title: "Rewards",
					requiresMerchantLoyalty: true,
					linkClass: "rewards",
					isLoyaltyControlled: true
				}
			}, {
				name: "mealPlanAdmin",
				url: "/mealPlan",
				templateUrl: "app/admin/mealPlanAdmin.html",
				data: {
					showInMenu: all(ifLoggedIn, ifMerchantHasMealPlan),
					title: "Meal Plan",
					linkClass: "mealPlan",
					isLoyaltyControlled: true
				}
			}, {
				name: "giftCardAdmin",
				url: "/giftCard",
				templateUrl: "app/admin/giftCardAdmin.html",
				data: {
					showInMenu: all(ifLoggedIn, ifMerchantHasGiftCard),
					title: "Gift Cards",
					linkClass: "giftCard",
					isLoyaltyControlled: true
				}
			}, {
				name: "campusCardAdmin",
				url: "/campusCard",
				templateUrl: "app/admin/campusCardAdmin.html",
				data: {
					showInMenu: all(ifLoggedIn, ifMerchantHasGiftCard, ifMerchantHasCampusCard),
					title: "Campus Card",
					linkClass: "campusCard",
					isLoyaltyControlled: true
				}
			}, {
				name: "offers",
				url: "/offers/:offerType",
				templateUrl: "app/admin/offers.html",
				data: {
					showInMenu: all(ifLoggedIn, ifMerchantHasLoyalty, ifShowOffers),
					title: "Offers",
					requiresMerchantLoyalty: true,
					linkClass: "offers",
					isLoyaltyControlled: true
				}
			}, {
				name: "punchCards",
				url: "/punchCards",
				templateUrl: "app/admin/punchCards.html",
				data: {
					showInMenu: all(ifLoggedIn, ifMerchantHasLoyalty, ifShowOffers),
					title: "Punch Cards",
					requiresMerchantLoyalty: true,
					linkClass: "punchCards",
					isLoyaltyControlled: true
				}
			}, {
				name: "personalInfo",
				url: "/personalInfo",
				templateUrl: "app/admin/personalInfo.html",
				data: {
					parentMenuItem: accountMenuItem,
					title: "My Account",
					showInMenu: all(ifInLocationContext, ifLoggedIn),
					isLoyaltyControlled: true
				}
			}, {
				name: "orderHistory",
				data: {
					parentMenuItem: accountMenuItem,
					title: "Order History",
					showInMenu: all(ifLoggedIn, ifNotMerchantSignUpWithLoyalty),
					justRaiseEvent: events.orderHistoryRequested
				}
			}, {
				name: "login",
				data: {
					preload: [
						"app/security/securityDialog.html", "app/security/signin.html", "app/security/signup.html",
						"app/security/resetPassword.html"
					],
					title: "Sign In / Sign Up",
					showInMenu: all(ifLoginAllowed, ifNotLoggedIn),
					justShowLogin: true
				}
			}, {
				name: "logout",
				controller: "logout",
				url: "/location/logout",
				data: {
					title: "Log Out",
					showInMenu: all(ifNotInLocationContext, ifLoggedIn)
				}
			}, {
				name: "locationLogout",
				controller: "logout",
				url: "/logout",
				data: {
					parentMenuItem: accountMenuItem,
					title: "Log Out",
					showInMenu: all(ifInLocationContext, ifLoggedIn)
				}
			}, {
				name: "changePassword",
				url: "/changePassword",
				templateUrl: "app/admin/changePassword.html",
				data: {
					title: "Change Password",
					showInMenu: false
				}
			}, {
				name: "changeName",
				url: "/changeName",
				templateUrl: "app/admin/changeName.html",
				data: {
					title: "Change Name",
					showInMenu: false
				}
			}, {
				name: "changeEmail",
				url: "/changeEmail",
				templateUrl: "app/admin/changeEmail.html",
				data: {
					title: "Change Email",
					showInMenu: false
				}
			}, {
				name: "changeBirthdate",
				url: "/changeBirthdate",
				templateUrl: "app/admin/changeBirthdate.html",
				data: {
					title: "Change Birthdate",
					showInMenu: false
				}
			}, {
				name: "changeCallbackNumber",
				url: "/changeCallbackNumber",
				templateUrl: "app/admin/changeCallbackNumber.html",
				data: {
					title: "Change Callback Number",
					showInMenu: false
				}
			}, {
                name: "changeEmailOptIn",
                url: "/changeEmailOptIn",
                templateUrl: "app/admin/changeEmailOptIn.html",
                data: {
                    title: "Change Email Opt-In",
                    showInMenu: false
                }
			}, {
				name: "deleteUser",
				url: "/deleteUser",
				templateUrl: "app/admin/deleteUser.html",
				data: {
					title: "Are you Sure?",
					showInMenu: false
				}
			}, {
				name: "checkout",
				url: "/checkout",
				templateUrl: "app/checkout/checkout.html",
				data: {
					preload: ["app/cart/cartItem.html"],
					requiresOrderTypeWorkflow: true,
					title: "Checkout",
					requiresAuthorization: true,
					isOrderAccessControlled: true,
					validateOrder: true,
					allowGuestCheckout: true,
					requiresActiveLocation: true,
					isLoyaltyControlled: true
				}
			}, {
				name: "thankYou",
				url: "/complete",
				templateUrl: "app/checkout/thankYou.html",
				data: {
					title: "Thank You",
					requiresAuthorization: true,
					allowGuestCheckout: true
				}
			}, {
				name: "waitForOrderProgress",
				url: "/ordering",
				templateUrl: "app/checkout/waitForOrderProgress.html",
				data: {
					title: "Ordering...",
					requiresAuthorization: true,
					allowGuestCheckout: true
				}
			}, {
				name: "orderProgressFail",
				url: "/orderingoops",
				templateUrl: "app/checkout/orderProgressFail.html",
				data: {
					title: "Oh No",
					requiresAuthorization: true,
					allowGuestCheckout: true
				}
			}, {
				name: "customerArrival",
				url: "/arrival/:urlId",
				templateUrl: "app/customerArrival/customerArrival.html",
				data: {
					title: "Thank You"
				}
			}, {
				name: "repeatOrder",
				url: "/repeatOrder/:orderId",
				onEnter: [
					"$stateParams", "orderHistoryService", function($stateParams, orderHistoryService) {
						orderHistoryService.getOrderById($stateParams.orderId);
					}
				],
				data: {
					requiresActivation: false,
					isOrderAccessControlled: true
				}
			}, {
				name: "loading",
				url: "/loading",
				templateUrl: "app/loading/loadingPage.html",
				data: {
					title: "Loading",
					showInMenu: false
				}
			}, {
				name: "signup",
				url: "/signup",
				templateUrl: "app/security/signupStandAlone.html",
				data: {
					title: "Sign Up",
					showInMenu: false
				}
			}, {
				name: "achEnrollment",
				url: "/achEnrollment",
				templateUrl: "app/achEnrollment/achEnrollment.html",
				data: {
					title: "Rofo Pay",
					showInMenu: all(ifLoggedIn, ifMerchantHasLoyalty, ifMerchantSignUpWithLoyalty)
				}
			}
		];

		_.each(states,
			function(state) {
				if (state.data.requiresAuthorization) {
					state.resolve = state.resolve || {};
					state.resolve.authenticationRequired = [
						"userService", function(userService) {
							try {
								return userService.isAuthenticated();
							} catch (e) {
								if (!userService.isAGuest()) {
									e.allowGuestCheckout = state.data.allowGuestCheckout;
									throw e;
								}
							}
						}
					];
				}
				if (state.data.requiresOrderTypeWorkflow) {
					state.resolve = state.resolve || {};
					state.resolve.checkOrderTypeWorkflow = [
						"$stateParams", "$q", "orderTypeWorkflowService", "cartService",
						function($stateParams, $q, orderTypeWorkflowService, cartService) {

							function getOrderTypeFromCartService() {
								return cartService.loadCurrent().then(function(cart) {
									var data = {
										orderType: cart.order.OrderType,
										fulfillmentType: cart.order.FulfillmentType
									};
									return data;
								});
							}

							function getOrderTypeFromState($stateParams) {
								return $q.when({
									orderType: $stateParams.orderType,
									fulfillmentType: $stateParams.fulfillmentType
								});
							}

							var getOrderType = ($stateParams.orderType && $stateParams.fulfillmentType)
								? getOrderTypeFromState($stateParams)
								: getOrderTypeFromCartService();

							return getOrderType.then(function(data) {
								return orderTypeWorkflowService.meetsRequirementsForOrderType(data.orderType,
									data.fulfillmentType);
							});
						}
					];
				}
				if (state.data.validateOrder) {
					state.resolve = state.resolve || {};
					state.resolve.validateOrder = [
						"cartService", function(cartService) {
							return cartService.checkForValidations();
						}
					];

				}
				if (state.data.requiresMerchantLoyalty) {
					state.resolve = state.resolve || {};
					state.resolve.validateMerchantLoyalty = [
						"loyaltyService", function(loyaltyService) {
							return loyaltyService.assertMerchantHasLoyalty();
						}
					];
				}

				if (state.data.validateOrderTypeRule) {
					state.resolve = state.resolve || {};
					state.resolve.validateOrderTypeRule = [
						"$stateParams", "orderTypeRuleService", function($stateParams, orderTypeRuleService) {
							return orderTypeRuleService.validateOrderTypeRule($stateParams.orderType,
								$stateParams.fulfillmentType);
						}
					];
				}

				if (state.data.validateOrderTypeRuleMenu) {
					state.resolve = state.resolve || {};
					state.resolve.validateOrderTypeRuleMenu = [
						"$stateParams", "orderTypeRuleService", function($stateParams, orderTypeRuleService) {
							return orderTypeRuleService.validateOrderTypeRuleMenu($stateParams.orderType,
								$stateParams.fulfillmentType,
								$stateParams.menuId);
						}
					];
				}

				if (state.data.requiresActiveLocation) {
					state.resolve = state.resolve || {};
					state.resolve.validateIsActive = [
						"merchantLocationService", function(merchantLocationService) {
							return merchantLocationService.validateLocationIsActive();
						}
					];
				}

				if (state.data.isOrderAccessControlled) {
					state.resolve = state.resolve || {};
					state.resolve.ensureOrderable = [
						"$stateParams", "merchantLocationService", function($stateParams, merchantLocationService) {
							if ($stateParams.isPreview) {
								return true;
							}
							return merchantLocationService.validateCanOrder();
						}
					];
				}
				//make sure loyalty users have a birthdate
				if (state.data.isLoyaltyControlled) {
					state.resolve = state.resolve || {};
					state.resolve.ensureLoyaltyUserHasBirthdate = [
						"$stateParams", "userService", "merchantLocationService",
						function($stateParams, userService, merchantLocationService) {
							if (userService.checkIsAuthenticated()) {
								return merchantLocationService.requiresBirthdate().then(function(b) {
									if (!b) return true;
									return userService.getUser(togoorder.merchantId).then(function(user) {
										if (!user.DateOfBirth) {
											throw new LoyaltyUserWithNoBirthdateException();
										}
										return true;
									});
								});
							} else {
								return true;
							}
						}
					];

					state.resolve.ensureLoyaltyUserHasCallbackNumber = [
						"$stateParams", "userService", "merchantLocationService",
						function($stateParams, userService, merchantLocationService) {
							if (userService.checkIsAuthenticated()) {
								return merchantLocationService.requiresCallbackNumber().then(function(b) {
									if (!b) return true;
									return userService.getUser(togoorder.merchantId).then(function(user) {
										if (!user.CallbackNumber) {
											throw new LoyaltyUserWithNoCallbackNumberException();
										}
										return true;
									});
								});
							} else {
								return true;
							}
						}
					];

					state.resolve.ensureLoyaltyUserHasEmail = [
						"$stateParams", "userService", "merchantLocationService",
						function($stateParams, userService, merchantLocationService) {
							if (userService.checkIsAuthenticated()) {
								return merchantLocationService.requiresEmail().then(function(b) {
									if (!b) return true;
									return userService.getUser(togoorder.merchantId).then(function(user) {
										if (!user.Email) {
											throw new LoyaltyUserWithNoEmailException();
										}
										return true;
									});
								});
							} else {
								return true;
							}
						}
					];
				}

				$stateProvider.state(state.name, state);
			});


	}

	function LoyaltyUserWithNoEmailException() {
		this.name = 'LoyaltyUserWithNoEmail';
		this.message = 'Our app works best when we know your Email!';
	}

	function LoyaltyUserWithNoBirthdateException() {
		this.name = 'LoyaltyUserWithNoBirthdate';
		this.message = 'Our app works best when we know your Birthdate!';
	}

	function LoyaltyUserWithNoCallbackNumberException() {
		this.name = 'LoyaltyUserWithNoCallbackNumber';
		this.message = 'Our app works best when we know your Phone Number!';
	}
})(angular, _);;
(function () {
    'use strict';

    angular.module('main').controller('rootController', ['$scope', '$rootScope', '$injector', '$q', '$uibModal', '$window', '$timeout', '$location', '$state', '$stateParams', 'browserInfo', 'notify', 'events', 'userService', 'cartService', 'common', 'orderHistoryService', 'menuWorkflowService', 'feedbackService', 'spinner', 'linkService', 'merchantMessageService', 'crmService', 'analyticsService', rootController]);

    function rootController($scope, $rootScope, $injector, $q, $modal, $window, $timeout, $location, $state, $stateParams, browserInfo, notify, events, userService, cartService, common, orderHistoryService, menuWorkflowService, feedbackService, spinner, linkService, merchantMessageService, crmService, analyticsService) {
        var vm = this,
            spinnerTimer,
            viewTimer,
            isJustViewChange,
            allStates = $state.get();

        vm.showView = false;
        vm.showSpinner = 'default';
        vm.activeStateName = "";
        vm.isMenuForcedClosed = true;
        vm.goToState = goToState;
        vm.showTerms = showTerms;
        vm.mainMenuStates = [];
        vm.cancelOrder = cancelOrder;
        vm.isOrderFlow = !togoorder.merchantLocation.MerchantSignUpWithLoyalty;
        vm.showFeedbackSurvey = showFeedbackSurvey;

        Object.defineProperty(vm, 'bodyClass', {
            get: function () {
                if (togoorder && togoorder.userData && togoorder.userData.isWebLogin) {
                    return 'web-login';
                }
                return 'no-web-login';
            }
        });

        initialize();

        function initialize() {
            var isAuthenticated = false;
            try {
                isAuthenticated = userService.isAuthenticated();
            } catch (e) {
                // do nothing with this error
            }
            if (isAuthenticated) {
                merchantMessageService.showMessages();

                togoorder.callInjectedStrategy('setAuthToken', togoorder.getSavedUserData());
            }
            refreshMenu();

            analyticsService.initialize();
        }

        function showFeedbackSurvey() {
            spinner.spinnerShow();
            feedbackService.getSurvey(togoorder.merchantLocation.MerchantId).then(function (data) {
                var modalInstance = $modal.open({
                    templateUrl: 'app/feedback/feedbackSurvey.html',
                    controller: 'feedbackSurvey as vm',
                    backdrop: 'static',
                    resolve: {
                        survey: function () { return data; }
                    }
                });
            }, function (error) {
                notify.info("Sorry, survey is no longer available");
            });
        }

        function cancelOrder() {
            togoorder.callInjectedStrategy('cancelOrder');
        }
        function showView(on) {
            // binding of 'showView' doesn't happen fast enough to avoid a flicker,so...
            // resorting to jquery to hide the parent element.
            if (on) {
                $("#view-container").show();
            }
            else {
                $("#view-container").hide();
            }
            vm.showView = on;
        }
        function toggleView(on, pause) {
            $timeout.cancel(viewTimer);
            if (pause === 0) {
                showView(on);
            } else {
                viewTimer = $timeout(function () {
                    showView(on);
                }, pause);
            }
        }

        function toggleSpinner(on, pause, s) {
            $timeout.cancel(spinnerTimer);
            if (pause == 0) {
                vm.showSpinner = on;
            } else {
                spinnerTimer = $timeout(function () {
                    vm.showSpinner = on;
                }, pause);
            }
        }

        function refreshMenu() {

            var menuStates = _.filter(allStates, function (state) {
                if (state.data && state.data.showInMenu) {
                    if (typeof (state.data.showInMenu) === 'function') {
                        return state.data.showInMenu($rootScope, userService, togoorder.merchantLocation);
                    } else {
                        return true;
                    }
                }
                return false;
            });

            var grouped = _.groupBy(menuStates, function (state) {
                return state.data && state.data.parentMenuItem && state.data.parentMenuItem && angular.toJson(state.data.parentMenuItem) || 'top';
            });

            var groupKeysWithSubItems = _.filter(_.keys(grouped), function (key) {
                return key !== 'top';
            });

            var itemsWithSubMenus = _.map(groupKeysWithSubItems, function (key) {
                var parentItem = angular.fromJson(key);
                return {
                    data: {
                        title: parentItem.title,
                        icon: parentItem.icon
                    },
                    //name: 'uib-dropdown',
                    items: grouped[key]
                };
            });

            vm.mainMenuStates = grouped.top;
            vm.itemsWithSubMenus = itemsWithSubMenus;

        }

        function refreshPage() {
            $window.location.reload();
        }

        function goToState(state, params, options) {
            if (!state.items) {
                if (state.data && state.data.onBeforeNavigate) {
                    $injector.invoke(state.data.onBeforeNavigate);
                }

                $state.go(state, params, options);
            }
        }

        function showTerms() {
            linkService.openLinkInNewWindow("Home/TermsOfService?merchantId=" + togoorder.merchantId, "tos");
            return false;
        }

        $rootScope.$on("$stateChangeSuccess", function (event, current) {
            //$("div.container.body-content").addClass("state-" + current.name);
            //if (vm.showView && isJustViewChange) {
            //	isJustViewChange = false;
            //^this is hacky... 
            //trying to prevent this from hiding the 
            // spinner even though the activate hasn't been called yet.
            //	console.log("toggleSpinner OFF");
            //	toggleSpinner(false, 0);
            //} else {
            //	console.log('DON\'T toggleSpinner OFF - wait for controllerActivateSuccess');
            //}
            refreshMenu();
            vm.activeStateName = current.name;
            let currentViewTitle = (current.data && current.data.title) || "Order ahead, and skip the line";
            $rootScope.title = `${currentViewTitle} - ${togoorder.merchantLocation.LocationName} - ToGoOrder.com`;
            $window.scrollTo(0, 0);
        });

        $rootScope.$on('$locationChangeSuccess', function () {
            $rootScope.actualLocation = $location.path();
        });

        $rootScope.$watch(function () { return $location.path(); }, function (newLocation, oldLocation) {
            if ($rootScope.actualLocation === newLocation) {
                //Can't go 'forward' or 'back' to the itemDetails
                if (newLocation.indexOf('/item') === newLocation.length - 5) {
                    $window.history.back();
                }
            }

        });

        $rootScope.$on("$stateChangeError",
            function (event, toState, toParams, fromState, fromParams, error) {
                error = error || {};

                switch (error.name) {
                    case 'AuthenticationRequired':
                        toggleView(true, 0);
                        toggleSpinner(false, 0);
                        userService.setNextState(toState.name,
                            toParams,
                            'You must sign in to access the ' + ((toState.data && toState.data.title) || 'requested') + ' page');
                        userService.login(null, error.allowGuestCheckout);
                        break;
                    case 'RedirectRequired':
                        var params = angular.extend($stateParams, toParams, error.nextState.params);
                        //console.log('params!', params, $stateParams, toParams, error.nextState.params);
                        goToState(error.nextState.name, params, { reload: true });
                        break;
                    case 'CartOrderItemInvalid':
                        toggleView(true, 0);
                        toggleSpinner(false, 0);
                        break;
                    case 'OrderTypeRuleMissing':
                        toggleView(true, 0);
                        toggleSpinner(false, 0);
                        goToState('locationHome', {}, { reload: true });
                        break;
                    case 'OrderTypeRuleMenuMismatch':
                        toggleView(true, 0);
                        toggleSpinner(false, 0);
                        menuWorkflowService.goToFirstState(
                            { orderType: $stateParams.orderType, fulfillmentType: $stateParams.fulfillmentType },
                            { reload: true });
                        break;
                    case 'OrderingNotAllowed':
                        toggleView(true, 0);
                        toggleSpinner(false, 0);
                        //goToState('orderingNotAllowed', { reload: true });

                        navigateToOrderingNotAllowed();

                        break;
                    case 'LoyaltyUserWithNoBirthdate':
                        toggleView(true, 0);
                        toggleSpinner(false, 0);
                        notify.info(error.message, error, arguments);
                        goToState('changeBirthdate', {}, {});
                        break;
                    case 'LoyaltyUserWithNoCallbackNumber':
                        toggleView(true, 0);
                        toggleSpinner(false, 0);
                        notify.info(error.message, error, arguments);
                        goToState('changeCallbackNumber', {}, {});
                        break;
                    case 'LoyaltyUserWithNoEmail':
                        toggleView(true, 0);
                        toggleSpinner(false, 0);
                        notify.info(error.message, error, arguments);
                        goToState('changeEmail', {}, {});
                        break;
                    default: {
                        var msg = "We can't seem to make the " +
                            ((toState && toState.data && toState.data.title) || "") +
                            " page work for you. Let's try again.";
                        notify.error(msg, error, arguments);
                        if (toState.name !== 'locationHome') {
                            goToState('locationHome', {}, { reload: true });
                        }
                    }
                }
            });

        $rootScope.$on('$stateChangeStart',
            function (event, next) {
                $('#navbarCollapse').collapse("hide");

                if (next.data && next.data.justShowLogin) {
                    userService.login(function () {
                        toggleSpinner(true, 0);
                        $state.reload();
                        refreshMenu();
                    });

                    event.preventDefault();
                } else if (next.data && next.data.justRaiseEvent) {
                    event.preventDefault();
                    common.broadcast(next.data.justRaiseEvent, next.data);
                } else if (next.data && next.data.requiresActivation !== false) {
                    toggleView(false, 0);
                    toggleSpinner(true, 100);
                } else {
                    // Don't toggle view here
                    //isJustViewChange = true;
                    //toggleSpinner(true, 100);
                }
            }
        );

        $scope.$on(events.controllerActivateSuccess,
            function () {
                toggleSpinner(false, 4, 'controllerActivateSuccess');
                toggleView(true, 100);
            }
        );

        $scope.$on(events.controllerActivateFail,
            function (data, exception) {
                if (exception && exception.name !== 'NormalException')
                    notify.error('Something went wrong. (Computers. Right?) Let\'s try that again.', data);
                goToState('locationHome', {}, { reload: true });
            }
        );

        $rootScope.$on(events.spinnerToggle,
            function (e, data) {
                toggleSpinner(data.show, 100, data.s);
            }
        );

        $rootScope.$on(events.changeDeliveryAddressRequest,
            function (e, data) {
                goToState('getDeliveryAddress', getStateParams());
            }
        );

        $rootScope.$on(events.changeTableNumberRequest,
            function (e, data) {
                goToState('getTableNumber', getStateParams());
            }
        );

        $rootScope.$on(events.orderHistoryRequested,
            function (e, data) {
                toggleSpinner(true, 100);
                orderHistoryService.showOrderHistory(togoorder.merchantLocation).then(function () {
                    toggleSpinner(false, 100);
                });
            }
        );

        $rootScope.$on(events.navigateToMerchantWebsite,
            function (e, data) {
                toggleSpinner(true, 100);
                $window.location.href = togoorder.locationWebsite;
            }
        );

        function navigateToOrderingNotAllowed() {
            toggleSpinner(true, 100);
            $window.location.href = "Home/InactiveLocation/" + togoorder.locationId;
        }

        function navigateToMerchantHome() {
            toggleSpinner(true, 100);
            $window.location.href = "?id=" + togoorder.merchantId + "#!/" + (togoorder.isUnlistedOrderType ? "unlisted" : "");
        }

        $rootScope.$on(events.navigateToMerchantHome, navigateToMerchantHome);

        function getStateParams() {
            var params = $stateParams;
            if (!params.orderType) {
                var cart = cartService.getCart();
                if (!cart || !cart.order) {
                    throw new Error('Can\'t get the OrderType');
                }
                params = {
                    menuId: cart.order.MenuId,
                    orderType: cart.order.OrderType,
                    fulfillmentType: cart.order.FulfillmentType
                };
            }
            return params;
        }

        function openSecurityForm(mode, args) {
            var modalInstance = $modal.open({
                templateUrl: 'app/security/securityDialog.html',
                controller: 'securityDialog as vm',
                backdrop: 'static',
                resolve: {
                    allowGuestCheckout: function () { return args.allowGuestCheckout; },
                    mode: function () { return mode; },
                    goSignUp: function () { return args.goSignUp; }
                }
            });

            modalInstance.result.then(function (userData) {
                if (userData.isAuthenticated) {
                    crmService.login();
                    common.broadcast(events.login, { userData });
                    notify.success("You are now signed in!");

                    merchantMessageService.showMessages();
                    if (args.callback) {
                        args.callback();
                    }
                }
            }, function () {
                if (!$state || !$state.current || !$state.current.name) {
                    goToState('locationHome', {}, { reload: true });
                } else {
                    toggleView(true, 0);
                }
            });
        }

        $rootScope.$on(events.signUpRequested, function (e, args) {
            if (!togoorder.callInjectedStrategy('signUp')) {
                openSecurityForm('signUp', args);
            }
        });

        $rootScope.$on(events.securityAuthorizationRequired, function (e, args) {

            // Be nice to handle this in spendgoAuthStrategy.js but that isn't angular-ized yet
            if (togoorder.spendgo && args.allowGuestCheckout) {
                // Prompt user for guest checkout option before sending to spendgo sso
                var result = notify.dialog({
                    dialogTitle: 'Sign in options',
                    dialogMessage: 'Would you like to sign in normally or check out as a guest?',
                    dismissButtonText: 'Checkout as Guest',
                    closeButtonText: 'Sign in / Sign up'
                });
                result.then(function () {
                    auth(args);
                },
                    function () {
                        userService.setUserAsGuest()
                            .then(function () {
                                goToState('checkout', {}, {});
                            });
                    });
            } else if (togoorder.merchantLocation.IsOnlyGuestCheckout) {
                var nextState = userService.getNextState();
                userService.setUserAsGuest().then(function () {
                    $state.go(nextState.name, nextState.params);
                });
            } else {
                auth(args);
            }
        });

        var isHubActive = false;
        $rootScope.$on(events.orderHubProgressDone,
            function (e, orderProgress) {
                isHubActive = false;
                try {
                    //console.log('$.connection.hub.stop', orderProgress);
                    $.connection.hub.stop();
                } catch (e) {
                    console.log(e);
                }

                try {
                    //console.log('$.connection.orderHub.connection.stop');

                    var orderHub = $.connection.orderHub;
                    orderHub.connection.stop();
                } catch (e) {
                    console.log(e);
                }

            });

        var orderUrlId = null;
        var connectionHub = null;
        function startHub() {
            function hubStart() {
                connectionHub.start().done(function () {
                    console.log("$.connection.hub.start().done(): " + connectionHub.id, connectionHub);
                });
            }

            if (!connectionHub) {
                connectionHub = $.connection.hub;

                connectionHub.logging = true;
                connectionHub.connectionSlow(function () {
                    console.log("$.connection.hub.connectionSlow");
                });
                connectionHub.reconnecting(function () {
                    console.log("$.connection.hub.reconnecting");
                });
                connectionHub.reconnected(function () {
                    console.log("$.connection.hub.reconnected");
                });
                var stateConversion = { 0: 'connecting', 1: 'connected', 2: 'reconnecting', 4: 'disconnected' };
                connectionHub.stateChanged(function (state) {
                    //console.log('$.connection.hub.stateChanged. from: ' +
                    //	stateConversion[state.oldState] +
                    //	' to: ' +
                    //	stateConversion[state.newState],
                    //	state);
                });
                connectionHub.disconnected(function () {
                    console.log("$.connection.hub.disconnected");
                    if (connectionHub.lastError) {
                        console.log("$.connection.hub.lastError", connectionHub.lastError);
                    }
                    if (!isHubActive) {
                        return;
                    }
                    setTimeout(function () {
                        console.log("Restart connection...");
                        hubStart();
                    },
                        1000); // Restart connection after x seconds.
                });
            }
            connectionHub.qs = "urlId=" + orderUrlId;
            hubStart();
            return connectionHub;
        }

        function connectToOrderHub(orderSummary) {
            if (orderUrlId === orderSummary.orderUrlId && isHubActive) {
                //console.log('orderUrlId Already', orderUrlId);
                return;
            }
            isHubActive = true;
            orderUrlId = orderSummary.orderUrlId;
            //console.log('orderUrlId', orderUrlId);

            var orderHub = $.connection.orderHub;

            orderHub.client.updateProgress = function (args) {
                //console.log("args: ", args);
                common.broadcast(events.orderHubProgress, args);
            };

            startHub();
        }
        $rootScope.$on(events.readyForOrderProgress,
            function (e, orderSummary) {
                if (togoorder.merchantLocation.IsPosIntegrationFailToCustomer) {
                    connectToOrderHub(orderSummary);
                }
            });

        function auth(args) {
            var authResult = togoorder.callInjectedStrategy('authenticateUser');
            if (!authResult) {
                openSecurityForm('logIn', args);
            } else {
                if (authResult.result) {
                    $q.when(authResult.result).then(function () {
                        common.log('authenticateUser handled');
                    }, function () {
                        common.log('authenticateUser not handled');
                        openSecurityForm('logIn', args);
                    });
                }
            }
        }
    }
})();
;
// Main configuration file. Sets up AngularJS module and routes and any other config objects
(function() {
	'use strict';

	angular.module('main').run(['$templateCache', '$http', '$state', cacheTemplates]);

	function cacheTemplates($templateCache, $http, $state) {

		var allStates = $state.get();

		var paths = [];
		_.each(allStates, function (state) {
			if (state && state.data && state.data.preload) {
				if (state.data.preload === true && state.templateUrl) {
					paths.push(state.templateUrl);
				}
				if (_.isArray(state.data.preload)) {
					_.each(state.data.preload, function(t) {
						paths.push(t);
					});
				}
			}
		});

		_.each(paths, function(path) {
			$http.get(path, { cache: $templateCache });
		});
	}
})();;
(function () {
    'use strict';

    angular.module('main').run(['$state', cacheTemplates]);

    function cacheTemplates($state) {
        FastClick.attach(document.body);
    }
})();;
(function () {
    'use strict';

    var controllerId = 'campusCardAdmin';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'giftCardService', 'cartService', 'menuWorkflowService', campusCardAdminController]);

    function campusCardAdminController($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, giftCardService, cartService, menuWorkflowService) {
        var vm = this,
	        locationId = togoorder.locationId;

        vm.user = undefined;
        vm.merchantLocation = {};
        vm.beginAddExistingGiftCard = beginAddExistingGiftCard;
        vm.isAddingExistingCard = false;
        vm.addExistingGiftCard = addExistingGiftCard;
        vm.cancelAddGiftCard = cancelAddGiftCard;
        vm.goToMenu = goToMenu;
        vm.existingCard = {
            merchantId: 0,
            cardNumber: '',
            isPrimary: true
        };
        vm.campusCard = {};

        function goToMenu() {
            cartService.loadCurrent().then(function () {
                var cart = cartService.getCart();
                if (!cart.order || !cart.order.OrderType) {
                    $state.go('locationHome');
                } else {
                    menuWorkflowService.continueOrdering(cart);
                }

            }, function () {
                $state.go('locationHome');
            });
        }

        function addExistingGiftCard(isValid) {
            if (!isValid) {
                notify.dialog('Almost!', 'All fields are required');
                return;
            }
            spinner.spinnerShow();

            vm.existingCard.merchantId = vm.merchantLocation.MerchantId;
            giftCardService.addCard(vm.existingCard)['finally'](function () {
                return getUser()['finally'](function () {
                    vm.isAddingExistingCard = false;
                    spinner.spinnerHide();
                    return getCampusCard();
                });
            });
        }

        function beginAddExistingGiftCard() {
            vm.isAddingExistingCard = true;
        }

        function cancelAddGiftCard() {
            vm.isAddingExistingCard = false;
        }

        activate();

        function activate() {
            common.activateController([getMerchantLocation().then(getUser).then(getCampusCard)], controllerId)
                .then(function () {
                    /*setTimeout(function () {
                            vm.campusCard.qrcode = new QRCode('qrcode_' + vm.campusCard.AccountNumber,
                                {
                                    text: vm.campusCard.AccountNumber,
                                    width: 50,
                                    height: 50,
                                    colorDark: "#000000",
                                    colorLight: "#ffffff",
                                    correctLevel: QRCode.CorrectLevel.H
                                });
                        },
                        1500);*/
		        });
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(locationId)
				.then(function (data) {
				    vm.merchantLocation = data;
				    vm.loyaltyProfileType = vm.merchantLocation.LoyaltyProfile && vm.merchantLocation.LoyaltyProfile.LoyaltyProviderType;
				    vm.existingCard.merchantId = vm.merchantLocation.MerchantId;
				    return data;
				});
        }

        function getUser() {
            return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId)
				.then(function (user) {
				    vm.user = user;
                });
        }

        function getCampusCard() {
            _.each(vm.user.GiftCards,
                function (gc) {
                   // console.log(gc);
                    if (gc.IsPrimary === true) {
                        return giftCardService.getBalance({
                            MerchantId: vm.merchantLocation.MerchantId,
                            CardNumber: gc.AccountNumber
                        }).then(function (data) {
                            gc.Balance = data.Balance;
                            vm.campusCard = gc;
                        });
                    }
                });
            return true;
        }
    }
})();;
(function() {
    'use strict';

    var controllerId = 'changeBirthdate';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'cartService', 'menuWorkflowService', changeBirthdate]);

    function changeBirthdate($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, cartService, menuWorkflowService) {
    	var vm = this,
	        merchantId = togoorder.merchantId;

    	vm.user = undefined;
        vm.isValidationVisible = false;
    	vm.goToMenu = goToMenu;
    	vm.cancel = cancel;
    	vm.changeBirthdate = changeBirthdate;
    	vm.errors = [];
    	vm.birthdate = undefined;
    	vm.email = undefined;
    	vm.isCalendarVisible = false;
        vm.showCalendar = showCalendar;
        vm.dateOptions = {
	        initDate: new Date('1980/01/01'),
	        formatYear: 'yyyy',
	        startingDay: 1,
	        altInputFormats: [
		        'M!/d!/yyyy',
		        'M!-d!-yyyy',
		        'M!.d!.yyyy',
		        'M!d!yyyy',
		        'MM!/dd!/yyyy',
		        'MM!-dd!-yyyy',
		        'MM!.dd!.yyyy',
		        'MM!dd!yyyy',
		        'MM/dd/yyyy',
		        'M!/d!/yy',
		        'M!-d!-yy',
		        'M!.d!.yy',
		        'M!d!yy',
		        'MM!/dd!/yy',
		        'MM!-dd!-yy',
		        'MM!.dd!.yy',
		        'MM!dd!yy',
		        'MM/dd/yy'
	        ]
        };

        Object.defineProperty(vm, "birthdateVm", {
	        get: function () {
		        return this.birthdate;
	        },
	        set: function (val) {
		        var m = val && moment(val);
		        while (m && m.isAfter(moment().add(-10, 'year'))) {
			        m = m.add(-100, 'year');
		        }
		        this.birthdate = m && m.toDate();
	        }
        });


        function cancel() {
	        $state.go('personalInfo');
        }

        function showCalendar($event) {
            $event.preventDefault();
            $event.stopPropagation();
            vm.isCalendarVisible = true;
        };

        function changeBirthdate(isValid) {
    	    vm.errors = [];

    	    if (!isValid) {
                vm.isValidationVisible = true;
                notify.warning('Please correct any errors.');
                vm.errors = ['Please correct any errors.'];
                return;
            }

    	    spinner.spinnerShow();

            userService.changeBirthdate(vm.birthdate)
                .then(onSuccess, onFail)
                ['finally'](function () {
                    spinner.spinnerHide();
                });
    	}

    	function onSuccess() {
    	    notify.dialog('Success', "Your Birthdate has been changed.").then(function() {
    	        $state.go('personalInfo');
	        });
	    }

    	function onFail() {
	        var error = arguments.length && arguments[0] && arguments[0].data && arguments[0].data.message || 'An unexpected error occurred.';
    	    //notify.error('Oops', "error");
	        vm.errors = [error];
	    }

	    function goToMenu() {
	        cartService.loadCurrent().then(function() {
	            var cart = cartService.getCart();
	            if (!cart.order || !cart.order.OrderType) {
	                $state.go('locationHome');
	            } else {
	                menuWorkflowService.continueOrdering(cart);
	            }

	        }, function() {
	            $state.go('locationHome');
	        });
	    }

        activate();

        function activate() {
	        common.activateController([getUser()], controllerId)
		        .then(function() {
		        });
        }


        function getUser() {
        	return userService.getUser(merchantId)
				.then(function (user) {
				    vm.user = user;
					vm.birthdate = user.DateOfBirth && moment(user.DateOfBirth).toDate();
	                vm.email = user.Email;
	            });
        }
    }
})();;
(function() {
    'use strict';

    var controllerId = 'changeCallbackNumber';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'cartService', 'menuWorkflowService', changeCallbackNumber]);

    function changeCallbackNumber($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, cartService, menuWorkflowService) {
    	var vm = this,
	        merchantId = togoorder.merchantId;

    	vm.user = undefined;
        vm.isValidationVisible = false;
    	vm.goToMenu = goToMenu;
    	vm.cancel = cancel;
    	vm.changeCallbackNumber = changeCallbackNumber;
    	vm.errors = [];
        vm.callbackNumber = undefined;
        vm.email = undefined;

        function cancel() {
	        $state.go('personalInfo');
	    }

        function changeCallbackNumber(isValid) {
    	    vm.errors = [];

    	    if (!isValid) {
                vm.isValidationVisible = true;
                notify.warning('Please correct any errors.');
                vm.errors = ['Please correct any errors.'];
                return;
            }

    	    spinner.spinnerShow();

            userService.changeCallbackNumber(vm.callbackNumber)
                .then(onSuccess, onFail)
                ['finally'](function () {
                    spinner.spinnerHide();
                });
    	}

    	function onSuccess() {
    	    notify.dialog('Success', "Your callback number has been changed.").then(function() {
    	        $state.go('personalInfo');
	        });
	    }

		function onFail() {
	        var error = arguments.length && arguments[0] && arguments[0].data && arguments[0].data.Message || 'An unexpected error occurred.';
			//notify.error('Oops', "error");
	        vm.errors = [error];
	    }

	    function goToMenu() {
	        cartService.loadCurrent().then(function() {
	            var cart = cartService.getCart();
	            if (!cart.order || !cart.order.OrderType) {
	                $state.go('locationHome');
	            } else {
	                menuWorkflowService.continueOrdering(cart);
	            }

	        }, function() {
	            $state.go('locationHome');
	        });
	    }

        activate();

        function activate() {
	        common.activateController([getUser()], controllerId)
		        .then(function() {
		        });
        }


        function getUser() {
        	return userService.getUser(merchantId)
				.then(function (user) {
				    vm.user = user;
				    vm.callbackNumber = user.CallbackNumber;
	                vm.email = user.Email;
	            });
        }
    }
})();;
(function () {
    'use strict';

    var controllerId = 'changeEmail';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'cartService', 'menuWorkflowService', 'crmService', changeEmail]);

    function changeEmail($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, cartService, menuWorkflowService, crmService) {
        var vm = this,
	        merchantId = togoorder.merchantId;

        vm.user = undefined;
        vm.isValidationVisible = false;
        vm.goToMenu = goToMenu;
        vm.cancel = cancel;
        vm.changeEmail = changeEmail;
        vm.errors = [];
        vm.firstName = undefined;
        vm.lastName = undefined;
        vm.email = undefined;

        function cancel() {
            $state.go('personalInfo');
        }

        function changeEmail(isValid) {
            vm.errors = [];

            if (!isValid) {
                vm.isValidationVisible = true;
                notify.warning('Please correct any errors.');
                vm.errors = ['Please correct any errors.'];
                return;
            }

            spinner.spinnerShow();
            userService.changeEmail(vm.email)
                .then(onSuccess, onFail)
                ['finally'](function () {
                    spinner.spinnerHide();
                });
        }

        function onSuccess() {
            notify.dialog('Success', "Your email has been changed.").then(function () {
                $state.go('personalInfo');
                crmService.changeEmail(vm.email);
            });
        }

        function onFail() {
            var error = arguments.length && arguments[0] && arguments[0].data && arguments[0].data.message || 'An unexpected error occurred.';
            //notify.error('Oops', "error");
            vm.errors = [error];
        }

        function goToMenu() {
            cartService.loadCurrent().then(function () {
                var cart = cartService.getCart();
                if (!cart.order || !cart.order.OrderType) {
                    $state.go('locationHome');
                } else {
                    menuWorkflowService.continueOrdering(cart);
                }

            }, function () {
                $state.go('locationHome');
            });
        }

        activate();

        function activate() {
            common.activateController([getUser()], controllerId)
		        .then(function () {
		        });
        }


        function getUser() {
            return userService.getUser(merchantId)
				.then(function (user) {
				    vm.user = user;
				    vm.firstName = user.FirstName;
				    vm.lastName = user.LastName;
				    vm.email = user.Email;
				});
        }
    }
})();;
(function() {
    'use strict';

    var controllerId = 'changeEmailOptIn';
    angular.module('main')
        .controller(controllerId, ['$state', 'notify', 'common', 'spinner', 'userService', 'cartService', 'menuWorkflowService', changeEmailOptIn]);

    function changeEmailOptIn($state, notify, common, spinner, userService, cartService, menuWorkflowService) {
    	var vm = this,
	        merchantId = togoorder.merchantId;

    	vm.user = undefined;
        vm.isValidationVisible = false;
    	vm.goToMenu = goToMenu;
    	vm.cancel = cancel;
    	vm.changeEmailOptIn = changeEmailOptIn;
    	vm.errors = [];
        vm.emailOptIn = undefined;
        vm.email = undefined;

        function cancel() {
	        $state.go('personalInfo');
	    }

        function changeEmailOptIn() {
    	    vm.errors = [];

    	    spinner.spinnerShow();

            userService.changeEmailOptIn(vm.emailOptIn)
                .then(onSuccess, onFail)
                ['finally'](function () {
                    spinner.spinnerHide();
                });
    	}

    	function onSuccess() {
    	    notify.dialog('Success', 'Your email opt-in setting has been changed.').then(function() {
    	        $state.go('personalInfo');
	        });
	    }

		function onFail() {
	        var error = arguments.length && arguments[0] && arguments[0].data && arguments[0].data.Message || 'An unexpected error occurred.';
			//notify.error('Oops', "error");
	        vm.errors = [error];
	    }

	    function goToMenu() {
	        cartService.loadCurrent().then(function() {
	            var cart = cartService.getCart();
	            if (!cart.order || !cart.order.OrderType) {
	                $state.go('locationHome');
	            } else {
	                menuWorkflowService.continueOrdering(cart);
	            }

	        }, function() {
	            $state.go('locationHome');
	        });
	    }

        activate();

        function activate() {
	        common.activateController([getUser()], controllerId)
		        .then(function() {
		        });
        }

        function getUser() {
        	return userService.getUser(merchantId)
				.then(function (user) {
				    vm.user = user;
				    vm.emailOptIn = user.EmailOptIn;
	                vm.email = user.Email;
	            });
        }
    }
})();
;
(function() {
    'use strict';

    var controllerId = 'changeName';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'cartService', 'menuWorkflowService', changeName]);

    function changeName($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, cartService, menuWorkflowService) {
    	var vm = this,
	        merchantId = togoorder.merchantId;

    	vm.user = undefined;
        vm.isValidationVisible = false;
    	vm.goToMenu = goToMenu;
    	vm.cancel = cancel;
    	vm.changeName = changeName;
    	vm.errors = [];
    	vm.firstName = undefined;
    	vm.lastName = undefined;
        vm.email = undefined;

        function cancel() {
            $state.go('personalInfo');
	    }

        function changeName(isValid) {
    	    vm.errors = [];

    	    if (!isValid) {
                vm.isValidationVisible = true;
                notify.warning('Please correct any errors.');
                vm.errors = ['Please correct any errors.'];
                return;
            }

            spinner.spinnerShow();
            userService.changeName(vm.firstName, vm.lastName)
                .then(onSuccess, onFail)
                ['finally'](function () {
                    spinner.spinnerHide();
                });
    	}

    	function onSuccess() {
    	    notify.dialog('Success', "Your name has been changed.").then(function() {
    	        $state.go('personalInfo');
	        });
	    }

    	function onFail() {
	        var error = arguments.length && arguments[0] && arguments[0].data && arguments[0].data.message || 'An unexpected error occurred.';
    	    //notify.error('Oops', "error");
	        vm.errors = [error];
	    }

	    function goToMenu() {
	        cartService.loadCurrent().then(function() {
	            var cart = cartService.getCart();
	            if (!cart.order || !cart.order.OrderType) {
	                $state.go('locationHome');
	            } else {
	                menuWorkflowService.continueOrdering(cart);
	            }

	        }, function() {
	            $state.go('locationHome');
	        });
	    }

        activate();

        function activate() {
	        common.activateController([getUser()], controllerId)
		        .then(function() {
		        });
        }


        function getUser() {
        	return userService.getUser(merchantId)
				.then(function (user) {
				    vm.user = user;
				    vm.firstName = user.FirstName;
				    vm.lastName = user.LastName;
	                vm.email = user.Email;
	            });
        }
    }
})();;
(function() {
    'use strict';

    var controllerId = 'changePassword';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'cartService', 'menuWorkflowService', changePassword]);

    function changePassword($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, cartService, menuWorkflowService) {
    	var vm = this,
	        merchantId = togoorder.merchantId;

    	vm.user = undefined;
    	vm.currentPassword = '';
    	vm.password = '';
    	vm.confirmPassword = '';
        vm.isValidationVisible = false;
    	vm.goToMenu = goToMenu;
    	vm.cancelChangePassword = cancelChangePassword;
    	vm.changePassword = doChangePassword;
    	vm.errors = [];
        vm.email = undefined;
        vm.passwordMinLength = 7;

    	function cancelChangePassword() {
    	    $state.go('personalInfo');
	    }

    	function doChangePassword(isValid) {
    	    vm.errors = [];

    	    if (!isValid) {
                vm.isValidationVisible = true;
                notify.warning('Please correct any errors.');
                vm.errors = ['Please correct any errors.'];
                return;
            }

            spinner.spinnerShow();
            userService.changePassword(vm.currentPassword, vm.password, vm.confirmPassword)
                .then(onSuccessfulChangePassword, onFailedChangePassword)
                ['finally'](function () {
                    spinner.spinnerHide();
                });
    	}

    	function onSuccessfulChangePassword() {
    	    notify.dialog('Success', "Your password has been changed.").then(function() {
    	        $state.go('personalInfo');
	        });
	    }

    	function onFailedChangePassword() {
	        var error = arguments.length && arguments[0] && arguments[0].data && arguments[0].data.message || 'An unexpected error occurred.';
    	    //notify.error('Oops', "error");
	        vm.errors = [error];
	    }

	    function goToMenu() {
	        cartService.loadCurrent().then(function() {
	            var cart = cartService.getCart();
	            if (!cart.order || !cart.order.OrderType) {
	                $state.go('locationHome');
	            } else {
	                menuWorkflowService.continueOrdering(cart);
	            }

	        }, function() {
	            $state.go('locationHome');
	        });
	    }

        activate();

        function activate() {
	        common.activateController([getUser()], controllerId)
		        .then(function() {
		        });
        }


        function getUser() {
        	return userService.getUser(merchantId)
				.then(function (user) {
				    vm.user = user;
	                vm.email = user.Email;
	            });
        }
    }
})();;
(function () {
    'use strict';

    var controllerId = 'deleteUser';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'cartService', 'menuWorkflowService', deleteUser]);

    function deleteUser($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, cartService, menuWorkflowService) {
        var vm = this,
            merchantId = togoorder.merchantId;

        vm.user = undefined;
        vm.isValidationVisible = false;
        vm.cancel = cancel;
        vm.deleteUser = deleteUser;
        vm.errors = [];
        vm.firstName = undefined;
        vm.lastName = undefined;
        vm.email = undefined;
        vm.merchantList = undefined;

        function cancel() {
            $state.go('personalInfo');
        }

        function deleteUser(isValid) {
            vm.errors = [];

            if (!isValid) {
                vm.isValidationVisible = true;
                notify.warning('Please correct any errors.');
                vm.errors = ['Please correct any errors.'];
                return;
            }

            spinner.spinnerShow();
            userService.deleteUser()
                .then(onSuccess, onFail)
            ['finally'](function () {
                spinner.spinnerHide();
            });
        }

        function onSuccess() {
            notify.dialog('Success', "Your account has been deleted").then(function () {
                $state.go('logout');
            });
        }

        function onFail() {
            var error = arguments.length && arguments[0] && arguments[0].data && arguments[0].data.message || 'An unexpected error occurred.';
            //notify.error('Oops', "error");
            vm.errors = [error];
        }

        activate();

        function activate() {
            common.activateController([getUser(), getUserMerchants()], controllerId)
                .then(function () {
                });
        }


        function getUser() {
            return userService.getUser(merchantId)
                .then(function (user) {
                    vm.user = user;
                    vm.firstName = user.FirstName;
                    vm.lastName = user.LastName;
                    vm.email = user.Email;
                });
        }
        function getUserMerchants() {
            console.log('getting user merchants');
            return userService.getUserMerchants().then(function (merchants) {
                vm.merchantList = merchants;
            })
        }
    }
})();;
(function () {
    'use strict';

    var controllerId = 'giftCardAdmin';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$q', '$log', '$state', '$stateParams', '$uibModal', '$filter', '$timeout', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'giftCardService', 'cartService', 'menuWorkflowService', 'vcRecaptchaService', giftCardAdminController]);

    function giftCardAdminController($rootScope, $scope, $q, $log, $state, $stateParams, $modal, $filter, $timeout, notify, common, spinner, events, merchantLocationService, userService, giftCardService, cartService, menuWorkflowService, vcRecaptchaService) {
        var vm = this,
	        locationId = togoorder.locationId;

        vm.user = undefined;
        vm.merchantLocation = {};
        vm.beginAddExistingGiftCard = beginAddExistingGiftCard;
        vm.isAddingExistingCard = false;
        vm.addExistingGiftCard = addExistingGiftCard;
        vm.deleteGiftCard = deleteGiftCard;
        vm.cancelAddGiftCard = cancelAddGiftCard;
        vm.goToMenu = goToMenu;
        vm.availableGiftCards = [];
        vm.existingCard = {
            merchantId: 0,
            cardNumber: '',
            isPrimary: false,
            cardPin: '',
            recaptchaToken: ''
        };
        vm.giftCardProviderType = null;
        vm.showCaptcha = false;
        vm.showPin = false;
        vm.recaptchaKey = togoorder.recaptchaKey;
        vm.recaptchaId = undefined;
        vm.onRecaptchaCreate = onRecaptchaCreate;
        vm.isInputMasked = false;
        vm.stripSpaces = stripSpaces;
        vm.needsPin = needsPin;
        vm.promptForPin = promptForPin;

        Object.defineProperty(vm, 'showExistingGiftCards', {
            get: function () {
                return (vm.user && vm.availableGiftCards.length) && !vm.isAddingExistingCard;
            }
        });

        Object.defineProperty(vm, 'showAddExistingGiftCardButton', {
            get: function () {
                return !vm.isAddingExistingCard;
            }
        });

        function stripSpaces() {
            vm.existingCard.cardNumber = (vm.existingCard.cardNumber || '').replaceAll(' ', '');
        }

        function needsPin(giftCard) {
            return giftCard.IsPinOnFile === false && vm.merchantLocation.GiftCardProfile.RequirePin;
        }

        function promptForPin(giftCard) {
	        $modal.open({
		        templateUrl: 'app/checkout/giftCardPinPrompt.html',
		        controller: 'giftCardPinPrompt as vm',
		        size: 'sm',
		        resolve: {
		        }
	        }).result.then(function (pin) {
		        spinner.spinnerShow();

		        return giftCardService.updatePin({
			        merchantId: vm.merchantLocation.MerchantId,
			        cardNumber: giftCard.AccountNumber,
			        cardPin: pin
		        }).then(function(a) { return a; }, function() {
			        $log.error('An error occurred while updating the PIN.');
			        notify.dialog('Sorry :/', 'An error occurred while updating the PIN.');
			        throw new Error();
		        });
	        }, function() {
                //neverminded
                return $q.reject();
	        }).then(function(result) {
		        if (!result.Success) {
			        $log.error(result.Message || 'An error occurred while retrieving the gift card.');
			        return notify.dialog('Sorry :/', 'An error occurred while retrieving the gift card.');
		        }
		        return getUser().then(function() {
			        var thisGiftCard = _.findWhere(vm.availableGiftCards, { AccountNumber: giftCard.AccountNumber });
			        if (thisGiftCard && thisGiftCard.Balance > 0) { 
				        notify.dialog('Woo Hoo!', "You've got " + $filter("currency")(thisGiftCard.Balance) + " on this card! Awe Yeah!");
			        }
		        });
	        })['finally'](function() {
		        spinner.spinnerHide();
	        });

        }
        
        function goToMenu() {
            cartService.loadCurrent().then(function () {
                var cart = cartService.getCart();
                if (!cart.order || !cart.order.OrderType) {
                    $state.go('locationHome');
                } else {
                    menuWorkflowService.continueOrdering(cart);
                }

            }, function () {
                $state.go('locationHome');
            });
        }

        function addExistingGiftCard(isValid) {
            if (!isValid) {
                notify.dialog('Almost!', 'All fields are required');
                return;
            }
            spinner.spinnerShow();

            vm.existingCard.merchantId = vm.merchantLocation.MerchantId;
            giftCardService.addCard(vm.existingCard).then(function () {
                cancelAddGiftCard();
                vm.existingCard.cardNumber = '';
                vm.existingCard.cardPin = '';
                vm.existingCard.recaptchaToken = '';
            })['finally'](function () {
                return getUser()['finally'](function () {
                    if (vm.showCaptcha) {
	                    vcRecaptchaService.reload(vm.recaptchaId);
                    }
                    spinner.spinnerHide();
                });
            });
        }

        function deleteGiftCard(accountNumber) {
            spinner.spinnerShow();

            giftCardService.deleteCard(accountNumber)
                .then(() => getUser(true))
                .finally(() => spinner.spinnerHide());
        }

        function setAvailableGiftCards() {
            if (vm.user.GiftCards && vm.user.GiftCards.length) {
                vm.availableGiftCards = _.where(vm.user.GiftCards, { IsPrimary: false });
                //$timeout(function () {
                //        _.each(vm.availableGiftCards,
                //            function (giftCard) {
                //                return giftCardService.getBalance({
                //                    MerchantId: vm.merchantLocation.MerchantId,
                //                    CardNumber: giftCard.AccountNumber
                //                }).then(function (data) {
                //                    giftCard.Balance = data.Balance;
                //                    giftCard.qrcode = new QRCode('qrcode_' + giftCard.AccountNumber,
                //                        {
                //                            text: giftCard.AccountNumber,
                //                            width: 128,
                //                            height: 128,
                //                            colorDark: "#000000",
                //                            colorLight: "#ffffff",
                //                            correctLevel: QRCode.CorrectLevel.H
                //                        });
                //                });
                //            });

                //    },
                //    1000);
            } else {
                vm.availableGiftCards = [];
                beginAddExistingGiftCard();
            }
        }

        function beginAddExistingGiftCard() {
            vm.isAddingExistingCard = true;
        }

        function cancelAddGiftCard() {
            vm.isAddingExistingCard = false;
        }

        activate();

        function activate() {
            common.activateController([getMerchantLocation().then(getUser)], controllerId)
                .then(function () {
                    if (!vm.availableGiftCards.length) {
                        beginAddExistingGiftCard();
                    }
		        });
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(locationId)
				.then(function (data) {
				    vm.merchantLocation = data;
				    vm.loyaltyProfileType = vm.merchantLocation.LoyaltyProfile && vm.merchantLocation.LoyaltyProfile.LoyaltyProviderType;
				    vm.existingCard.merchantId = vm.merchantLocation.MerchantId;

                    if (vm.merchantLocation.GiftCardProfile) {
                        vm.giftCardProviderType = vm.merchantLocation.GiftCardProfile.GiftCardProviderType;
                        vm.showPin = vm.merchantLocation.GiftCardProfile.RequirePin;
                        vm.showCaptcha = vm.giftCardProviderType === 2; // 2 is GiftCardProviderType.Paytronix
                        vm.isInputMasked = vm.giftCardProviderType === 2; // 2 is GiftCardProviderType.Paytronix
                    }
                    
				    return data;
				});
        }

        function getUser(fresh) {
            return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId, fresh === true)
				.then(function (user) {
				    vm.user = user;
				    setAvailableGiftCards();
				});
        }

        function onRecaptchaCreate(widgetId) {
            vm.recaptchaId = widgetId;
            $log.debug(`recaptcha widget created with id ${widgetId}`);
        };
    }
})();;
(function() {
	'use strict';

	var controllerId = 'loyaltyAdmin';
	angular.module('main')
		.controller(controllerId,
			[
				'$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common',
				'spinner', 'events', 'merchantLocationService', 'userService', 'loyaltyService', 'cartService',
				'menuWorkflowService', loyaltyAdminController
			]);

	function loyaltyAdminController($rootScope,
		$scope,
		$log,
		$state,
		$stateParams,
		$modal,
		$filter,
		notify,
		common,
		spinner,
		events,
		merchantLocationService,
		userService,
		loyaltyService,
		cartService,
		menuWorkflowService) {
		var vm = this,
			isUpdating = !!$stateParams.updating,
			locationId = togoorder.locationId;

		vm.user = undefined;
		vm.showAccountNumber = undefined;
		vm.showUserPhoneNumber = undefined;
		vm.addNewLoyaltyCard = addNewLoyaltyCard;
		vm.updateLoyaltyCard = updateLoyaltyCard;
		vm.beginAddExistingLoyaltyCard = beginAddExistingLoyaltyCard;
		vm.addExistingLoyaltyCard = addExistingLoyaltyCard;
		vm.showAddNewLoyaltyCard = false;
		vm.showExistingRewardCards = false;
		vm.isAddingExistingCard = isUpdating;
		vm.cancelAddLoyaltyCard = cancelAddLoyaltyCard;
		vm.goToMenu = goToMenu;
		vm.loyaltyProfileType = undefined;
		vm.signUpWithLoyalty = togoorder.merchantLocation.MerchantSignUpWithLoyalty;
		vm.updateCard = {
			oldAccountNumber: '',
			newAccountNumber: ''
		};
		vm.existingCard = {
			cardNumber: '',
			pin: ''
		};
		vm.newCard = {
			phoneNumber: '',
			merchantId: null
		};

		vm.loyaltyPrefix = togoorder.merchantLocation.LoyaltyProfile != null
			? togoorder.merchantLocation.LoyaltyProfile.AccountPrefix
			: '';
		vm.loyaltyLength = togoorder.merchantLocation.LoyaltyProfile != null
			? togoorder.merchantLocation.LoyaltyProfile.AccountLength
			: 0;

		Object.defineProperty(vm,
			'showAddNewLoyaltyCard',
			{
				get: function() {
					return (!vm.user || !vm.availableLoyalties.length) && !vm.isAddingExistingCard;
				}
			});

		Object.defineProperty(vm,
			'showExistingRewardCards',
			{
				get: function() {
					return (vm.user && vm.availableLoyalties.length) && !vm.isAddingExistingCard;
				}
			});

		Object.defineProperty(vm,
			'showAddExistingLoyaltyCardButton',
			{
				get: function() {
					return !vm.isAddingExistingCard;
				}
			});

		function goToMenu() {
			cartService.loadCurrent().then(function() {
					var cart = cartService.getCart();
					if (!cart.order || !cart.order.OrderType) {
						$state.go('locationHome');
					} else {
						menuWorkflowService.continueOrdering(cart);
					}

				},
				function() {
					$state.go('locationHome');
				});
		}

		function addNewLoyaltyCard(isValid) {
			if (!isValid) {
				notify.dialog('Almost!', 'All fields are required');
				return;
			}
			spinner.spinnerShow();
			loyaltyService.createCard(vm.newCard)['finally'](function() {
				return getUser(true)['finally'](function() {
					spinner.spinnerHide();
				});
			});
		}

		function addExistingLoyaltyCard(isValid) {
			if (!isValid) {
				notify.dialog('Almost!', 'All fields are required');
				return;
			}
			spinner.spinnerShow();

			vm.existingCard.merchantId = vm.merchantLocation.MerchantId;
			loyaltyService.addCard(vm.existingCard).then(function() {
				vm.isAddingExistingCard = false;
			})['finally'](function() {
				return getUser(true)['finally'](function() {
					spinner.spinnerHide();
				});
			});
		}

		function updateLoyaltyCard(isValid) {
			vm.updateCard.oldAccountNumber = vm.availableLoyalties[0].AccountNumber;
			if (vm.loyaltyProfileType === 2 &&
				vm.updateCard.confirmNewAccountNumber != vm.updateCard.newAccountNumber) {
				notify.dialog('Almost!', 'The card number you entered does not match!');
				return;
			}
			if (vm.loyaltyProfileType === 2 &&
				(vm.loyaltyPrefix + vm.updateCard.newAccountNumber).length != vm.loyaltyLength) {
				notify.dialog('Almost!', 'The card number you entered is the wrong length!');
				return;
			}
			if (vm.loyaltyProfileType === 2) {
				vm.updateCard.newAccountNumber = vm.loyaltyPrefix + vm.updateCard.newAccountNumber;
			}
			if (!isValid) {
				notify.dialog('Almost!', 'All fields are required');
				return;
			}
			spinner.spinnerShow();

			loyaltyService.updateCard(vm.updateCard).then(function() {
				vm.isAddingExistingCard = false;
			})['finally'](function() {
				return getUser(true)['finally'](function() {
					spinner.spinnerHide();
					if (vm.signUpWithLoyalty &&
						vm.availableLoyalties.length &&
						vm.availableLoyalties[0].AchStatus == null) {
						$modal.open({
							templateUrl: 'app/security/enrollAfterSignupModal.html',
							controller: 'enrollAfterSignupModal as vm',
							size: 'md',
							resolve: {
								isFromSignup: false
							}
						}).result.then(function(res) {
							if (res) {
								$state.go('achEnrollment');
							} else {
								$state.go('locationHome');
							}
						});
					} else {
						notify.dialog('Success', "Your card has been updated.").then(function() {
							$state.go('locationHome');
						});
					}
				});
			});
		}

		function beginAddExistingLoyaltyCard() {
			vm.isAddingExistingCard = true;
		}

		function cancelAddLoyaltyCard() {
			vm.isAddingExistingCard = false;
			if (isUpdating) {
				$state.go('locationHome');
			}
		}

		activate();

		function activate() {
			common.activateController([getMerchantLocation().then(wipeOffers).then(getUser)], controllerId)
				.then(function() {
				});
		}

		function getMerchantLocation() {
			return merchantLocationService.getById(locationId)
				.then(function(data) {
					vm.merchantLocation = data;
					vm.loyaltyProfileType = vm.merchantLocation.LoyaltyProfile &&
						vm.merchantLocation.LoyaltyProfile.LoyaltyProviderType;
					vm.newCard.merchantId = vm.merchantLocation.MerchantId;

					vm.showAccountNumber = vm.loyaltyProfileType !== 9 && vm.loyaltyProfileType !== 6; // 9 is Spendgo
					vm.showUserPhoneNumber = vm.loyaltyProfileType === 9 || vm.loyaltyProfileType === 6;

					return data;
				});
		}

		function getUser(fresh) {
			return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId, fresh === true)
				.then(function(user) {
					vm.user = user;
					vm.availableLoyalties = vm.user.Loyalties || [];
				});
		}

		function wipeOffers() {
			if (vm.loyaltyProfileType === 6) {
				return loyaltyService.wipeOffers() //WOC-1421 Square loyalty offers must be re-selected
					.then(_.noop, function (er) {
						alertError(er);
						throw er;
					});
			}
		}
	}
})();;
(function () {
	'use strict';

	var controllerId = 'mealPlanAdmin';
	angular.module('main')
		.controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', '$timeout', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'mealPlanService', 'cartService', 'menuWorkflowService', mealPlanAdminController]);

	function mealPlanAdminController($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, $timeout, notify, common, spinner, events, merchantLocationService, userService, mealPlanService, cartService, menuWorkflowService) {
		var vm = this,
			locationId = togoorder.locationId;

		vm.user = undefined;
		vm.merchantLocation = {};
		vm.beginAddExistingMealPlan = beginAddExistingMealPlan;
		vm.isAddingExistingCard = false;
		vm.addExistingMealPlan = addExistingMealPlan;
		vm.cancelAddMealPlan = cancelAddMealPlan;
		vm.goToMenu = goToMenu;
		vm.availableMealPlans = [];
		vm.existingCard = {
			merchantId: 0,
			cardNumber: '',
			isPrimary: false
		};


		Object.defineProperty(vm, 'showExistingMealPlans', {
			get: function () {
				return (vm.user && vm.availableMealPlans.length) && !vm.isAddingExistingCard;
			}
		});

		//Object.defineProperty(vm, 'showAddExistingMealPlanButton', {
		//	get: function () {
		//		return !vm.isAddingExistingCard;
		//	}
		//});


		function goToMenu() {
			cartService.loadCurrent().then(function () {
				var cart = cartService.getCart();
				if (!cart.order || !cart.order.OrderType) {
					$state.go('locationHome');
				} else {
					menuWorkflowService.continueOrdering(cart);
				}

			}, function () {
				$state.go('locationHome');
			});
		}

		function addExistingMealPlan(isValid) {
			if (!isValid) {
				notify.dialog('Almost!', 'All fields are required');
				return;
			}
			spinner.spinnerShow();

			vm.existingCard.merchantId = vm.merchantLocation.MerchantId;
			mealPlanService.addCard(vm.existingCard).then(function () {
				cancelAddMealPlan();
			})['finally'](function () {
				return getUser()['finally'](function () {
					spinner.spinnerHide();
				});
			});
		}

		function beginAddExistingMealPlan() {
			vm.isAddingExistingCard = true;
		}

		function cancelAddMealPlan() {
			vm.isAddingExistingCard = false;
		}

		activate();

		function activate() {
			common.activateController([getMerchantLocation().then(getUser)], controllerId)
				.then(function () {

				});
		}

		function getMerchantLocation() {
			return merchantLocationService.getById(locationId)
				.then(function (data) {
					vm.merchantLocation = data;
					vm.loyaltyProfileType = vm.merchantLocation.LoyaltyProfile && vm.merchantLocation.LoyaltyProfile.LoyaltyProviderType;
					vm.existingCard.merchantId = vm.merchantLocation.MerchantId;
					return data;
				});
		}

		function getUser() {
			return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId)
				.then(function (user) {
					vm.user = user;

					if (vm.user.MealPlans && vm.user.MealPlans.length) {
						vm.availableMealPlans = _.where(vm.user.MealPlans, { IsPrimary: false });
						$timeout(function () {
							_.each(vm.availableMealPlans,
								function (mealPlan) {
									return mealPlanService.getBalance({
										MerchantId: vm.merchantLocation.MerchantId,
										CardNumber: mealPlan.AccountNumber
									}).then(function (data) {
										mealPlan.Balance = data.Balance;
										//mealPlan.qrcode = new QRCode('qrcode_' + mealPlan.AccountNumber,
										// {
										//  text: mealPlan.AccountNumber,
										//  width: 128,
										//  height: 128,
										//  colorDark: "#000000",
										//  colorLight: "#ffffff",
										//  correctLevel: QRCode.CorrectLevel.H
										// });
									});
								});

						},
							1000);
					}

					if (!vm.availableMealPlans.length) {
						beginAddExistingMealPlan();
					}

				});
		}
	}
})();;
(function () {
    'use strict';

    var controllerId = 'offers';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$q', '$log', '$state', '$stateParams', '$uibModal', '$filter', '$window', '$timeout', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'loyaltyService', 'cartService', 'menuWorkflowService', offersController]);

    function offersController($rootScope, $scope, $q, $log, $state, $stateParams, $modal, $filter, $window, $timeout, notify, common, spinner, events, merchantLocationService, userService, loyaltyService, cartService, menuWorkflowService) {
		var vm = this,
			locationId = togoorder.locationId;

        vm.user = undefined;
        vm.offerList = undefined;
        vm.loyaltyProfileType = undefined;
        vm.optOffer = optOffer;
        vm.zoomOffer = zoomOffer;
        vm.getOfferUiId = getOfferUiId;
        vm.getPunches = getPunches;
        vm.getImageName = getImageName;
        vm.goToMenu = goToMenu;
        vm.hasStandardOffers = undefined;
        vm.hasAutomaticOffers = undefined;
        vm.hasPunchCards = undefined;
        vm.hasOffers = undefined;
        vm.standardOffers = [];
        vm.automaticOffers = [];
        vm.punchCards = [];

        vm.getCardImageUrl = getCardImageUrl;
		vm.canAfford = canAfford;

		vm.offerSpinnerOptions = {
			lines: 9,
			length: 28,
			width: 14,
			radius: 25,
			scale: 0.25,
			corners: 1,
			color: '#000',
			opacity: 0.25,
			fps: 20,
			zIndex: 2e9,
			className: 'spinner',
			top: '50%',
			left: '50%',
			shadow: false,
			hwaccel: false,
			position: 'absolute'
		};

        function goToMenu() {
            cartService.loadCurrent().then(function () {
                var cart = cartService.getCart();
                if (!cart.order || !cart.order.OrderType) {
                    $state.go('locationHome');
                } else {
                    menuWorkflowService.continueOrdering(cart);
                }

            }, function () {
                $state.go('locationHome');
            });
        }

        function canAfford(offer) {
            if (vm.loyaltyProfileType !== 3) {
                return true;
            }

            if (vm.user.Loyalties[0].RewardPointsSummary.Points >= offer.PointsCost) {
                return true;
            }
            return false;
        }

        function getOffers() {
            if (!vm.user || !vm.user.Loyalties || !vm.user.Loyalties.length) {
                //console.log("No loyalties");
                setOfferList([]);
                return $q.when([]);
            }
            var offerListRequest = {
                CardNumber: vm.user.Loyalties[0].AccountNumber,
                MerchantId: vm.merchantLocation.MerchantId,
                OfferType: !$stateParams.offerType ? 7 : $stateParams.offerType
            };

            return loyaltyService.getOffers(offerListRequest).then(function (data) {
                setOfferList(data.Offers);
            });
        }

        function setOfferList(offerList) {
            vm.offerList = offerList;
            var standardOffers = _.filter(vm.offerList, function (offer) {
				return offer.Optable && offer.OfferType !== 3 && offer.OfferType !== 5;
            });
            var automaticOffers = _.filter(vm.offerList, function (offer) {
				return !offer.Optable && offer.OfferType !== 3 && offer.OfferType !== 5;
            });
            var punchCards = _.filter(vm.offerList, function (offer) {
                return offer.OfferType === 3;
            });

            vm.hasStandardOffers = standardOffers && standardOffers.length;
            vm.hasAutomaticOffers = automaticOffers && automaticOffers.length;
            vm.hasPunchCards = punchCards && punchCards.length;
            vm.hasOffers = vm.offerList && vm.offerList.length;

            vm.standardOffers = standardOffers;
            vm.automaticOffers = automaticOffers;
            vm.punchCards = punchCards;

            _.each(vm.punchCards, function (offer) {
                offer.Punches = vm.getPunches(offer.MaxPunches, offer.Balance);
                offer.PunchMessage = offer.Punches.length ? "" : "0 punches earned";
            });
        }

        function getCardImageUrl(offer) {
            return offer.Graphic && offer.Graphic || 'https://storage.googleapis.com/content.togoorder.com/merchant-content/ToGoTech/togo_logo_card2.png';
        }

        function getPunches(t, b) {
            var punchBoxes = [];
            for (var i = 1; i <= t; i++) {
                var punchBox = {};
                if (i <= b) {
                    punchBox.Image = "PunchCheck.png";
                } else {
                    punchBox.Image = "PunchUnchecked.png";
                }
                punchBoxes.push(punchBox);
            }
            return punchBoxes;
        }
        
        function getImageName(offer) {
            if ((offer.OptedIn || offer.pendingOptIn) && !offer.pendingOptOut) {
                return "OfferChecked.png";
            }
            if (!offer.OptedIn || offer.pendingOptOut) {
                return "OfferUnchecked.png";
            }
        }

        function optOffer(offer) {
	        if (offer.pendingOptIn || offer.pendingOptOut) {
		        return false;
	        }

	        spinner.spinnerShow();
            if (offer.OptedIn === true) {
                offer.pendingOptOut = true;
                var resetPending = function () {
                    offer.pendingOptOut = undefined;
                    spinner.spinnerHide();
                };
                loyaltyService.optOutOfOffer({ CardNumber: vm.user.Loyalties[0].AccountNumber, OfferId: offer.OfferId, MerchantId: vm.merchantLocation.MerchantId })
                    .then(function (data) {
                        getOffers()['finally'](resetPending);
                    }, resetPending)['finally'](function () {
                        $("#zoomModal").modal("hide");
                    });
            } else {
                offer.pendingOptIn = true;
                var resetPending = function () {
                    offer.pendingOptIn = undefined;
                    spinner.spinnerHide();
                };
                loyaltyService.optInToOffer({ CardNumber: vm.user.Loyalties[0].AccountNumber, OfferId: offer.OfferId, MerchantId: vm.merchantLocation.MerchantId })
                    .then(function (data) {
                        getOffers()['finally'](resetPending);
                    }, resetPending)['finally'](function () {
                        $("#zoomModal").modal("hide");
                    });
            }
            getOffers();
            return false;
        }

		function getOfferUiId(offer) {
			return common.safeClassName(offer.OfferId + offer.ExternalId + offer.RedemptionCode);
		}

        function zoomOffer(offer) {
            var modal = $("#zoomModal");
            if (modal.hasClass('in')) {
                modal.modal("hide");
            } else {
                var offerDiv = $("#offer_" + getOfferUiId(offer));
                modal.html("");
                var width = ($window.innerWidth * 0.4);
                var height = width * 0.3;
                var css = {
                    'position': 'absolute',
                    "width": width + "px",
                    "height": height + "px",
                    "left": ($window.innerWidth - width) / 2 + "px",
                    "top": "3%"
                };
                offerDiv.clone(true).addClass('offerZoomDiv').css(css).prepend("<a class='close' style='margin-right:10px;text-decoration:none' data-dismiss='modal' title='Close' data-ng-click=' $(\"#qrcode_" + getOfferUiId(offer) + "\").hide();dismiss()'>×</a>").appendTo(modal).find(".optButtonDiv").css("z-index", "10000");
                $('.modal #qrcode_' + getOfferUiId(offer)).show();
                modal.modal("show");
            }
        }

        activate();

        function activate() {
            common.activateController([getMerchantLocation().then(getUser).then(getOffers)], controllerId).then(function () {
	            //$timeout(function () {
             //       _.each(vm.automaticOffers,
             //           function (offer) {
             //               offer.qrcode = new QRCode('qrcode_' + getOfferUiId(offer),
             //                   {
             //                       text: vm.user.Loyalties[0].AccountNumber + '|' + (offer.RedemptionCode || ''),
             //                       width: 128,
             //                       height: 128,
             //                       colorDark: "#000000",
             //                       colorLight: "#ffffff",
             //                       correctLevel: QRCode.CorrectLevel.H
             //                   });
             //           });
             //       _.each(vm.standardOffers,
             //           function (offer) {
             //               offer.qrcode = new QRCode('qrcode_' + getOfferUiId(offer),
             //                   {
             //                       text: vm.user.Loyalties[0].AccountNumber + '|' + (offer.RedemptionCode || ''),
             //                       width: 128,
             //                       height: 128,
             //                       colorDark: "#000000",
             //                       colorLight: "#ffffff",
             //                       correctLevel: QRCode.CorrectLevel.H
             //                   });
             //           });
             //       _.each(vm.punchCards,
             //           function (offer) {
             //               offer.qrcode = new QRCode('qrcode_' + getOfferUiId(offer),
             //                   {
             //                       text: vm.user.Loyalties[0].AccountNumber + '|' + (offer.RedemptionCode || ''),
             //                       width: 128,
             //                       height: 128,
             //                       colorDark: "#000000",
             //                       colorLight: "#ffffff",
             //                       correctLevel: QRCode.CorrectLevel.H
             //                   });
             //           });
             //   },
             //       1000);
            });
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(locationId)
				.then(function (data) {
				    vm.merchantLocation = data;
				    vm.loyaltyProfileType = vm.merchantLocation.LoyaltyProfile && vm.merchantLocation.LoyaltyProfile.LoyaltyProviderType;
				    vm.merchantId = vm.merchantLocation.MerchantId;
				    return data;
				});
        }

        function getUser() {
            return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId)
				.then(function (user) {
				    vm.user = user;
				});
        }
    }
})();;
(function() {
    'use strict';

    var controllerId = 'personalInfo';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'cartService', 'menuWorkflowService', personalInfo]);

    function personalInfo($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, cartService, menuWorkflowService) {
    	var vm = this,
	        merchantId = togoorder.merchantId;

    	vm.user = undefined;
    	vm.goToMenu = goToMenu;
    	vm.email = undefined;
    	vm.editName = editName;
    	vm.editCallbackNumber = editCallbackNumber;
    	vm.editBirthdate = editBirthdate;
    	vm.editPassword = editPassword;
        vm.editEmail = editEmail;
        vm.editEmailOptIn = editEmailOptIn;
        vm.deleteUserAccount = deleteUserAccount;

    	function editName() {
    	    $state.go('changeName');
    	}
    	function editEmail() {
    	    $state.go('changeEmail');
    	}

    	function editCallbackNumber() {
    	    $state.go('changeCallbackNumber');
    	}

    	function editBirthdate() {
    	    $state.go('changeBirthdate');
    	}

    	function editPassword() {
	        $state.go('changePassword');
	    }

        function editEmailOptIn() {
            $state.go('changeEmailOptIn');
        }

        function deleteUserAccount() {
            $state.go('deleteUser');
        }

	    function goToMenu() {
	        cartService.loadCurrent().then(function() {
	            var cart = cartService.getCart();
	            if (!cart.order || !cart.order.OrderType) {
	                $state.go('locationHome');
	            } else {
	                menuWorkflowService.continueOrdering(cart);
	            }

	        }, function() {
	            $state.go('locationHome');
	        });
	    }

        activate();

        function activate() {
	        common.activateController([getUser()], controllerId)
		        .then(function() {
		        });
        }

        function getUser() {
        	return userService.getUser(merchantId)
				.then(function (user) {
				    vm.user = user;
	                vm.email = user.Email;
	            });
        }
    }
})();;
(function () {
    'use strict';

    var controllerId = 'punchCards';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$q', '$state', '$stateParams', '$uibModal', '$filter', '$window', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'loyaltyService', 'cartService', 'menuWorkflowService', punchCardsController]);

    function punchCardsController($rootScope, $scope, $log, $q, $state, $stateParams, $modal, $filter, $window, notify, common, spinner, events, merchantLocationService, userService, loyaltyService, cartService, menuWorkflowService) {
        var vm = this,
	        locationId = togoorder.locationId;

        vm.user = undefined;
        vm.offerList = undefined;
        vm.loyaltyProfileType = undefined;
        vm.getPunches = getPunches;
        vm.zoomOffer = zoomOffer;
        vm.getCardImageUrl = getCardImageUrl;
        vm.goToMenu = goToMenu;

        function goToMenu() {
            cartService.loadCurrent().then(function () {
                var cart = cartService.getCart();
                if (!cart.order || !cart.order.OrderType) {
                    $state.go('locationHome');
                } else {
                    menuWorkflowService.continueOrdering(cart);
                }

            }, function () {
                $state.go('locationHome');
            });
        }

        function getOffers() {
            if (!vm.user || !vm.user.Loyalties || !vm.user.Loyalties.length) {
                setOfferList([]);
                return $q.when([]);
            }
            var punchCardRequest = {
                CardNumber: vm.user.Loyalties[0].AccountNumber,
                MerchantId: vm.merchantLocation.MerchantId
            };
            return loyaltyService.getPunchCards(punchCardRequest).then(function (data) {
                setOfferList(data.Cards);
            });
        }

        function setOfferList(offerList) {
            vm.offerList = offerList;
            _.each(vm.offerList, function (offer) {
                offer.Punches = vm.getPunches(offer.Threshold, offer.Balance);
                offer.PunchMessage = offer.Punches.length ? "" : "0 punches earned";
            });
        }

        function getPunches(t, b) {
            var punchBoxes = [];
            for (var i = 1; i <= t; i++) {
                var punchBox = {};
                if (i <= b) {
                    punchBox.Image = "PunchCheck.png";
                } else {
                    punchBox.Image = "PunchUnchecked.png";
                }
                punchBoxes.push(punchBox);
            }
            return punchBoxes;
        }

        activate();

        function activate() {
            common.activateController([getMerchantLocation().then(getUser).then(getOffers)], controllerId);
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(locationId)
				.then(function (data) {
				    vm.merchantLocation = data;
				    vm.loyaltyProfileType = vm.merchantLocation.LoyaltyProfile && vm.merchantLocation.LoyaltyProfile.LoyaltyProviderType;
				    vm.merchantId = vm.merchantLocation.MerchantId;
				    return data;
				});
        }

        function getUser() {
            return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId)
				.then(function (user) {
				    vm.user = user;
				});
        }

        function zoomOffer(offer) {
            var modal = $("#zoomModal");
            if (modal.hasClass('in')) {
                modal.modal("hide");
            } else {
                var offerDiv = $("#offer_" + offer.OfferId);
                modal.html("");
                var width = ($window.innerWidth * 0.8);
                var height = width * 0.6;
                var css = {
                    'position': 'absolute',
                    "width": width + "px",
                    "height": height + "px",
                    "left": ($window.innerWidth * 0.1) + "px",
                    "top": "3%"
                };
                offerDiv.clone(true).css(css).appendTo(modal).find(".optButtonDiv").css("z-index", "10000");
                modal.modal("show");
            }
        }

        function getCardImageUrl(offer) {
            return offer.Graphic && offer.Graphic || 'https://storage.googleapis.com/content.togoorder.com/merchant-content/RoyalFarms/I/BackgroundOffer.png';
        }
    }
})();;
(function () {
    'use strict';

    var controllerId = 'achEnrollment';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'spinner', 'events', 'merchantLocationService', 'userService', 'loyaltyService', 'cartService', 'menuWorkflowService', achEnrollmentController]);

    function achEnrollmentController($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, notify, common, spinner, events, merchantLocationService, userService, loyaltyService, cartService, menuWorkflowService) {
        var vm = this,
	        locationId = togoorder.locationId;

        vm.user = undefined;
        vm.iframeType = 'enroll';
        vm.showMessage = false;
        vm.changeIframe = false;
        vm.showUpdateLink = false;
        vm.goLoyaltyAdmin = goLoyaltyAdmin;
        vm.message = '';
        vm.supportMessage = '';
        activate();

        function activate() {
            common.activateController([getMerchantLocation().then(getUser)], controllerId)
		        .then(function () {
		            if (!vm.showMessage && vm.user.Loyalties[0].AccountNumber[9] != 9 && !vm.changeIframe) {
		                return setTimeout(function () { $('#zippyForm').submit(); }, 500);
		            }
		            if (vm.user.Loyalties[0].AccountNumber[9] == 9) {
		                vm.message = 'You must have a plastic card to enroll for RoFo Pay. Please pick up a plastic card at your local Royal Farms.';
		                vm.showMessage = true;
		                vm.showUpdateLink = true;
		            }
		            return false;
		        });
        }
        function getUser() {
            return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId)
				.then(function (user) {
				    vm.user = user;
				    if (vm.user.Loyalties.length) {
				        var loyaltyAccount = vm.user.Loyalties[0];
                        vm.iframeType = loyaltyAccount.AchStatus == 2 ? 'verifyIframe' : 'enrollIframe';
                        vm.message = loyaltyAccount.AchStatusMessage;
                        if (loyaltyAccount.AchStatus != null && !(loyaltyAccount.AchStatus == 3 || loyaltyAccount.AchStatus == 2)) {
                            vm.showMessage = true;
                            vm.supportMessage += "Please contact customer service about your card at: 1-844-830-4792";
                        }
                        if (loyaltyAccount.AchStatus == 3) {
                            $('#ZipLforms').attr('src', 'https://secure.paymentcard.com/secure/userlogin.php');
                            vm.changeIframe = true;
                        }
                    }
                });
        }
        function getMerchantLocation() {
            return merchantLocationService.getById(locationId)
				.then(function (data) {
				    vm.merchantLocation = data;
				    return data;
				});
        }
        function goLoyaltyAdmin() {
            $state.go("loyaltyAdmin", { updating: true });
        }
    }
})();;
(function () {
    'use strict';
    angular.module('common', []);
})();
;
(function() {
	'use strict';

	angular.module('common').factory('Address', [addressFactory]);

	function addressFactory() {

		function Address() {
            this.StreetNumber = '';
            this.AddressLine1 = '';
			this.AddressLine2 = '';
			this.City = '';
			this.State = '';
			this.Zipcode = '';
			this.Country = '';
			this.Lat = 0;
			this.Lng = 0;
			this.SpecialInstructions = '';
			this.ExtraInstructions = '';
        }

		Address.prototype.parseGooglePlace = function(place) {
			var addressString = '';

			if (place.address_components) {
				addressString = [
					(place.address_components[0] && place.address_components[0].short_name || ''),
					(place.address_components[1] && place.address_components[1].short_name || ''),
					(place.address_components[2] && place.address_components[2].short_name || '')
				].join(' ');
			}

			var componentForm = {
				street_number: 'short_name',
				route: 'long_name',
				locality: 'long_name',
				sublocality_level_1: 'long_name',
				sublocality: 'long_name',
				neighborhood: 'long_name',
				administrative_area_level_1: 'short_name',
				postal_code: 'short_name',
				country: 'long_name'
			};

			if (addressString) {

				var tempAddress = {};

				for (var i = 0; i < place.address_components.length; i++) {
					var addressType = place.address_components[i].types[0];
					if (componentForm[addressType]) {
						tempAddress[addressType] = place.address_components[i][componentForm[addressType]];
					}
				}
				if (tempAddress.street_number) {

					var address = new Address();
					address.displayString = addressString;
					address.AddressLine1 = tempAddress.street_number + ' ' + tempAddress.route;
					address.City = tempAddress.locality || tempAddress.sublocality || tempAddress.sublocality_level_1 || tempAddress.neighborhood;
					address.State = tempAddress.administrative_area_level_1;
					address.Zipcode = tempAddress.postal_code;
					address.Country = tempAddress.country;
					var latitude = place.geometry.location.lat;
					address.Lat = _.isFunction(latitude) ? latitude() : latitude;
					var longitude = place.geometry.location.lng;
					address.Lng = _.isFunction(longitude) ? longitude() : longitude;

					return address;
				} else {
					return { displayString: addressString };
				}
			}
		};

        Address.prototype.getGoogleAddress = function() {
	        if (!this.City) {
		        return '';
	        }
	        return this.AddressLine1 + ', ' + this.City + ', ' + this.State + ', ' + this.Country;
        };

		Address.prototype.parseMapBoxFeature = function(feature) {
			
            var state, countryName, city, neighborhood, postCode;
            if(feature.context){
                $.each(feature.context, function(i, v){
                    if(v.id.indexOf('region') >= 0)
                        state = v.text;
                    if(v.id.indexOf('country') >= 0)
                        countryName = v.text;
                    if(v.id.indexOf('place') >= 0)
                        city = v.text;
                    if(v.id.indexOf('postcode') >= 0)
                        postCode = v.text;
                    if(v.id.indexOf('neighborhood') >= 0)
                        neighborhood = v.text;
                });
            }

            if (feature.place_name) {
				if (feature.address) {
					var address = new Address();
                    address.StreetNumber = feature.address;
                    address.displayString = feature.address + ' ' + feature.text + ' ' + (city || neighborhood);
                    address.AddressLine1 = feature.address + ' ' + feature.text;
					address.City = city || neighborhood;
					address.State = state;
					address.Zipcode = postCode;
					address.Country = countryName;
                    address.Lng = feature.geometry.coordinates[0];
                    address.Lat = feature.geometry.coordinates[1];

                    return address;
				} else {
					return { displayString: feature.place_name };
				}
			}
		};

        Address.prototype.getMapBoxAddress = function() {
			if (!this.City) {
				return '';
			}

			return this.AddressLine1 + ', ' + this.City + ', ' + this.State + ' ' + this.Zipcode + ', ' + this.Country;
        };

		return Address;
	}
})();;
(function () {
	'use strict';

	var controllerId = 'alert';
	angular.module('common').controller(controllerId, ['$uibModalInstance', 'alertOptions', alert]);

	function alert($modalInstance, alertOptions) {
		var vm = this;

		vm.dialogTitle = alertOptions.dialogTitle || '';
		vm.dialogMessage = alertOptions.dialogMessage || '';
		vm.dismissButtonText = alertOptions.dismissButtonText;
        vm.closeButtonText = alertOptions.closeButtonText || 'OK';
	    vm.buttonActions = alertOptions.buttonActions || [];

	    vm.close = $modalInstance.close;
	    vm.dismiss = $modalInstance.dismiss;

	}

})();;
(function () {
    'use strict';

    angular.module('common').factory('browserInfo', ['$log', browserInfo]);

    function browserInfo($log) {
        var service = {
            isSize: isSize
        };

        return service;

        function isSize(size) {
            return $('#is-' + size).is(':visible');
        }
    }
})();;
(function() {
	"use strict";

	// Define the common module 
	// Contains services:
	//  - common
	//  - toast notification
	var commonModule = angular.module("common");

	// Must configure the common service and set its 
	// events via the commonConfigProvider
	commonModule.provider("commonConfig",
		function() {
			this.config = {
				//controllerActivateSuccessEvent: '',
				//spinnerToggleEvent: ''
        
			};

			this.$get = function() {
				return {
					config: this.config
				};
			};
		});

	commonModule.factory("common", ["$q", "$rootScope", "$log", "commonConfig", "messages", common]);

	function common($q, $rootScope, $log, commonConfig, messages) {

		var activationCount = 0, activateEventQueue = [];
		var service = {
			activateController: activateController,
			broadcast: broadcast,
			exceptions: {
				RedirectRequiredException: RedirectRequiredException,
				NormalException: NormalException
			},
			monetize: monetize,
			any: any,
			safeClassName: safeClassName,
			messages: messages,
			getDistanceInMiles: getDistanceInMiles,
			log: $log.log
		};

		return service;
		
		function getDistanceInMiles(lat1, lon1, lat2, lon2) {
			var radius = 3959; // Earth�s mean radius in miles
			var dLat = rad(lat2 - lat1);
			var dLong = rad(lon2 - lon1);
			var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
				Math.cos(rad(lat1)) *
				Math.cos(rad(lat2)) *
				Math.sin(dLong / 2) *
				Math.sin(dLong / 2);
			var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
			var d = radius * c;
			return d; // returns the distance in miles
		}

		function rad(x) {
			return x * Math.PI / 180;
		}

		function safeClassName(value) {
			return value &&
				value.replace(/ /g, "-").replace(/[^a-zA-Z0-9_]+/g, "-").replace(/\-$/, "").replace(/^\-/, "");
		}

		function any(promises) {
			var d = $q.defer();
			var cnt = 0;
			_.each(promises,
				function(p) {
					cnt++;
					p.then(function(result) {
						d.resolve(result);
					})["finally"](function(result) {
						cnt--;
						if (cnt === 0) {
							d.reject(result);
						}
					});
				});
			return d.promise;
		}

		function round(num, precision) {
			return (+(Math.round(+(num + "e" + precision)) + "e" + -precision)) || 0;
		}

		function monetize(val, precision) {
			if (precision === undefined) {
				precision = 2;
			}
			return parseFloat(round(round(val, precision + 1), precision));
		}

		function activateController(promises, controllerId) {
			activationCount++;
			var p = $q.all(promises).then(function(eventArgs) {
					var data = { controllerId: controllerId };
					activateEventQueue.push(function() {
						broadcast(commonConfig.config.controllerActivateSuccessEvent, data);
					});

					return data;
				},
				function(data) {
					if (data) {
						activateEventQueue.push(function() {
							broadcast(commonConfig.config.controllerActivateFailEvent, data);
						});
					}
					throw data;
				})["finally"](function() {
				activationCount--;
				if (activationCount === 0) {
					_.each(activateEventQueue,
						function(f) {
							f();
						});
					activateEventQueue = [];
				}
			});

			return p;
		}

		function broadcast() {
			return $rootScope.$broadcast.apply($rootScope, arguments);
		}

		function RedirectRequiredException(message, nextState) {
			this.name = "RedirectRequired";
			this.message = message;
			this.beKind = true;
			this.nextState = nextState;
		}

		function NormalException(message) {
			this.name = "NormalException";
			this.message = message;
		}
	}
})();;
(function () {
    'use strict';

    var app = angular.module('common');

    app.config(['commonConfigProvider', 'events', function (cfg, events) {
        cfg.config.controllerActivateSuccessEvent = events.controllerActivateSuccess;
        cfg.config.controllerActivateFailEvent = events.controllerActivateFail;
        cfg.config.spinnerToggleEvent = events.spinnerToggle;
        cfg.config.cartItemSelectedEvent = events.cartItemSelected;
        cfg.config.cartItemIsValidChangedEvent = events.cartItemIsValidChanged;
        cfg.config.securityAuthorizationRequiredEvent = events.securityAuthorizationRequired;
        cfg.config.signUpRequested = events.signUpRequested;
        cfg.config.includedItemsChanged = events.includedItemsChanged;
        cfg.config.presentCart = events.presentCart;
        cfg.config.changeDeliveryAddressRequest = events.changeDeliveryAddressRequest;
        cfg.config.changeTableNumberRequest = events.changeTableNumberRequest;
        cfg.config.orderHistoryRequested = events.orderHistoryRequested;
        cfg.config.navigateToMerchantHome = events.navigateToMerchantHome;
        cfg.config.navigateToMerchantWebsite = events.navigateToMerchantWebsite;
        cfg.config.navigateToMerchantWebsite = events.navigateToMerchantWebsite;
        cfg.config.addItemToNextCart = events.addItemToNextCart;
        cfg.config.newPaymentMethodAdded = events.newPaymentMethodAdded;
		cfg.config.promoCodeRemoved = events.promoCodeRemoved;
    }]);
    
})();;
(function() {
	'use strict';

	var app = angular.module('common');

	app.constant("events", {
	    controllerActivateSuccess: "controller.activateSuccess",
	    controllerActivateFail: "controller.activateFail",
		spinnerToggle: "spinner.toggle",
		cartItemSelected: "cart.itemSelected",
		cartItemIsValidChanged: "cart.itemIsValidChanged",
		securityAuthorizationRequired: "security.authorizationRequired",
		signUpRequested: "security.signUpRequested",
		includedItemsChanged: "orderItem.includedItemsChanged",
		presentCart: "cart.presentCart",
		changeDeliveryAddressRequest: 'order.changeDeliveryAddressRequest',
		changeTableNumberRequest: 'order.changeTableNumberRequest',
        orderHistoryRequested: 'user.orderHistoryRequested',
        addItemToNextCart: 'cart.addItemToNext',
        navigateToMerchantHome: 'navigation.merchantHome',
        navigateToMerchantWebsite: 'navigation.merchantWebsite',
		newPaymentMethodAdded: 'checkout.newPaymentMethodAdded',
		promoCodeRemoved: 'checkout.promoCodeRemoved',
		itemQuantityChanged: 'order.itemQuantityChanged',
		orderPlaced: 'order.placed',
	    orderHubProgress: 'order.hubProgress',
	    orderHubProgressDone: 'order.hubProgressDone', 
	    readyForOrderProgress: 'order.readyForOrderProgress',//
		promotionIsSelected: 'checkout.promoCodeAdded',
		beginCheckout: 'checkout.begin',
		itemRemoved: 'cart.itemRemoved',
		login: 'login',
		signUp: 'signUp',
		logout: 'logout',
		itemIsViewed: 'item.view',
		menuIsShown: 'menu.view'
	});

	app.constant("messages", {
	    totalEstimation: 'The tax listed on this site is an estimate. ' +
	        'Also, product prices are occasionally different on the site than in the restaurant. ' +
	        'Once your order is submitted, the final product prices and tax will be calculated. ' +
	        'If the total is different, the corrected amount will be charged to your card. ' +
	        'Please check your email receipt for the final total.'
	});
})();;
(function () {
	'use strict';

    var serviceId = 'deliveryCacheService';

    angular.module('common').factory(serviceId, ['$log', '$q', 'storageService', 'Address', deliveryCacheService]);

    function deliveryCacheService($log, $q, storageService, Address) {

		return {
			cacheTableNumber: cacheTableNumber,
			getTableNumber: getTableNumber,
			cacheDeliveryAddress: cacheDeliveryAddress,
			getDeliveryAddress: getDeliveryAddress
		};
        
		function cacheTableNumber(tableNumber, locationId) {
		    try {
		        storageService.set("table-" + locationId, tableNumber);
			} catch (e) {
				$log.error('Error while saving data.');
			}
			return $q.when(1);
		}

		function cacheDeliveryAddress(address) {
			var addressJson = angular.toJson(address);
		    try {
		        storageService.set("deliveryAddress", addressJson);
			} catch (e) {
				$log.error('Error while saving data.');
			}

			return $q.when(1);
		}

		function getTableNumber(locationId) {
		    var tableNumber = storageService.get("table-" + locationId);
			if (tableNumber) {
				return $q.when(tableNumber);
			} else {
			    return $q.reject("We need your table number, please.");
			}
		}

		function getDeliveryAddress() {
		    var addressJson = storageService.get("deliveryAddress");
			if (addressJson) {
				var addressData = angular.fromJson(addressJson);
				var address = angular.extend(new Address(), addressData);
			    address.SpecialInstructions = (address.ExtraInstructions || '') + (address.SpecialInstructions || '');
				return $q.when(address);
			} else {
				return $q.reject("We need your delivery address, please.");
			}
		}
	}
})();;
(function() {
	
	angular.module('common').filter('groupBy', ['$parse', groupByFilter]);

	function groupByFilter($parse) {
		return function (list, groupBy) {
			var filtered = [];
			var prevItem = null;
			var isGroupChanged = false;
			// this is a new field which is added to each item where we append "_CHANGED"
			// to indicate a field change in the list
			//was var newField = groupBy + '_CHANGED'; - JB 12/17/2013
			var newField = 'groupBy_CHANGED';

			// loop through each item in the list
			angular.forEach(list, function (item) {

				isGroupChanged = false;

				// if not the first item
				if (prevItem !== null) {

					// check if any of the group by field changed

					//force groupBy into Array
					groupBy = angular.isArray(groupBy) ? groupBy : [groupBy];

					//check each group by parameter
					for (var i = 0, len = groupBy.length; i < len; i++) {
						if ($parse(groupBy[i])(prevItem) !== $parse(groupBy[i])(item)) {
							isGroupChanged = true;
							break;
						}
					}


				}// otherwise we have the first item in the list which is new
				else {
					isGroupChanged = true;
				}

				// if the group changed, then add a new field to the item
				// to indicate this
				if (isGroupChanged) {
					item[newField] = true;
				} else {
					item[newField] = false;
				}

				filtered.push(item);
				prevItem = item;

			});

			return filtered;
		};
	};
})();;
(function () {
    'use strict';
    
    angular.module('common').factory('notify', ['$log', '$injector', notify]);

    function notify($log, $injector) {
        var service = {
        	info: info,
        	error: error,
        	errorMessage: errorMessage,
        	success: success,
        	warning: warning,
        	warn: warning,
            dialog: dialog
        };

        return service;

        function info(message) {
        	$log.info.apply($log, arguments);
        	toastr.info(message);
        }

        function warning(message) {
        	$log.warn.apply($log, arguments);
        	toastr.warning(message);
        }

        function success(message) {
        	$log.info.apply($log, arguments);
        	toastr.success(message);
        }

        function error(message) {
            _.each(arguments, function(arg) {
                $log.error.call($log, arg);
            });
        	toastr.error(message, 'Hmm');
        }

        function errorMessage(message) {
        	$log.error.apply($log, arguments);
        	toastr.error(message, 'Hmm', { timeOut: 0, extendedTimeOut: 0 });
        }


        function dialog(title, body) {
            var alertOptions = arguments[0];
            if (_.isString(title)) {
                alertOptions = {
                    dialogTitle: title,
                    dialogMessage: body,
                    dismissButtonText: '',
                    closeButtonText: '',
                    cssClass: ''
                };
            }
            var $modal = $injector.get('$uibModal');
            return $modal.open({
                templateUrl: 'shared/common/alert.html',
                controller: 'alert as vm',
                size: 'md',
                windowClass: (alertOptions.cssClass || undefined),
                resolve: {
                    alertOptions: function () {
                        return alertOptions;
                    }
                }
            }).result;
        }
	}
})();;
(function () {
    'use strict';

     //Must configure the common service and set its 
     //events via the commonConfigProvider

    angular.module('common')
        .factory('spinner', ['common', 'commonConfig', spinner]);

    function spinner(common, commonConfig) {
        var service = {
            spinnerHide: spinnerHide,
            spinnerShow: spinnerShow
        };

        return service;

        function spinnerHide(s) { spinnerToggle(false, s); }

        function spinnerShow() { spinnerToggle(true); }

        function spinnerToggle(show, s) {
            common.broadcast(commonConfig.config.spinnerToggleEvent, { show: show, s: s });
        }
    }

	angular.module('common').directive('elSpinnerSmall', ['$window', elSpinnerSmall]);

	function elSpinnerSmall($window) {
		var directive = {
			replace: true,
			template: '<div role="status" aria-live="polite" class="lds-wrapper"><div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div></div>',
			restrict: 'A'
		};
		return directive;
	}

	angular.module('common').directive('elSpinner', ['$window', elSpinner]);

	function elSpinner($window) {
		var directive = {
			link: link,
			restrict: 'A'
		};
		return directive;

		function link(scope, element, attrs) {
			var spinner = null;
			scope.$watch(attrs.elSpinner, function (customOptions) {
				if (spinner) {
					spinner.stop();
				}

				var options = {
					lines: 13,
					length: 11,
					width: 5,
					radius: 15,
					corners: 1.0,
					rotate: 0,
					trail: 60,
					speed: 1.0,
					direction: 1,
					position: 'relative'
				};
				angular.extend(options, customOptions);
				spinner = new $window.Spinner(options);
				spinner.spin(element[0]);
			}, true);

		}

	}


    angular.module('common').directive('pageWaitSpinner', ['$window', pageWaitSpinner]);

    function pageWaitSpinner($window) {
		var directive = {
			link: link,
			template: '<div class="modal-backdrop fade in cover-all"></div>',
			restrict: 'A'
		};
		return directive;

		function link(scope, element, attrs) {
			scope.spinner = null;
			scope.$watch(attrs.pageWaitSpinner, function (options) {
				if (scope.spinner) {
					scope.spinner.stop();
				}
				options = {
				    lines: 13,
				    length: 11,
				    width: 5,
				    radius: 15,
				    corners: 1.0,
				    rotate: 0,
				    trail: 60,
				    speed: 1.0,
				    direction: 1,
				    top: '40%',
                    position: 'fixed'
				};
				scope.spinner = new $window.Spinner(options);
				scope.spinner.spin(element[0]);
			}, true);

		}

	}
})();;
(function () {
    'use strict';

    var serviceId = 'storageService';

    angular.module('common').factory(serviceId, ['$window', '$q', '$log', storageService]);

    function storageService($window, $q, $log) {
        return {
            clear: clear,
            get: get,
            set: set
        };

        function set(key, value) {
            try {
                $window.localStorage[key] = value;
            } catch (e) {
                $log.error('Error writing to localStorage', e);
                throw new Error('Error saving cart. This application will likely not function properly.');
            }
        }

        function get(key) {
            return $window.localStorage[key];
        }

        function clear() {
            $window.localStorage.clear();
        }
    }
})();;
(function () {
    'use strict';

    var serviceId = 'hoursOfOperationService';

    angular.module('main').factory(serviceId, ['$http', '$q', '$log', hoursOfOperationService]);

    function hoursOfOperationService($http, $q, $log) {
        var service = {
            getHoursOfOperationDisplay: getHoursOfOperationDisplay
        };


        function HoursOfOperation() {
            this.weekdayParts = [];
            this.rangeParts = [];
        }

        HoursOfOperation.prototype.toString = function () {
	        if (this.weekdayParts && this.weekdayParts.length) {
		        return 'Available ' + this.weekdayParts.join(' and ') + '\n' + this.rangeParts.join(' ');
	        }
	        return "Not available";
        };

        HoursOfOperation.prototype.addWeekday = function (s) {
            this.weekdayParts.push(s);
        };

        HoursOfOperation.prototype.addRange = function (s) {
            this.rangeParts.push(s);
        };

        HoursOfOperation.prototype.parse = function (hoursOfOperation) {
            var self = this;
            _.each(hoursOfOperation.WeekdayHours, function (hoo) {
                self.addWeekday(new WeekdayHours(hoo.WeekDay, hoo.Start + " - " + hoo.Stop));
            });

            var startDate = moment(hoursOfOperation.StartDate);
            var endDate = moment(hoursOfOperation.EndDate);
            var throughDate = endDate.add(-1, 'days');
			if (hoursOfOperation.StartDate && hoursOfOperation.EndDate) {
				if (startDate.isAfter(togoorder.utcNow) && endDate.isAfter(togoorder.utcNow)) {
                    self.addRange("Available from " + startDate.format('dddd, MMMM D') + " through " + throughDate.format('dddd, MMMM D'));
				}
				else if (endDate.isAfter(togoorder.utcNow)) {
					self.addRange("Available through " + throughDate.format('dddd, MMMM D'));
				}
            } else {
                if (hoursOfOperation.StartDate) {
                    if (startDate.isAfter(togoorder.utcNow)) {
                        self.addRange("Available starting on " + startDate.format('dddd, MMMM D'));
                    }
                }
                if (hoursOfOperation.EndDate) {
                    if (endDate.isAfter(togoorder.utcNow)) {
                        self.addRange("Available through " + throughDate.format('dddd, MMMM D'));
                    }
                }
            }
        }

        function WeekdayHours(day, hours) {
            this.day = day;
            this.hours = hours;
        }

        WeekdayHours.prototype.toString = function () {
            return this.day + ': ' + this.hours;
        };

        
        function getHoursOfOperationDisplay(hoursOfOperation) {
            var hoo = new HoursOfOperation();
            hoo.parse(hoursOfOperation);
            return hoo;
    
        }

        return service;
    }
})();;
(function () {
	'use strict';

	var serviceId = 'orderTypeViewModelServiceFactory';

	angular.module('main').factory(serviceId, ['$http', '$q', '$state', '$timeout', '$log', '$location', 'common', 'hoursOfOperationService', orderTypeViewModelServiceFactory]);

	function orderTypeViewModelServiceFactory($http, $q, $state, $timeout, $log, $location, common, hoursOfOperationService) {

		return {
			getLocationInstance: getLocationInstance
		};

		function getLocationInstance() {
			var takeoutDescription = 'Take Out',
				curbsideDescription = 'Curbside Pickup',
				carsideDescription = 'Curbside',
				tableNumberData = { orderTypeDescription: 'Order From The Table' },
				driveThruData = {
					orderTypeDescription: 'Drive-Thru',
					extendForVehicleInformation: extendDriveThruDataForVehicleInformation
				},
				driveThruWithVehicleInfoData = { orderTypeDescription: 'Drive-Thru' },
				curbsideData = { orderTypeDescription: curbsideDescription },
				carsideData = {
					orderTypeDescription: carsideDescription
				},
				takeoutCurbsideData = { orderTypeDescription: takeoutDescription + ' / Curbside' },
				deliveryAddressData = { orderTypeDescription: 'Delivery' },
				normalData = {
					 orderTypeDescription: takeoutDescription,
					 extendForVehicleInformation: extendNormalDataForVehicleInformation
				},
				cateringForPickupData = { orderTypeDescription: 'Catering for Pickup' },
				cateringForDeliveryData = { orderTypeDescription: 'Catering for Delivery' },
				fulfillmentTypeMap = {
					2: { //2: OrderFulfillmentType.MerchantDelivery
						1: tableNumberData, // 1: OrderType.DineIn
						10: cateringForDeliveryData, //10: OrderType.Catering
						'otherwise': deliveryAddressData
					},
					'otherwise': { //Probably 1: OrderFulfillmentType.CustomerPickup
						1: tableNumberData, // 1: OrderType.DineIn
						4: deliveryAddressData, //4: OrderType.Delivery
						7: driveThruData, // 7: OrderType.DriveThru
						8: carsideData, // 8: OrderType.Carside
						10: cateringForPickupData, //10: OrderType.Catering
						'otherwise': normalData
					}
				};
			/*
All the valid combinations:
OrderType	OrderFulfillmentType	Description
1	2	"Order From the Table"
2	1	"Take Out" 
4	2	"Delivery"
7	1	"Drive Thru"8	1 "Carside"
10	1	"Catering for Pickup"
10	2	"Catering for Delivery"
*/

			return {
				takeoutDescription: takeoutDescription,
				curbsideDescription: curbsideDescription,
				getOrderTypeViewModels: getOrderTypeViewModels,
				tableNumberData: tableNumberData,
				deliveryAddressData: deliveryAddressData,
				normalData: normalData,
				driveThruData: driveThruData,
				driveThruWithVehicleInfoData: driveThruWithVehicleInfoData,
				takeoutCurbsideData: takeoutCurbsideData,
				curbsideData: curbsideData,
				carsideData: carsideData,
				cateringForPickupData: cateringForPickupData,
				cateringForDeliveryData: cateringForDeliveryData,
				fulfillmentTypeMap: fulfillmentTypeMap,
				getOrderTypeData: getOrderTypeData
			};

			function extendNormalDataForVehicleInformation(orderTypeRule, hasVehicleInformation) {
				if (orderTypeRule.IsCurbsideOffered) {
					if (orderTypeRule.IsVehicleInformationRequired) {
						angular.extend(normalData, curbsideData);
					} else {
						// When called from thankYou.js, we know if the user entered 
						// vehicle information, so we pass in true or false for hasVehicleInformation.
						// If hasVehicleInformation is undefined, then it's NOT from
						// thankYou.js, so we don't yet know if the user is going to 
						// choose curbside.
						if (hasVehicleInformation) {
							angular.extend(normalData, curbsideData);
						} else if (hasVehicleInformation === undefined) {
							angular.extend(normalData, takeoutCurbsideData);
						}
					}
				}
			}

			function extendDriveThruDataForVehicleInformation(orderTypeRule) {
				if (orderTypeRule.IsVehicleInformationRequired) {
					angular.extend(driveThruData, driveThruWithVehicleInfoData);
				}
			}

			function setOrderTypeRule(orderTypeRule) {
				let orderTypeData = getOrderTypeData(orderTypeRule.OrderType, orderTypeRule.FulfillmentType);

				if (orderTypeData.extendForVehicleInformation) {
					orderTypeData.extendForVehicleInformation(orderTypeRule);
				}

			}
			 
			function getOrderTypeViewModels(location, isUnlisted) {
				var unlistedOnly = sessionStorage.getItem('is_unlistedOnly');

				//chooseMenu is not being called everytime so it makes the sessionStorage inconsistent.
				//if the url contains unlistedOnly at any point the sessionStorage is set again.
				if (window.location.href.includes('unlistedOnly') || $.cookie('is_unlistedOnly')) {
					sessionStorage.setItem('is_unlistedOnly', 'true');
					$.cookie('is_unlistedOnly', true, {expires: .01});
					if (window.location.href.includes('unlistedOnly')) {
						$.cookie('unlisted_url', window.location.href, {expires: .01});
                    }
					unlistedOnly = 'true';
                }

				var orderTypeViewModels = _.filter(location.OrderTypeViewModels,
					function (ot) {
						return !!(ot.MenuIds && ot.MenuIds.length);
					});
				orderTypeViewModels = _.filter(orderTypeViewModels,
					function (ot) {
						return unlistedOnly === 'true' ? true : ot.IsUnlisted === !!isUnlisted;
					});
				_.each(orderTypeViewModels, setOrderTypeRule);
				orderTypeViewModels = _.map(orderTypeViewModels,
					function (g) {
						return {
							OrderType: g.OrderType,
							FulfillmentType: g.FulfillmentType,
							HoursOfOperationDisplay: hoursOfOperationService.getHoursOfOperationDisplay(
								g.HoursOfOperation)
						};
					});
				orderTypeViewModels = _.map(orderTypeViewModels,
					function (ot) {
						var orderTypeData = getOrderTypeData(ot.OrderType, ot.FulfillmentType);

						ot.Description = orderTypeData.orderTypeDescription;
						ot.NormalizedDescription = common.safeClassName(ot.Description);
						return ot;
					});
				return _.sortBy(orderTypeViewModels, 'OrderType');
			}

			function getOrderTypeData(orderType, fulfillmentType) {
				if (!orderType) {
					throw new Error('orderType required');
				}
				if (!fulfillmentType) {
					throw new Error('fulfillmentType required');
				}
				var workflow = (fulfillmentTypeMap[fulfillmentType] || fulfillmentTypeMap.otherwise);
				if (!workflow.orderTypeDescription) {
					return workflow[orderType] || workflow.otherwise;
				}
				return workflow;
			}
		}
	}
})();;
(function() {
    var togoorder = window.togoorder = window.togoorder || {};
	var cookiePath = '/';
    function getInjector() {
        var elem = angular.element(document.querySelector('[data-ng-controller]'));
        var $injector = elem.injector();
        return $injector;
    }

	togoorder.userData = {
		isAuthenticated: false,
		username: '',
		bearerToken: '',
		expirationDate: null,
        merchantId: 0
	};
	togoorder.getSavedUserData = function () {
		var savedJson = $.cookie('auth_data');
		if (!savedJson) {
			return null;
		}
		var savedUserData = angular.fromJson(savedJson);
		if (savedUserData.merchantId !== togoorder.merchantId) {
			return null;
		}
		return togoorder.userData = savedUserData;
	};
	togoorder.clearUserData = function () {
		togoorder.userData.isAuthenticated = false;
		togoorder.userData.username = '';
		togoorder.userData.bearerToken = '';
		togoorder.userData.expirationDate = null;
		togoorder.userData.merchantId = 0;
		removeUserData();
	};
	togoorder.getInjector = function() {
        var $injector = getInjector();
		return $injector;
	};
	togoorder.goToNextState = function () {
		var $injector = togoorder.getInjector();
		var userService = $injector.get('userService');
		var $state = $injector.get('$state');
	    try {
	        var nextState = userService.getNextState();
	        $state.go(nextState.name, nextState.params);
	    } catch (e) {
	        if (e.name === 'NextStateUndefined') {
	            window.location.reload();
	        } else {
	            throw e;
	        }
	    }
	};

	function removeUserData() {
		$.removeCookie('auth_data'); // <-- path-less for cookies set before the below change 2018-04-13
		$.removeCookie('auth_data', { path: cookiePath });
	}

    var setAuthCookieDefered = $.Deferred();
    togoorder.setAuthCookiePromise = setAuthCookieDefered.promise();

	togoorder.setAuthCookie = function (userName, accessToken, expirationDate, isSavedSession, isWebLogin) {
		removeUserData();

		togoorder.userData.isAuthenticated = true;
		togoorder.userData.username = userName;
		togoorder.userData.bearerToken = accessToken;
		togoorder.userData.expirationDate = expirationDate;
		togoorder.userData.merchantId = togoorder.merchantId;
		togoorder.userData.isWebLogin = !!isWebLogin;
		
		var userJson = angular.toJson(togoorder.userData);

		if (isSavedSession) {
			$.cookie('auth_data', userJson, { expires: 7, path: cookiePath });
		} else {
			$.cookie('auth_data', userJson, { path: cookiePath });
		}

		setAuthCookieDefered.resolve(userJson);
	};
    togoorder.mobileStrategy = togoorder.mobileStrategy || {};
    togoorder.authStrategy = togoorder.authStrategy || {};
    togoorder.callInjectedStrategy = function(method) {
	    if (togoorder.setMobileStrategy) {
		    togoorder.setMobileStrategy();
	    }
	    //try authStrategy first
	    var strategy = togoorder.authStrategy[method] || togoorder.mobileStrategy[method];
	    if (strategy) {
		    var args = _.toArray(arguments);
		    args.shift();
		    var result = strategy.apply(togoorder.mobileStrategy, args);
		    return { result: result };
	    }
	    return undefined;
    };
	togoorder.newPaymentMethodAdded = function(paymentMethodId) {
		var $injector = getInjector();

		var common = $injector.get('common');
		var events = $injector.get('events');

		common.broadcast(events.newPaymentMethodAdded, { paymentMethodId: paymentMethodId });
    };

    togoorder.squareSettings = {
        squareLocationId: '',
        squareApplicationId: '',
        squarePaymentAdded: false
    };

    togoorder.squareCardData = {
        billingPostalCode: '',
        cardBrand: '',
        digitalWalletType: '',
        expMonth: 0,
        expYear: 0,
        lastFour: '',
        Id: 0,
        CardId: '',
        UserId: 0
    };
})();;
(function () {
    'use strict';

    var controllerId = 'loadingPage';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$scope', '$log', '$state', '$stateParams', '$uibModal', '$filter', '$window', '$location', '$timeout', 'notify', 'common', 'spinner', loadingPageController]);

    function loadingPageController($rootScope, $scope, $log, $state, $stateParams, $modal, $filter, $window, $location, $timeout, notify, common, spinner) {
        var vm = this,
            params = $location.search();

        activate();
        $window.togoorder.setAuthCookie(params["userName"], params["token"], "", true);
        function activate() {
            common.activateController([waitForSetAuthCookie()], controllerId);
        }

        function waitForSetAuthCookie() {
            return togoorder.setAuthCookiePromise.then(function () {
                $location.path(params["forwardUrl"]);
            });
        }
    }
})();;
(function () {
    'use strict';

    var controllerId = 'checkout';
    angular.module('main')
        .controller(controllerId, ['$timeout', '$rootScope', '$scope', '$state', '$log', '$q', '$http', '$window', '$stateParams', '$uibModal', '$filter', 'linkService', 'notify', 'common', 'events', 'cartService', 'merchantLocationService', 'userService', 'enumService', 'spinner', 'Payment', 'staticData', 'orderService', 'hoursService', 'orderTypeWorkflowService', 'deliveryService', 'timeService', 'browserInfo', 'promoCodeService', 'menuWorkflowService', 'menuService', 'orderTypeRuleService', 'OrderItem', 'OfferExt', 'api', 'menuItemDetailViewModel', 'loyaltyService', 'storageService', 'mealPlanService', 'giftCardService', 'Address', 'vcRecaptchaService', 'paymentGatewayProfileSettingsService', checkoutCountroller]);
    function checkoutCountroller($timeout, $rootScope, $scope, $state, $log, $q, $http, $window, $stateParams, $modal, $filter, linkService, notify, common, events, cartService, merchantLocationService, userService, enumService, spinner, Payment, staticData, orderService, hoursService, orderTypeWorkflowService, deliveryService, timeService, browserInfo, promoCodeService, menuWorkflowService, menuService, orderTypeRuleService, OrderItem, OfferExt, api, menuItemDetailViewModel, loyaltyService, storageService, mealPlanService, giftCardService, Address, vcRecaptchaService, paymentGatewayProfileSettingsService) {

        var vm = this,
            monthAbbreviations = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'],
            gratuityLevels = [],
            placeOrderOnce = _.once(placeOrder),
            menu,
            onNewPaymentMethodAddedOnce = _.once(onNewPaymentMethodAdded),
            loyaltyProviderType = undefined,
            mealPlanProviderType = undefined,
            giftCardProviderType = undefined,
            isPriceRequestNeeded = true,
            selectedTimeNotAvailableError,
            isDeliveryServiceTimeGood = true;

        vm.squareCustomer = null;
        vm.safeClassName = common.safeClassName;
        vm.posIntegrationType = togoorder.PosIntegrationType;
        vm.merchantLocation = {};
        vm.squareCardData = togoorder.squareCardData;
        vm.orderTypeDisplay = undefined;
        vm.showValidation = showValidation;
        vm.isValidationVisible = false;
        vm.showCartSidebar = showCartSidebar;
        vm.isCartInPage = true;
        vm.hideCartSidebar = hideCartSidebar;
        vm.payWithNewCreditCard = payWithNewCreditCard;
        vm.payWithDifferentCreditCard = payWithDifferentCreditCard;
        vm.isSidebarVisible = false;
        vm.pickupDate = null;
        vm.dayChoices = undefined;
        vm.paymentMethods = undefined;
        vm.checkout = checkout;
        vm.hasCurrentOrder = undefined;
        vm.useDeliveryAddressForBillingAddress = useDeliveryAddressForBillingAddress;
        vm.goHome = goHome;
        vm.goToMenu = goToMenu;
        vm.loyaltyDifferentAmount = undefined;
        vm.giftCardDifferentAmount = undefined;
        vm.checkingOut = false;
        vm.order = {};
        vm.user = {};
        vm.cardTypes = null;
        vm.months = getMonths();
        vm.years = [];
        vm.states = staticData.states;
        vm.deliveryTemplate = undefined;
        vm.deliveryAddress = null;
        vm.waitTimeInMinutes = 0;
        vm.orderTypeRuleWaitTimeInMinutes = 0; // Only the Order type rule wait time
        vm.tableNumber = '';
        vm.now = null;
        vm.pickupDateTimePrefix = '';
        vm.browserTimeZoneOffset = timeService.getBrowserTimeZoneOffset();
        vm.gratuityChoices = undefined;
        vm.isAGuest = userService.isAGuest();
        vm.useCalendar = false;
        vm.messages = common.messages;
        vm.isPickupDateSelectable = undefined;
        vm.showCalendar = function ($event) {
            $event.preventDefault();
            $event.stopPropagation();
            vm.isCalendarVisible = true;
        };
        vm.hoursServiceInstance = null;
        vm.isCalendarVisible = false;
        vm.completeOrder = completeOrder;
        vm.showLoyaltyDifferentAmount = false;
        vm.showGiftCardDifferentAmount = false;
        vm.showLoyaltySection = showLoyaltySection;
        vm.showSecuritySection = showSecuritySection;
        vm.showPostalSection = showPostalSection;
        vm.showNameSection = showNameSection;
        vm.showCvvSection = showCvvSection;
        vm.showAddressSection = showAddressSection;
        vm.deletePaymentMethod = deletePaymentMethod;
        vm.deleteGiftCardAccount = deleteGiftCardAccount;
        vm.isCvvRequired = isCvvRequired;
        vm.paymentTime = 'now';
        vm.promoCode = null;
        vm.promotion = null;
        vm.applyPromoCode = _.debounce(applyPromoCode, 300, true);
        vm.showCvvHelp = showCvvHelp;
        vm.bestLoyalty = {};
        vm.changeLoyaltyAmount = changeLoyaltyAmount;
        vm.changeGiftCardAmount = changeGiftCardAmount;
        vm.safeClassName = common.safeClassName;
        vm.isEmailRequired = undefined;
        vm.isNameRequired = undefined;
        vm.isPhoneRequired = undefined;
        vm.isSelectedCard = isSelectedCard;
        vm.getLoyaltyAppliedAmount = getLoyaltyAppliedAmount;
        vm.getMealPlanAppliedAmount = getMealPlanAppliedAmount;
        vm.getGiftCardAppliedAmount = getGiftCardAppliedAmount;
        vm.payLaterText = undefined;
        vm.minimumAge = undefined;
        vm.showMinimumAgeSection = undefined;
        vm.isAgeConfirmed = true;
        vm.datepickerOptions = {
            dateDisabled: dateDisabled,
            minDate: new Date(),
            showWeeks: false
        };
        vm.setLoyalty = setLoyalty;
        vm.showPaymentTimeAllowed = undefined;
        vm.setMealPlanPayment = setMealPlanPayment;
        vm.setGiftCardPayment = setGiftCardPayment;
        vm.scanCard = scanCard;
        vm.hasOffers = false;
        vm.orderTypePrompt = null;
        vm.isCardScanSupported = undefined;
        vm.selectPaymentMethod = selectPaymentMethod;
        vm.eligibleOffers = undefined;
        vm.isBusy = isBusy;
        vm.optOffer = optOffer;
        vm.offerDiscountTotal = 0;
        vm.limitOneOffer = undefined;
        vm.canApplyDifferentGiftCardAmount = undefined;
        vm.mustApplyGiftCard = undefined;
        vm.setPaymentTime = setPaymentTime;
        vm.getPaymentTimeSelectionVisible = getPaymentTimeSelectionVisible;
        vm.isPromoAllowed = isPromoAllowed;
        vm.requiredItems = undefined;
        vm.item = menuItemDetailViewModel;
        vm.isFutureDayOrderingAllowed = undefined;
        vm.isCreditCardPaymentAllowed = undefined;
        vm.isNoTimesAvailable = isNoTimesAvailable;
        vm.isTimesAvailable = isTimesAvailable;
        vm.gratuityChoiceOther = { value: 0, tipSize: 0 };
        vm.gratuityChoiceNoTip = { value: 0 };
        vm.newOfferExt = newOfferExt;
        vm.showAddGiftCard = false;
        vm.showAddGiftCardPin = false;
        vm.newGiftCard = null;
        vm.newGiftCardPin = null;
        vm.recaptchaToken = null;
        vm.addGiftCard = addGiftCard;
        vm.showCreditCardFields = showCreditCardFields;
        vm.getTopCssClasses = getTopCssClasses;
        vm.sqFormCnt = 0;
        vm.getItemCount = getItemCount;
        vm.getItemIncludedItemsTemplate = menuWorkflowService.getItemIncludedItemsTemplate;
        vm.deliveryServicePickupTimeUtc = undefined;
        vm.isGiftCardFormShown = false;
        vm.showGiftCardForm = showGiftCardForm;
        vm.isGiftCardInputMasked = false;
        vm.showCaptcha = showCaptcha;
        vm.recaptchaKey = togoorder.recaptchaKey;
        vm.onRecaptchaCreate = onRecaptchaCreate;
        vm.stripSpaces = stripSpaces;
        vm.deliveryServiceDeliveryTime = undefined;
        vm.deliveryServiceTimeToDeliver = undefined;
        vm.getPaymentGatewayProfileSettings = getPaymentGatewayProfileSettings;

        //Required inputs messaging....
        vm.mandatoryText = { name: "required input", url: "shared/mandatoryMsg.html" }
        //Eval tipping override
        vm.isDeliveryGratuityOverride = isDeliveryGratuityOverride;
        vm.isAddressRequiredAtCheckout = "true";
        vm.isDigitalWalletProfileGoogle = false;
        vm.isDigitalWalletProfileApple = false;
        vm.loadCollectJs = loadCollectJs;
        vm.getCollectJSiframe = getCollectJSiframe;
        vm.ensureCollectJsCallbackListening = ensureCollectJsCallbackListening;
        vm.addCollectJsEventListener = addCollectJsEventListener;
        vm.collectJsCallback = collectJsCallback;
        vm.getLocationIdFromUrl = getLocationIdFromUrl;
        vm.isDeliveryAddressUsedForBilling = false;

        vm.isFutureOrderChargedImmediately = false;
        vm.isDigitalWalletPaymentSupported = isDigitalWalletPaymentSupported;

        getPaymentGatewayProfileSettings();
        getIsDigitalWalletProfile();
        loadCollectJs();


        function getPaymentGatewayProfileSettings() {
            paymentGatewayProfileSettingsService.isAddressRequiredAtCheckout({
                locationId: togoorder.locationId
            }).then(function (response) {
                vm.isAddressRequiredAtCheckout = response;
            }, function () {
                $log.error('An error occurred while getPaymentGatewayProfileSettings');
                notify.dialog('Sorry :/', 'An error occurred while getPaymentGatewayProfileSettings.');
                throw new Error();
            });

        }

        function getIsDigitalWalletProfile() {
            paymentGatewayProfileSettingsService.isDigitalWalletProfile({
                locationId: togoorder.locationId
            }).then(function (response) {
                vm.isDigitalWalletProfileGoogle = response.Item1 === "True";
                vm.isDigitalWalletProfileApple = response.Item2 === "True";
                vm.loadCollectJs();
            }, function () {
                $log.error('An error occurred while updating the PIN.');
                notify.dialog('Sorry :/', 'An error occurred while updating the PIN.');
                throw new Error();
            });

        }

        function showGiftCardForm(val) {
            if (val === undefined) {
                val = true;
            }

            vm.isGiftCardFormShown = val;
        }

        function showCaptcha() {
            return vm.isGiftCardFormShown && vm.giftCardProviderType === 2;
        }

        function showCreditCardFields() {
            return vm.order.TotalForCreditCard && vm.isCreditCardPaymentAllowed;
        }

        function newOfferExt(offer) {
            return new OfferExt(offer, toggleOffer);
        }

        function isPromoAllowed() {
            return vm.merchantLocation.PromotionAvailability === 0 ||
                (vm.merchantLocation.PromotionAvailability === 2 && vm.paymentTime === 'now');
        }

        function getPaymentTimeSelectionVisible() {
            return vm.order.isPayable && !vm.order.Payment.GiftCardPayment && !vm.order.Payment.MealPlanPayment;
        }

        function setPaymentTime(paymentTime) {
            vm.paymentTime = paymentTime;
            vm.order.Gratuity = null;
        }

        function selectPaymentMethod(paymentMethod) {
            vm.order.Payment.ExistingCreditCard.PaymentMethodId = paymentMethod.Id;
        }

        function scanCard() {
            togoorder.callInjectedStrategy('scanCard');
        }

        function dateDisabled(obj) {
            var m = convertTimelessDateForTimezone(obj.date, vm.merchantLocation.TimeZoneOffset);
            var isDisabled = !vm.hoursServiceInstance.isDateSelectable(m, obj.mode);
            return isDisabled;
        }

        function setLoyalty(loyalty) {
            vm.order.invalidatePricedOrderLoyalty();
            vm.order.Payment.LoyaltyPayment = loyalty;
            if ((vm.order.LoyaltyPaymentAmount + vm.order.OfferPaymentAmount) >= vm.order.Total && !vm.order.Payment.GiftCardPayment) {
                setGiftCardPayment(null);
            }
            else if ((vm.order.LoyaltyPaymentAmount + vm.order.OfferPaymentAmount) >= vm.order.Total && !vm.order.Payment.MealPlanPayment) {
                setMealPlanPayment(null);
            } else {
                rePriceOrder();
            }
        }

        function setMealPlanPayment(mealPlan) {
            vm.order.Payment.MealPlanPayment = mealPlan;
            rePriceOrder();
        }

        function setGiftCardPayment(giftCard) {
            if (giftCard && giftCard.IsPinOnFile === false && vm.merchantLocation.GiftCardProfile.RequirePin) {
                $modal.open({
                    templateUrl: 'app/checkout/giftCardPinPrompt.html',
                    controller: 'giftCardPinPrompt as vm',
                    size: 'sm',
                    resolve: {
                    }
                }).result.then(function (pin) {
                    spinner.spinnerShow();

                    return giftCardService.updatePin({
                        merchantId: vm.merchantLocation.MerchantId,
                        cardNumber: giftCard.AccountNumber,
                        cardPin: pin
                    }).then(function (a) { return a; }, function () {
                        $log.error('An error occurred while updating the PIN.');
                        notify.dialog('Sorry :/', 'An error occurred while updating the PIN.');
                        throw new Error();
                    });
                }, function () {
                    //neverminded the dialog.
                    return $q.reject();
                }).then(function (result) {
                    if (!result.Success) {
                        $log.error(result.Message || 'An error occurred while retrieving the gift card.');
                        return notify.dialog('Sorry :/', 'An error occurred while retrieving the gift card.');
                    }
                    return getUser().then(function () {
                        var thisGiftCard = _.findWhere(vm.user.GiftCards, { AccountNumber: giftCard.AccountNumber });
                        if (thisGiftCard) {
                            return setGiftCardPayment(thisGiftCard);
                        }
                    });
                }).then()['finally'](function () {
                    spinner.spinnerHide();
                });

            } else {
                vm.order.Payment.GiftCardPayment = giftCard;
                rePriceOrder();
            }
        }

        function isCvvRequired(paymentMethod) {
            return vm.order.Payment.ExistingCreditCard.IsSelected &&
                vm.order.Payment.ExistingCreditCard.PaymentMethodId === paymentMethod.Id &&
                paymentMethod.IsCardVerificationValueRequired;
        }

        function showLoyaltySection() {
            const show = (vm.order.isPayable || vm.order.LoyaltyDiscount !== 0) && (vm.order.TotalForLoyalty + vm.order.LoyaltyDiscount);
            //console.log(`show loyalty section? ${show ? 'yeah' : 'nah'} isPayable: ${vm.order.isPayable} loyaltyDiscount: ${vm.order.LoyaltyDiscount} totalForLoyalty: ${vm.order.TotalForLoyalty}`);
            return show;
        }

        function showSecuritySection(paymentMethod) {
            return vm.order.Payment.ExistingCreditCard.IsSelected &&
                vm.order.Payment.ExistingCreditCard.PaymentMethodId === paymentMethod.Id &&
                (showPostalSection(paymentMethod) ||
                    showCvvSection(paymentMethod) ||
                    showNameSection(paymentMethod) ||
                    showAddressSection(paymentMethod));
        }

        function showCvvSection(paymentMethod) {
            return vm.order.Payment.ExistingCreditCard.IsSelected &&
                paymentMethod.IsCardVerificationValueRequired;
        }

        function showPostalSection(paymentMethod) {

            // Direct Settlement FirstAtlantic 
            if (vm.isAddressRequiredAtCheckout == 'false') {
                return false;
            }

            // postal code not on file
            return vm.order.Payment.ExistingCreditCard.IsSelected &&
                !paymentMethod.PostalCode &&
                vm.order.Payment.ExistingCreditCard.PaymentMethodId === paymentMethod.Id;
        }

        function showAddressSection(paymentMethod) {
            if (vm.posIntegrationType === 'Square') {
                return false;
            }

            // Direct Settlement FirstAtlantic 
            if (vm.isAddressRequiredAtCheckout == 'false') {
                return false;
            }

            // address1 not on file
            return vm.order.Payment.ExistingCreditCard.IsSelected &&
                !paymentMethod.AddressLine1 &&
                vm.order.Payment.ExistingCreditCard.PaymentMethodId === paymentMethod.Id;
        }

        function showNameSection(paymentMethod) {
            // last/first name not on file
            // If there's no address on file, also allow them to modify the name
            if (vm.posIntegrationType === 'Square') {
                return false;
            }

            // Direct Settlement FirstAtlantic 
            if (vm.isAddressRequiredAtCheckout == 'false') {
                return false;
            }

            return vm.order.Payment.ExistingCreditCard.IsSelected &&
                (!paymentMethod.LastName || !paymentMethod.AddressLine1) &&
                vm.order.Payment.ExistingCreditCard.PaymentMethodId === paymentMethod.Id;
        }

        function deletePaymentMethod(paymentMethod, $event) {
            $event.stopPropagation();
            spinner.spinnerShow();
            return userService.deletePaymentMethod(paymentMethod).then(function () {
                togoorder.callInjectedStrategy('cardDeleted', paymentMethod.Id);
                return getPaymentMethods();
            })['finally'](function () {
                spinner.spinnerHide();
            });
        }

        function deleteGiftCardAccount(giftCardAccountNumber, $event) {
            $event.stopPropagation();
            spinner.spinnerShow();
            return giftCardService.deleteCard(giftCardAccountNumber).then(function () {
                setGiftCardPayment(null);
                return getUser(true).then(function () {
                    spinner.spinnerHide();
                });
            });
        }

        function isSelectedCard(paymentMethod) {
            return vm.order.Payment.ExistingCreditCard.PaymentMethodId === paymentMethod.Id;
        }

        function changeLoyaltyAmount(amount) {
            vm.order.invalidatePricedOrderLoyalty();
            if (isNaN(+amount)) {
                vm.order.LoyaltyOverrideAmount = vm.order.MaxLoyaltyPaymentAmount + vm.order.MaxLoyaltyDiscountAmount;
            } else {
                vm.order.LoyaltyOverrideAmount = amount;
            }
            vm.loyaltyDifferentAmount = $filter('currency')(getLoyaltyAppliedAmount(), '');
            vm.showLoyaltyDifferentAmount = false;
            rePriceOrder();
        }

        function getLoyaltyAppliedAmount() {
            return (vm.order.LoyaltyPaymentAmount || -vm.order.LoyaltyDiscount);
        }

        function changeGiftCardAmount(amount) {
            //debugger;
            if (isNaN(+amount)) {
                vm.order.GiftCardOverrideAmount = vm.order.MaxGiftCardPaymentAmount;
            } else {
                vm.order.GiftCardOverrideAmount = amount;
            }

            vm.giftCardDifferentAmount = $filter('currency')(getGiftCardAppliedAmount(), '');

            vm.showGiftCardDifferentAmount = false;
        }

        function getMealPlanAppliedAmount() {
            return vm.order.MealPlanPaymentAmount || 0;
        }

        function getGiftCardAppliedAmount() {
            //debugger;
            return vm.order.GiftCardPaymentAmount || 0;
        }

        function addGiftCard() {

            if (!vm.newGiftCard) {
                notify.dialog('Missing something', "Gift card number is required");
                return;
            }

            if (vm.merchantLocation.GiftCardProfile.RequirePin && !vm.newGiftCardPin) {
                notify.dialog('Missing something', "Gift card PIN is required");
                return;
            }

            spinner.spinnerShow();
            var newCard = {
                merchantId: vm.merchantLocation.MerchantId,
                cardNumber: vm.newGiftCard,
                isPrimary: false,
                cardPin: vm.newGiftCardPin,
                recaptchaToken: vm.recaptchaToken
            };
            if (vm.isAGuest) {
                return giftCardService.getBalance(newCard).then(function (balance) {
                    if (balance.Success) {
                        if (vm.user.GiftCards == undefined) {
                            vm.user.GiftCards = [];
                        }
                        let verifiedCard = {
                            Balance: balance.Balance,
                            AccountNumber: newCard.cardNumber,
                            IsPrimary: false,
                            PinNumber: vm.newGiftCardPin

                        };
                        vm.user.GiftCards.push(verifiedCard);
                        setGiftCardPayment(verifiedCard);
                        vm.showGiftCards = vm.user.GiftCards.length > 0;
                        showGiftCardForm(false);
                    }
                    else {
                        var error = new Error('There was an issue trying to retrieve that gift card.');
                        error.showAlert = true;
                    }
                    vm.newGiftCard = null;
                    vm.newGiftCardPin = null;
                    if (vm.showCaptcha()) {
                        vcRecaptchaService.reload(vm.recaptchaId);
                    }
                    spinner.spinnerHide();
                }, error => {
                    if (vm.showCaptcha()) {
                        vcRecaptchaService.reload(vm.recaptchaId);
                    }
                });
            }
            else {
                return giftCardService.addCard(newCard).then(function () {
                    return getUser().then(function () {
                        vm.newGiftCard = null;
                        vm.newGiftCardPin = null;
                        vm.isGiftCardFormShown = false;
                        if (vm.showCaptcha()) {
                            vcRecaptchaService.reload(vm.recaptchaId);
                        }
                        spinner.spinnerHide();
                    });
                });
            }
        }

        function stripSpaces() {
            vm.newGiftCard = vm.newGiftCard.replaceAll(' ', '');
        }

        function convertTimelessDateForTimezone(date, timeZoneOffset) {
            var m = moment.utc({
                year: date.getFullYear(),
                month: date.getMonth(),
                day: date.getDate()
            }).utcOffset(timeZoneOffset);
            m.minutes(-timeZoneOffset);
            return m;
        }

        var pickupCalendarDate = new Date();
        Object.defineProperty(vm, 'pickupCalendarDate', {
            get: function () {
                return pickupCalendarDate;
            },
            set: function (value) {
                pickupCalendarDate = value;
                var m = convertTimelessDateForTimezone(pickupCalendarDate, vm.merchantLocation.TimeZoneOffset);
                vm.pickupDate = vm.hoursServiceInstance.getPickupDateTimeForDate(m);
            }
        });

        Object.defineProperty(vm, 'isPromoApplicable', {
            get: function () {
                if (!vm.promoCode) {
                    return false;
                }
                if (vm.promoCode.toUpperCase() === ((vm.order.Promotion && vm.order.Promotion.Code) || '').toUpperCase()) {
                    // console.log('vm.promoCode === vm.order.Promotion.Code', ((vm.order.Promotion && vm.order.Promotion.Code) || ''));
                    return false;
                }
                if (vm.promoCode.toUpperCase() === ((vm.promotion && vm.promotion.Code) || '').toUpperCase()) {
                    // console.log('vm.promoCode === vm.promotion.Code', ((vm.promotion && vm.promotion.Code) || ''));
                    return false;
                }
                return true;
            }
        });

        Object.defineProperty(vm, 'orderPickupDateTime', {
            get: function () {
                return vm.order.PickupDateTime;
            },
            set: function (value) {
                vm.order.PickupDateTime = value;
                // assuming the only reason the isPriceRequestNeeded is 
                // false would be that the price request failed due to 
                // daily special.  PriceCheckStatusType.MenuItemDayExcluded

                if (isPriceRequestNeeded) {
                    rePriceOrder();
                }

                spinner.spinnerShow();
                confirmNewDeliveryServiceTime(value).then(function (isGood) {
                    isDeliveryServiceTimeGood = isGood;

                    if (!isGood) {
                        var error = new Error(selectedTimeNotAvailableError);
                        error.showAlert = true;
                        throw error;
                    }
                })['finally'](function () {
                    spinner.spinnerHide();
                });
            }
        });

        var selectedGratuityChoice = null;
        Object.defineProperty(vm, 'selectedGratuityChoice', {
            get: function () {
                //debugger;
                return selectedGratuityChoice;
            },
            set: function (value) {
                //debugger;
                selectedGratuityChoice = value;
                vm.order.Gratuity = common.monetize(selectedGratuityChoice.value);

                if (selectedGratuityChoice === vm.gratuityChoiceOther) {
                    common.broadcast('otherGratuitySelected');
                }
            }
        });

        activate();

        $rootScope.$on(events.newPaymentMethodAdded, onNewPaymentMethodAddedOnce);

        function onNewPaymentMethodAdded(event, data) {
            spinner.spinnerShow();
            $timeout(function () {
                getPaymentMethods().then(function () {
                    spinner.spinnerHide();
                    payWithDifferentCreditCard();

                    $timeout(function () {

                        var paymentMethod = _.find(vm.paymentMethods,
                            function (pm) {
                                return pm.Id.toLowerCase() === data.paymentMethodId.toLowerCase();
                            });
                        selectPaymentMethod(paymentMethod);
                        vm.order.Payment.CardHolder.FirstName = paymentMethod.FirstName;
                        vm.order.Payment.CardHolder.LastName = paymentMethod.LastName;
                        onNewPaymentMethodAddedOnce = _.once(onNewPaymentMethodAdded);
                    },
                        1000);
                });
            },
                1000);
        }

        $scope.$on(events.promoCodeRemoved, function () {
            vm.promoCode = '';
            //I wonder if this is when a sparkfly transaction should be voided
            vm.order.Payment.ExternalPromotion = null;
            rePriceOrder();
        });

        $scope.$watch('vm.pickupDate', function (newValue, oldValue) {
            if (oldValue) {
                vm.order.PickupDateTime = "";
            }
            if (newValue && newValue.showCalendar) {
                if (vm.dayChoices && vm.dayChoices.length) {
                    setPickupCalendarDate(vm.dayChoices[0].date.add(6, 'days'));
                }
                vm.useCalendar = true;
            }
        });

        function setPickupCalendarDate(m) {
            vm.pickupCalendarDate = m.toDate();
        }

        $scope.$watch('vm.promoCode', function (n, o) {
            vm.promotion = {};
        });

        function getPromotion(promoCode, order) {
            if (!promoCode) {
                return $q.when();
            }

            var promo = {
                promoCode: promoCode,
                pickupDateTimeUtc: order.PickupDateTime ? moment(order.PickupDateTime).toDate() : '',
                orderTotal: order.Subtotal,
                cartOrder: order
            };

            return promoCodeService.getPromotion(promo, order.MenuId).then(function (promotion) {
                vm.promotion = promotion;

                vm.promotion.showDiscountPercentage = promotion.DiscountPercentage > 0 && !(promotion.DiscountPercentage === 100 && vm.promotion.DiscountAmount > 0);
                vm.promotion.hasMax = promotion.DiscountPercentage > 0 && promotion.DiscountAmount > 0;
                vm.promotion.showDiscountAmount = vm.promotion.DiscountAmount > 0 && !vm.promotion.showDiscountPercentage;
                vm.order.Payment.ExternalPromotion = promotion.ExternalPromotionResponse;
                return vm.promotion;
            });
        }

        function showCvvHelp() {
            linkService.openLinkInNewWindow('http://www.cvvnumber.com/cvv.html');
        }

        function applyPromoCode() {
            if (!vm.isPromoApplicable) {
                return;
            }
            spinner.spinnerShow();

            getPromotion(vm.promoCode, vm.order).then(function () {
                if (vm.promotion.IsValid) {
                    vm.order.Promotion = vm.promotion;
                    //just let this go async.
                    rePriceOrder();
                }
            }, function (error) {
                vm.promotion = { IsValid: false, message: error.data.message, Code: vm.promoCode };
            })['finally'](function () {
                spinner.spinnerHide();
            }).then(function () {

            });
        }

        function alertExcludedItems(excludedItems, dayOfWeek) {
            var modalResult = $modal.open({
                templateUrl: 'app/itemPrompt/excludedItemsDialog.html',
                controller: 'excludedItemsDialog as vm',
                size: 'md',
                resolve: {
                    excludedItems: function () { return excludedItems; },
                    pickupDate: function () { return vm.pickupDate; },
                    dayOfWeek: function () { return dayOfWeek; }
                }
            }).result;

            return modalResult;
        }

        function alertUnavailableItems(unavaliableItems) {
            var modalResult = $modal.open({
                templateUrl: 'app/itemPrompt/unavailableItemDialog.html',
                controller: 'unavailableItemDialog as vm',
                size: 'md',
                resolve: {
                    unavailableItems: function () { return unavaliableItems; }
                }
            }).result;

            return modalResult;
        }

        function alertError(er) {
            if (er && er.data && er.data.unavailableItems && er.data.unavailableItems.length) {
                return alertUnavailableItems(er.data.unavailableItems);
            } else if (er && er.data && er.data.excludedItems && er.data.excludedItems.length) {
                return alertExcludedItems(er.data.excludedItems, er.data.dayOfWeek);
            } else {
                $log.error(er);
                return notify.dialog('An Error Occured', getErrorMessage(er) || 'Oops');
            }
        }

        function getErrorStatus(er) {
            return er && er.data && (er.data.status || er.data.Status);
        }

        function getErrorMessage(er) {
            return er && er.data && (er.data.message || er.data.Message);
        }

        function goToLocationHome() {
            if (togoorder.locationWebsite) {
                $window.location.href = togoorder.locationWebsite;
            } else {
                goHome();
            }
        }

        function rePriceOrder(getUpsellItems) {
            //debugger;
            return orderService.rePriceOrder(vm.order, vm.merchantLocation, getUpsellItems).then(function (priceResult) {
                if (priceResult) {
                    vm.offerDiscountTotal = priceResult.Order.OfferDiscount;
                    var selectedOffer = _.filter(vm.eligibleOffers, function (offer) {
                        return offer.OptedIn;
                    });
                    if (selectedOffer) {
                        selectedOffer.RewardValue = priceResult.Order.OfferDiscount;
                    }
                }
                return priceResult;
            }, function (er) {
                var pricingError = 2; //PriceCheckStatusType.PricingError = 2
                var posPricingError = 3; //PriceCheckStatusType.PosPricingError = 3
                var errorStatus = getErrorStatus(er);
                if (errorStatus === posPricingError || errorStatus === pricingError) {
                    alertError(er).then(goToLocationHome, function () {
                        var unavailableItems = (er && er.data && er.data.unavailableItems) || [];
                        _.each(unavailableItems, function (item) {
                            vm.order.deleteItemById(item.id);
                        });
                        menuService.clearMenuCache();
                        goToMenu();
                    });
                } else {
                    alertError(er);
                }
                throw er;
            });
        }

        function priceOrder() {
            return rePriceOrder(true).then(function (priceResult) {
                if (priceResult && priceResult.ItemPrompts && priceResult.ItemPrompts.length) {

                    var p = $q.when();
                    _.each(priceResult.ItemPrompts, function (itemPrompt) {

                        p = p.then(function () {
                            return openItemPrompt(itemPrompt).then(itemPromptSelected, itemPromptNotSelected);
                        }, angular.noop);
                    });
                }

                if (priceResult && priceResult.RequiredItems && priceResult.RequiredItems.length) {
                    // We can only support one right now.
                    vm.requiredItems = [priceResult.RequiredItems[0]];
                    var menuItem = vm.requiredItems[0];
                    var orderItem = cartService.findItemInCart(menuItem.Id);

                    if (!orderItem) {
                        orderItem = new OrderItem(menuItem);
                        orderItem.SortIndex = 32767;

                        orderItem._isRequiredItem = true;

                        cartService.addOrderItem(orderItem);
                    }

                    orderItem._shouldShow = true;

                    vm.item.setCurrentItem(orderItem, menuItem);
                    cartService.saveCart();
                    cartService.loadCurrent();

                    rePriceOrder();
                }
                isPriceRequestNeeded = false;
            }, function (e) {
                isPriceRequestNeeded = true;
                if (e.data.redirectOnError) {
                    throw e;
                }
            });
        }

        function openItemPrompt(itemPrompt) {
            var modalResult = $modal.open({
                templateUrl: 'app/itemPrompt/itemPromptDialog.html',
                controller: 'itemPromptDialog as vm',
                size: 'md',
                resolve: {
                    itemPrompts: function () { return [itemPrompt]; }
                }
            }).result;

            return modalResult;
        }

        function itemPromptSelected(itemPrompt) {
            if (itemPrompt.HasModifiers) {
                cartService.addItemToNextCart({
                    id: itemPrompt.Id,
                    sectionId: itemPrompt.SectionId
                });
                goToMenu();
                return $q.reject();
            }

            var menuItem = menuService.getItemById(menu, itemPrompt.Id);
            cartService.addOrderItem(new OrderItem(menuItem));
            cartService.saveCart();

            notify.success(itemPrompt.Name + " added to cart!");

            return rePriceOrder();
        }

        function itemPromptNotSelected() {
            //nothing.
        }

        function isBusy() {
            return api.getPendingRequestCount() !== 0;
        }

        function completeOrder(isValid, event) {

            let amountAppliedToNonCreditCards = common.monetize(vm.getGiftCardAppliedAmount() + vm.getMealPlanAppliedAmount());
            if (!vm.isCreditCardPaymentAllowed && vm.order.Total > amountAppliedToNonCreditCards) {
                notify.dialog('Insufficient Funds!', 'You do not have sufficient funds to place this order.');
                notify.warning("Insufficient Funds! You do not have sufficient funds to place this order.");
                return;
            }

            if (isValid) {
                if (vm.posIntegrationType === 'Square' && event) {
                    handleSquarePaymentMethodSubmission(event, squareCard)
                }
                else {
                    if (event && event.wallet) {
                        vm.order.Payment.DigitalWallet = {
                            DigitalWalletToken: event.token,
                            DigitalWalletType: event.tokenType,
                            CardNetwork: event.wallet.cardNetwork,
                            Last4: event.wallet.cardDetails
                        };
                    }

                    vm.checkout();
                }
            } else {
                vm.showValidation();
            }
        }

        function getYears() {
            var years = {};
            var thisYear = vm.now.year();
            for (var y = 0; y < 20; y++) {
                var year = thisYear + y;
                years['' + year] = year;
            }
            vm.years = years;
            return years;
        }

        function getMonths() {
            var months = [];
            for (var m = 0; m < 12; m++) {
                var month = m + 1;
                var monthString = (month < 10) ? '0' + month : '' + month;
                months[m] = { display: monthString + ' (' + monthAbbreviations[m] + ')', val: month };
            }
            return months;
        }

        function activate() {
            timeService.initialize();

            common.activateController([initializeData()], controllerId)
                .then(function () {

                    vm.order.DeliveryAddress = vm.deliveryAddress;
                    vm.order.TableNumber = vm.tableNumber;

                    vm.gratuityChoices = getGratuityChoices();
                    defaultGratuityChoice();

                    defaultSelectPaymentMethod();

                    $scope.$watch(function () {
                        if (vm.order.TotalForCreditCard) {
                            if (!vm.order.Payment.ExistingCreditCard.IsSelected &&
                                !vm.order.Payment.NewCreditCard.IsSelected) {
                                defaultSelectPaymentMethod();
                            }
                        } else {
                            vm.order.Payment.ExistingCreditCard.IsSelected = false;
                            vm.order.Payment.NewCreditCard.IsSelected = false;
                        }
                        updateCollectPrice();
                    });

                    if (vm.loyaltyProviderType === 3) {
                        $timeout(function () { vm.order.Payment.GiftCardPayment = vm.campusCard; }, 100);
                    }

                    initializeResizeEvent();

                    $scope.$on(events.includedItemsChanged, function () {
                        if (vm.order.pricedOrder) {
                            vm.order.pricedOrder.isInvalidated = true;
                        }
                    });
                });
        }

        function defaultSelectPaymentMethod() {
            if (!vm.paymentMethods || !vm.paymentMethods.length) {
                payWithNewCreditCard();
            } else {
                payWithDifferentCreditCard();
            }
        }

        function initializeSidebar() {
            $window.setTimeout(function () {
                $('.ui.sidebar').sidebar({ overlay: true }).show();
                if (vm.isCartInPage) {
                    $('.cart-tab').hide();
                } else {
                    $('.cart-tab').show();
                }
            }, 750);
        }

        function initializeResizeEvent() {
            // Hmmm. Hacky.
            var resizeHandler = function () {
                vm.isCartInPage = browserInfo.isSize("md") || browserInfo.isSize("lg");
                initializeSidebar();
            };

            var onResizing = _.debounce(function () {
                $timeout(function () { vm.isCartInPage = true; });
            }, 333, true);
            var onResized = _.debounce(function () {
                $timeout(resizeHandler);
            }, 333, false);

            $($window).resize(function () {
                onResizing();
                onResized();
            });
            resizeHandler();
        }

        function initializeData() {
            return $q.all([getOrder().then(function () {
                return $q.all([getDeliveryAddress(), getMenu()]);
            })]).then(getOrderDependentData).then(getOffersEligibleForRedemption);
        }

        function getMenu() {
            return menuService.getMenu(vm.order.MenuId)
                .then(function (data) {
                    menu = data;
                    vm.minimumAge = menu.MinimumAge;
                    vm.showMinimumAgeSection = !!vm.minimumAge;
                    vm.isAgeConfirmed = !vm.showMinimumAgeSection;
                    return data;
                });
        }

        function getGratuityChoices() {
            return _.map(vm.gratuityLevels, function (gratuityLevel) {
                var value = gratuityLevel.Value;
                if (gratuityLevel.IsPercentage) {
                    value = common.monetize((gratuityLevel.Value / 100) * vm.order.Subtotal);
                }
                return {
                    value: value,
                    tipSize: gratuityLevel.Value,
                    isPercentage: gratuityLevel.IsPercentage,
                    isDefault: gratuityLevel.IsDefault
                };
            });
        }

        function defaultGratuityChoice() {
            // This global setting is defined here and in order cloud orderTypeRuleEdit.js deliveryServiceDefaultTip.get()

            var otr = orderTypeRuleService.getOrderTypeRule(vm.order.OrderType, vm.order.FulfillmentType, vm.merchantLocation);

            switch (otr.DeliveryService) {
                case 1:     // 1 is door dash drive

                    vm.selectedGratuityChoice = _.find(vm.gratuityChoices, function (gc) {
                        return gc.tipSize === 15;
                    });
            }

            if (!vm.selectedGratuityChoice) {
                var defaultGratuityChoice = _.find(vm.gratuityChoices, function (gc) {
                    return gc.isDefault === true;
                })
                if (defaultGratuityChoice) {
                    vm.selectedGratuityChoice = defaultGratuityChoice;
                }
            }
        }

        /**
         * WOC-934/1170: Allow tipping even if OC setting is set to NO TIPPING **for delivery** OrderType,
         *  ONLY if a delivery OrderType **includes a Delivery Service*** (DDD),
         *  otherwise, return OC setting
         *
         *  Note: Tipping === Gratuity
         *
         *  Order Types:
         *      DineIn = 1
         *      CarryOut = 2
         *      Delivery = 4
         *      DailyDelivery = 5
         *      GroupOrder = 6
         *      DriveThru = 7
         *      Carside = 8
         *      Catering = 10
         *      Event = 20
         *
         *  Fulfillment Types:
         *      CustomerPickup = 1
         *      MerchantDelivery = 2 *Delivery...
         *      CourierDelivery = 3
         *
         * Delivery Service:
         *      1 = DDD
         *      0 = something else...
         *
         *  IMPORTANT: the tip is converted into a Surcharge in the database
         *      goes to uber/dd driver not because this is *NOT* a tip for the merchant
         *      REF: ToGoTechnologies.Infrastructure.Services.CaptureOrder
         *     
         */
        function isDeliveryGratuityOverride() {
            var otr = orderTypeRuleService.getOrderTypeRule(vm.order.OrderType, vm.order.FulfillmentType, vm.merchantLocation);
            // Check for Delivery Fulfillment type *and* DeliveryService (only)
            if (vm.order.FulfillmentType === 2 && otr.DeliveryService === 1) {
                return true;
            }
            return vm.merchantLocation.IsTipAllowed;
        }

        function payWithNewCreditCard() {
            vm.isCardScanSupported = window.togoorder.isCardScanSupported;

            vm.order.Payment.ExistingCreditCard.IsSelected = false;
            vm.order.Payment.NewCreditCard.IsSelected = true;
            if (!vm.cardTypes) {
                spinner.spinnerShow();
                enumService.getCreditCardTypes().then(function (cardTypes) {
                    vm.cardTypes = cardTypes;
                    spinner.spinnerHide();
                });
            }
            if (vm.posIntegrationType === 'Square') {
                vm.sqFormCnt++;
                if (vm.sqFormCnt <= 1) {
                    // call the cdn needed here? 
                    //    paymentForm.build();
                }
            }
        }

        function getDeliveryAddress() {
            if (orderTypeWorkflowService.isDelivery(vm.order.OrderType, vm.order.FulfillmentType)) {
                return deliveryService.getDeliveryAddress()
                    .then(function (deliveryAddress) {
                        vm.deliveryAddress = deliveryAddress;
                    }, function () { /*no problem*/ });
            }
            return $q.when();
        }

        function useDeliveryAddressForBillingAddress() {
            vm.isDeliveryAddressUsedForBilling = !vm.isDeliveryAddressUsedForBilling;

            if(!vm.isDeliveryAddressUsedForBilling){
                vm.order.Payment.CardHolder.Address = null;
            }
            else{
                vm.order.Payment.CardHolder.Address = angular.copy(vm.deliveryAddress);

                var state = vm.states.find((el)=> el.name === vm.order.Payment.CardHolder.Address.State);
                if(state){
                    vm.order.Payment.CardHolder.Address.State = state.value;
                }
            }
        }

        function payWithDifferentCreditCard() {
            vm.order.Payment.ExistingCreditCard.IsSelected = true;
            vm.order.Payment.NewCreditCard.IsSelected = false;
        }

        function getOrder() {
            return cartService.loadCurrent().then(function () {
                var cart = cartService.getCart();
                if (!cart.order) {
                    vm.hasCurrentOrder = false;
                    return $q.reject("No Order");
                } else if (!cart.order.Items.length) {
                    vm.hasCurrentOrder = false;
                } else {
                    vm.hasCurrentOrder = true;
                }

                vm.order = cart.order;
                vm.order.Gratuity = null;

                vm.order.Payment = new Payment();

                return cart.order;
            });
        }

        function wipeOffers() {
            if (vm.loyaltyProviderType === 6) {
                return loyaltyService.wipeOffers() //WOC-1386 Square loyalty offers must be re-selected
                    .then(_.noop, function (er) {
                        alertError(er);
                        throw er;
                    });
            }
        }

        function getOffersEligibleForRedemption() {
            var providersWithOffers = [4, 3, 6, 7, 8, 9];
            vm.hasOffers = (providersWithOffers.indexOf(vm.loyaltyProviderType) > -1);
            if (vm.hasOffers && vm.user && vm.bestLoyalty && vm.bestLoyalty.AccountNumber) {
                spinner.spinnerShow();
                return loyaltyService.getOffersEligibleForRedemption({
                    merchantId: vm.merchantLocation.MerchantId,
                    cardNumber: vm.bestLoyalty.AccountNumber,
                    cartOrder: vm.order
                }).then(function (data) {
                    _.each(data.Offers,
                        function (v, k) {
                            if (vm.loyaltyProviderType === 3)
                                v.OfferId = 'cart' + v.OfferId;
                            v.OptedIn = (v.Optable || vm.limitOneOffer) ? v.OptedIn : true;
                        });
                    vm.eligibleOffers = _.filter(data.Offers, function (offer) {
                        if (vm.loyaltyProviderType === 3 || vm.loyaltyProviderType === 2 || vm.loyaltyProviderType == 7) {
                            return vm.order.Subtotal + vm.order.PromoSavings - vm.order.OfferDiscount >= offer.Threshold && vm.order.Subtotal >= offer.RewardValue;
                        }
                        return true;
                    });

                    // added this because do not want to change the behaviour of other offer types. 
                    //if (vm.loyaltyProviderType != 7) {
                    applyOfferDiscounts();
                    //}

                    return rePriceOrder().then(function () {
                        if (vm.loyaltyProviderType != 7) {
                            applyOfferDiscounts();
                        }
                    });
                })["finally"](function () {
                    spinner.spinnerHide();
                });
            }
            return false;
        }

        function applyOfferDiscounts() {
            var pricingOverrideProviders = [8];
            // BBI??? if (pricingOverrideProviders.indexOf(vm.loyaltyProviderType) > -1) return;
            vm.offerDiscountTotal = 0;
            _.each(vm.eligibleOffers,
                function (v, k) {

                    if ((!v.Optable && !vm.limitOneOffer) || v.OptedIn === true) {
                        // if the discount is a percentage
                        if (vm.loyaltyProviderType == 7 && v.OfferRewardType == 2) {

                            v.RewardValue = vm.order.Subtotal * v.RewardValue;
                        }


                        vm.offerDiscountTotal += v.RewardValue;
                    }
                });

            vm.order.SelectedOfferAmount = vm.offerDiscountTotal;
            vm.order.Payment.OfferPayment = vm.order.SelectedOfferAmount ? {
                AccountNumber: vm.bestLoyalty.AccountNumber
            } : null;
        }

        function toggleOffer(offer) {
            vm.order.invalidatePricedOrderOffers();
            return optWithReprice(offer);
        }

        function optWithReprice(offer) {
            return optOffer(offer).then(function () {
                return getOffersEligibleForRedemption();
            });
        }

        function optOffer(offer) {
            if (offer.Optable || vm.limitOneOffer === true) {
                spinner.spinnerShow();
                if (offer.OptedIn) {

                    var payment = vm.order.Payment;
                    var loyaltyPayment = (payment.LoyaltyPayment)
                        ? {
                            LoyaltyAccountNumber: payment.LoyaltyPayment.AccountNumber,
                            LoyaltyPaymentAmount: vm.order.LoyaltyPaymentAmount
                        }
                        : null;
                    var offerPayment = (payment.OfferPayment) ? {
                        LoyaltyAccountNumber: payment.OfferPayment.AccountNumber,
                        OfferPaymentAmount: vm.order.OfferPaymentAmount
                    } : null;

                    var optInRequest = {
                        CardNumber: vm.bestLoyalty.AccountNumber,
                        OfferId: offer.OfferId,
                        MerchantId: vm.merchantLocation.MerchantId,
                        Order: vm.order,
                        CartPromotion: vm.order.Promotion,
                        LoyaltyPayment: loyaltyPayment,
                        OfferPayment: offerPayment
                    };

                    return loyaltyService.optInToOffer(optInRequest)
                        .then(
                            function () {
                                applyOfferDiscounts();
                                return true;
                            },
                            function (response) {
                                $log.error('opt-in error: ' + response.data);
                                return notify.dialog('Offer not eligible', 'Please add eligible item(s) to redeem this offer');
                            })
                    ['finally'](function () { spinner.spinnerHide(16); });
                } else {
                    return loyaltyService.optOutOfOffer({ CardNumber: vm.bestLoyalty.AccountNumber, OfferId: offer.OfferId, MerchantId: vm.merchantLocation.MerchantId })
                        .then(function () { applyOfferDiscounts(); return true; })
                    ['finally'](function () { spinner.spinnerHide(17); });
                }
            }
            return $q.when(1);
        }

        function goHome() {
            $state.go('locationHome');
        }

        $scope.sqPaymentAdded = true;
        $scope.sqPaymentAddedbtn = true;

        $scope.loadSquarePaymentInfo = function () {
            $scope.sqPaymentAdded = false;
            $scope.sqPaymentAddedbtn = true;
            //Object.assign(vm.order, { Nonce: localStorage.getItem("Nonce") });
            vm.order.Payment.NewCreditCard.isSelected = true;
            vm.order.Payment.NewCreditCard.CreditCard.CardType = vm.squareCardData.cardBrand;
            vm.order.Payment.NewCreditCard.CreditCard.CardNumber = storageService.get("Nonce");
            vm.order.Payment.NewCreditCard.CreditCard.LastFourDigits = vm.squareCardData.lastFour;
            vm.order.Payment.NewCreditCard.CreditCard.ExpirationMonth = vm.squareCardData.expMonth;
            vm.order.Payment.NewCreditCard.CreditCard.ExpirationYear = vm.squareCardData.expYear;
            vm.order.Payment.CardHolder.FirstName = vm.order.Customer.FirstName;
            vm.order.Payment.CardHolder.LastName = vm.order.Customer.LastName;
            vm.order.Payment.CardHolder.Address = new Address();
            vm.order.Payment.CardHolder.Address.Zipcode = vm.squareCardData.billingPostalCode;

            $scope.$apply();
        };

        angular.element(document).ready(function () {
            if (togoorder.PosIntegrationType === "Square" && vm.order.TotalForTenders !== 0.0) {
                loadSqForm();
            };
        });

        // Function to handle initialization of iframe for CollectJS
        function loadCollectJs() {
            if (vm.isDigitalWalletProfileGoogle || vm.isDigitalWalletProfileApple) {
                setTimeout(() => {
                    const iframe = vm.getCollectJSiframe();
                    // monitor the iframe load event to ensure the callback is listening on iframe url changes
                    if (iframe) {
                        vm.ensureCollectJsCallbackListening();
                        iframe.addEventListener('load', () => {
                            vm.ensureCollectJsCallbackListening();
                        });
                    }
                }, 1);
            }
        }

        // Centralize the logic for referencing the CollectJS iframe
        function getCollectJSiframe() {
            return window.document.getElementById('collectjs-iframe');
        }

        // Update the price used by CollectJS to ensure it matches the order total
        function updateCollectPrice() {
            const iframe = vm.getCollectJSiframe();
            if (iframe) {
                const iframeUrl = new URL(iframe.src);
                const collectJSPrice = iframeUrl.searchParams.get('price');
                const properPrice = vm.order.TotalForCreditCard.toString();

                if (collectJSPrice !== properPrice) {
                    iframeUrl.searchParams.set('price', properPrice);
                    iframeUrl.searchParams.set('location', vm.getLocationIdFromUrl());
                    iframe.src = iframeUrl;
                }
            }
        }

        // A clear function for adding an event listener to the CollectJS iframe window
        function addCollectJsEventListener(eventType, callback) {
            vm.getCollectJSiframe().contentWindow.addEventListener(eventType, callback);
        }

        // Use a staticly defined function to ensure duplicate listeners are not added
        function collectJsCallback({ detail: response }) {
            if (!vm.isDigitalWalletPaymentSupported()) {
                console.warn("Unexpected Digital Wallet payment");
                return;
            }

            const newCreditCardFormRequired = $("#new-payment-div [required]");
            const existingCreditCardFormRequired = $("#existing-payment-div [required]");
            // disable form validation for the credit card form because user is making digital wallet payment
            newCreditCardFormRequired.prop("required", false);
            existingCreditCardFormRequired.prop("required", false);
            // check form validity to ensure the form is valid before submitting
            const isValid = form.checkValidity();
            // re-enable form validation for the credit card form in case user tries to click checkout button
            // instead of using digital wallet payment
            newCreditCardFormRequired.prop("required", true);
            existingCreditCardFormRequired.prop("required", true);

            vm.completeOrder(isValid, response);
        }

        // Function to ensure we have a listener for the CollectJS callback
        function ensureCollectJsCallbackListening() {
            vm.addCollectJsEventListener('collectjs-response', vm.collectJsCallback);
        }

        function isDigitalWalletPaymentSupported() {
            if ((vm.isDigitalWalletProfileGoogle || vm.isDigitalWalletProfileApple) == false) {
                return false;
            }

            if (vm.paymentTime !== 'now') {
                return false;
            }

            if (vm.isFutureOrderChargedImmediately) {
                return true;
            }

            if (!vm.pickupDate || !vm.pickupDate.date) {
                return true;
            }

            return vm.pickupDate.date.isSame(new Date(), 'day');
        }

        function getLocationIdFromUrl() {
            return window.location.pathname.split('/')[2];
        }

        function loadSqForm() {
            var url = "./app/Square/sqpaymentform-basic.js";
            GetLocationPosSettings(togoorder.locationId, url);
        }

        function GetLocationPosSettings(locationId, url) {
            api.post('api/Square/GetSquareLocationInfo', { 'locationId': locationId })
                .then(function (response) {
                    togoorder.squareSettings.squareApplicationId = response.data.SquareApplicationId;
                    togoorder.squareSettings.squareLocationId = response.data.SquareLocationId;
                    loadSqScript(url);
                },
                    function (response) {
                        console.log("Error occured");
                    });
        }

        function loadSqScript(url) {
            var head = document.getElementsByTagName('head')[0];
            var script = document.createElement('script');
            script.type = 'text/javascript';
            script.src = url;
            head.appendChild(script);
        }

        function goToMenu() {
            $stateParams.merchantLocationId = vm.order.LocationId;
            $stateParams.orderType = vm.order.OrderType;
            $stateParams.fulfillmentType = vm.order.FulfillmentType;
            $stateParams.menuId = vm.order.MenuId;
            var cart = cartService.getCart();
            $stateParams.day = cart.day;
            menuWorkflowService.goToSections($stateParams);
        }

        function getOrderDependentData() {
            return $q.all([
                getMerchantLocation()
                    .then(getOrderTypeRule)
                    .then(function () {
                        return $q.all([
                            vm.isAGuest ? $q.when() : getUser().then(wipeOffers),
                            getNow().then(getYears).then(initializeOrderType).then(configurePickupDate).then(priceOrder)
                        ]);
                    },
                        function () {
                            goHome();
                        }),
                vm.isAGuest ? $q.when() : getPaymentMethods()
            ]).then(function () {
                if (!vm.isFutureDayOrderingAllowed) {
                    validate(vm.now);
                }
            }, function (er) {
                goToMenu();
            });
        }

        function initializeOrderType() {
            orderTypeWorkflowService.getDisplayData(vm.order.OrderType, vm.order.FulfillmentType, vm);

            vm.payLaterText = orderTypeWorkflowService.getPayLaterText(vm.order.OrderType, vm.order.FulfillmentType);
            vm.deliveryTemplate = orderTypeWorkflowService.getDisplayTemplate(vm.order.OrderType, vm.order.FulfillmentType);
            vm.pickupDateTimePrefix = orderTypeWorkflowService.getPickupDateTimePrefix(vm.order.OrderType, vm.order.FulfillmentType);
            vm.isPickupDateSelectable = orderTypeWorkflowService.isPickupDateSelectable(vm.order.OrderType, vm.order.FulfillmentType);
            vm.useCalendar = orderTypeWorkflowService.isCalendarRequired(vm.order.OrderType, vm.order.FulfillmentType);
            vm.orderTypePrompt = orderTypeWorkflowService.getPromptTemplate(vm.order.OrderType, vm.order.FulfillmentType);
            vm.orderTypeDisplay = orderTypeWorkflowService.getOrderTypeDescription(vm.order.OrderType, vm.order.FulfillmentType);

            selectedTimeNotAvailableError = "The selected " + (vm.pickupDateTimePrefix || "").toLowerCase() + " time is no longer available. Please select a later time.";

            if (vm.isFutureDayOrderingAllowed) {
                vm.noPickupTimesAvailableText = "Sorry. There are no available " + (vm.pickupDateTimePrefix || "").toLowerCase() + " times for the selected day.";
            } else {
                vm.noPickupTimesAvailableText = "Sorry. There are no available " + (vm.pickupDateTimePrefix || "").toLowerCase() + " times remaining for today for " + vm.orderTypeDisplay + ".";
            }
        }

        function getOrderTypeRule() {
            var otr = orderTypeRuleService.getOrderTypeRule(vm.order.OrderType, vm.order.FulfillmentType, vm.merchantLocation);

            orderTypeWorkflowService.setOrderTypeRule(otr);

            var isDeferredPaymentAllowed = otr.IsDeferredPaymentAllowed;
            var isPayNowAllowed = otr.IsPayNowAllowed;

            vm.showPaymentTimeAllowed = isDeferredPaymentAllowed && isPayNowAllowed;
            vm.paymentTime = isPayNowAllowed ? 'now' : 'later';

            vm.isFutureDayOrderingAllowed = otr.IsFutureDayOrderingAllowed;
            vm.isCreditCardPaymentAllowed = otr.IsCreditCardPaymentAllowed;
            vm.isFutureOrderChargedImmediately = otr.CardChargeTiming === 0;

            return $q.when();
        }

        function isNoTimesAvailable() {
            return !isTimesAvailable();
        }

        function isTimesAvailable() {
            var isPickupDateAPickupDateTime = !!(vm.pickupDate && vm.pickupDate.retrieveTimeChoices);
            //if (vm.isFutureDayOrderingAllowed) {
            //	return !isPickupDateAPickupDateTime || vm.pickupDate.retrieveTimeChoices().length;
            //} else {
            return isPickupDateAPickupDateTime && vm.pickupDate.retrieveTimeChoices().length;
            //}
        }

        function getNow() {
            return timeService.getNowForTimeZoneId(vm.merchantLocation.TimeZoneId)
                .then(function (data) {
                    vm.now = data.localNow;
                    vm.localCalendarDate = data.localCalendarDate;
                });
        }

        function configurePickupDate() {
            var orderTypeRule = _.find(vm.merchantLocation.OrderTypeViewModels, function (ot) {
                return ot.OrderType == vm.order.OrderType && ot.FulfillmentType == vm.order.FulfillmentType;
            });
            //if (!orderTypeRule) {
            //    console.log('no OrderTypeViewModel found for OrderType: ' + vm.order.OrderType);
            //}

            var getHoursService = function (isDoorDashDelivery, deliveryServiceDeliveryTime, deliveryServiceTimeToDeliver) {
                return hoursService.getInstance(vm.merchantLocation.Id, vm.waitTimeInMinutes, vm.order.MenuId, vm.merchantLocation.TimeZoneOffset, isDoorDashDelivery, deliveryServiceDeliveryTime, deliveryServiceTimeToDeliver).then(function (serviceInstance) {
                    vm.hoursServiceInstance = serviceInstance;
                    vm.hoursServiceInstance.setNow(vm.now);

                    setDayChoices();
                    return vm.hoursServiceInstance;
                });
            };

            var waitTimeDefer = $q.defer();

            vm.waitTimeInMinutes = orderTypeRule.WaitTimeInMinutes; //moving this here because the wait time is now the same for delivery or otherwise
            if (vm.order.Total >= orderTypeRule.LargeOrderThreshold && orderTypeRule.LargeOrderThreshold > 0)
                vm.waitTimeInMinutes = orderTypeRule.LargeOrderWaitTimeInMinutes;

            // Calculate lead time for delivery service if configured, otherwise use wait times from order type rule
            if (orderTypeRule.DeliveryService) {

                // Pickup time is now or next earliest allowed pickup time + the customer's wait time
                let otrWaitTime = vm.order.Total >= orderTypeRule.LargeOrderThreshold && orderTypeRule.LargeOrderThreshold > 0
                    ? orderTypeRule.LargeOrderWaitTimeInMinutes
                    : orderTypeRule.WaitTimeInMinutes;
                vm.orderTypeRuleWaitTimeInMinutes = otrWaitTime;
                var pickupTimeUtc = moment().utc().add(otrWaitTime, 'minutes');

                // Temp instance to take advantage of its time calculations
                getHoursService().then(function () {
                    var locationStartTime = getLocationStartTime(pickupTimeUtc);

                    vm.pickupDate = null;
                    locationStartTime.add(otrWaitTime, 'minutes');

                    pickupTimeUtc.utcOffset(locationStartTime._offset); //Use offset from location start time for accurate comparison
                    // If pick-up time is before earliest pickup time allowed, use latter instead
                    pickupTimeUtc = pickupTimeUtc <= locationStartTime ? locationStartTime : pickupTimeUtc;

                    var addressTo = vm.deliveryAddress || vm.order.Payment.CardHolder.Address;
                    deliveryService.getDeliveryServiceEstimate(orderTypeRule, addressTo, vm.order.Total, null, pickupTimeUtc)
                        .then(function (estimate) {
                            var estDeliveryUtc = moment.utc(estimate.deliveryTimeUtc);
                            var estPickupUtc = moment.utc(estimate.pickupTimeUtc);
                            var waitTimeRaw = moment.duration(estDeliveryUtc.diff(estPickupUtc)).asMinutes();
                            vm.deliveryServiceTimeToDeliver = Math.ceil(waitTimeRaw);
                            //vm.waitTimeInMinutes = orderTypeRule.WaitTimeInMinutes;                      

                            vm.deliveryServicePickupTimeUtc = estimate.pickupTimeUtc;
                            vm.deliveryServiceDeliveryTime = estimate.deliveryTimeUtc; //Door Dash delivery time for TODAY
                            waitTimeDefer.resolve();
                        },
                            function () {
                                alertError({ message: 'delivery service area now unavailable - this should not happen' });
                                waitTimeDefer.reject();
                            });
                });
            } else {


                waitTimeDefer.resolve();
            }

            return waitTimeDefer.promise.then(function () {
                if (!orderTypeWorkflowService.isDelivery(orderTypeRule.OrderType, orderTypeRule.FulfillmentType)) {
                    return getHoursService(false);
                }

                return deliveryService.getCurrentDeliveryZoneInfo(orderTypeRule).then(function (zoneInfo) {
                    if (zoneInfo && zoneInfo.zone && !orderTypeRule.DeliveryService)
                        vm.waitTimeInMinutes += (zoneInfo.zone.MinimumDeliveryTimeInMinutes || 0);
                    var isDDD = orderTypeRule.OrderType == 4 && orderTypeRule.DeliveryService == 1;
                    return getHoursService(isDDD, vm.deliveryServiceDeliveryTime, vm.deliveryServiceTimeToDeliver);
                });
            });
        }

        function getLocationStartTime(pickupTimeUtc) {
            if (vm.pickupDate && vm.pickupDate.getNextAvailableTime()) {
                return vm.pickupDate.getNextAvailableTime();
            }
            return pickupTimeUtc;
        }

        function setDayChoices() {
            var dayChoices = vm.hoursServiceInstance.getAvailablePickupDateTimesForMenu(vm.now);

            if (!vm.useCalendar) {
                vm.dayChoices = dayChoices;
                if (vm.dayChoices.length) {
                    var newPickupDate;
                    if (vm.pickupDate) {
                        newPickupDate = _.find(vm.dayChoices, function (dc) {
                            return dc.date.isSame(vm.pickupDate.date);
                        });
                        vm.pickupDate = newPickupDate || null;
                    } else {
                        newPickupDate = _.find(vm.dayChoices, function (dc) {
                            var isSame = vm.localCalendarDate.isSame(dc.date, 'day');
                            return isSame;
                        });
                        vm.pickupDate = newPickupDate || null;
                    }

                    if (vm.pickupDate) {
                        var timeChoices = vm.pickupDate.retrieveTimeChoices();
                        if (timeChoices.length) {
                            vm.order.PickupDateTime = timeChoices[0].value;
                        } else {
                            vm.pickupDate = null;
                        }
                    }
                    if (vm.dayChoices.length > 2) {
                        vm.dayChoices.push({
                            getDisplayDay: function () {
                                return "(See More Dates...)";
                            },
                            showCalendar: true,
                            date: togoorder.utcNow
                        });
                    }

                    vm.dayChoices = removeDuplicateDatesFromDropdown(vm.dayChoices);

                } else {
                    vm.useCalendar = true;
                    setDayChoices();
                    return;
                }
            } else {
                setPickupCalendarDate(togoorder.utcNow);
            }
            if (!vm.isFutureDayOrderingAllowed && !vm.pickupDate) {
                notify.dialog('So Sorry...', vm.noPickupTimesAvailableText);
                throw new common.exceptions.NormalException(vm.noPickupTimesAvailableText);
            }
        }

        function validate(now) {
            return $q.all([now || getNow()]).then(function () {
                if (vm.hoursServiceInstance) {
                    vm.hoursServiceInstance.setNow(vm.now);
                } else {
                    return;
                }

                if (!vm.isPickupDateSelectable) {
                    return $q.when(1);
                } else {
                    if (!isDeliveryServiceTimeGood || !vm.hoursServiceInstance.isPickupDateAvailable(vm.order.PickupDateTime, vm.pickupDate, vm.now)) {
                        setDayChoices();
                        var error = new Error(selectedTimeNotAvailableError);
                        error.showAlert = true;
                        throw error;
                    }
                }

                return vm.hoursServiceInstance.validatePickupTime(vm.order.PickupDateTime)
                    .then(function (data) {
                        return true;
                    }).then(function () {
                        return validateOffers();
                    }).catch(function (er) {
                        if (er.name === 'TimeSlotNotAvailable') {
                            notify.dialog('Choose a Different Pickup Time', 'Wow. That is apparently a popular pickup time and is no longer available. Please choose another time');
                            setDayChoices();
                        } else if (er.name === 'OffersInvalid') {
                            notify.dialog('Please add eligible item(s) to redeem the offers', er.message);
                        } else {
                            alertError(er);
                        }
                        throw er;
                    });
            });
        }

        function OffersInvalidException(message) {
            this.name = 'OffersInvalid';
            this.message = message;
        }

        function validateOffers() {
            var eligibleOptedOfferIds = [];
            _.each(vm.eligibleOffers, function (offer, key) {
                if (!offer.Optable || offer.OptedIn) {
                    eligibleOptedOfferIds.push(offer.OfferId);
                }
            });

            if (!eligibleOptedOfferIds.length || vm.loyaltyProviderType !== 9)
                return $q.when(true);

            var payment = vm.order.Payment;
            var loyaltyPayment = (payment.LoyaltyPayment)
                ? {
                    LoyaltyAccountNumber: payment.LoyaltyPayment.AccountNumber,
                    LoyaltyPaymentAmount: vm.order.LoyaltyPaymentAmount
                }
                : null;
            var offerPayment = (payment.OfferPayment) ? {
                LoyaltyAccountNumber: payment.OfferPayment.AccountNumber,
                OfferPaymentAmount: vm.order.OfferPaymentAmount
            } : null;

            var validateRequest = {
                AccountNumber: vm.bestLoyalty.AccountNumber,
                OfferIds: eligibleOptedOfferIds,
                Order: vm.order,
                CartPromotion: vm.order.Promotion,
                LoyaltyPayment: loyaltyPayment,
                OfferPayment: offerPayment
            };

            return loyaltyService.validateOffers(validateRequest)
                .then(
                    function (response) {
                        return true;
                    },
                    function (response) {
                        $log.error('offer validation error: ' + response.data);
                        throw new OffersInvalidException(response.data);
                    });
        }

        function placeOrder() {
            if (!vm.isPickupDateSelectable) {
                vm.order.PickupDateTime = vm.now.clone().add(vm.waitTimeInMinutes, 'minutes');
            }

            let amountAppliedToNoCreditCard = common.monetize(vm.getGiftCardAppliedAmount() + vm.getMealPlanAppliedAmount());
            if (!vm.isCreditCardPaymentAllowed && vm.order.Total > amountAppliedToNoCreditCard) {
                notify.dialog('Insufficient Funds!', 'You have insufficient funds for this order.');
                notify.warning("Insufficient Funds! You have insufficient funds for this order.");
                return $q.when();
            }

            var eligibleOptedOffers = [];
            _.each(vm.eligibleOffers, function (offer, key) {
                if (!offer.Optable || offer.OptedIn) {
                    eligibleOptedOffers.push(offer.OfferId);
                }
            });

            return orderService.placeOrder(vm.order, vm.order.Payment, vm.merchantLocation, vm.paymentTime !== 'now', vm.pickupDate, eligibleOptedOffers, vm.deliveryServicePickupTimeUtc)
                .then(function (result) {

                    $log.info(result);

                    //if (!vm.isAGuest && eligibleOptedOffers && eligibleOptedOffers.length) {
                    //    loyaltyService.wipeOffers()
                    //        .then(_.noop, function (er) {
                    //            alertError(er);
                    //            throw er;
                    //        });
                    //}

                    if (vm.merchantLocation.IsPosIntegrationFailToCustomer) {
                        $state.go('waitForOrderProgress');
                    } else {
                        $state.go('thankYou');
                    }
                }, function (er) {
                    alertError(er);
                    throw er;
                });
        }

        function checkout() {
            vm.checkingOut = true;
            spinner.spinnerShow();

            var onError = function (er) {

                if (er.data && (er.data.status >= 1 && er.data.status <= 5)) {
                    // Reset credit card inputs
                    if (vm.order.Payment.NewCreditCard && vm.order.Payment.NewCreditCard.CreditCard) {
                        vm.order.Payment.NewCreditCard.CreditCard = {};
                        vm.order.Payment.CardHolder = {};
                    }
                }

                spinner.spinnerHide(18);
                placeOrderOnce = _.once(placeOrder);
                vm.checkingOut = false;
                throw er;
            };

            validate().then(function () {
                if (!vm.order.pricedOrder || vm.order.pricedOrder.isInvalidated) {
                    return rePriceOrder();
                } else {
                    return true;
                }
            }).then(function () {
                placeOrderOnce().then(_.noop, onError);
            }, onError);
        }

        function getPaymentMethods() {
            return userService.getPaymentMethods(vm.order.LocationId)
                .then(function (data) {
                    vm.paymentMethods = data;
                    return data;
                });
        }

        function getUser(force) {
            return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId, force === true)
                .then(function (user) {
                    vm.user = user;
                    vm.order.Customer.PhoneNumber.Number = user.CallbackNumber;
                    vm.order.Customer.VehicleColor = user.VehicleColor;
                    vm.order.Customer.VehicleModel = user.VehicleModel;
                    vm.order.Customer.EmailAddress = user.Email;
                    vm.order.Customer.LastName = user.LastName;
                    vm.order.Customer.FirstName = user.FirstName;
                    vm.order.Customer.GuestEmailOptIn = null;
                    vm.order.Customer.Id = user.Id;
                    vm.showMealPlans = menu.AllowMealPlan && vm.user.MealPlans && vm.user.MealPlans.length;
                    if (vm.showMealPlans) {
                        vm.setMealPlanPayment(vm.user.MealPlans[0]);
                    }
                    vm.campusCard = vm.user.GiftCards && vm.user.GiftCards.length && vm.user.GiftCards[0].IsPrimary ? vm.user.GiftCards[0] : null;
                    vm.showGiftCards = vm.campusCard
                        ? vm.user.GiftCards.length > 1
                        : vm.user.GiftCards && vm.user.GiftCards.length;
                    vm.bestLoyalty = vm.user.Loyalties && vm.user.Loyalties.length && vm.user.Loyalties[0];

                    //if (vm.user.Loyalties.length) {
                    //	var selectedLoyalty = _.find(vm.user.Loyalties, function (loyalty) {
                    //		return !!loyalty.RewardPointsSummary.MonetaryWorth;
                    //	});
                    //	vm.order.Payment.LoyaltyPayment = selectedLoyalty;
                    //}
                });
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(vm.order.LocationId)
                .then(function (data) {
                    vm.merchantLocation = data;
                    vm.loyaltyProviderType = vm.merchantLocation.LoyaltyProfile && vm.merchantLocation.LoyaltyProfile.LoyaltyProviderType;
                    vm.mealPlanProviderType = vm.merchantLocation.MealPlanProfile && vm.merchantLocation.MealPlanProfile.MealPlanProviderType;

                    if (vm.merchantLocation.GiftCardProfile) {
                        vm.giftCardProviderType = vm.merchantLocation.GiftCardProfile.GiftCardProviderType;
                        vm.order.giftCardProviderType = vm.giftCardProviderType;                            //I know this is redundant, but I didn't know how else to get the order to be aware of giftcard provider type
                        vm.canApplyDifferentGiftCardAmount = vm.merchantLocation.GiftCardProfile.IsCustomAmountAllowed;
                        vm.mustApplyGiftCard = vm.merchantLocation.GiftCardProfile.IsMustUse;
                        vm.showAddGiftCardPin = vm.merchantLocation.GiftCardProfile.RequirePin;
                    }
                    vm.isEmailRequired = vm.merchantLocation.IsGuestEmailRequired;
                    vm.isNameRequired = vm.merchantLocation.IsGuestNameRequired;
                    vm.isPhoneRequired = vm.merchantLocation.IsGuestPhoneRequired;
                    vm.limitOneOffer = vm.loyaltyProviderType === 3 || vm.loyaltyProviderType === 6 || vm.loyaltyProviderType === 8 || vm.loyaltyProviderType === 9;
                    vm.showAddGiftCard = (vm.giftCardProviderType === 5 // 5 is GiftCardProviderType.Svs 
                        || vm.giftCardProviderType === 6 // 6 is GiftCardProviderType.Ackroo
                        || vm.giftCardProviderType === 7 // 7 is GiftCardProviderType.Square
                        || vm.giftCardProviderType === 1 // 1 is GiftCardProviderType.Focus
                        || vm.giftCardProviderType === 9 // 9 is GiftCardProviderType.Factor4
                        || vm.giftCardProviderType === 2 //2 is GiftCardProviderType.Paytronix
                        || vm.giftCardProviderType === 10);//10 is GiftCardProviderType.Valutec
                    //If it's paytronix mask the inputs
                    vm.isGiftCardInputMasked = vm.giftCardProviderType === 2;
                    vm.gratuityLevels = vm.merchantLocation.GratuityLevels.filter((l) => l.Value !== 0);

                    if (vm.gratuityLevels.length === 0 && vm.merchantLocation.GratuityLevels.length === 0) {
                        vm.gratuityLevels = [
                            {
                                Value: 10,
                                IsPercentage: true,
                                IsDefault: false
                            },
                            {
                                Value: 15,
                                IsPercentage: true,
                                IsDefault: false
                            },
                            {
                                Value: 20,
                                IsPercentage: true,
                                IsDefault: false
                            },
                            {
                                Value: 25,
                                IsPercentage: true,
                                IsDefault: false
                            }]
                    }

                    vm.gratuityLevels.sort((a, b) => (a.Value > b.Value) ? 1 : ((b.Value > a.Value) ? -1 : 0));

                    return data;
                });
        }

        function onRecaptchaCreate(widgetId) {
            vm.recaptchaId = widgetId;
            $log.debug(`recaptcha widget created with id ${widgetId}`);
        };

        function showCartSidebar() {
            $('.ui.sidebar').sidebar('show');
            vm.isSidebarVisible = true;
        }

        function hideCartSidebar() {
            $('.ui.sidebar').sidebar('hide');
            vm.isSidebarVisible = false;
        }

        function showValidation() {
            vm.isValidationVisible = true;
            notify.dialog('Almost Perfect!', 'Please review the sections marked with a star.');
            notify.warning("Almost Perfect! Please review the sections marked with a star.");
        }

        function getTopCssClasses() {
            if (!vm.orderTypeDisplay || !vm.order) {
                return undefined;
            }
            return [vm.orderTypeDisplay && vm.safeClassName(vm.orderTypeDisplay + '-Checkout'), 'menu-' + vm.order.MenuId];
        }

        function getItemCount() {
            var cart = cartService.getCart();
            return (cartService.getItems(cart.order) || []).length;
        }

        function removeDuplicateDatesFromDropdown(dayChoices) {
            dayChoices = (dayChoices.reduce((filter, current) => {
                var dk = filter.find(item => item.getDisplayDay() === current.getDisplayDay());
                if (!dk) {
                    return filter.concat([current]);
                } else {
                    return filter;
                }
            }, []));

            return dayChoices;
        }

        function confirmNewDeliveryServiceTime(newTime) {
            var orderTypeRule = _.find(vm.merchantLocation.OrderTypeViewModels, function (ot) {
                return ot.OrderType == vm.order.OrderType && ot.FulfillmentType == vm.order.FulfillmentType;
            });
            if (!orderTypeRule) {
                $log.error('no OrderTypeViewModel found for OrderType: ' + vm.order.OrderType);
            }

            var deferred = $q.defer();

            // If a delivery service is configured, confirm that our newly chosen time works
            if (orderTypeRule.DeliveryService) {
                deliveryService.getDeliveryServiceEstimate(orderTypeRule, vm.deliveryAddress || vm.order.Payment.CardHolder.Address, vm.order.Total, newTime)
                    .then(function (estimate) {
                        // We gave them the desired delivery time and they return a pickup time - check that the pickup time jives with the wait time

                        var waitTime = vm.order.Total >= orderTypeRule.LargeOrderThreshold && orderTypeRule.LargeOrderThreshold > 0
                            ? orderTypeRule.LargeOrderWaitTimeInMinutes
                            : orderTypeRule.WaitTimeInMinutes;

                        var estPickupUtc = moment.utc(estimate.pickupTimeUtc);
                        var earliestUtc = moment.utc().add(waitTime, 'minutes');
                        $log.debug(`delivery service estimated pickup time: ${estPickupUtc}, earliest merchant pickup time: ${earliestUtc}`);

                        if (earliestUtc.isSameOrBefore(estPickupUtc)) {
                            $log.debug(`setting new delivery service estimated pickup time: ${estimate.pickupTimeUtc}`);
                            vm.deliveryServicePickupTimeUtc = estimate.pickupTimeUtc;
                            deferred.resolve(true);
                        }
                        else
                            deferred.resolve(false);
                    },
                        function () {
                            alertError({ message: 'delivery service area now unavailable during a new time selection - this should not happen' });
                            deferred.reject('delivery service area now unavailable during a new time selection - this should not happen');
                        });
            } else {
                deferred.resolve(true);
            }

            return deferred.promise;
        }
    }
})();
;
(function () {
	'use strict';

	var controllerId = 'giftCardPinPrompt';
	angular.module('common').controller(controllerId, ['$uibModalInstance', giftCardPinPrompt]);

	function giftCardPinPrompt($modalInstance) {
		var vm = this;

		vm.pin = '';
		vm.returnPin = returnPin;


		function returnPin() {
			vm.close(vm.pin);
		}
	    vm.close = $modalInstance.close;
	    vm.dismiss = $modalInstance.dismiss;

	}

})();;
(function () {
	'use strict';

	var controllerId = 'orderProgressFail';
	angular.module('main')
		.controller(controllerId, ['$scope', '$q', '$state', '$stateParams', '$window', '$timeout', 'storageService', 'common', 'merchantLocationService', 'orderService', 'orderTypeWorkflowService', 'cartService', 'timeService', 'userService', 'api', 'orderTypeRuleService', 'events', 'spinner', orderProgressFail]);

	function orderProgressFail($scope, $q, $state, $stateParams, $window, $timeout, storageService, common, merchantLocationService, orderService, orderTypeWorkflowService, cartService, timeService, userService, api, orderTypeRuleService, events, spinner) {
		var vm = this,
			locationId = togoorder.locationId;

		vm.merchantLocation = {};
		vm.orderConfirmation = {};
		vm.deliveryTemplate = '';
		vm.tableNumber = '?';
		vm.address = {};
		vm.browserTimeZoneOffset = timeService.getBrowserTimeZoneOffset();
		vm.pickupDate = {};
		vm.orderTypeDisplay = undefined;
		vm.isAGuest = userService.isAGuest();
		vm.safeClassName = common.safeClassName;
		vm.currentOrderProgressText = "";
		vm.currentOrderProgressSubtext = "";
		vm.errorMessage = undefined;
		vm.locationPhone = undefined;
		vm.success = undefined;
		vm.activated = false;

		function getOrderSummary() {
			return orderService.getOrderSummary()
				.then(function (orderConfirmation) {
					vm.orderConfirmation = orderConfirmation;
					vm.tableNumber = vm.orderConfirmation.tableNumber;
					vm.vehicleColor = vm.orderConfirmation.vehicleColor;
					vm.vehicleModel = vm.orderConfirmation.vehicleModel;
					vm.address = vm.orderConfirmation.deliveryAddress;

					return orderConfirmation;
				}, function() {
					console.log('No order summary.');
					$state.go('locationHome');
				});
		}

		function getMerchantLocation() {
			return merchantLocationService.getById(locationId)
				.then(function (data) {
					vm.merchantLocation = data;
					vm.errorMessage = vm.merchantLocation.PosIntegrationFailToCustomerMessage || "";
					if (vm.merchantLocation.PosIntegrationFailToCustomerShowLocationPhone) {
						vm.locationPhone = vm.merchantLocation.Phone || "";
					}
					return data;
				});
		}

		function clearSession() {
			$window.sessionStorage.clear();
			storageService.clear();
		}
		
		function activate() {
			clearSession();
			cartService.clear();

			spinner.spinnerShow();
			common.activateController([getMerchantLocation(), getOrderSummary()], controllerId)
				.then(function () {
					vm.activated = true;
				}, function () {
					// ?
				});
		}

		activate();
	}



})();;
(function () {
	'use strict';

	var controllerId = 'thankYou';
	angular.module('main')
		.controller(controllerId, ['$scope', '$q', '$state', '$stateParams', '$window', '$timeout', 'storageService', 'common', 'merchantLocationService', 'orderService', 'orderTypeWorkflowService', 'cartService', 'timeService', 'userService', 'api', 'orderTypeRuleService', 'events', 'notify', thankYou]);

	function thankYou($scope, $q, $state, $stateParams, $window, $timeout, storageService, common, merchantLocationService, orderService, orderTypeWorkflowService, cartService, timeService, userService, api, orderTypeRuleService, events, notify) {
		var vm = this,
			locationId = togoorder.locationId;

		vm.merchantLocation = {};
		vm.orderConfirmation = {};
		vm.deliveryTemplate = '';
		vm.tableNumber = '?';
		vm.address = {};
		vm.done = done;
		vm.browserTimeZoneOffset = timeService.getBrowserTimeZoneOffset();
		vm.pickupDate = {};
		vm.orderTypeDisplay = undefined;
		vm.isAGuest = userService.isAGuest();
		vm.safeClassName = common.safeClassName;
		vm.showImHere = false;
		vm.imHere = imHere;
		vm.currentOrderProgressText = "Thank You";

		function clearSession() {
			$window.sessionStorage.clear();
			storageService.clear();
		}

		function getMerchantLocation() {
			return merchantLocationService.getById(locationId)
				.then(function (data) {
					vm.merchantLocation = data;
					return data;
				});
		}

		function getOrderSummary() {
			return orderService.getOrderSummary()
				.then(function (orderConfirmation) {
					vm.orderConfirmation = orderConfirmation;
					vm.tableNumber = vm.orderConfirmation.tableNumber;
					vm.vehicleColor = vm.orderConfirmation.vehicleColor;
					vm.vehicleModel = vm.orderConfirmation.vehicleModel;
					vm.address = vm.orderConfirmation.deliveryAddress;

					return orderConfirmation;
				});
		}

		function done() {
			if ($.cookie('is_unlistedOnly')) {
				clearSession();
				$window.sessionStorage.setItem('is_unlistedOnly', 'true');

			} else {
				clearSession();
			}

			if (togoorder.callInjectedStrategy('cancelOrder')) {
				return;
			}

			if (togoorder.locationWebsite) {
				$window.location.href = togoorder.locationWebsite;
			} else {

				if ($.cookie('is_unlistedOnly')) {
					$window.location.href = $.cookie('unlisted_url')
				} else {
					$state.go('locationHome');
				}

			}
		}

		function imHere() {
			$window.location.href = `${togoorder.baseUrlToMainSite}/ArrivalMessage/${vm.orderConfirmation.orderUrlId}`;
		}

		function executeMobileStrategy() {
			var result = togoorder.callInjectedStrategy('onOrderComplete', vm.orderConfirmation, $q, api);
			return (result && result.result) || $q.when(1);
		}

		function activate() {
			clearSession();
			cartService.clear();

			notify.success('Order Submitted!');

			common.activateController([getMerchantLocation(), getOrderSummary().then(executeMobileStrategy)])
				.then(function () {
					var orderTypeRule = orderTypeRuleService.getOrderTypeRule(vm.orderConfirmation.orderType, vm.orderConfirmation.fulfillmentType, togoorder.merchantLocation);
					orderTypeWorkflowService.setOrderTypeRule(orderTypeRule, !!vm.orderConfirmation.vehicleModel);

					vm.deliveryTemplate = orderTypeWorkflowService.getDisplayTemplate(vm.orderConfirmation.orderType, vm.orderConfirmation.fulfillmentType);

					vm.orderTypeDisplay = orderTypeWorkflowService.getOrderTypeDescription(vm.orderConfirmation.orderType, vm.orderConfirmation.fulfillmentType);

					var takeoutCurbsideDisplay = orderTypeWorkflowService.getTakeoutCurbsideDescription(vm.orderConfirmation.orderType,
						vm.orderConfirmation.fulfillmentType,
						vm.orderConfirmation.vehicleModel,
						vm.orderConfirmation.vehicleColor);

					if (takeoutCurbsideDisplay) {
						vm.orderTypeDisplay = takeoutCurbsideDisplay;
					}

					var utc = moment.utc(vm.orderConfirmation.pickupDate);
					vm.pickupDate = utc.clone().utcOffset(vm.merchantLocation.TimeZoneOffset);

					vm.showImHere = (vm.vehicleModel || orderTypeRule.IsAlwaysCustomerArrival) && togoorder.merchantLocation.AcceptsImHereLink;
				}, function () {
					done();
				});
		}

		activate();
	}


})();;
(function () {
	'use strict';

	var controllerId = 'waitForOrderProgress';
	angular.module('main')
		.controller(controllerId, ['$scope', '$q', '$state', '$stateParams', '$window', '$timeout', 'storageService', 'common', 'merchantLocationService', 'orderService', 'orderTypeWorkflowService', 'cartService', 'timeService', 'userService', 'api', 'orderTypeRuleService', 'events', 'spinner', waitForOrderProgress]);

	function waitForOrderProgress($scope, $q, $state, $stateParams, $window, $timeout, storageService, common, merchantLocationService, orderService, orderTypeWorkflowService, cartService, timeService, userService, api, orderTypeRuleService, events, spinner) {
		var vm = this,
			locationId = togoorder.locationId;

		vm.merchantLocation = {};
		vm.orderConfirmation = {};
		vm.deliveryTemplate = '';
		vm.tableNumber = '?';
		vm.address = {};
		vm.browserTimeZoneOffset = timeService.getBrowserTimeZoneOffset();
		vm.pickupDate = {};
		vm.orderTypeDisplay = undefined;
		vm.isAGuest = userService.isAGuest();
		vm.safeClassName = common.safeClassName;
		vm.currentOrderProgressText = "";
		vm.currentOrderProgressSubtext = "";
		vm.success = undefined;
		vm.activated = false;

		function getOrderSummary() {
			return orderService.getOrderSummary()
				.then(function (orderConfirmation) {
					vm.orderConfirmation = orderConfirmation;
					vm.tableNumber = vm.orderConfirmation.tableNumber;
					vm.vehicleColor = vm.orderConfirmation.vehicleColor;
					vm.vehicleModel = vm.orderConfirmation.vehicleModel;
					vm.address = vm.orderConfirmation.deliveryAddress;

					return orderConfirmation;
				});
		}

		function getMerchantLocation() {
			return merchantLocationService.getById(locationId)
				.then(function (data) {
					vm.merchantLocation = data;
					return data;
				});
		}

		function clearSession() {
			$window.sessionStorage.clear();
			storageService.clear();
		}

		function waitForFinalOrderProgress() {
			subscribeToOrderHubProgress();

			common.broadcast(events.readyForOrderProgress, vm.orderConfirmation);

			initializeOtherStuff();
		}

		function activate() {
			clearSession();
			cartService.clear();

			spinner.spinnerShow();
			common.activateController([getMerchantLocation(), getOrderSummary()], controllerId)
				.then(function () {
					spinner.spinnerShow();
					vm.activated = true;

					return waitForFinalOrderProgress();
				}, function (er) {
					console.log('error waiting for order progress...', er);
					$state.go('locationHome');
				});
		}

		activate();

		function subscribeToOrderHubProgress() {
			$timeout(function () {
				vm.currentOrderProgressText = "Placing Order...";
			}, 10);

			$scope.$on(events.orderHubProgress,
				function (e, args) {
					try {
						var orderProgress = angular.fromJson(args);

						$timeout(function () {
							switch (orderProgress.Progress) {
								case ("InitializingFutureOrder"):
									vm.currentOrderProgressText = "Initializing Future Order...";
									vm.currentOrderProgressSubtext = "";
									break;
								case ("FiringIntoThePos"):
									vm.currentOrderProgressText = "Alerting the Cooks...";
									vm.currentOrderProgressSubtext = "";
									break;
								case ("SendingOrderToPos"):
									vm.currentOrderProgressText = "Sending your Order to the Kitchen...";
									vm.currentOrderProgressSubtext = "Looks delicious!";
									break;
								case ("InitializingAPriceRequest"):
									vm.currentOrderProgressText = "Calculating the Final Price...";
									vm.currentOrderProgressSubtext = "";
									break;
								case ("SendingCustomerEmail"):
									vm.currentOrderProgressText = "YES!";
									vm.currentOrderProgressSubtext = "";
									break;
								case ("OrderStatusChange"):
									switch (orderProgress.OrderStatus) {
										case ("New"):
											vm.currentOrderProgressText = "Thinking...";
											vm.currentOrderProgressSubtext = "This is the best order we've had today!";
											break;
										case ("Confirmed"):
										case ("Complete"):
											vm.currentOrderProgressText = "YES!";
											vm.currentOrderProgressSubtext = "";
											break;
										case ("Cancelled"):
											vm.currentOrderProgressText = "Oh No... An Error Occurred.";
											vm.currentOrderProgressSubtext = "";
											break;
										default:
											vm.currentOrderProgressText = "??";
											vm.currentOrderProgressSubtext = "";
											break;
									}

									break;
								case ("UpdatingPosIntegration"):
									switch (orderProgress.IntegrationQueueStatus) {
										case ("New"):
										case ("PulledOrPublished"):
											vm.currentOrderProgressText = "Initializing Communication with the Restaurant...";
											vm.currentOrderProgressSubtext = "This is the best order we've had today!";
											break;
										case ("PullAcknowledged"):
										case ("IntegrationPendingRecheck"):
											vm.currentOrderProgressText = "Communicating with the Restaurant...";
											vm.currentOrderProgressSubtext = "Looking good!";
											break;
										case ("IntegrationAcknowledged"):
											vm.currentOrderProgressText = "Almost there...";
											vm.currentOrderProgressSubtext = "The Store is Looking Over the Order...";
											break;
										case ("PullFailed"):
										case ("IntegrationFailed"):
											vm.currentOrderProgressText = "Oh No... An Error Occurred.";
											vm.currentOrderProgressSubtext = "";
											break;
										default:
											vm.currentOrderProgressText = "??";
											vm.currentOrderProgressSubtext = "";
											break;
									}

									break;
								default:
									vm.currentOrderProgressText = "??";
									vm.currentOrderProgressSubtext = "";
									break;
							}
							switch (orderProgress.IntegrationQueueStatus) {
								case ("IntegrationFailed"):
									vm.currentOrderProgressText = "Oh No... An Error Occurred.";
									vm.currentOrderProgressSubtext = "";
									break;
								default:
									break;
							}
							var allDone = false;
							if (orderProgress.IsSuccess === true) {
								vm.success = true;
								allDone = true;
							} else if (orderProgress.IsSuccess === false) {
								vm.success = false;
								allDone = true;
							} // else it's undefined or null

							if (allDone) {
								spinner.spinnerHide();

								common.broadcast(events.orderHubProgressDone, orderProgress);
								if (vm.success) {
									$state.go("thankYou", {}, {});
								} else {
									$state.go("orderProgressFail", {}, {});
								}
							}
						}, 10);
					} catch (e) {
						console.log(e);
					}
				});
		}


		function initializeOtherStuff() {

			var orderTypeRule = orderTypeRuleService.getOrderTypeRule(vm.orderConfirmation.orderType, vm.orderConfirmation.fulfillmentType, togoorder.merchantLocation);
			orderTypeWorkflowService.setOrderTypeRule(orderTypeRule, !!vm.orderConfirmation.vehicleModel);

			vm.deliveryTemplate = orderTypeWorkflowService.getDisplayTemplate(vm.orderConfirmation.orderType, vm.orderConfirmation.fulfillmentType);

			vm.orderTypeDisplay = orderTypeWorkflowService.getOrderTypeDescription(vm.orderConfirmation.orderType, vm.orderConfirmation.fulfillmentType);

			var takeoutCurbsideDisplay = orderTypeWorkflowService.getTakeoutCurbsideDescription(vm.orderConfirmation.orderType,
				vm.orderConfirmation.fulfillmentType,
				vm.orderConfirmation.vehicleModel,
				vm.orderConfirmation.vehicleColor);

			if (takeoutCurbsideDisplay) {
				vm.orderTypeDisplay = takeoutCurbsideDisplay;
			}

			var utc = moment.utc(vm.orderConfirmation.pickupDate);
			vm.pickupDate = utc.clone().utcOffset(vm.merchantLocation.TimeZoneOffset);

		}
	}
})();;
(function() {
	"use strict";

	var controllerId = "customerArrival";
	angular.module("main")
		.controller(controllerId, ["$scope", "$q", "$state", "$stateParams", "$window", "common", customerArrival]);

	function customerArrival($scope, $q, $state, $stateParams, $window, common) {
		var vm = this,
			locationId = togoorder.locationId;

		function activate() {
			common.activateController([]);
		}

		activate();
	}

})();;
(function () {
    'use strict';

    var controllerId = 'curbsidePrompt';
    angular.module('main')
		.controller(controllerId, ['$rootScope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'events', 'cartService', curbsidePrompt]);

	function curbsidePrompt($rootScope, $log, $state, $stateParams, $modal, $filter, notify, common, events, cartService) {
		var vm = this, isUseCurbside, order;

		initialize();

		function initialize() {
			var cart = cartService.getCart();
			order = cart.order;
		}

		Object.defineProperty(vm, 'isUseCurbside', {
			get: function () {
				return isUseCurbside;
			}, set: function(val) {
				isUseCurbside = val;
				toggleCurbside();
			}
		});

		function toggleCurbside() {
			if (vm.isUseCurbside) {
				order.VehicleColor = order.Customer.VehicleColor;
				order.VehicleModel = order.Customer.VehicleModel;
			} else {
				order.VehicleColor = '';
				order.VehicleModel = '';
			}
		}
	}
})();;
(function () {
    'use strict';

    var controllerId = 'carsidePrompt';
    angular.module('main')
		.controller(controllerId, ['$rootScope', '$log', '$state', '$stateParams', '$uibModal', '$filter', 'notify', 'common', 'events', 'cartService', carsidePrompt]);

	function carsidePrompt($rootScope, $log, $state, $stateParams, $modal, $filter, notify, common, events, cartService) {
		var vm = this, order;

		initialize();

		function initialize() {
			var cart = cartService.getCart();
			order = cart.order;
		}
	}
})();;
(function () {
	'use strict';

	angular.module('main').factory('OfferExt', ['$rootScope', '$timeout', 'common', 'events', offerExtFactory]);

	function offerExtFactory($rootScope, $timeout, common, events) {

		function OfferExt(offer, onChanged) {
			this.offer = offer;
			this.onChanged = onChanged;
		}

		Object.defineProperty(OfferExt.prototype, "isOptedIn", {
			get: function() {
				return this.offer.OptedIn;
			},
			set: function(val) {
				if (!this.offer.Optable) {
					return;
				}
				var currentValue = !!this.offer.OptedIn;
				val = !!val;
				var isChanged = (currentValue !== val);
				this.offer.OptedIn = val;
				if (isChanged && this.onChanged) {
					this.onChanged(this.offer);
				}
			}
		});

		return OfferExt;
	}
})();;
(function () {
	'use strict';

	angular.module('main').factory('Order', ['common', orderFactory]);

	function orderFactory(common) {

		function Order(locationId, menuId) {
			this.SalesTaxRate = 0;
			this.Gratuity = null;
			this.Items = [];
			this.LocationId = locationId;
			this.DeliveryZoneId = null;
			this.DeliveryFee = 0;
			this.OrderType = null;
			this.FulfillmentType = null;
			this.MenuId = menuId;
			this.PickupDateTime = null;
			this.TableNumber = null;
			this.DeliveryAddress = {};
			this.DeliveryDistance = 0;
			this.PickupTimeZoneId = undefined;
			this.Promotion = {};
			this.IsUsual = false;
			this.SurchargePercentage = 0;
			this.ConvenienceFee = 0;
			this.Customer = {
				EmailAddress: null,
				LastName: null,
				FirstName: null,
				GuestEmailOptIn: false,
				PhoneNumber: {
					Number: null
				}
			};

			this.loyaltyOverrideAmount = undefined;
			this.mealPlanOverrideAmount = undefined;
			this.giftCardOverrideAmount = undefined;
			this.isOfferPreTax = false;
			this.isLoyaltyPreTax = false;
			this.loyaltyDoesNotCoverTip = false;
			this.isMealPlanPreTax = false;
			this.isGiftCardPreTax = false;
			this.isConvenienceFeePreTax = false;
			this.isSurchargePreTax = false;
			this.isSurchargeOnNetSales = false;
			this.isDeliveryFeePreTax = false;
			this.giftCardProviderType = undefined;
			this.Payment = null;
			this.SelectedOfferAmount = 0;
			this.LastValidated = new Date();
			var pricedOrder;

			var self = this;

			Object.defineProperty(this,
				"PickupDateTimeLocal",
				{
					get: function () {
						return (self.PickupDateTime && moment.isMoment(self.PickupDateTime))
							? self.PickupDateTime.locale('en').format('YYYY-MM-DD HH:mm')
							: null;
					},
					set: function (val) {
						//
					},
					enumerable: true
				});

			Object.defineProperty(this,
				"pricedOrder",
				{
					get: function () {
						return pricedOrder;
					},
					set: function (data) {
						pricedOrder = data && data.IsSuccess && new PricedOrder(self, data);
					},
					enumerable: false
				});
		}

		Order.prototype.setLocation = function (location) {
			this.SalesTaxRate = location.SalesTaxRate;
			this.SalesTaxRoundingStrategy = location.SalesTaxRoundingStrategy;
			this.isOfferPreTax = !!(location.LoyaltyProfile && location.LoyaltyProfile.IsOfferPreTax);
			this.isLoyaltyPreTax = !!(location.LoyaltyProfile && location.LoyaltyProfile.IsPreTax);
			this.loyaltyDoesNotCoverTip = !!(location.LoyaltyProfile && location.LoyaltyProfile.DoNotCoverTip);
			this.isMealPlanPreTax = !!(location.Merchant &&
				location.Merchant.MealPlanProfiles &&
				location.Merchant.MealPlanProfiles.length &&
				location.Merchant.MealPlanProfiles[0].IsPreTax);
			this.isGiftCardPreTax = !!(location.Merchant &&
				location.Merchant.GiftCardProfiles &&
				location.Merchant.GiftCardProfiles.length &&
				location.Merchant.GiftCardProfiles[0].IsPreTax);
		};

		Order.prototype.setDeliveryZone = function (deliveryZone, distance, orderTypeRule) {

			this.setOrderTypeRule(orderTypeRule);

			this.DeliveryZoneId = deliveryZone.Id;
			this.DeliveryFee = 0;
			this.DeliveryDistance = distance;
			if (deliveryZone.FlatFee) {
				this.DeliveryFee += deliveryZone.FlatFee;
			}
			if (deliveryZone.PerMileFee) {
				this.DeliveryFee += distance * deliveryZone.PerMileFee;
			}
			if (deliveryZone.PercentageFee) {
				this.SurchargePercentage = deliveryZone.PercentageFee;
				this.isSurchargePreTax = this.isDeliveryFeePreTax;
			}
		};

		Order.prototype.setDeliveryService = function (orderTypeRule) {

			this.DeliveryFee = orderTypeRule.DeliveryFeePaidByCustomer || 0;

			if (orderTypeRule.DeliveryServiceChargePaidByCustomer) {
				//            if (orderTypeRule.IsDeliveryServiceChargePercentageOfSales)
				//                this.SurchargePercentage = orderTypeRule.DeliveryServiceChargePaidByCustomer;
				//else
				this.ConvenienceFee = orderTypeRule.DeliveryServiceChargePaidByCustomer;
			}
		};

		Order.prototype.setOrderTypeRule = function (orderTypeRule) {
			this.OrderType = orderTypeRule.OrderType;
			this.FulfillmentType = orderTypeRule.FulfillmentType;
			this.SurchargePercentage = orderTypeRule.SurchargePercentage;
			this.ConvenienceFee = orderTypeRule.ConvenienceFee;

			this.isConvenienceFeePreTax = orderTypeRule.IsConvenienceFeePreTax;
			this.isSurchargePreTax = orderTypeRule.IsSurchargePreTax;
			this.isSurchargeOnNetSales = orderTypeRule.IsSurchargeOnNetSales;
			this.isDeliveryFeePreTax = orderTypeRule.IsDeliveryFeePreTax;
		};

		Order.prototype.addItem = function (orderItem) {
			this.Items.push(orderItem);
		};

		Order.prototype.deleteItem = function (orderItem) {
			this.Items = _.reject(this.Items,
				function (item) {
					return orderItem === item;
				});
			if (this.Items.length == 0) this.Gratuity = 0;
		};

		Order.prototype.invalidatePricedOrderLoyalty = function () {
			this.pricedOrder.LoyaltyDiscount = undefined;
		};

		Order.prototype.invalidatePricedOrderOffers = function () {
			this.pricedOrder.OfferDiscount = undefined;
		};

		function removeItemById(itemId, parent) {
			parent.Items = _.reject(parent.Items,
				function (item) {
					var isMatch = (itemId === item.ItemId);
					return isMatch;
				});
			_.each(parent.Items,
				function (item) {
					removeItemById(itemId, item);
				});
		}

		Order.prototype.deleteItemById = function (itemId) {
			removeItemById(itemId, this);
		};

		Object.defineProperty(Order.prototype,
			"PostTaxDeliveryFee",
			{
				get: function () {
					if (this.isDeliveryFeePreTax) {
						return 0;
					}
					if (this.pricedOrder && this.pricedOrder.DeliveryFee !== undefined) {
						return common.monetize(this.pricedOrder.DeliveryFee || 0);
					}
					return common.monetize(this.DeliveryFee);
				}
			});


		Object.defineProperty(Order.prototype,
			"PreTaxDeliveryFee",
			{
				get: function () {
					if (!this.isDeliveryFeePreTax) {
						return 0;
					}
					if (this.pricedOrder && this.pricedOrder.DeliveryFee !== undefined) {
						return common.monetize(this.pricedOrder.DeliveryFee || 0);
					}
					return common.monetize(this.DeliveryFee);
				}
			});

		Object.defineProperty(Order.prototype,
			"PostTaxSurcharge",
			{
				get: function () {
					if (this.isSurchargePreTax) {
						return 0;
					}
					if (this.pricedOrder && this.pricedOrder.TotalSurcharge !== undefined) {
						return common.monetize(this.pricedOrder.TotalSurcharge || 0);
					}
					var val = (this.Subtotal) * this.SurchargePercentage / 100;
					return common.monetize(val);
				}
			});

		Object.defineProperty(Order.prototype,
			"PreTaxSurcharge",
			{
				get: function () {
					if (!this.isSurchargePreTax) {
						return 0;
					}
					if (this.pricedOrder && this.pricedOrder.TotalSurcharge !== undefined) {
						return common.monetize(this.pricedOrder.TotalSurcharge || 0);
					}
					var val = (this.Subtotal) * this.SurchargePercentage / 100;
					return common.monetize(val);
				}
			});

		//for order-level promotions
		Object.defineProperty(Order.prototype,
            "PreTaxTotalDiscount",
			{
				get: function () {
                    if (this.pricedOrder && this.pricedOrder.PreTaxDiscountAmount !== undefined) {
                        return common.monetize(this.pricedOrder.PreTaxDiscountAmount || 0);
					}
					var discount = 0;

					if (this.Promotion.DiscountPercentage || this.Promotion.DiscountPercentage > 0) {
						discount = this.Subtotal * this.Promotion.DiscountPercentage / 100;
						if ((this.Promotion.DiscountAmount || this.Promotion.DiscountAmount > 0) &&
							discount > this.Promotion.DiscountAmount) {
							discount = this.Promotion.DiscountAmount;
						}
					} else if (this.Promotion.DiscountAmount || this.Promotion.DiscountAmount > 0) {
						discount = this.Promotion.DiscountAmount;
					}
					discount = Math.min(discount, this.Subtotal);
					return 0 - common.monetize(discount);
				}
			});

		Object.defineProperty(Order.prototype,
	        "PostTaxTotalDiscount",
	        {
		        get: function() {
			        if (this.pricedOrder && this.pricedOrder.PostTaxDiscountAmount !== undefined) {
				        return common.monetize(this.pricedOrder.PostTaxDiscountAmount || 0);
			        }
			        return 0;
		        }
	        });

        Object.defineProperty(Order.prototype,
	        "TotalDiscount",
	        {
		        get: function() {
                    //console.log("pre/post tax discounts:", this.PreTaxTotalDiscount, this.PostTaxTotalDiscount);
			        return this.PreTaxTotalDiscount + this.PostTaxTotalDiscount;
		        }
	        });

        Object.defineProperty(Order.prototype,
			"ItemPromotionDiscount",
			{
				get: function () {
					return (this.PreItemPromotionSubtotal > 0 ? this.Subtotal - this.PreItemPromotionSubtotal : 0);
				}
			});

		Object.defineProperty(Order.prototype,
			"isPayable",
			{
				get: function () {
					return this.Total > 0;
				}
			});

		Object.defineProperty(Order.prototype,
			"Subtotal",
			{
				get: function () {
					if (this.pricedOrder && this.pricedOrder.Subtotal !== undefined) {
						return common.monetize(this.pricedOrder.Subtotal || 0);
					}
					var val = _.reduce(this.Items,
						function (agg, item) {
							return agg + item.TotalPrice;
						},
						0);
					return common.monetize(val);
				}
			});

		Object.defineProperty(Order.prototype,
			"PreItemPromotionSubtotal",
			{
				get: function () {
					var val = _.reduce(this.Items,
						function (agg, item) {
							return agg + item.PreItemPromotionTotalPrice;
						},
						0);
					return common.monetize(val);
				}
			});

		Object.defineProperty(Order.prototype,
			"PromoSavings",
			{
				get: function () {
                    var subPromoSavings = (0 - this.ItemPromotionDiscount - this.PreTaxTotalDiscount);
					// TODO: taxSavings needs to use item-level taxes.
					var taxSavings = subPromoSavings * (this.SalesTaxRate / 100);
					var surchargeSavings = subPromoSavings * (this.SurchargePercentage / 100);
                    let result = common.monetize(subPromoSavings + taxSavings + surchargeSavings - this.PostTaxTotalDiscount);
					return result;
				}
			});

		function getAdjustedSubtotalWithoutOffersAndLoyalty(order) {
			var number = order.Subtotal +
				order.PreTaxTotalDiscount +
				order.PreTaxDeliveryFee +
				order.PreTaxConvenienceFee +
				order.PreTaxSurcharge;

			return Math.max(number, 0);
		}

		function getAdjustedSubtotalWithoutLoyalty(order) {
			var number = order.Subtotal +
				order.PreTaxTotalDiscount +
				order.PreTaxDeliveryFee +
				order.PreTaxConvenienceFee +
				order.PreTaxSurcharge +
				order.OfferDiscount;

			return Math.max(number, 0);
		}

		function getPreTaxAdjustments(order) {
			return order.PreTaxTotalDiscount +
				order.PreTaxDeliveryFee +
				order.PreTaxConvenienceFee +
				order.PreTaxSurcharge +
				order.OfferDiscount +
				order.LoyaltyDiscount;
		}

		function getAdjustedSubtotal(order) {
			var number = order.Subtotal + getPreTaxAdjustments(order);

			var val = Math.max(number, 0);

			return common.monetize(val);
		}

		Object.defineProperty(Order.prototype,
			"PreTaxConvenienceFee",
			{
				get: function () {
					if (!this.isConvenienceFeePreTax) {
						return 0;
					}
					if (this.pricedOrder && this.pricedOrder.ConvenienceFee !== undefined) {
						return common.monetize(this.pricedOrder.ConvenienceFee || 0);
					}
					return common.monetize(this.ConvenienceFee);
				}
			});

		Object.defineProperty(Order.prototype,
			"PostTaxConvenienceFee",
			{
				get: function () {
					if (this.isConvenienceFeePreTax) {
						return 0;
					}
					if (this.pricedOrder && this.pricedOrder.ConvenienceFee !== undefined) {
						return common.monetize(this.pricedOrder.ConvenienceFee || 0);
					}
					return common.monetize(this.ConvenienceFee);
				}
			});

		Object.defineProperty(Order.prototype,
			"ItemLevelTax",
			{
				get: function () {
					if (this.pricedOrder && this.pricedOrder.Tax !== undefined) {
						return common.monetize(this.pricedOrder.Tax || 0);
					}
					var order = this;
					var totalAdjustments = getPreTaxAdjustments(this);
					var itemTotal = this.Subtotal;

					switch (order.SalesTaxRoundingStrategy) {
						case (1): { // 1: SumItemTaxThenRoundToTwoDigits
							var val = _.reduce(this.Items,
								function(agg, item) {
									var taxRate = item.ItemTaxRate;
									if (taxRate === null || taxRate === undefined) {
										taxRate = order.SalesTaxRate;
									}

									var itemRatioOfSubtotal = (item.TotalPrice / itemTotal);

									var taxableItemPrice =
										item.TotalPrice + (totalAdjustments * itemRatioOfSubtotal);

									let itemTax = taxableItemPrice * taxRate / 100;
									return agg + itemTax;
								}, 0);

							return common.monetize(val);
						}
						default: { // 0: RoundItemTaxToThreeDigitsThenSumThenRoundToTwoDigits

							let itemsByTaxRate = _.groupBy(this.Items, function (item) {
								var applicableTaxRate = item.ItemTaxRate;
								if (applicableTaxRate === null || applicableTaxRate === undefined) {
									applicableTaxRate = order.SalesTaxRate;
								}
								return applicableTaxRate;
							}); // => {0.0: [item1, item2], 9.75: [item3, item4]}
							
							let totalsByTaxRate = _.mapObject(itemsByTaxRate,
								function (items, taxRate) {
									return _.reduce(items,
										function (agg, item) {
											var itemRatioOfSubtotal = (item.TotalPrice / itemTotal);

											var taxableItemPrice =
												item.TotalPrice + (totalAdjustments * itemRatioOfSubtotal);

											let itemTax = common.monetize(taxableItemPrice * taxRate / 100, 3);
											return agg + itemTax;
										},
										0);
								}); // => {0.0: 1.983, 9.75: 4.755}

							let valuesWithTax = _.values(totalsByTaxRate); // => [1.983, 4.755]

							let val = _.reduce(valuesWithTax,
								function (agg, v) {
									return agg + common.monetize(v, 2);
								},
								0);

							return common.monetize(val);
						}
					}

				}
			});

		function getLoyaltyBalance(payment) {
			if (!payment ||
				!payment.LoyaltyPayment ||
				!payment.LoyaltyPayment.RewardPointsSummary ||
				payment.LoyaltyPayment.RewardPointsSummary.MonetaryWorth === 0) {
				return 0;
			}
			return payment.LoyaltyPayment.RewardPointsSummary.MonetaryWorth;
		}

		function getMealPlanBalance(payment) {
			if (!payment || !payment.MealPlanPayment || payment.MealPlanPayment.Balance === 0) {
				return 0;
			}

			return payment.MealPlanPayment.Balance;
		}

		function getGiftCardBalance(payment) {
			if (!payment || !payment.GiftCardPayment || payment.GiftCardPayment.Balance === 0) {
				return 0;
			}

			return payment.GiftCardPayment.Balance;
		}

		Object.defineProperty(Order.prototype,
			"TotalForTenders",
			{
				get: function () {
					return common.monetize(this.Total - this.LoyaltyPaymentAmount - this.OfferPaymentAmount);
				}
			});


		/**
		 * Note: Gratuity is part of Total 
		 */
		Object.defineProperty(Order.prototype,
			"Total",
			{
				get: function () {
					if (this.pricedOrder && this.pricedOrder.TotalFinal !== undefined) {
						return common.monetize(this.pricedOrder.TotalFinal || 0);
					}

					//debugger;

					var val = getAdjustedSubtotal(this) +
						this.ItemLevelTax +
						(this.Gratuity || 0) +
						this.PostTaxDeliveryFee +
						this.PostTaxConvenienceFee +
						this.PostTaxSurcharge;

					return common.monetize(val);
				}
			});

		Object.defineProperty(Order.prototype,
			"LoyaltyDiscount",
			{
				get: function () {
					if (!this.isLoyaltyPreTax) {
						return 0;
					}

					function getValue(self) {
						var amount = self.MaxLoyaltyDiscountAmount;

						if (self.loyaltyOverrideAmount !== undefined) {
							amount = Math.min(amount, self.loyaltyOverrideAmount);
						}

						return -common.monetize(amount);
					}

					Object.defineProperty(this,
						"LoyaltyDiscount",
						{
							get: function () {
								if (this.pricedOrder && this.pricedOrder.LoyaltyDiscount !== undefined) {
									return this.pricedOrder.LoyaltyDiscount || 0;
								}
								return getValue(this);
							},
							set: function () { },
							enumerable: true
						});
					return getValue(this);
				},
				set: function () { }
			});

		Object.defineProperty(Order.prototype,
			"LoyaltyOverrideAmount",
			{
				get: function () {
					return this.loyaltyOverrideAmount;
				},
				set: function (val) {
					val = val || undefined;
					this.loyaltyOverrideAmount = val;
				}
			});

		Object.defineProperty(Order.prototype,
			"LoyaltyPaymentAmount",
			{
				get: function () {
					if (this.isLoyaltyPreTax) {
						return 0;
					}

					function getValue(self) {
						var amount = self.MaxLoyaltyPaymentAmount;

						if (self.loyaltyOverrideAmount !== undefined) {
							amount = Math.min(amount, self.loyaltyOverrideAmount);
						}

						return common.monetize(amount);
					}

					Object.defineProperty(this,
						"LoyaltyPaymentAmount",
						{
							get: function () {
								return getValue(this);
							},
							set: function () { },
							enumerable: true
						});
					return getValue(this);
				},
				set: function () { }
			});


		Object.defineProperty(Order.prototype,
			"MaxOfferDiscountAmount",
			{
				get: function () {
					//console.log('this.isOfferPreTax', this.isOfferPreTax);
					if (!this.isOfferPreTax) {
						return 0;
					}
					var amount = Math.min(getAdjustedSubtotalWithoutOffersAndLoyalty(this), this.SelectedOfferAmount);
					return common.monetize(amount);
				}
			});

		/**
		 * This includes Gratuity from this.Total
		 */
		Object.defineProperty(Order.prototype,
			"MaxOfferPaymentAmount",
			{
				get: function () {
					if (this.isOfferPreTax) {
						return 0;
					}

					var total = this.Total; // - this.Gratuity;

					var amount = Math.min(total - this.GiftCardPaymentAmount - this.MealPlanPaymentAmount, this.SelectedOfferAmount);

					//debugger;
					return common.monetize(amount);
				}
			});

		Object.defineProperty(Order.prototype,
			"MaxLoyaltyDiscountAmount",
			{
				get: function () {
					if (!this.isLoyaltyPreTax) {
						return 0;
					}

					var amount = Math.min(getAdjustedSubtotalWithoutLoyalty(this), getLoyaltyBalance(this.Payment));
					return common.monetize(amount);
				}
			});

		Object.defineProperty(Order.prototype,
			"MaxLoyaltyPaymentAmount",
			{
				get: function () {
					if (this.isLoyaltyPreTax) {
						return 0;
					}
					var total = this.TotalForLoyalty - (this.loyaltyDoesNotCoverTip ? this.Gratuity : 0);
					var amount = Math.min(total, getLoyaltyBalance(this.Payment));

					return common.monetize(amount);
				}
			});

		/**
		 * This calc includes Gratuity in this.Total
		 */
		Object.defineProperty(Order.prototype,
			"TotalForLoyalty",
			{
				get: function () {
					var val = Math.max(0, this.Total - this.OfferPaymentAmount - this.GiftCardPaymentAmount - this.MealPlanPaymentAmount);
					return common.monetize(val);
				}
			});

		Object.defineProperty(Order.prototype,
			"TotalForCreditCard",
			{
				get: function () {
					var val = Math.max(0, this.Total - this.LoyaltyPaymentAmount - this.OfferPaymentAmount - this.GiftCardPaymentAmount - this.MealPlanPaymentAmount);
					return common.monetize(val);
				}
			});

		Object.defineProperty(Order.prototype,
			"MealPlanOverrideAmount",
			{
				get: function () {
					return this.mealPlanOverrideAmount;
				},
				set: function (val) {
					val = val || undefined;
					this.mealPlanOverrideAmount = val;
				}
			});

		Object.defineProperty(Order.prototype,
			"MealPlanPaymentAmount",
			{
				get: function () {
					if (this.isMealPlanPreTax) {
						return 0;
					}

					function getValue(self) {
						var amount = self.MaxMealPlanPaymentAmount;

						if (self.mealPlanOverrideAmount !== undefined) {
							amount = Math.min(amount, self.mealPlanOverrideAmount);
						}

						return common.monetize(amount);
					}

					Object.defineProperty(this,
						"MealPlanPaymentAmount",
						{
							get: function () {
								return getValue(this);
							},
							set: function () { },
							enumerable: true
						});
					return getValue(this);
				},
				set: function () { }
			});

		Object.defineProperty(Order.prototype,
			"GiftCardOverrideAmount",
			{
				get: function () {
					return this.giftCardOverrideAmount;
				},
				set: function (val) {
					val = val || undefined;
					this.giftCardOverrideAmount = val;
				}
			});

		/**
		 * MaxGiftCardPaymentAmount or setter: giftCardOverrideAmount
		 */
		Object.defineProperty(Order.prototype,
			"GiftCardPaymentAmount",
			{
				get: function () {
					if (this.isGiftCardPreTax) {
						return 0;
					}

					function getValue(self) {
						var amount = self.MaxGiftCardPaymentAmount;

						if (self.giftCardOverrideAmount !== undefined) {
							amount = Math.min(amount, self.giftCardOverrideAmount);
						}

						return common.monetize(amount);
					}

					Object.defineProperty(this,
						"GiftCardPaymentAmount",
						{
							get: function () {
								return getValue(this);
							},
							set: function () { },
							enumerable: true
						});
					return getValue(this);
				},
				set: function () { }
			});

		Object.defineProperty(Order.prototype,
			"OfferDiscount",
			{
				get: function () {
					if (!this.isOfferPreTax) {
						return 0;
					}

					function getValue(self) {
						return -common.monetize(self.MaxOfferDiscountAmount);
					}

					Object.defineProperty(this,
						"OfferDiscount",
						{
							get: function () {
								let value = getValue(this);
								return value;
							},
							set: function () { },
							enumerable: true
						});

					return getValue(this);
				},
				set: function (val) {
					//nothing... just here for deserialization.
				}
			});

		Object.defineProperty(Order.prototype,
			"OfferPaymentAmount",
			{
				get: function () {
					if (this.isOfferPreTax) {
						return 0;
					}
					return common.monetize(this.MaxOfferPaymentAmount);
				},
				set: function () { }
			});

		Object.defineProperty(Order.prototype,
			"MaxMealPlanPaymentAmount",
			{
				get: function () {
					if (this.isMealPlanPreTax) {
						return 0;
					}
					var amount = Math.min(this.Total, getMealPlanBalance(this.Payment));
					return common.monetize(amount);
				}
			});

		/**
		 * This calculation includes Gratuity from this.Total
		 * WOC-830: Tips should not be allocated to Gift Card Payment
		 */
		Object.defineProperty(Order.prototype,
			"MaxGiftCardPaymentAmount",
			{
				get: function () {
					if (this.isGiftCardPreTax) {
						return 0;
					}

					// Disallow gratuity from being allocated to Gift Card
					var total = this.Total - this.MealPlanPaymentAmount;
					if (this.giftCardProviderType != 1) {   //Focus is 1. WOC-1323 We needed to allow Focus GC to pay for tip with giftcard
						total -= this.Gratuity;
					}
					var amount = Math.min(total, getGiftCardBalance(this.Payment));

					//if (amount === this.Total && this.Gratuity > 0)
					//    debugger;

					return common.monetize(amount);
				}
			});

		Object.defineProperty(Order.prototype,
			"TotalLessMealPlan",
			{
				get: function () {
					if (this.isMealPlanPreTax) {
						return this.Total;
					}
					var val = Math.max(0, this.Total - this.MealPlanPaymentAmount);
					return common.monetize(val);
				}
			});

		Object.defineProperty(Order.prototype,
			"TotalLessGiftCard",
			{
				get: function () {
					if (this.isGiftCardPreTax) {
						return this.Total;
					}
					var val = Math.max(0, this.Total - this.GiftCardPaymentAmount);
					return common.monetize(val);
				}
			});

		function PricedOrder(order, data) {

			var self = this;

			angular.extend(this, data);

			/**
			 * Gratuity is added to Total
			 */
			Object.defineProperty(this,
				"TotalFinal",
				{
					get: function () {
						var val = self.Total + order.Gratuity;
						return common.monetize(val);
					}
				});

		}


		return Order;
	}
})();;
(function () {
    'use strict';

    angular.module('main').factory('OrderItem', ['common', 'events', orderItemFactory]);

	function orderItemFactory(common, events) {

		function OrderItem(menuItem, parentOrderItem, group) {
			var sortIndex = menuItem.SortIndex || 1;
			if (group) {
				if (group.HasSizes) {
					sortIndex = 0;
				} else {
					sortIndex += group.SortIndex * 1000000;
				}
			}
			this.countEquivalent = menuItem.CountEquivalent;
            this.UniqueId = (+new Date()) & 0xffffffff;
            this.ItemId = menuItem.Id;
            this.GroupInstanceId = menuItem.GroupInstanceId;
            this.ItemName = menuItem.Name;
            this.SpecialInstructions = "";
            this.SortIndex = sortIndex;
            this.ItemPrice = menuItem.Price;
            this.ItemTaxRate = menuItem.ItemTaxRate;
            this.OriginalItemPrice = undefined;
            this.PrimaryImageUrl = menuItem.PrimaryImageUrl;
            this.OverridePrice = undefined;
			this.Items = [];
			this.IsItemCountIndependent = menuItem.IsItemCountIndependent;
            this.IsChildItem = !!parentOrderItem;
			this.MinimumCount = menuItem.MinimumCount || 1;
            this.SectionBrand = menuItem.SectionBrand;
            //console.log('this.SectionBrand', this.SectionBrand);
            this.clonedIncludedGroups = [];
            this.Sku = menuItem.Sku;
            this.PluId = menuItem.PluId;
			
			var self = this;
            if (parentOrderItem) {
                Object.defineProperty(this, "Quantity", {
                    enumerable: true,
					get: function () {
						return self.IsItemCountIndependent ? 1 : parentOrderItem.Quantity;
                    },
					set: function (v) {
						if (self.IsItemCountIndependent) {
							if (1 !== v) {
								throw new Error("Error while hydrating an order. - Quantity mismatch");
							}
						}else if (parentOrderItem.Quantity !== v) {
							throw new Error("Error while hydrating an order. - Quantity mismatch");
                        }
                    }
                });
            } else {
	            var quantity = 0;
                Object.defineProperty(this, "Quantity", {
                    enumerable: true,
                    get: function () {
                        return quantity;
                    },
                    set: function (val) {
                        val = val ? parseInt(val, 10) : 0;
                        val = isNaN(val) ? quantity : val;
						quantity = Math.max(val || 1, self.MinimumCount || 1);

						common.broadcast(events.itemQuantityChanged, { itemId: self.ItemId, quantity: val, minimumCount: self.MinimumCount });
                    }
				});
	            this.Quantity = this.MinimumCount;
            }

            var consumerName = "";
            Object.defineProperty(this, "ConsumerName", {
                enumerable: true,
                get: function () {
                    return consumerName;
                },
                set: function (val) {
                    consumerName = val && val.toUpperCase();
                }
			});
			
			this._shouldShow = menuItem.IsShownInCart;
            this._sortedItems = [];
            this._isPriceVisible = menuItem.IsPriceVisible;
		}


        Object.defineProperty(OrderItem.prototype, "ExtendedPrice", {
            get: function () {
                return this.ItemPrice * this.Quantity;
            }
		});

		Object.defineProperty(OrderItem.prototype, "shouldShow", {
		    get: function () {
				return this._shouldShow || this._shouldShow === undefined;
		    }
	    });

		Object.defineProperty(OrderItem.prototype, "isPriceVisible", {
			get: function () {
				return this._isPriceVisible;
			}
		});

        function getTotalBasePrice(item) {
            return (item.ItemPrice * item.Quantity +
                _.reduce(item.Items, function (agg, i) {
                    return agg + getTotalBasePrice(i);
                }, 0));
        }

        Object.defineProperty(OrderItem.prototype, "TotalPrice", {
            get: function () {
                return getTotalBasePrice(this);
            }
        });

        function getOriginalTotalBasePrice(item) {
            return ((item.OriginalItemPrice * item.Quantity || 0) +
                _.reduce(item.Items, function (agg, i) {
                    return agg + getOriginalTotalBasePrice(i);
                }, 0));
        }

        Object.defineProperty(OrderItem.prototype, "PreItemPromotionTotalPrice", {
            get: function () {
                return getOriginalTotalBasePrice(this);
            }
        });

        Object.defineProperty(OrderItem.prototype, "SortedItems", {
            get: function () {
                if (!this.Items.isSortCached) {
                    this.Items.isSortCached = true;
                    this._sortedItems = _.sortBy(this.Items, 'SortIndex');
                }
                return this._sortedItems;
            }
        });

        OrderItem.prototype.modifyPrice = function(price, rule) {
	        this.OverridePrice = {
		        original: this.ItemPrice,
		        override: price,
		        pricingRule: rule
	        };
	        this.ItemPrice = price;
        };

        OrderItem.prototype.unModifyPrice = function(rule) {
	        if (this.OverridePrice && this.OverridePrice.pricingRule === rule) {
		        this.ItemPrice = this.OverridePrice.original;
		        this.OverridePrice = undefined;
	        }
        };

        OrderItem.prototype.removeChildItem = function (itemId, menuItemIncludedGroupXrefId) {
            this.Items = _.reject(this.Items, function (orderItem) {
                return orderItem.ItemId === itemId && orderItem.GroupInstanceId === menuItemIncludedGroupXrefId;
            });
            this.Items.isSortCached = false;
        };

        OrderItem.prototype.deleteItem = function (orderItem) {
            this.Items = _.reject(this.Items, function (oi) {
                return oi.ItemId === orderItem.ItemId;
            });
            this.Items.isSortCached = false;
        };

        OrderItem.prototype.addChildItem = function (item, isExclusive) {
            if (isExclusive) {
                this.Items = _.reject(this.Items, function (orderItem) {
                    return orderItem.GroupInstanceId === item.GroupInstanceId;
                });
            }
            this.Items.push(item);
            this.Items.isSortCached = false;
            return item;
        };

        OrderItem.prototype.changeQuantity = function(amount, menuItem) {
	        var quantity = this.Quantity + amount;
	        quantity = Math.max(menuItem.MinimumCount, quantity);
			if (menuItem.MaximumCount) {
				quantity = Math.min(menuItem.MaximumCount, quantity);
			}
			this.Quantity = quantity;
        };

        OrderItem.prototype.addIncludedGroupClone = function(cloneProspect) {
	        var copy = cloneIncludedGroup(cloneProspect);
	        this.clonedIncludedGroups.push(copy);
        };

        OrderItem.prototype.removeIncludedGroupClone = function() {
	        var removedGroup = this.clonedIncludedGroups.pop();
	        var removedGroupInstanceId = removedGroup.GroupInstanceId;
	        this.Items = _.filter(this.Items,
		        function(item) {
			        return item.GroupInstanceId !== removedGroupInstanceId;
		        });
        };

		function cloneIncludedGroup(includedGroup) {
			var copy = angular.copy(includedGroup);
			copy.GroupInstanceId = "0" + copy.GroupInstanceId;
			copy.Class = "clone";
			_.each(copy.MenuItems,
				function (mi) {
					mi.GroupInstanceId = copy.GroupInstanceId;
				});

			return copy;
		}

        return OrderItem;
    }
})();;
(function () {
	'use strict';

	angular.module('main').factory('OrderItemExt', ['$rootScope', '$timeout', 'common', 'events', 'OrderItem', 'includedGroupValidator', 'orderPricingService', 'cartService', 'notify', orderItemExtFactory]);

	function orderItemExtFactory($rootScope, $timeout, common, events, OrderItem, includedGroupValidator, orderPricingService, cartService, notify) {

		var onItemsChanged = _.debounce(function () {
			common.broadcast(events.includedItemsChanged, {});
		}, 100);

		function OrderItemExt(orderItem) {
			this.potentialIncludedGroups = {};
			this.orderItem = orderItem;
			this.includedOrderItemExtCache = {};
		}

		OrderItemExt.prototype.withIncludedGroup = function (includedGroup) {
			//(includedGroup.allowItemCountForClone === 0 && this.potentialIncludedGroups[includedGroup.GroupInstanceId]) ||
			return this.potentialIncludedGroups[includedGroup.GroupInstanceId] =
				this.potentialIncludedGroups[includedGroup.GroupInstanceId] ||
				new PotentialIncludedGroup(this, includedGroup);
		};

		OrderItemExt.prototype.cacheIncludedOrderItemExt = function (orderItemExt) {
			var groupInstanceCache = this.includedOrderItemExtCache[orderItemExt.orderItem.GroupInstanceId] = this.includedOrderItemExtCache[orderItemExt.orderItem.GroupInstanceId] || {};
			groupInstanceCache[orderItemExt.orderItem.ItemId] = orderItemExt;
		};

		OrderItemExt.prototype.getCachedIncludedOrderItemExt = function (groupInstanceId, itemId) {
			return this.includedOrderItemExtCache[groupInstanceId] && this.includedOrderItemExtCache[groupInstanceId][itemId];
		};

		OrderItemExt.prototype.getCachedIncludedOrderItem = function (groupInstanceId, itemId) {
			var ext = this.getCachedIncludedOrderItemExt(groupInstanceId, itemId);
			return ext && ext.orderItem;
		};

		// === PotentialIncludedGroup ===
		function PotentialIncludedGroup(parentOrderItemExt, includedGroup) {

			this._potentialIncludedItems = {};
			this.parentOrderItemExt = parentOrderItemExt;

			this.includedGroup = includedGroup;
			this.selectedOrderItems = [];
			this.ui = {
				isDisplayed: false,
				isStickyUi: includedGroup.DisplayStyle === 'sticky-group'
			};

			this._exclusiveMenuItem = undefined;
			this._nonExclusiveMenuItemIds = undefined;
			this.selectedOrderItemsChanged(false);
		}

		PotentialIncludedGroup.prototype.withMenuItem = function (menuItem) {
			return this._potentialIncludedItems[menuItem.Id] =
				this._potentialIncludedItems[menuItem.Id] ||
				new PotentialIncludedItem(this.parentOrderItemExt, menuItem, this);
		};

		PotentialIncludedGroup.prototype.selectedOrderItemsChanged = function (broadcast) {
			this.selectedOrderItems = _.where(this.parentOrderItemExt.orderItem.Items, {
				GroupInstanceId: this.includedGroup.GroupInstanceId
			});
			_.each(this._potentialIncludedItems, function (ii) {
				ii.isIncludedChanged();
			});

			if (broadcast) {
				if (this.selectedOrderItems.length > this.includedGroup.MaxChoices) {
					let selectedItem = this.selectedOrderItems[this.selectedOrderItems.length - 1];
					let selectedItemElement = document.querySelector('input[title="' + selectedItem.ItemName + '"]');

					selectedItemElement.click();

					notify.dialog('The maximum number of modifiers allowed has been reached');
				}

				onItemsChanged();
			}
		};

		PotentialIncludedGroup.prototype.isValid = function () {
			return !_.size(this.validate());
		};

		PotentialIncludedGroup.prototype.validate = function () {
			return includedGroupValidator.validate(this.includedGroup, this.selectedOrderItems);
		};

		PotentialIncludedGroup.prototype.exclusiveMenuItemChanged = function (broadcast) {
			var matchingOrderItems = this.selectedOrderItems;
			if (matchingOrderItems.length > 1) {
				//throw new Error("More than one match found for a single-selection choice.");
				this._exclusiveMenuItem = undefined;
			}
			if (matchingOrderItems.length === 0) {
				this._exclusiveMenuItem = undefined;
			} else {
				this._exclusiveMenuItem = _.findWhere(this.includedGroup.MenuItems, {
					Id: matchingOrderItems[0].ItemId
				});
			}
			if (broadcast) {
				onItemsChanged();
			}
		};

		Object.defineProperty(PotentialIncludedGroup.prototype, 'nonExclusiveMenuItemIds', {
			get: function () {
				if (!this._nonExclusiveMenuItemIds) {
					this.selectedOrderItemsChanged(false);
					this._nonExclusiveMenuItemIds = _.pluck(this.selectedOrderItems, 'ItemId');
				}
				return this._nonExclusiveMenuItemIds;
			},
			set: function (selectedMenuItemIds) {
				var self = this;
				this._nonExclusiveMenuItemIds = selectedMenuItemIds;
				_.each(self.selectedOrderItems, function (oldSelectedOrderItem) {
					var isMatchingMenuItem = function (newSelectedMenuItemId) {
						return oldSelectedOrderItem.ItemId == newSelectedMenuItemId;
					};
					if (!_.some(selectedMenuItemIds, isMatchingMenuItem)) {
						self.parentOrderItemExt.orderItem.removeChildItem(oldSelectedOrderItem.ItemId, oldSelectedOrderItem.GroupInstanceId);
					}
				});
				_.each(selectedMenuItemIds, function (newSelectedMenuItemId) {
					if (!_.some(self.selectedOrderItems, function (oldSelectedOrderItem) {
						return oldSelectedOrderItem.ItemId == newSelectedMenuItemId;
					})) {
						var orderItem = self.parentOrderItemExt.getCachedIncludedOrderItem(self.includedGroup.GroupInstanceId, newSelectedMenuItemId) || new OrderItem(_.findWhere(self.includedGroup.MenuItems, { Id: newSelectedMenuItemId }), self.parentOrderItemExt.orderItem, self.includedGroup);
						self.parentOrderItemExt.orderItem.addChildItem(orderItem, false);
					}
				});
				self.selectedOrderItemsChanged(true);
			}
		});

		Object.defineProperty(PotentialIncludedGroup.prototype, "exclusiveMenuItem", {
			get: function () {
				if (!this._exclusiveMenuItem) {
					this.exclusiveMenuItemChanged(false);//because MenuItems are lazy-loaded.
				}
				return this._exclusiveMenuItem;
			},
			set: function (menuItem) {
				var self = this, isChanged = false;

				if (self._exclusiveMenuItem && self._exclusiveMenuItem.Id != menuItem.Id) {
					self.parentOrderItemExt.orderItem.removeChildItem(menuItem.Id, menuItem.GroupInstanceId);

					if (window.location.href.includes("checkout")) {
						orderPricingService.rePriceOrder(cartService.getCart().order);
					}

					isChanged = true;
				}
				if (!self._exclusiveMenuItem || self._exclusiveMenuItem.Id != menuItem.Id) {
					var orderItem = self.parentOrderItemExt.getCachedIncludedOrderItem(menuItem.GroupInstanceId, menuItem.Id) || new OrderItem(menuItem, self.parentOrderItemExt.orderItem, self.includedGroup);
					self.parentOrderItemExt.orderItem.addChildItem(orderItem, true);


					if (window.location.href.includes("checkout")) {
						orderPricingService.rePriceOrder(cartService.getCart().order);
					}

					isChanged = true;
				}
				if (isChanged) {
					self.selectedOrderItemsChanged(false);
					self.exclusiveMenuItemChanged(true);
				}
			}
		});

		Object.defineProperty(PotentialIncludedGroup.prototype, "selectionCount", {
			get: function () {
				return orderPricingService.getSelectionCount(this.selectedOrderItems);
			},
			set: function () { }
		});


		// === PotentialIncludedItem ===
		function PotentialIncludedItem(parentOrderItemExt, menuItem, potentialIncludedGroup) {
			this.parentOrderItemExt = parentOrderItemExt;
			this.menuItem = menuItem;
			this.potentialIncludedGroup = potentialIncludedGroup;

			this._isIncluded = false;
			this.isIncludedChanged();
			if (menuItem.IsDefaultSelection && !potentialIncludedGroup.selectedOrderItems.length) {
				this.isIncluded = true;
			}
		}

		PotentialIncludedItem.prototype.getOrderItemExt = function () {
			var self = this;

			var orderItem = _.findWhere(self.parentOrderItemExt.orderItem.Items, {
				ItemId: self.menuItem.Id,
				GroupInstanceId: self.menuItem.GroupInstanceId
			});
			if (orderItem) {
				var orderItemExt = self.parentOrderItemExt.getCachedIncludedOrderItemExt(self.menuItem.GroupInstanceId, self.menuItem.Id);
				if (orderItemExt) {
					return orderItemExt;
				}
				orderItemExt = new OrderItemExt(orderItem);
				self.parentOrderItemExt.cacheIncludedOrderItemExt(orderItemExt);
				return orderItemExt;
			}
			return undefined;
		};

		PotentialIncludedItem.prototype.isIncludedChanged = function () {
			this._isIncluded = !!this.getOrderItemExt();
		};

		Object.defineProperty(PotentialIncludedItem.prototype, "isIncluded", {
			get: function () {
				return this._isIncluded;
			},
			set: function (val) {
				var self = this;
				if (val) {
					if (!self._isIncluded) {
						self._isIncluded = true;
						$timeout(function () {
							let previousTotalPrice;
							if (window.location.href.includes("checkout")) {
								previousTotalPrice = self.parentOrderItemExt.orderItem.TotalPrice;
							}

							var orderItem = self.parentOrderItemExt.getCachedIncludedOrderItem(self.menuItem.GroupInstanceId, self.menuItem.Id) || new OrderItem(self.menuItem, self.parentOrderItemExt.orderItem, self.potentialIncludedGroup.includedGroup);
							self.parentOrderItemExt.orderItem.addChildItem(orderItem);
							self.potentialIncludedGroup._nonExclusiveMenuItemIds = undefined;
							self.potentialIncludedGroup.selectedOrderItemsChanged(true);


							if (window.location.href.includes("checkout")) {
								orderPricingService.rePriceOrder(cartService.getCart().order);
							}

						}, 10);
					}
				} else if (val === false) {
					self._isIncluded = false;
					$timeout(function () {
						let previousTotalPrice;
						if (window.location.href.includes("checkout")) {
							previousTotalPrice = self.parentOrderItemExt.orderItem.TotalPrice;
						}

						self.parentOrderItemExt.orderItem.removeChildItem(self.menuItem.Id, self.menuItem.GroupInstanceId);
						self.potentialIncludedGroup._nonExclusiveMenuItemIds = undefined;
						self.potentialIncludedGroup.selectedOrderItemsChanged(true);

						if (window.location.href.includes("checkout")) {
							let requiredItemTaxRate = 0;
							if (self.parentOrderItemExt.orderItem.ItemTaxRate != null) {
								requiredItemTaxRate = self.parentOrderItemExt.orderItem.ItemTaxRate / 100;
							}



							let requiredItemPrice = self.menuItem.Price;

							let curTotalPrice = self.parentOrderItemExt.orderItem.TotalPrice;

							let prevTotalTax = previousTotalPrice * requiredItemTaxRate;

							let curTotalTax = curTotalPrice * requiredItemTaxRate;

							let diffTotal = Math.abs(prevTotalTax - curTotalTax);

							// add to the subtotal
							cartService.getCart().order.pricedOrder.Subtotal -= requiredItemPrice;

							// add the required item tax to the tax
							cartService.getCart().order.pricedOrder.Tax -= diffTotal;

							// add to the total
							cartService.getCart().order.pricedOrder.Total = cartService.getCart().order.pricedOrder.Subtotal + cartService.getCart().order.pricedOrder.Tax;

						}
					}, 10);
				}
			}
		});

		return OrderItemExt;
	}
})();;
(function () {
	'use strict';

	angular.module('main').factory('Payment', ['Address', paymentFactory]);

	function paymentFactory(Address) {

		function Payment() {
			this.ExistingCreditCard = {
				IsSelected: false,
				PaymentMethodId: null
			};
			this.NewCreditCard = {
				CreditCard: {
					CardType: '',
					CardNumber: '',
					ExpirationMonth: '',
					ExpirationYear: '',
					CardVerificationValue: ''
				},
				IsSaved: false,
				IsSelected: false
			};
			this.CardHolder = {
				FirstName: '',
				LastName: '',
                PostalCode: '',
				Address: new Address()
			};

			this.LoyaltyPayment = null;
			this.OfferPayment = null;
			this.MealPlanPayment = null;
			this.GiftCardPayment = null;
		}

		return Payment;
	}
})();;
(function () {
	'use strict';

	angular.module('main').factory('User', [userFactory]);

	function userFactory() {

		function User(data) {
			_.extend(this, data);

			var tempEmailRegEx = /temp\d+@togoorder.com/g;

			this.Email = this.Email.replace(tempEmailRegEx, '');
		}

		return User;
	}
})();;
(function () {
    'use strict';

    angular.module('common').directive('flickityCarousel', ['$timeout', function ($timeout) {
        return {
            scope: {
                flickityCarousel: "<",
                selectedIndex: "=",
                loaded: "&"
            },
            restrict: 'A',
            transclude: true,
            template: '',
            link: function (scope, element, attrs) {
                let options = scope.flickityCarousel;

                function selectedIndexChanged(newValue, isInstant) {
                    var flkty = scope._flickity;
                    if (!flkty || newValue === undefined || newValue < 0) {
                        return;
                    }
                    var elements = flkty.flickity('getCellElements');
                    var selectedElement = $(elements[newValue]);

                    if (!selectedElement.is('.is-unique-selected') && !selectedElement.is('.is-nav-selected')) {
                        flkty.flickity('select', newValue, undefined, isInstant);
                        $(elements).removeClass('is-unique-selected');
                        selectedElement.addClass('is-unique-selected');
                    }
                }

                scope.$watch('selectedIndex', function (newValue, oldValue) {
                    $timeout(function () {
                        selectedIndexChanged(newValue);
                    });
                });

                function initializeFlickity() {
                    scope._flickity = element.flickity(options);
                    if (scope.loaded) {
                        scope.loaded();
                    }
                    scope._flickity.on('staticClick.flickity', function(event, pointer, cellElement, cellIndex) {
                        selectedIndexChanged(cellIndex);
                    });
                    selectedIndexChanged(scope.selectedIndex, true);
                }

                //wow. this is ugly. show me how to do this right.
                function giveItAWhirl(waited) {
                    let cells = element.find(options.cellSelector);
                    let clientWidth = (cells[cells.length - 1] || {}).clientWidth;
                    var rendered = clientWidth > 0;
                    if (!rendered && waited < 20000) {
                        $timeout(function () {
                            giveItAWhirl(waited + 12);
                        }, 12);
                    } else {
                        initializeFlickity();
                    }
                }

                $timeout(function () {
                    giveItAWhirl(0);
                }, 0);


            }
        };
    }]);

})();;
(function () {
	'use strict';

	var common = angular.module('common');
	
	common.directive("input", ['$timeout',
		function ($timeout) {
			return {
				restrict: 'E',
				link: function (scope, element, attrs) {
					$timeout(function () {
						element.placeholder();
					}, 0, false);
				}
			};
		}
	]);

	common.directive("select", ['$timeout',
		function ($timeout) {
			return {
				restrict: 'E',
				link: function (scope, element, attrs, ctrl) {
					$timeout(function () {
						element.find("option[value='?']")
							.text(element.data("placeholder") || "")
							.addClass("non-option")
							.prop('hidden', true)
							.prop('disabled', true);
					}, 0, false);
				}
			};
		}
	]);

	common.directive("focusClick", ['$timeout',
		function ($timeout) {
			return {
				restrict: 'A',
				link: function (scope, element, attrs) {
					var el = $(element);
					var isFocusNative = false;

					var focusHandler = function () {
						isFocusNative = true;
					};

					el.bind("focus", focusHandler);

					el.click(function () {
						el.unbind("focus", focusHandler);
						if (!isFocusNative) {
							$timeout(function() {
								el.focus();
							}, 0, false);
						}
						return false;
					});
				}
			};
		}
	]);

	common.directive('focusOn', ['$timeout',function ($timeout) {
		return function (scope, elem, attr) {
			scope.$on(attr.focusOn,
				function(e) {
					if (elem.is(":focus")) {
						return;
					}
					$timeout(function() {
							elem[0].focus();
							elem[0].select();
						}, 0, false);
				});
		};
	}]);

	common.directive('keyboardActivate', [function () {
		return function (scope, elem, attr) {
			var el = $(elem);
			el.bind("keypress", function(e) {
				if (e.keyCode === 13 || e.keyCode === 32) {
					el.click();
				}
			});
		};
	}]);

})();
;
(function() {
    'use strict';
    var app = angular.module('common');

	var formats = "(999)999-9999|(999) 999-9999|999-999-9999|999.999.9999|9999999999";
	var pattern = "^(" +
		formats
		.replace(/([\(\)])/g, "\\$1")
		.replace(/9/g, "\\d") +
		")$";
	
	var phonePattern = new RegExp(pattern);

	app.directive('restrictValidate', [
		'$parse', function ($parse) {
			return {
				restrict: 'A',
				require: 'ngModel',
				link: function (scope, iElement, iAttrs, ngModel) {

					scope.$watch(iAttrs.ngModel, function (value) {
						if (!value) {
							ngModel.$setValidity('format', true);
							return;
						}
						value = value + '';

						var regEx;
						if (iAttrs.restrictValidate === 'phone') {
							regEx = phonePattern;
						} else {
							regEx = new RegExp(iAttrs.restrictValidate, 'g');
						}

						var isMatch = regEx.test(value);
						ngModel.$setValidity('format', isMatch);
					});
				}
			};
		}
	]);

	var phoneRestriction = new RegExp('[^0-9\\.\\-\\(\\) ]|[ ]{2,}', 'g');

    app.directive('restrict', [
        '$parse', function($parse) {
            return {
                restrict: 'A',
                require: 'ngModel',
				link: function (scope, iElement, iAttrs, ngModel) {

					scope.$watch(iAttrs.ngModel, function (value) {
                        if (!value) {
                            return;
                        }
						value = value + '';

						var regEx;
						if (iAttrs.restrict === 'phone') {
							regEx = phoneRestriction;
						} else {
							regEx = new RegExp(iAttrs.restrict, 'g');
						}

						var newVal = value.toLowerCase().replace(regEx, '');

						$parse(iAttrs.ngModel).assign(scope, newVal); 
                    });
                }
            };
        }
	]);

	app.directive('allowPattern', [
		'$parse', function ($parse) {
			return {
				restrict: 'A',
				require: 'ngModel',
				link: function (scope, iElement, iAttrs, ngModel) {

					scope.$watch(iAttrs.ngModel, function (value) {

						if (!value) {
							return;
						}
						value = value + '';

						var regEx = new RegExp(iAttrs.allowPattern, 'g');

						var matches = regEx.exec(value);

						var newVal = (matches && matches.length && matches[0]) || '';

						$parse(iAttrs.ngModel).assign(scope, newVal);
					});
				}
			};
		}
	]);


})();

;
(function () {
	'use strict';

	angular.module('common')
		.directive("passwordVerify", function () {
			return {
				require: "ngModel",
				scope: {
					passwordVerify: '='
				},
				link: function (scope, element, attrs, ctrl) {
					function validator(viewValue) {
						var origin = scope.passwordVerify || '';
						var same = (origin === viewValue);
						ctrl.$setValidity("passwordVerify", same);
						return viewValue;
					}
					ctrl.$parsers.unshift(validator);

					scope.$watch(function () {
						return scope.passwordVerify;
					}, function () {
						validator(ctrl.$viewValue);
					});
				}
			};
		});
})();
;
(function () {
    'use strict';
    var app = angular.module('common');

    app.directive("qrcode", ['$timeout',
        function ($timeout) {
            return {
                restrict: 'A',
                link: function (scope, element, attrs) {
	                $timeout(function() {
		                var el = $(element);
		                new QRCode(el.attr("id"),
			                {
				                text: attrs.qrcode,
				                width: 100,
				                height: 100,
				                colorDark: "#000000",
				                colorLight: "#ffffff",
				                correctLevel: QRCode.CorrectLevel.H
			                });
	                });
	                //element.mask(attrs.mask);
                }
            };
        }
    ]);

})();
;
(function() {
	'use strict';

	angular.module('common').directive('sAccordion', function() {
		return {
			restrict: 'A',
			link: function(scope, element, attrs) {
				element.accordion(scope.$eval(attrs.sAccordion));
			}
		};
	});
})();;
(function () {
    'use strict';

    var controllerId = 'enrollAfterSignupModal';
    angular.module('common').controller(controllerId, ['$uibModalInstance', 'isFromSignup', enrollAfterSignupModal]);

    function enrollAfterSignupModal($modalInstance, isFromSignup) {
        var vm = this;
        vm.isFromSignup = isFromSignup;
        vm.close = function () { $modalInstance.close(false); };
        vm.ok = function () { $modalInstance.close(true); };
    }

})();;
(function() {
    'use strict';

    var controllerId = 'logout';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$state', '$q', '$window', '$timeout', 'common', 'events', 'userService', 'cartService', 'crmService', logout]);

    function logout($rootScope, $state, $q, $window, $timeout, common, events, userService, cartService, crmService) {
        var tgo = togoorder || {};

	    var userName = togoorder.userData.username;

        userService.removeAuthentication();
		cartService.destroy();
		common.broadcast(events.logout, userName);
		crmService.logout();


		$timeout(function() {
				var logoutResult = togoorder.callInjectedStrategy('logout', userName);
				if (!logoutResult) {
					common.log('logout...');
					afterLogout();
				} else {
					if (logoutResult.result) {
						$q.when(logoutResult.result).then(function() {
								common.log('logout handled');
							},
							function() {
								common.log('logout not handled');
								afterLogout();
							});
					}
					return;
				}
			},
			4);

		function afterLogout() {
			if (tgo.locationId) {
				if (tgo.locationWebsite) {
					$window.location.replace(tgo.locationWebsite);
				} else {
					$state.go('locationHome', {}, { location: 'replace' });
				}
			} else {
				$state.go('home', {}, { location: 'replace' });
			}
		}
    }
})();;
(function () {
    'use strict';

    var serviceId = 'resetPasswordService';

    angular.module('main').service(serviceId, ['$http', '$q', 'api', 'userService', 'securityFormService', 'notify', 'spinner', resetPasswordService]);

    function resetPasswordService($http, $q, api, userService, securityFormService, notify, spinner) {
        var service = this;
        var loginViewModel = securityFormService.loginViewModel;

        service.initialize = initialize;
        service.resetPassword = resetPassword;
        service.resetPasswordViewModel = {
            errors: [],
            isValidationVisible: false
        };

        function initialize() {
            service.resetPasswordViewModel.errors = [];
            service.resetPasswordViewModel.isValidationVisible = false;
        }

        function resetPassword(isValid, onSuccessfulResetPassword) {
            initialize();
            if (!isValid) {
                service.resetPasswordViewModel.isValidationVisible = true;
                notify.warning('Please correct any errors before continuing.');
                return;
            }
            spinner.spinnerShow();
            service.resetPasswordViewModel.emailAddress = loginViewModel.userName;
            userService.resetPassword(service.resetPasswordViewModel)
                .then(function (result) {

                    initialize();

                    var message = 'Be on the lookout for an email with a link to reset your password.';

                    if (result && result.data && result.data.Message) {
                        message = result.data.Message;
                    }

                    notify.dialog('Success!', message);

                    onSuccessfulResetPassword();
                }, onFailedResetPassword)
            ['finally'](function () {
                spinner.spinnerHide();
            });
        }

        function onFailedResetPassword(error) {
            if (error && service.resetPasswordViewModel.errors.indexOf(error) === -1) {
                service.resetPasswordViewModel.errors.push(error);
            }
        }

    }
})();
;
(function () {
    'use strict';

    var controllerId = 'rofoTipModal';
    angular.module('common').controller(controllerId, ['$uibModalInstance', rofoTipModal]);

    function rofoTipModal($modalInstance) {
        var vm = this;

        vm.close = $modalInstance.close;
    }

})();;
(function() {
    'use strict';


    var controllerId = "samlLogin";
    angular.module('main').controller(controllerId, ['$rootScope', '$state', '$stateParams', '$window', '$location', '$timeout', 'common', 'events', 'userService', samlLogin]);

    function samlLogin($rootScope, $state, $stateParams, $window, $location, $timeout, common, events, userService) {
        //console.log('$stateParams', $stateParams);

        var returnUrlHash = $stateParams.returnUrlHash;

        activate();

        function activate() {
            common.activateController([], controllerId);

            if (userService.isAuthenticated()) {
                //do tricks here with hash versus state
                $state.go(returnUrlHash || 'locationHome', {}, { location: 'replace' });
            } else {
                $window.location.replace('https://' + $window.location.host + '/Saml/?id=' + togoorder.merchantId + '&locationId=' + togoorder.locationId + '&returnUrlHash=' + returnUrlHash);
            }
        }

    }

})();;
(function () {
	'use strict';

	var controllerId = 'securityDialog';
	angular.module('main')
        .controller(controllerId, ['$rootScope', '$state', '$uibModalInstance', '$uibModal', '$window', 'common', 'events', 'spinner', 'notify', 'userService', 'linkService', 'signupService', 'securityFormService', 'resetPasswordService', 'signinService', 'allowGuestCheckout', 'mode', 'goSignUp', securityDialogController]);

	function securityDialogController($rootScope, $state, $modalInstance, $modal, $window, common, events, spinner, notify, userService, linkService, signupService, securityFormService, resetPasswordService, signinService, allowGuestCheckout, mode, goSignUp) {
		var vm = this,
			nextState = null;

		vm.signInViewModel = signinService.signInViewModel;
		vm.signUpViewModel = signupService.signUpViewModel;
		vm.loginViewModel = securityFormService.loginViewModel;
	    vm.resetPasswordViewModel = resetPasswordService.resetPasswordViewModel;

		vm.login = login;
		vm.signUp = signUp;
	    vm.resetPassword = resetPassword;
		vm.cancel = cancel;
		vm.showSignUp = showSignUp;
		vm.showResetPassword = showResetPassword;
		vm.showSignIn = showSignIn;
		vm.isLogin = (mode !== 'signUp');
		vm.isSignup = !vm.isLogin;
		vm.isResetPassword = false;
		vm.allowGuestCheckout = allowGuestCheckout;
		vm.continueAsGuest = continueAsGuest;
	    vm.togglePhysical = togglePhysical;
	    vm.goSignUp = goSignUp;
	    vm.toggleEnrollNext = toggleEnrollNext;
	    vm.showRofoTip = showRofoTip;

        //Required inputs messaging....
        vm.mandatoryText = { name: "required input", url: "shared/mandatoryMsg.html"}
		//Stricter regex than this angular version's built in chromium regex (foo@foo is valid - because me@intenal_domain)
        vm.validEmailPattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;


        initialize();

		function initialize() {
			try {
				nextState = userService.getNextState();
			} catch (e) {
				nextState = null;
			}

			if (nextState !== null) {
				var nameBuffer = nextState.name + '';
				var errorBuffer = nextState.error + '';
				var paramsBuffer = nextState.params || {};
				userService.clearNextState();
				nextState = {
					name: nameBuffer,
					error: errorBuffer,
					params: paramsBuffer
				};
				//vm.stateError = nextState.error ? nextState.error : 'You must be signed-in to view this page';
			}
		    if (vm.goSignUp) {
                showSignUp();
            }
		}

		function resetPassword(isValid) {
		    resetPasswordService.resetPassword(isValid, function() {
		        showSignIn();
		    }); 
		}

	    function signUp(isValid) {
		    signupService.signUp(isValid, function() {
		        showSignIn();
		    });
		}

		function login(isValid) {
		    signinService.signIn(isValid, onSuccessfulLogin);
		}

		function cancel() {
		    signupService.initialize();
		    signinService.initialize();
			$modalInstance.dismiss();
			$modalInstance = null;
		}

		function onSuccessfulLogin() {
			$modalInstance.close(userService.getUserData());
			$modalInstance = null;
			if (vm.signUpViewModel.enrollAfterSignup) {
			    $state.go('achEnrollment');
			}
			else if (nextState && nextState.name) {
			    $state.go(nextState.name, nextState.params);
			} else {
			    $state.reload();
			}
		}

        function showRofoTip() {
            $modal.open({
                templateUrl: 'app/security/rofoTipModal.html',
                controller: 'rofoTipModal as vm',
                size: 'md'
            });
        }

		function continueAsGuest() {
			userService.setUserAsGuest()
				.then(function() {
					spinner.spinnerHide();

					$modalInstance.close({});
					$modalInstance = null;
					if (nextState && nextState.name) {
						$state.go(nextState.name, nextState.params);
					}
				});
		}

		function showResetPassword() {
			if (togoorder.callInjectedStrategy('forgotPassword')) {
				spinner.spinnerShow();
				return;
			}
			vm.isLogin = false;
		    vm.isSignup = false;
		    vm.isResetPassword = false;

		    // this needs to be directive-ified
		    $(".security-dialog .accordion").accordion('open', 2);

		}

		function showSignUp() {
			vm.isLogin = false;
			vm.isSignup = true;
			vm.isResetPassword = false;

			vm.signUpViewModel.signUpWithLoyalty = togoorder.merchantLocation.MerchantSignUpWithLoyalty;
			vm.signUpViewModel.loyaltyPrefix = togoorder.merchantLocation.LoyaltyProfile != null ? togoorder.merchantLocation.LoyaltyProfile.AccountPrefix : '';
			vm.signUpViewModel.loyaltyLength = togoorder.merchantLocation.LoyaltyProfile != null ? togoorder.merchantLocation.LoyaltyProfile.AccountLength : 0;

			// this needs to be directive-ified
			$(".security-dialog .accordion").accordion('open', 1);

		}

		function showSignIn() {
			vm.isLogin = true;
			vm.isSignup = false;
			vm.isResetPassword = false;

			signinService.initialize();

			// this needs to be directive-ified
			$(".security-dialog .accordion").accordion('open', 0);
		}

		function togglePhysical() {
		    vm.signUpViewModel.physicalCard = !vm.signUpViewModel.physicalCard;
            if (vm.signUpViewModel.physicalCard) {
                $modal.open({
                    templateUrl: 'app/security/enrollAfterSignupModal.html',
                    controller: 'enrollAfterSignupModal as vm',
                    size: 'md',
                    resolve: {
                        isFromSignup: true
                    }
                }).result.then(function (res) {
                    vm.signUpViewModel.enrollAfterSignup = res;
                });
            } else {
                $("#cardNumber").val('');
                $("#confirmCardNumber").val('');
                vm.signUpViewModel.enrollAfterSignup = false;
            }
		}

		function toggleEnrollNext() {
		    vm.signUpViewModel.enrollAfterSignup = !vm.signUpViewModel.enrollAfterSignup;
        }

		$rootScope.$on('$stateChangeStart', function (e, toState, toParams, fromState, fromParams) {
			if ($modalInstance) { //modal is open
				cancel();
			}
		});


	}
})();;
(function() {
    'use strict';

    var serviceId = 'securityFormService';

    angular.module('main').service(serviceId, ['$http', '$q', 'api', 'userService', 'notify', 'spinner', securityFormService]);

    function securityFormService($http, $q, api, userService, notify, spinner) {
        var service = this;

        var isFishbowlLoyalty = togoorder && togoorder.merchantLocation && togoorder.merchantLocation.LoyaltyProfile  && togoorder.merchantLocation.LoyaltyProfile.LoyaltyProviderType === 11;

        service.loginViewModel = {
            userName: '',
            password: '',
            passwordMinLength: 7,
            isOptInRequired: isFishbowlLoyalty
        };
    }
})();
;
(function() {
    'use strict';

    var controllerId = "showLogin";
    angular.module('main').controller(controllerId, ['$rootScope', '$state', '$stateParams', '$location', '$timeout', 'common', 'events', 'userService', showLogin]);

    function showLogin($rootScope, $state, $stateParams, $location, $timeout, common, events, userService) {
        //console.log('$stateParams', $stateParams);

        activate();

        function activate() {
            common.activateController([], controllerId);

            userService.login(function () {
                $state.go($stateParams.returnStateName || 'locationHome', {}, { location: 'replace' });
            });
        }

    }

})();;
(function() {
    'use strict';

    var serviceId = 'signinService';

    angular.module('main').service(serviceId, ['$http', '$q', '$state', 'api', 'userService', 'securityFormService', 'notify', 'spinner', signinService]);

    function signinService($http, $q, $state, api, userService, securityFormService, notify, spinner) {
        var service = this;

        service.loginViewModel = securityFormService.loginViewModel;

        service.signInViewModel = {
            isPersisted: false,
            errors: [],
            isValidationVisible: false
        };

        this.signIn = signIn;
        this.initialize = initialize;

        function signIn(isValid, onSuccessfulLogin) {
            service.initialize();
            if (!isValid) {
                service.signInViewModel.isValidationVisible = true;
                notify.warning('Please correct any errors marked with a star before signing in.');
                return;
            }
            spinner.spinnerShow();
            userService.authenticate(service.loginViewModel.userName,
                    service.loginViewModel.password,
                    service.signInViewModel.isPersisted)
                .then(onSuccessfulLogin, onFailedLogin);

        }

        function onFailedLogin(error) {
            if (error && service.signInViewModel.errors.indexOf(error) === -1) {
				service.signInViewModel.errors.push(error);
	            spinner.spinnerHide('signIn');
            }
        }

        function initialize() {
            service.signInViewModel.errors = [];
            service.signInViewModel.isValidationVisible = false;
        }

    }
})();
;
(function () {
	'use strict';

	var serviceId = 'signupService';

	angular.module('main').service(serviceId, ['$http', '$q', '$timeout', 'api', 'securityFormService', 'linkService', 'userService', 'notify', 'spinner', 'crmService', 'common', 'events', signupService]);

	function signupService($http, $q, $timeout, api, securityFormService, linkService, userService, notify, spinner, crmService, common, events) {
	    var service = this;
	    service.signUp = signUp;
	    service.initialize = initialize;
        function SignUpViewModel() {
            this.lastName="";
            this.firstName =  "";
            this.confirmPassword = "";
            this.confirmEmailAddress = "";
            this.agreedToTerms = false;
            this.errors = [];
            this.isValidationVisible = false;
            this.applicationId = togoorder.applicationId;
            this.opened = false;
            this.dateOptions = {
                initDate :  new Date('1980/01/01'),
                formatYear :  'yyyy',
                startingDay: 1,
                altInputFormats: [
	                'M!/d!/yyyy', 
	                'M!-d!-yyyy', 
	                'M!.d!.yyyy', 
	                'M!d!yyyy', 
	                'MM!/dd!/yyyy', 
	                'MM!-dd!-yyyy', 
	                'MM!.dd!.yyyy', 
	                'MM!dd!yyyy', 
	                'MM/dd/yyyy',
	                'M!/d!/yy', 
	                'M!-d!-yy', 
	                'M!.d!.yy', 
	                'M!d!yy', 
	                'MM!/dd!/yy', 
	                'MM!-dd!-yy', 
	                'MM!.dd!.yy', 
	                'MM!dd!yy', 
	                'MM/dd/yy'

                ]
            };
            this.open = open;
            this.showTerms = showTerms;
            this.phone = '';
            this.zipCode = '';
            this.cardNumber = '';
            this.confirmCardNumber = '';
            this.birthdate = undefined;
            this.physicalCard = false;
            this.enrollAfterSignup = null;
			this.signUpWithLoyalty = togoorder.merchantLocation.MerchantSignUpWithLoyalty;
	        this.loyaltyProviderType = togoorder.merchantLocation.LoyaltyProfile && togoorder.merchantLocation.LoyaltyProfile.LoyaltyProviderType;
            this.loyaltyPrefix = togoorder.merchantLocation.LoyaltyProfile != null ? togoorder.merchantLocation.LoyaltyProfile.AccountPrefix : '';
            this.loyaltyLength = togoorder.merchantLocation.LoyaltyProfile != null ? togoorder.merchantLocation.LoyaltyProfile.AccountLength : 0;
            this.brandName = togoorder.merchantName;
            this.emailOptIn = false;
            this.optInText = togoorder.merchantLocation.CustomOptIn || `Love ${this.brandName}? Sign up to receive the latest news.`;
            this.isPhoneRequired = this.signUpWithLoyalty ||
	            this.loyaltyProviderType == 6 ||
	            this.loyaltyProviderType == 7 ||
	            this.loyaltyProviderType == 8 ||
	            this.loyaltyProviderType == 10 ||
	            (togoorder.authStrategy && togoorder.authStrategy.isPhoneRequired);

            this.isZipRequired = this.signUpWithLoyalty ||
	            (togoorder.authStrategy && togoorder.authStrategy.isZipRequired);
        }

        Object.defineProperty(SignUpViewModel.prototype, "birthdateVm", {
            get: function () {
                return this.birthdate;
            },
            set: function (val) {
                var m = val && moment(val);
                while (m && m.isAfter(moment().add(-10, 'year'))) {
                    m = m.add(-100, 'year');
                }
                this.birthdate = m && m.toDate();
            }
        });

        service.signUpViewModel = new SignUpViewModel();


	    function showTerms() {
	        linkService.openLinkInNewWindow("Home/TermsOfService?merchantId=" + togoorder.merchantId, "tos");
	        return false;
	    }

	    function open($event) {
	        if (!service.signUpViewModel.opened) {
	            $event.preventDefault();
	            $event.stopPropagation();
	            service.signUpViewModel.opened = true;
	        } else {
	            service.signUpViewModel.opened = false;
	        }
        }

	    function initialize() {
	        service.signUpViewModel.errors = [];
	        service.signUpViewModel.isValidationVisible = false;
	    }

	    function signUp(isValid, onSuccess) {
	        initialize();

	        if (service.signUpViewModel.cardNumber &&
	            service.signUpViewModel.cardNumber.length > 0 &&
	            service.signUpViewModel.cardNumber.length !==
	            service.signUpViewModel.loyaltyLength - service.signUpViewModel.loyaltyPrefix.length) {

	            service.signUpViewModel.isValidationVisible = true;
	            notify.warning('Please check your card number.');
	            return;
	        }

	        service.signUpViewModel.cardNumber = service.signUpViewModel.cardNumber || '';
	        service.signUpViewModel.confirmCardNumber = service.signUpViewModel.confirmCardNumber || '';

	        if (service.signUpViewModel.cardNumber !== service.signUpViewModel.confirmCardNumber) {
	            service.signUpViewModel.isValidationVisible = true;
	            notify.warning('The card you entered does not match!');
	            return;
	        }

	        if (securityFormService.loginViewModel.userName !== service.signUpViewModel.confirmEmailAddress) {
	            service.signUpViewModel.isValidationVisible = true;
	            notify.warning('The email you entered does not match!');
	            return;
	        }

	        if (!isValid) {
	            service.signUpViewModel.isValidationVisible = true;
	            notify.warning('Please correct any errors marked with a star before signing up.');
	            return;
	        }
	        spinner.spinnerShow();

	        service.signUpViewModel.emailAddress = securityFormService.loginViewModel.userName;
	        service.signUpViewModel.password = securityFormService.loginViewModel.password;
	        service.signUpViewModel.locationId = togoorder.locationId;
	        service.signUpViewModel.merchantId = togoorder.merchantId;
	        service.signUpViewModel.cardNumber = service.signUpViewModel.cardNumber === "" ? null : "" + service.signUpViewModel.loyaltyPrefix + service.signUpViewModel.cardNumber;
	        userService.signUp(service.signUpViewModel).then(function() {
	            onSuccessfulSignUp();
				onSuccess();
				common.broadcast(events.signUp, {userName: securityFormService.loginViewModel.userName});
				crmService.signUp()
	        }, onFailedSignUp);
	    }

	    function onSuccessfulSignUp() {
			spinner.spinnerHide('onSuccessfulSignUp');
	        initialize();
	    }

	    function onFailedSignUp(error) {
			spinner.spinnerHide('onFailedSignUp');

	        if (error && service.signUpViewModel.errors.indexOf(error) === -1) {
	            service.signUpViewModel.errors.push(error);
	        }
	    }

	}
})();;
(function() {
	'use strict';

	var controllerId = 'signupStandAlone';
	angular.module('main')
        .controller(controllerId, ['$rootScope', '$state', '$window', 'signupService', 'securityFormService', 'common', 'events', 'userService', 'cartService', signupStandAlone]);

	function signupStandAlone($rootScope, $state, $window, signupService, securityFormService, common, events, userService, cartService) {
	    var tgo = togoorder || {},
	        vm = this;

	    vm.signUp = signUp;
	    vm.signUpViewModel = signupService.signUpViewModel;
	    vm.loginViewModel = securityFormService.loginViewModel;

		activate();

		function activate() {
		    common.activateController([], controllerId)
		    .then(function () {
		    });
		}

		function signUp(isValid) {
		    signupService.signUp(isValid, function() {
		        togoorder.callInjectedStrategy('signupSuccessful');
		    });
		}

	}
})();;
(function () {
	'use strict';

	var controllerId = 'userFeatureAlert';
	angular.module('common').controller(controllerId, ['$uibModalInstance', userFeatureAlert]);

	function userFeatureAlert($modalInstance) {
		var vm = this;

		vm.close = close;
		vm.dismiss = dismiss;

		function close() {
			$modalInstance.close();
		}


		function dismiss() {
		    $modalInstance.dismiss();
		}

	}

})();;
(function () {
	'use strict';

	// Helpful links:
	// https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtag#purchase
	// https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtm

	angular.module('main').factory('analyticsService', ['$rootScope', 'events', analyticsService]);

	function analyticsService($rootScope, events) {

		return {
			initialize: initialize,
		};

		function initialize() {
			// we don't need ecommerce object for this event, just send null
			$rootScope.$on(events.promotionIsSelected, (e, payload) => sendData("select_promotion", null));
			// we don't need ecommerce object for this event, just send null
			$rootScope.$on(events.newPaymentMethodAdded, (e, payload) => sendData("add_payment_info", null));
			$rootScope.$on(events.addItemToNextCart, (e, payload) => sendData("add_to_cart", convertToEcommerceMenuItemPayload(payload)));
			$rootScope.$on(events.itemRemoved, (e, payload) => sendData("remove_from_cart", convertToEcommerceMenuItemPayload(payload)));
			$rootScope.$on(events.login, (e, payload) => sendAuthenticationData("login"));
			$rootScope.$on(events.signUp, (e, payload) => sendAuthenticationData("sign_up"));
			$rootScope.$on(events.presentCart, (e, payload) => sendData("view_cart", payload));
			$rootScope.$on(events.itemIsViewed, (e, payload) => sendData("view_item", convertToEcommerceMenuItemPayload(payload)));
			// we don't need ecommerce object for this event, just send null
			$rootScope.$on(events.menuIsShown, (e, payload) => sendData("view_item_list", null));
			// we don't need ecommerce object for this event, just send null
			$rootScope.$on(events.changeDeliveryAddressRequest, (e, payload) => sendData("add_shipping_info", null));
			$rootScope.$on(events.orderPlaced, (e, payload) => sendData("purchase", convertToEcommercePurchasePayload(payload)));

			function sendData(eventName, payload) {
				window.dataLayer = window.dataLayer || [];

				// It's recommended that you use the following command to clear the ecommerce object
				// Learn more here: https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtm#clear_the_ecommerce_object
				window.dataLayer.push({ ecommerce: null });

				window.dataLayer.push({
					event: eventName,
					ecommerce: payload
				});
			};

			function convertToEcommercePurchasePayload(payload) {
				let itemsPayload = [];

				var googleAnalyticsItem = {
					currency: "USD",
					value: payload.order.Total,
					transaction_id: `${payload.orderId}`,
					tax: (payload.order.PricedOrder && payload.order.PricedOrder.Tax) ? payload.order.PricedOrder.Tax : 0,
					shipping: (payload.order.PricedOrder && payload.order.PricedOrder.DeliveryFee) ? payload.order.PricedOrder.DeliveryFee : 0,
					coupon: (payload.order.Promotion && payload.order.Promotion.code) ? `${payload.order.Promotion.code}` : null,
					user_data: {
						first_name: (payload.order.Customer && payload.order.Customer.FirstName) ? payload.order.Customer.FirstName : "",
						last_name: (payload.order.Customer && payload.order.Customer.LastName) ? payload.order.Customer.LastName : "",
						email: (payload.order.Customer && payload.order.Customer.EmailAddress) ? payload.order.Customer.EmailAddress : "",
						phone: (payload.order.Customer && payload.order.Customer.PhoneNumber && payload.order.Customer.PhoneNumber.Number) ? payload.order.Customer.PhoneNumber.Number : "",
					}
				};

				// get items
				itemsPayload = itemsPayload.concat(payload.order.Items.map(item => getMenuItemsPayloadFromOrderItem({
					menuItem: {
						PluId: item.PluId,
						ItemId: item.ItemId,
						ItemName: item.ItemName,
						ItemPrice: item.ItemPrice,
						Quantity: item.Quantity,
						Items: item.Items
					},
					locationId: payload.order.LocationId
				})));

				// flatten any nested arrays within itemsPayload
				itemsPayload = itemsPayload.flat();

				// add items
				googleAnalyticsItem.items = itemsPayload;

				return googleAnalyticsItem;
			}

			function convertToEcommerceMenuItemPayload(payload) {
				var googleAnalyticsItem = {
					currency: "USD",
					value: payload.type === "MenuItem" ? payload.menuItem.Price : payload.menuItem.TotalPrice,
					items: payload.type === "MenuItem" ? getMenuItemsPayloadFromMenuItem(payload) : getMenuItemsPayloadFromOrderItem(payload)
				};

				return googleAnalyticsItem;
            }

			function getMenuItemsPayloadFromMenuItem(payload) {
				return [{
					item_name: payload.menuItem.Name,
					item_id: payload.menuItem.PluId ? payload.menuItem.PluId : `${payload.menuItem.Id}`,
					price: payload.menuItem.Price,
					currency: 'USD',
					quantity: 1,
					location_id: payload.locationId ? `${payload.locationId}` : `not defined`
				}]
			}

			function getMenuItemsPayloadFromOrderItem(payload) {
				let itemsPayload = [];

				// Process the parent item if it exists
				if (payload.menuItem) {
					itemsPayload.push({
						item_id: payload.menuItem.PluId ? payload.menuItem.PluId : `${payload.menuItem.ItemId}`,
						item_name: payload.menuItem.ItemName,
						price: payload.menuItem.ItemPrice,
						currency: 'USD',
						quantity: payload.menuItem.Quantity,
						location_id: payload.locationId ? `${payload.locationId}` : 'not defined'
					});
				}

				// Process the child items if they exist
				if (payload.menuItem.Items.length > 0) {
					itemsPayload = itemsPayload.concat(payload.menuItem.Items.map(item => ({
						item_id: item.PluId ? item.PluId : `${item.ItemId}`,
						item_name: item.ItemName,
						price: item.ItemPrice,
						currency: 'USD',
						quantity: item.Quantity,
						location_id: payload.locationId ? `${payload.locationId}` : 'not defined'
					})));
				}

				return itemsPayload;
			}

			function sendAuthenticationData(eventName) {
				window.dataLayer = window.dataLayer || [];
				window.dataLayer.push({
					event: eventName,
					method: 'ToGo'
				});
			};
		};
	};	
})();;
(function() {
    'use strict';

    angular.module('main').factory('api', ['$http', '$q', '$log', apiFactory]);

    function apiFactory($http, $q, $log) {
        var api;

        function resolveApiUrl(relativePath) {
            return togoorder.apiUrl + relativePath;
        }

        api = {
            get: function(url, data, config) {
                return $http.get(resolveApiUrl(url), data, config);
            },

            post: function(url, data, config) {
                return $http.post(resolveApiUrl(url), data, config);
            },

            put: function(url, data, config) {
                return $http.put(resolveApiUrl(url), data, config);
			},

			delete: function (url, data, config) {
	            return $http.delete(resolveApiUrl(url), data, config);
            },

            http: function(config) {
                config.url = resolveApiUrl(config.url);
                return $http(config);
			},
			getPendingRequestCount: function() {
				return $http.pendingRequests.length;
			}
        };

        return api;

    }
})();;
(function () {
	'use strict';

	angular.module('main').factory('cartService', ['$log', '$window', '$q', '$timeout', '$state', 'common', 'notify', 'events', 'orderService', 'orderPricingService', 'storageService', 'merchantLocationService', 'orderTypeWorkflowService', 'orderTypeRuleService', 'deliveryService', 'menuService', 'Order', 'crmService', cartService]);

	function cartService($log, $window, $q, $timeout, $state, common, notify, events, orderService, orderPricingService, storageService, merchantLocationService, orderTypeWorkflowService, orderTypeRuleService, deliveryService, menuService, Order, crmService) {
		var service = {
			initializeCart: initializeCart,
			saveCart: saveCart,
			clear: clear,
			waitForInitialization: waitForInitialization,
			checkForValidations: checkForValidations,
			setOrder: setOrder,
			destroy: destroy,
			getCart: getCart,
			loadCurrent: loadCurrent,
			noCart: noCart,
			addItemToNextCart: addItemToNextCart,
			getStoredItemsToAdd: getStoredItemsToAdd,
			removeStoredItemsToAdd: removeStoredItemsToAdd,
			addOrderItem: addOrderItem,
			findItemInCart: findItemInCart,
			getItems: getItems
		},
			cart = null,
			initializedDefered = $q.defer();

		clear();

		function Cart() {
			this.order = {};
			this.minimumOrder = 0;
			this.orderTypeRule = {};
		}

		Cart.prototype.isValid = function () {
			return this.isAboveMinimumOrder() && this.isBelowMaximumOrder() && this.hasOrderItems;
		};
		Cart.prototype.getMinimumOrderShortage = function () {
			return this.minimumOrder - this.order.Subtotal;
		};
		Cart.prototype.isAboveMinimumOrder = function () {
			return this.getMinimumOrderShortage() <= 0;
		};
		Cart.prototype.getMaximumOrderOverage = function () {
			if (this.maximumOrder === 0) {
				return 0;
			}
			return this.order.Subtotal - this.maximumOrder;
		};
		Cart.prototype.isBelowMaximumOrder = function () {
			return this.getMaximumOrderOverage() <= 0;
		};
		Cart.prototype.hasOrderItems = function () {
			var items = this.order && getItems(this.order);
			return !!(items && items.length);
		};

		function getItems(order) {
			return _.where(order.Items, { shouldShow: true });
		}

		function getCart() {
			return cart;
		}

		function findItemInCart(menuItemId) {
			return _.findWhere(cart.order.Items, { ItemId: menuItemId });
		}

		function addOrderItem(item) {
			crmService.addItemToCart(item);
			cart.order.addItem(item);
			common.broadcast(events.addItemToNextCart, { menuItem: item, locationId: cart.order.LocationId, type: "OrderItem" });
		}

		function addItemToNextCart(item) {
			var itemsToAdd = getStoredItemsToAdd();
			itemsToAdd.push(item);
			var itemIdsToAddJson = angular.toJson(itemsToAdd);
			storageService.set("itemsToAdd", itemIdsToAddJson);
		}

		function removeStoredItemsToAdd(item) {
			var itemsToAdd = getStoredItemsToAdd();
			itemsToAdd = _.reject(itemsToAdd, function (i) {
				return i.id === item.id;
			});
			var itemIdsToAddJson = angular.toJson(itemsToAdd);
			storageService.set("itemsToAdd", itemIdsToAddJson);
		}

		function getStoredItemsToAdd() {
			var itemIdsToAddJson = storageService.get("itemsToAdd");
			var itemsToAdd = angular.fromJson(itemIdsToAddJson || "[]");
			return itemsToAdd;
		}

		function CartOrderItemInvalidException(message) {
			this.name = 'CartOrderItemInvalid';
			this.message = message;
		}

		function getOrderTypeRule(orderType, fulfillmentType, merchantLocation) {
			return orderTypeRuleService.getOrderTypeRule(orderType, fulfillmentType, merchantLocation);
		}

		function getIsValidOrderTypeRuleMenu(orderTypeRule, menuId) {
			return (_.some(orderTypeRule.MenuIds, function (id) {
				return id == menuId;
			}));
		}

		function getOrderTypeRuleForMenuId(merchantLocation, menuId) {
			return _.find(merchantLocation.OrderTypeViewModels, function (ot) {
				return getIsValidOrderTypeRuleMenu(ot, menuId);
			});
		}

		function noCart() {
			initializedDefered.resolve(1);
		}

		function initializeCart(menuId, orderType, fulfillmentType, merchantLocation, day) {
			var orderTypeRule = getOrderTypeRule(orderType, fulfillmentType, merchantLocation);
			if (!orderTypeRule) {
				notify.error('The Menu is no longer available');
				notify.dialog(':(', 'So Sorry. This menu is no longer available.');
				destroy();
				initializedDefered.reject();
				return initializedDefered.promise;
			}
			var isValidOrderTypeRuleMenu = getIsValidOrderTypeRuleMenu(orderTypeRule, menuId);
			if (!isValidOrderTypeRuleMenu) {
				notify.error('This menu is not valid with ' + orderTypeRule.Description + ' orders.');
				notify.dialog('Not sure what happened', '...but the selected menu is not valid with ' + orderTypeRule.Description + ' orders.');
				destroy();
				initializedDefered.reject();
				return initializedDefered.promise;
			}

			var cartPromise;
			if (cart && !cart.isEmpty && cart.order.OrderType === orderType && cart.order.FulfillmentType === fulfillmentType && cart.order.MenuId === menuId) {
				cartPromise = $q.when(cart);
			} else {
				cartPromise = getCachedCart(menuId, orderTypeRule)
					.then(function (cachedCart) {
						if (cachedCart.order.LocationId != merchantLocation.Id) {
							$log.warn("merchant location IDs do not match.");
							throw new Error("Something went wrong.");
						}

						cachedCart.order.Items = cachedCart.order.Items.filter(item => !item._isRequiredItem);

						return setCart(cachedCart, orderTypeRule, merchantLocation, day);
					}, function () {
						var newCart = {
							order: new Order(merchantLocation.Id, menuId)
						};
						return setCart(newCart, orderTypeRule, merchantLocation, day);
					});
			}
			return cartPromise.then(function () {
				cart.order.Payment = null;
				cart.orderTypeRule = orderTypeRule;
				initializedDefered.resolve(1);

				return cart;
			});
		}

		function setLocationSpecifics(merchantLocation) {
			cart.order.setLocation(merchantLocation);

			return $q.when(1);
		}

		function setOrderTypeRule(orderTypeRule) {
			cart.minimumOrder = orderTypeRule.MinimumOrder;
			cart.maximumOrder = orderTypeRule.MaximumOrder;
			cart.order.setOrderTypeRule(orderTypeRule);

			if (orderTypeRule.DeliveryService) {
				cart.order.setDeliveryService(orderTypeRule);
				//cart.order.DeliveryFee = orderTypeRule.DeliveryFeePaidByCustomer;
				return $q.when(1);
			} else {
				return deliveryService.getCurrentDeliveryZoneInfo(orderTypeRule)
					.then(function (zoneInfo) {
						cart.order.setDeliveryZone(zoneInfo.zone, zoneInfo.distance, orderTypeRule);
						return zoneInfo;
					});
			}
		}

		function checkForValidations() {
			return loadCurrent().then(function () {
				var firstInvalidOrderItem = _.find(cart.order.Items, function (item) {
					var errors = item.errors;
					if (errors) {
						$log.debug(errors);
					}
					return !!errors;
				});
				if (firstInvalidOrderItem) {
					$timeout(function () {
						common.broadcast(events.cartItemSelected, firstInvalidOrderItem);
					}, 300);
					throw new CartOrderItemInvalidException("One of the items in your cart is invalid.");
				}
			});
		}

		function setCart(cartData, orderTypeRule, merchantLocation, day) {
			//console.log('cartData', angular.toJson(cartData));
			clear();
			cart = angular.extend(cart, cartData);

			cart.isEmpty = false;
			cart.day = day || cart.day;

			return $q.all(setOrderTypeRule(orderTypeRule), setLocationSpecifics(merchantLocation)).then(function () {
				cacheCurrentCart(angular.toJson(cart));
				return cart;
			});
		}

		function setOrder(orderData, merchantLocation) {
			return initializeCart(orderData.MenuId, orderData.OrderType, orderData.FulfillmentType, merchantLocation)
				.then(function () {
					var orderTypeRule = cart.orderTypeRule;
					return orderService.hydrateOrder(orderData).then(function (order) {
						let cartData = {
							order: order,
							minimumOrder: orderTypeRule.MinimumOrder,
							maximumOrder: orderTypeRule.MaximumOrder
						};

						return setCart(cartData, orderTypeRule, merchantLocation).then(saveCart);
					});
				});
		}

		function destroy() {
			storageService.clear();
			$window.sessionStorage.clear();
			clear();
		}

		function clear() {
			cart = new Cart();
			cart.isEmpty = true;
			var oldDefer = initializedDefered;
			initializedDefered = $q.defer();
			initializedDefered.promise.then(function (n) {
				oldDefer.resolve(n + 1);
			});
		}

		function waitForInitialization() {
			return initializedDefered.promise;
		}

		function hydrateCart(cartJson) {
			var cartData = angular.fromJson(cartJson);
			return orderService.hydrateOrder(cartData.order).then(function (order) {
				cartData.order = order;
				return cartData;
			});
		}

		function getCachedCart(menuId, orderTypeRule) {
			var cartJson = storageService.get('cart-' + menuId);
			if (!cartJson) {
				return $q.reject('No cached cart found');
			}
			return hydrateCart(cartJson);
		}

		function saveCart() {
			var order = cart.order;
			if (order) {
				var cartJson = angular.toJson(cart);
				cacheCart(order, cartJson);
				cacheCurrentCart(cartJson);
			}
			return $q.when(true);
		}

		function loadCurrent() {
			if (!cart || cart.isEmpty) {
				var cartJson = storageService.get("currentCart");
				if (!cartJson) {
					return $q.reject('No current cart found');
				}
				return hydrateCart(cartJson).then(function (cartData) {
					var order = cartData.order;

					if (!order.OrderType || !order.FulfillmentType) {
						return $q.reject();
					}
					if (order.LocationId != togoorder.locationId) {
						return $q.reject();
					}
					return merchantLocationService.getById(togoorder.locationId)
						.then(function (merchantLocation) {
							var orderTypeRule = getOrderTypeRule(order.OrderType, order.FulfillmentType, merchantLocation);
							return setCart(cartData, orderTypeRule, merchantLocation);
						});
				});

			}
			return $q.when(cart);
		}

		function storeCartJson(order, cartJson) {
			storageService.set('cart-' + order.MenuId, cartJson);
		}

		function cacheCart(order, cartJson) {
			try {
				storeCartJson(order, cartJson);
			} catch (e) {
				$window.sessionStorage.clear();
				try {
					storeCartJson(order, cartJson);
				} catch (e) {
					//what can ya do?
					$log.error('Error writing to sessionStorage', e);
				}
			}
		}

		function cacheCurrentCart(cartJson) {
			try {
				storageService.set("currentCart", cartJson);
			} catch (e) {
				$window.sessionStorage.clear();
				storageService.set("currentCart", cartJson);
			}
		}

		return service;
	}

})();;
(function () {
	'use strict';

	angular.module('main').factory('crmService', ['$log', '$window', '$q', '$timeout', '$state', 'common', 'notify', 'events', 'userService', crmService]);

	function crmService($log, $window, $q, $timeout, $state, common, notify, events, userService) {
		var vm = this,
			merchantId = togoorder.merchantId;
		var service = {
			login: login,
			changeEmail: changeEmail,
			logout: logout,
			addItemToCart: addItemToCart,
			purchase: purchase,
			applyPromo: applyPromo,
			usedReward: usedReward,
			signUp: signUp,
			removeItemFromCart: removeItemFromCart
		},
			
			mParticle = window.mParticle,
			// if we've failed to load the javascript file from mParticle, this won't be defined
			mParticleInitalized = mParticle && mParticle.eCommerce.createProduct

		async function login() {
			try {
				if (mParticleInitalized) {
					const userData = await userService.getUser(merchantId);
					const identityRequest = {
						userIdentities: {
							email: userData.Email,
							other3: userData.Id.toString(),
							mobile_number: userData.CallbackNumber
						}
					}
					const callback = function () {
						const currentUser = mParticle.Identity.getCurrentUser();
						currentUser.setUserAttribute('$FirstName', userData.FirstName);
						currentUser.setUserAttribute('$LastName', userData.LastName);
						currentUser.setUserAttribute('email_subscribe', userData.EmailOptIn ? 'opted_in' : 'unsubscribed');
						currentUser.setUserAttribute('$Mobile', userData.CallbackNumber);
						if (userData.DateOfBirth) {
							const date = new Date(userData.DateOfBirth);
							const year = date.getFullYear();

							let month = (1 + date.getMonth()).toString();
							month = month.length > 1 ? month : '0' + month;

							let day = date.getDate().toString();
							day = day.length > 1 ? day : '0' + day;
							const dateString = year + '-' + month + '-' + day;
							currentUser.setUserAttribute('dob', dateString);
						}
						if (window.appboy) {
							if (userData.EmailOptIn) {
								window.appboy.User.NotificationSubscriptionTypes.OPTED_IN;
							} else {
								window.appboy.User.NotificationSubscriptionTypes.UNSUBSCRIBED;
							}
						} else if (window.braze) {
							if (userData.EmailOptIn) {
								window.braze.User.NotificationSubscriptionTypes.OPTED_IN;
							} else {
								window.braze.User.NotificationSubscriptionTypes.UNSUBSCRIBED;
							}
						}
						
                    }
					mParticle.Identity.login(identityRequest, callback);
					
				}
			} catch (ex) { }
		}

		function logout() {
			try {
				if (mParticleInitalized) {
					mParticle.Identity.logout({});
				}
			} catch (ex) { }
        }

		function changeEmail(newEmail) {
			try {
				if (mParticleInitalized) {
					const identityRequest = {
						userIdentities: { email: newEmail }
					}
					mParticle.Identity.modify(identityRequest);
					}
			} catch (ex) { }
		}

		function addItemToCart(item) {
			try {
				if (mParticleInitalized) {
					const items = convertTgoItemToMParticle(item);
					items.forEach(i => {
						mParticle.eCommerce.logProductAction(mParticle.ProductActionType.AddToCart, i);
					})
				}
			} catch (ex) { }
		}

		function purchase(cart, orderId) {
			try {
				if (mParticleInitalized) { 
					let items = [];
					cart.Items.forEach(i => {
						items = items.concat(convertTgoItemToMParticle(i));
					});
					const customAttributes = {
						store_id: cart.LocationId,
						order_type_rules: cart.OrderType,
						order_id: orderId,
						discount_total: -(cart.LoyaltyDiscount + cart.OfferDiscount + cart.PricedOrder.DiscountAmount)
					};
					const customFlags = {};
					const transactionAttributes = {
						Revenue: cart.PricedOrder.Subtotal
					};
					mParticle.eCommerce.logProductAction(
						mParticle.ProductActionType.Purchase,
						items,
						customAttributes,
						customFlags,
						transactionAttributes);
					
					if (userService.isAGuest()) {
						const callback = function () {
							const currentUser = mParticle.Identity.getCurrentUser();
							currentUser.setUserAttribute('$FirstName', cart.Customer.FirstName);
							currentUser.setUserAttribute('$LastName', cart.Customer.LastName);
							logout();
                        };
						const identityRequest = {
							userIdentities: { email: cart.Customer.EmailAddress }
						}
						mParticle.Identity.login(identityRequest, callback);
					}
				}
			} catch (ex) {
				console.error('mparticleerror', ex);
			}
		}

		function applyPromo(promo) {
			try { 
				if (promo && promo !== '') {
					if (mParticleInitalized) {
						mParticle.logEvent(
							'Used Promo Code',
							mParticle.EventType.Transaction,
							{ 'promoCode': promo }
						);
					}
				}
			} catch (ex) { }
        }

		function convertTgoItemToMParticle(item) {
			let productArray = [];
			try {
				const product = mParticle.eCommerce.createProduct(
					item.ItemName,			// Name
					item.PluId,				// SKU
					item.ItemPrice,         // Price
					item.Quantity           // Quantity
				);
				productArray.push(product);
				if (item.Items) {
					item.Items.forEach(i => {
						productArray = productArray.concat(convertTgoItemToMParticle(i));
					});
				}
			} catch (ex) { }
			return productArray;
		}

		function usedReward(rewards) {
			try { 
				if (mParticleInitalized) {
					if (rewards) {
						rewards.forEach(r => {
							mParticle.logEvent(
								'Used Rewards',
								mParticle.EventType.Transaction,
								{ 'rewardId': r }
							);
						})
					}
				}
			} catch (ex) { }
		}

		async function signUp() {
			try {
				if (mParticleInitalized) {
					const userData = await userService.getUser(merchantId);
					mParticle.logEvent('Create Account', window.mParticle.EventType.Other, { 'category': 'Create Account', 'email': userData.Email });
				}
			} catch (ex) { }
		}

		function removeItemFromCart(item) {
			try {
				if (mParticleInitalized) {
					const items = convertTgoItemToMParticle(item);
					items.forEach(i => {
						mParticle.eCommerce.logProductAction(mParticle.ProductActionType.RemoveFromCart, i);
					})
				}
			} catch (ex) { }
        }

		return service;
	}

})();;
(function () {
	'use strict';

	var serviceId = 'deliveryService';

    angular.module('main').factory(serviceId, ['$log', '$http', '$window', '$q', 'common', 'storageService', 'deliveryCacheService', 'merchantLocationService', 'doorDashService', 'timeService', deliveryService]);

	function deliveryService($log, $http, $window, $q, common, storageService, deliveryCacheService, merchantLocationService, doorDashService, timeService) {

	    return {
	        cacheTableNumber: deliveryCacheService.cacheTableNumber,
	        getTableNumber: deliveryCacheService.getTableNumber,
	        cacheDeliveryAddress: deliveryCacheService.cacheDeliveryAddress,
	        getDeliveryAddress: deliveryCacheService.getDeliveryAddress,
	        checkAddressIsInDeliveryArea: checkAddressIsInDeliveryArea,
	        checkAddressIsInAnyDeliveryArea: checkAddressIsInAnyDeliveryArea,
	        getCheapestZoneForAddress: getCheapestZoneForAddress,
	        getDeliveryZones: getDeliveryZones,
	        sortDeliveryZones: sortDeliveryZones,
	        getDeliveryDistance: getDeliveryDistance,
	        getCurrentDeliveryZoneInfo: getCurrentDeliveryZoneInfo,
            checkAddressIsInDeliveryServiceArea: checkAddressIsInDeliveryServiceArea,
            getDeliveryServiceEstimate: getDeliveryServiceEstimate
	    };

		function getDeliveryZones(orderType, fulfillmentType) {
		    return merchantLocationService.getById(togoorder.locationId)
		        .then(function (data) {
		            var orderTypeViewModel = _(data.OrderTypeViewModels).find(function (otvm) {
		                return otvm.OrderType == orderType && otvm.FulfillmentType == fulfillmentType;
		            });
		            return sortDeliveryZones(orderTypeViewModel.DeliveryZones);
		        });
		}

		function sortDeliveryZones(deliveryZones) {

		    deliveryZones = _.sortBy(deliveryZones, 'FlatFee');
		    deliveryZones = _.sortBy(deliveryZones, 'PerMileFee');
		    deliveryZones = _.sortBy(deliveryZones, 'PercentageFee');

		    return deliveryZones;
		}

		function getCurrentDeliveryZoneInfo(orderTypeRule) {
		    return getDeliveryZones(orderTypeRule.OrderType, orderTypeRule.FulfillmentType)
                .then(function (zones) {
                    if (!zones || !zones.length) {
                        return false;
                    }
                    return getCheapestZoneForAddress(zones);
                }).then(function (zone) {
                    if (zone.PerMileFee) {
                        return getDeliveryDistance()
                        .then(function (distance) {
                            return {
                                zone: zone,
                                distance: distance
                            };
                        });
                    }
                    return $q.when({
                        zone: zone,
                        distance: 0
                    });
                });
		}

		function getDeliveryDistance() {
		    return merchantLocationService.getById(togoorder.locationId)
		        .then(function(location) {
		            return deliveryCacheService.getDeliveryAddress()
                        .then(function(address) {
		                    return getCoordinatesForAddress(address);
		                })
                        .then(function (coordinate) {
		                    var p1 = {
		                        lat: function() {
		                            return location.Latitude;
		                        },
		                        lng: function() {
		                            return location.Longitude;
		                        }
		                    };
                            return common.getDistanceInMiles(location.Latitude, location.Longitude, coordinate.lat(), coordinate.lng());
		            });
		        });
		}

        function getCheapestZoneForAddress(zones) {
            return deliveryCacheService.getDeliveryAddress().then(function (address) {

                var zoneChecks = _.map(zones, function (zone) {
                    return checkAddressIsInDeliveryArea(address, zone.Coordinates)
                    .then(function () {
                        return zone;
                    }, function () {
                        return null;
                    });
                });

                return $q.all(zoneChecks).then(function(zones) {
                    var deliveryZones = _.filter(zones, function(x) { return x; });
                    deliveryZones = sortDeliveryZones(deliveryZones);
                    var cheapestZone = deliveryZones && deliveryZones.length && deliveryZones[0];
                    return cheapestZone;
                });
            });

        }

        function checkAddressIsInAnyDeliveryArea(address, deliveryZones) {
            var promises = _.map(deliveryZones, function (dz) {
                return checkAddressIsInDeliveryArea(address, dz.Coordinates);
            });
            return common.any(promises);
        }

        function getCoordinatesForAddress(address) {
            if (address.Lat) {
                return $q.when(new google.maps.LatLng(address.Lat, address.Lng));
            } else {
                var defer = $q.defer();

                var mapboxClient = mapboxSdk({ accessToken: togoorder.MapBoxApiKey });
                mapboxClient.geocoding.forwardGeocode({
                        query: address.getGoogleAddress(),
                        limit: 1,
                        autocomplete: false,
                        types: ['address']
                    })
                    .send()
                    .then(response => {
                            const coords = response.body.features[0].geometry.coordinates;
                            defer.resolve(new google.maps.LatLng(coords[1], coords[0]));
                        },
                        error => { 
                            console.error(`Error during geocode request: ${error.message}`);
                            defer.reject(error.statusCode);
                        });

                //var mygc = new google.maps.Geocoder();
                //mygc.geocode({ 'address': address.getGoogleAddress() }, function (results, status) {
                //    if (status !== "OK") {
                //        defer.reject(status);
                //        return;
                //    }
                //    var loc = results[0].geometry.location;
                //    defer.resolve(new google.maps.LatLng(loc.lat(), loc.lng()));
                //});

                return defer.promise;
            }
        }

        function checkAddressIsInDeliveryArea(address, deliveryAreaCoordinates) {
		    var coords = _.map(deliveryAreaCoordinates, function (c) {
				return new google.maps.LatLng(c.Lat, c.Lng);
		    });
			var deliveryArea = new google.maps.Polygon({
				paths: coords
			});

            return getCoordinatesForAddress(address)
                .then(function (coordinates) {
                    $log.debug('address:', address);
                    $log.debug('coordinates.lat:', coordinates.lat());
                    $log.debug('coordinates.lng:', coordinates.lng());
                    $log.debug('deliveryAreaCoordinates:', deliveryAreaCoordinates);
                    var isLocationInDeliveryArea = google.maps.geometry.poly.containsLocation(coordinates, deliveryArea);
                    if (isLocationInDeliveryArea) {
                        return $q.when(true);
                    } else {
                        return $q.reject();
                    }
                });
		}

        function getDeliveryServiceEstimate(orderTypeRule, dropoffAddress, orderTotal, deliveryTimeUtc, pickupTimeUtc) {
            switch (orderTypeRule.DeliveryService) {
                case 1: // Door Dash Drive
                    
                    return doorDashService.getEstimate(dropoffAddress, pickupTimeUtc, deliveryTimeUtc, orderTotal, orderTypeRule)
                        .then(function (data) {
                            var estimate = {
                                deliveryTimeUtc: data.DeliveryTimeUtc,
                                pickupTimeUtc: data.PickupTimeUtc,
                                fee: data.fee
                            };
                            return $q.when(estimate);
                        },
                            function () { return $q.reject(); });

            default:
                return $q.reject('unsupported delivery service');
            }
        }

        function checkAddressIsInDeliveryServiceArea(orderTypeRule, dropoffAddress, orderTotal) {

            return getDeliveryServiceEstimate(orderTypeRule, dropoffAddress, orderTotal, moment().utc())
                .then(function (estimate) {
                    if (estimate) {
                        return $q.when(true);
                    } else {
                        return $q.reject();
                    }
                },
                function() { return $q.reject(); });
        }
	}
})();;
(function () {
    'use strict';

	var serviceId = 'doorDashService';

    angular.module('main').factory(serviceId, ['api', '$log', '$q', doorDashService]);

	function doorDashService(api, $log, $q) {
        
        var service = {
            getEstimate: getEstimate
        };

        return service;

        function fail(error) {
            $log.error(`door dash api error - status: ${error.status} details: ${angular.toJson(error.data)}`);
            return $q.reject(error.data);
        }

        function success(response) {
			$log.debug('door dash api success');
            return $q.when(response.data);
        }

        function getEstimate(dropoffAddress, pickupTimeUtc, deliveryTimeUtc, orderTotal, orderTypeRule) {
            if (pickupTimeUtc && deliveryTimeUtc)
                return $q.reject('can only specify pickup time or delivery time, not both');

            var estimateRequest = {
                address: {
                    city: dropoffAddress.City,
                    state: dropoffAddress.State,
                    addressLine1: dropoffAddress.AddressLine1,
                    addressLine2: dropoffAddress.AddressLine2,
                    zipcode: dropoffAddress.Zipcode
                },
                orderTotal: orderTotal,
                pickupTimeUtc: pickupTimeUtc,
                deliveryTimeUtc: deliveryTimeUtc,
                locationId: togoorder.locationId,
                orderType: orderTypeRule.OrderType,
                orderFulfillmentType: orderTypeRule.FulfillmentType
            };

            //var config = {
            //    headers: {
            //        Authorization: 'Bearer ' + togoorder.doorDashApiKey
            //    }
            //};

            return api.post('api/OrderService/DeliveryServiceEstimate', estimateRequest)
                .then(success, fail);
        }
    }
})();;
(function () {
    'use strict';

    var serviceId = 'enumService';

    angular.module('main').factory(serviceId, ['$http', '$q', 'api', enumService]);

    function enumService($http, $q, api) {

        return {
            getCreditCardTypes: getCreditCardTypes
        };

        function getCreditCardTypes() {
            var url = 'api/Enum/CardType';
            return api.get(url, { cache: true }).then(function (response) {
                return response.data;
            });
        }
    }
})();;
(function () {
    'use strict';

    var serviceId = 'feedbackService';

    angular.module('main').factory(serviceId, ['$http', '$q', 'api', feedbackService]);

    function feedbackService($http, $q, api) {
        return {
            getSurvey: getSurvey,
            saveFeedback: saveFeedback
        };

        function getSurvey(merchantId) {
            return api.get('api/Survey/'+merchantId)
				.then(function (response) {
				    return response.data;
				});
        }

        function saveFeedback(feedback) {
            return api.post('api/Survey/', feedback)
				.then(function (response) {
				    return response.data;
				});
        }
    }
})();;
(function () {
'use strict';

var serviceId = 'giftCardService';

angular.module('main').factory(serviceId, ['$http', '$q', 'api', giftCardService]);

	function giftCardService($http, $q, api) {
		var locationId = togoorder.locationId;

		return {
			addCard: addCard,
			updatePin: updatePin,
			getBalance: getBalance,
			redeem: redeem,
			voidTransaction: voidTransaction,
			deleteCard
		};

		function addCard(cardRequest) {
			cardRequest.LocationId = locationId;
			return api.post('services/GiftCard/AddCard', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function deleteCard(accountNumber) {
			var cacheName = `services/GiftCard/DeleteCard?accountNumber=${accountNumber}&locationId=${locationId}`;
			return api.delete(cacheName)
				.then(function (response) {
					return response.data;
				});
		}

		function updatePin(request) {
			request.LocationId = locationId;
			return api.post('services/GiftCard/UpdatePin', request)
				.then(function(response) {
					return response.data;
				});
		}

		function getBalance(cardRequest) {
			cardRequest.LocationId = locationId;
			return api.post('services/GiftCard/Balance', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function redeem(cardRequest) {
			cardRequest.LocationId = locationId;
			return api.post('services/GiftCard/Redeem', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function voidTransaction(cardRequest) {
			cardRequest.LocationId = locationId;
			return api.post('services/GiftCard/Void', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}
	}
})();;
(function () {
    'use strict';

    var serviceId = 'hoursService';

    angular.module('main').factory(serviceId, ['$http', '$q', '$log', 'api', hoursService]);

    /**
     * Used in checkout.js
     * @param {any} $http
     * @param {any} $q
     * @param {any} $log
     * @param {any} api
     */
    function hoursService($http, $q, $log, api) {
        var service = {
            getInstance: getInstance
        };

        function getOrderPickupTimeExclusions(merchantLocationId, menuId) {
            var url = 'api/Location/' + merchantLocationId + '/OrderPickupTimeExclusions/' + menuId;
            return api.get(url, { cache: false }).then(function (response) {
                return response.data;
            });
        }

        function getHoursByMenuId(merchantLocationId, menuId) {
            var url = 'api/Location/' + merchantLocationId + '/Menu/' + menuId + '/HourSpans';
            return api.get(url, { cache: true }).then(function (response) {
                return response.data;
            });
        }

        function getExclusions(merchantLocationId, timeZoneOffset, menuId) {
            var get = function () {
                return getOrderPickupTimeExclusions(merchantLocationId, menuId)
                    .then(function (orderCountByTimeSlot) {
                        var results = _.map(orderCountByTimeSlot,
                            function (exclusion) {
                                var exclusionMoment = moment.utc(exclusion.PickupTime).utcOffset(timeZoneOffset);
                                return exclusionMoment;
                            });
                        return results;
                    });
            };

            return get();
        }

        function getInstance(merchantLocationId, waitTimeInMinutes, menuId, timeZoneOffset, isDoorDashDelivery, deliveryServiceDeliveryTime, deliveryServiceTimeToDeliver) {
            var getExclusionsAction = function () {
                return getExclusions(merchantLocationId, timeZoneOffset, menuId);
            };
            var p1 = getHoursByMenuId(merchantLocationId, menuId);
            var p2 = getExclusionsAction();

            return $q.all([p1, p2]).then(function (result) {
                return new HoursService(merchantLocationId,
                    waitTimeInMinutes,
                    menuId,
                    result[0],
                    result[1],
                    getExclusionsAction,
                    timeZoneOffset,
                    isDoorDashDelivery,
                    deliveryServiceDeliveryTime,
                    deliveryServiceTimeToDeliver);
            });
        }

        function TimeSlotNotAvailableException(message) {
            this.name = 'TimeSlotNotAvailable';
            this.message = message;
        }

        function convertTimelessDateForTimezone(m, timeZoneOffset) {
            m = m.utcOffset(timeZoneOffset);
            m.minutes(-timeZoneOffset);
            return m;
        }

        function HoursService(merchantLocationId,
            waitTimeInMinutes,
            menuId,
            hourSpanResponse,
            timeSlotExclusions,
            getExclusionsAction,
            timeZoneOffset,
            isDoorDashDelivery,
            deliveryServiceDeliveryTime,
            deliveryServiceTimeToDeliver) {
            var now = null,
                isDoorDashDelivery = isDoorDashDelivery,
                deliveryServiceDeliveryTime = deliveryServiceDeliveryTime,
                hourSpanDays = hourSpanResponse.HourSpanDays,
                menuStart = hourSpanResponse.MenuStartDate && convertTimelessDateForTimezone(moment.utc(hourSpanResponse.MenuStartDate), timeZoneOffset),
                menuEnd = hourSpanResponse.MenuEndDate && convertTimelessDateForTimezone(moment.utc(hourSpanResponse.MenuEndDate), timeZoneOffset),
                availableWeekdays = hourSpanResponse.AvailableWeekdays,
                locationHolidays = hourSpanResponse.Holidays,
                holidays = _.map(hourSpanResponse.Holidays, function (h) {
                    let date = convertTimelessDateForTimezone(moment.utc(h.Date), timeZoneOffset);
                    return date;
                });
            _.each(locationHolidays, function (h) {
                h.Date = convertTimelessDateForTimezone(moment.utc(h.Date), timeZoneOffset);
            });
            var self = this;
            this.validatePickupTime = function (pickupTime) {
                return getExclusionsAction().then(function (data) {
                    timeSlotExclusions = data;
                    var isPickupTimeAnExclusion = _.some(timeSlotExclusions,
                        function (exclusion) {
                            return exclusion.isSame(pickupTime);
                        });
                    if (isPickupTimeAnExclusion) {
                        throw new TimeSlotNotAvailableException();
                    }
                    return true;
                });
            };

            function isHoliday(calendarDate) {
                return _.some(holidays,
                    function (h) {
                        var isSame = calendarDate.isSame(h, 'day');
                        return isSame;
                    });
            }

            function isInsideMenuDateRange(calendarDate) {
                if (menuStart) {
                    if (calendarDate.isBefore(menuStart)) {
                        return false;
                    }
                }
                if (menuEnd) {
                    if (calendarDate.isAfter(menuEnd) || calendarDate.isSame(menuEnd)) {
                        return false;
                    }
                }
                return true;
            }

            function getHolidayForDate(calendarDate) {
                return locationHolidays.find(lh => calendarDate.isSame(lh.Date, 'day'));
            }

            function isDateSelectable(calendarDate, mode) {
                // is it a day of week for this menu?
                var holiday = null;
                if ((mode === 'day' && !_.contains(availableWeekdays, calendarDate.day()))) {
                    holiday = isHoliday(calendarDate)
                    if (!holiday) {
                        return false;
                    }
                }

                if (!isInsideMenuDateRange(calendarDate)) {
                    return false;
                }
                if (holiday == null) {
                    holiday = isHoliday(calendarDate);
                }
                if (holiday) {
                    let locationHoliday = getHolidayForDate(calendarDate);
                    holiday = locationHoliday.HourSpans.length == 0;
                }

                return !holiday;

            }

            this.isPickupDateAvailable = function (selectedTime, pickupDate, currentNow) {
                var availableTimes = pickupDate.getTimeChoices(currentNow);

                var pickupTime = _.find(availableTimes,
                    function (dc) {
                        return dc.value.isSame(selectedTime);
                    });

                return !!pickupTime;
            };

            this.getAvailablePickupDateTimesForMenu = _.memoize(function (currentNow) {
                // var self = this;
                now = currentNow || now;
                //Assume the user needs x minutes to fill out the form
                var nowOffset = now.clone().add(1, 'minutes');

                var dayChoices = _.map(hourSpanDays,
                    function (hourSpanDay) {
                        var dayHourSpans = _.sortBy(hourSpanDay.HourSpans, 'StartTimeOfDay');

                        var day = moment.utc(hourSpanDay.Date).utcOffset(timeZoneOffset);

                        if (isDoorDashDelivery && day.isSame(now, 'days')) {
                            /**WOC-1442 A note for you -- we pass the door dash delivery service delivery time along so that time choices
                             that are BEFORE Door Dash's estimated delivery time are filtered out. The waitTimeInMinutes doesn't necessarily
                             filter out all the times before Door Dash's estimated delivery time (or the location's open + prep + order type rule wait).
                             We ONLY pass this along if the PickupDateTime is for TODAY, as that is the date that the delivery service delivery
                             time was retrieved for.
                             Door Dash Delivery future day order time choices are being incorrectly filtered due to the way we are getting the "waitTimeInMinutes".
                             The "waitTimeInMinutes" is truly only for TODAY, and we are applying this same wait time for future day orders.
                             The most correct thing to do would be to get an estimate for the future day's open + prep + order type rule wait. Leaving this as a TODO. 
                            **/
                            return new PickupDateTime(day.clone(),
                                dayHourSpans,
                                waitTimeInMinutes,
                                timeSlotExclusions,
                                nowOffset, isDoorDashDelivery, deliveryServiceDeliveryTime, deliveryServiceTimeToDeliver);
                        } else {
                            return new PickupDateTime(day.clone(),
                                dayHourSpans,
                                waitTimeInMinutes,
                                timeSlotExclusions,
                                nowOffset, isDoorDashDelivery, null, deliveryServiceTimeToDeliver); // this seems to fix but the comment above is accurate. we need to change how we get future day's open + prep + order type rule wait.
                        }
                    });

                dayChoices = _.filter(dayChoices, function (dayChoice) {
                    return isDateSelectable(dayChoice.date, 'day');
                });

                dayChoices = _.sortBy(dayChoices,
                    function (dayChoice) {
                        return dayChoice.date.valueOf();
                    });

                let max = Math.min(dayChoices.length, 6);
                return dayChoices.slice(0, max);	//need only the first several, this contains more than it used to as holidays are now included
            });

            this.getPickupDateTimeForDate = function (date) {
                //Assume the user needs x minutes to fill out the form
                var nowOffset = now.clone().add(1, 'minutes');

                var day = date;

                if (!isDateSelectable(day, 'day')) {
                    return new PickupDateTime(day.clone(), [], waitTimeInMinutes, [], nowOffset, isDoorDashDelivery);
                }

                if (isHoliday(date)) {
                    let locationHoliday = getHolidayForDate(date);
                    return new PickupDateTime(day.clone(), locationHoliday.HourSpans, waitTimeInMinutes, timeSlotExclusions, nowOffset, isDoorDashDelivery, deliveryServiceDeliveryTime, deliveryServiceTimeToDeliver);
                }

                var hourSpanDay = _.findWhere(hourSpanDays, { StartDayOfWeek: day.day(), IsHoliday: false });

                if (!hourSpanDay) {
                    return new PickupDateTime(day.clone(), [], waitTimeInMinutes, [], nowOffset, isDoorDashDelivery);
                }
                var dayHourSpans = _.sortBy(hourSpanDay.HourSpans, 'StartTimeOfDay');

                return new PickupDateTime(day.clone(), dayHourSpans, waitTimeInMinutes, timeSlotExclusions, nowOffset, isDoorDashDelivery, deliveryServiceDeliveryTime, deliveryServiceTimeToDeliver);
            };

            this.isDateSelectable = isDateSelectable;

            this.setNow = function (val) {
                now = val;
            };
        }

        function PickupDateTime(date, hourSpans, waitTimeInMinutes, timeSlotExclusions, now, isDoorDashDelivery, deliveryServiceDeliveryTime, deliveryServiceTimeToDeliver) {
            var self = this;
            this.date = date;
            this.description = hourSpans.length && hourSpans[0].Description; //they are grouped so this works.
            this.siteAddress = hourSpans.length && hourSpans[0].SiteAddress;
            this.timeChoices = null;
            this.genericOpenCloseTime = [];
            this.isDoorDashDelivery = isDoorDashDelivery;
            this.deliveryServiceDeliveryTime = deliveryServiceDeliveryTime;
            this.deliveryServiceTimeToDeliver = deliveryServiceTimeToDeliver;

            function getStartDateTime(startTime) {
                var startTimeHours = parseInt(startTime, 10) / 100;
                var startTimeHourPart = Math.floor(startTimeHours);
                var startTimeMinutePart = Math.round((startTimeHours % 1) * 100);
                var calculatedStartDateTime = date.clone().hour(startTimeHourPart)
                    .minute(startTimeMinutePart)
                    .second(0)
                    .millisecond(0);

                return calculatedStartDateTime;
            }

            function getDurationInMinutes(durationInTicks) {
                var ticksPerMillisecond = 10000;
                var millisecondsPerSecond = 1000;
                var secondsPerMinute = 60;
                var minutes = durationInTicks / ticksPerMillisecond / millisecondsPerSecond / secondsPerMinute;
                return minutes;
            }

            this.closingTime = null;

            function setClosingTime() {

                _.each(hourSpans,
                    function (hourSpan) {

                        var startDateTime = getStartDateTime(hourSpan.StartTimeOfDay);
                        var durationInMinutes = getDurationInMinutes(hourSpan.DurationInTicks);

                        var closingTime = startDateTime.clone().add(durationInMinutes, 'minutes');

                        if (!self.closingTime || closingTime.isAfter(self.closingTime)) {
                            self.closingTime = closingTime;
                        }

                    });
            }

            setClosingTime();

            this.getTimeChoices = function (currentNow) {
                now = currentNow;
                var timeChoices = [];

                _.each(hourSpans,
                    function (hourSpan) {

                        var startDateTime = getStartDateTime(hourSpan.StartTimeOfDay);
                        var durationInMinutes = getDurationInMinutes(hourSpan.DurationInTicks);

                        setClosingTime(startDateTime, durationInMinutes);

                        var times = [];
                        if (durationInMinutes) {
                            times[0] = startDateTime;
                            for (var i = 1; i <= durationInMinutes / 5; i++) {
                                times[i] = times[i - 1].clone().add(5, 'minutes');
                            }
                        }

                        Array.prototype.push.apply(timeChoices,
                            _.map(times,
                                function (time) {
                                    return {
                                        value: time.clone(),
                                        display: time.format('h:mm A'),
                                        group: time.format('h A'),
                                        additionalWaitTimeInMinutes: hourSpan.AdditionalWaitTimeInMinutes
                                    };
                                }));
                    });

                timeChoices = _.sortBy(timeChoices,
                    function (t) {
                        return -t.additionalWaitTimeInMinutes;
                    });

                timeChoices = _.sortBy(timeChoices,
                    function (t) {
                        return t.value;
                    });

                timeChoices = _.uniq(timeChoices,
                    function (x) {
                        return x.display;
                    });

                timeChoices = _.filter(timeChoices,
                    function (t) {
                        var waitTime = waitTimeInMinutes + t.additionalWaitTimeInMinutes;

                        if (deliveryServiceTimeToDeliver != null) {
                            waitTime += deliveryServiceTimeToDeliver;
                        }

                        var earliestTime = now.clone().add(waitTime, 'minutes');
                        var time = t.value;
                        return time.isAfter(earliestTime) &&
                            !_.some(timeSlotExclusions,
                                function (exclusion) {
                                    return exclusion.isSame(time);
                                });
                    });

                // WOC-1320 Handle menus that have split times
                timeChoices = _.filter(timeChoices,
                    function (t) {
                        var waitTime = waitTimeInMinutes + t.additionalWaitTimeInMinutes;
                        if (deliveryServiceTimeToDeliver != null) {
                            waitTime += deliveryServiceTimeToDeliver;
                        }
                        var canSelectThisTime = false;
                        var time = t.value;

                        _.each(self.genericOpenCloseTime,
                            function (menuHour) {
                                var earliestTime = menuHour["start"].clone();
                                if (isDoorDashDelivery || deliveryServiceDeliveryTime) { // Door Dash delivery orders should filter for prep time
                                    earliestTime = menuHour["start"].clone().add(waitTime, 'minutes');
                                }
                                var closingTime = menuHour["close"];

                                var timeIsAfterEarliestTime = time.isAfter(earliestTime, 'minutes') || time.isSame(earliestTime, 'minutes');
                                var timeIsBeforeClosingTime = time.isBefore(closingTime, 'minutes') || time.isSame(closingTime, 'minutes');

                                var selectableTime = (timeIsAfterEarliestTime && timeIsBeforeClosingTime) || canSelectThisTime;
                                canSelectThisTime = selectableTime;
                            });
                        return canSelectThisTime;
                    });

                if (deliveryServiceDeliveryTime != null) {
                    var earliestDeliveryServiceDeliveryTime = moment.utc(deliveryServiceDeliveryTime);

                    timeChoices = _.filter(timeChoices,
                        function (t) {
                            var time = t.value;
                            return time.isAfter(earliestDeliveryServiceDeliveryTime);
                        });
                }

                return timeChoices;
            };

            this.retrieveTimeChoices = function (currentNow) {
                now = currentNow || now;
                if (!self.timeChoices || currentNow) {
                    self.timeChoices = self.getTimeChoices(now);
                }
                return self.timeChoices;
            };

            this.getNextAvailableTime = function (currentNow) {
                now = currentNow || now;
                var nextAvailableTime = null;

                _.each(hourSpans,
                    function (hourSpan) {
                        var startTime = getStartDateTime(hourSpan.StartTimeOfDay);
                        var durationInMinutes = getDurationInMinutes(hourSpan.DurationInTicks);
                        var closingTime = startTime.clone().add(durationInMinutes, 'minutes');

                        if (now.isBefore(closingTime) && (!nextAvailableTime || startTime.isBefore(nextAvailableTime))) {
                            nextAvailableTime = startTime;
                        }
                    });

                return nextAvailableTime;
            };

            if (hourSpans) {
                /**
                 * Set generic start and close times for menu's with multiple hour spans.
                 * NOTE - We only care about actual start/close times. Locations can be configured w/ hour spans that overlap,
                 * so this is an attempt to get definitive start and close hour spans.
                 * */

                var tempGenericOpenCloseTime = [];

                _.each(hourSpans,
                    function (hourSpan) {
                        var startDateTime = getStartDateTime(hourSpan.StartTimeOfDay);
                        var durationInMinutes = getDurationInMinutes(hourSpan.DurationInTicks);
                        tempGenericOpenCloseTime.push(
                            {
                                start: startDateTime,
                                close: startDateTime.clone().add(durationInMinutes, 'minutes')
                            }
                        );
                    });

                // Filter by start, then close times
                tempGenericOpenCloseTime = tempGenericOpenCloseTime.sort((a, b) => {
                    if (a["start"].isSame(b["start"])) {
                        return a["close"].isBefore(b["close"]) ? -1 : 1
                    } else {
                        return a["start"].isBefore(b["start"]) ? -1 : 1
                    }
                });

                var finalGenericOpenCloseTime = []
                var hourSpansLength = tempGenericOpenCloseTime.length;

                for (let i = 0; i < hourSpansLength; i++) {
                    var timeSpanOpen = tempGenericOpenCloseTime[i]["start"];
                    var thisTimeSpanClose = tempGenericOpenCloseTime[i]["close"];
                    var nextTimeSpanOpen = null;
                    var nextTimeSpanClose = null;
                    var nextTimeSpan = i + 1;

                    if (nextTimeSpan != hourSpansLength) {
                        nextTimeSpanOpen = tempGenericOpenCloseTime[nextTimeSpan]["start"];
                        nextTimeSpanClose = tempGenericOpenCloseTime[nextTimeSpan]["close"];
                    }

                    // If this time span is b/w a final time span, skip
                    var thisTimeSpanOpenIsWithinExistingFinal = finalGenericOpenCloseTime.some(x => x["close"].isAfter(timeSpanOpen));
                    var thisTimeSpanCloseIsWithinExistingFinal = finalGenericOpenCloseTime.some(x => x["close"].isAfter(thisTimeSpanClose)) || finalGenericOpenCloseTime.some(x => x["close"].isSame(thisTimeSpanClose));
                    var thisTimeSpanCloseIsAfterExistingFinal = finalGenericOpenCloseTime.some(x => x["close"].isBefore(thisTimeSpanClose));

                    var indexOfThisTimeSpanCloseIsBeforeExistingFinal = -1;
                    var index = finalGenericOpenCloseTime.length - 1;
                    for (; index >= 0; index--) {
                        if (finalGenericOpenCloseTime[index]["close"].isBefore(thisTimeSpanClose)) {
                            indexOfThisTimeSpanCloseIsBeforeExistingFinal = index;
                            break;
                        }
                    }

                    if (thisTimeSpanOpenIsWithinExistingFinal && thisTimeSpanCloseIsWithinExistingFinal) {
                        continue;
                    } else if (thisTimeSpanOpenIsWithinExistingFinal && thisTimeSpanCloseIsAfterExistingFinal && indexOfThisTimeSpanCloseIsBeforeExistingFinal > -1) {
                        finalGenericOpenCloseTime[indexOfThisTimeSpanCloseIsBeforeExistingFinal]["close"] = thisTimeSpanClose;
                        continue;
                    }

                    var indexOfThisTimeSpanOpenSameAsFinalTimeSpanClose = finalGenericOpenCloseTime.findIndex(x => x["close"].isSame(timeSpanOpen));

                    if (nextTimeSpanOpen == null) {
                        if (indexOfThisTimeSpanOpenSameAsFinalTimeSpanClose == -1) {
                            // Last time span that wasn't covered in other final time spans. Just add it
                            finalGenericOpenCloseTime.push({ start: timeSpanOpen, close: thisTimeSpanClose });
                        }
                        else {
                            finalGenericOpenCloseTime[indexOfThisTimeSpanOpenSameAsFinalTimeSpanClose]["close"] = thisTimeSpanClose;
                        }
                    }
                    else {
                        if (indexOfThisTimeSpanOpenSameAsFinalTimeSpanClose != -1) {
                            finalGenericOpenCloseTime[indexOfThisTimeSpanOpenSameAsFinalTimeSpanClose]["close"] = thisTimeSpanClose;
                        }
                        else if (nextTimeSpanOpen.isAfter(thisTimeSpanClose)) {
                            finalGenericOpenCloseTime.push({ start: timeSpanOpen, close: thisTimeSpanClose });
                        }
                        else if (nextTimeSpanOpen.isBefore(thisTimeSpanClose) && nextTimeSpanClose.isBefore(thisTimeSpanClose)) {
                            finalGenericOpenCloseTime.push({ start: timeSpanOpen, close: thisTimeSpanClose });
                        }
                        else if (nextTimeSpanOpen.isBefore(thisTimeSpanClose) || nextTimeSpanOpen.isSame(thisTimeSpanClose)) {
                            finalGenericOpenCloseTime.push({ start: timeSpanOpen, close: nextTimeSpanClose });
                        }
                    }
                }
                self.genericOpenCloseTime = finalGenericOpenCloseTime;
            }
        }

        PickupDateTime.prototype.getDisplayDay = function () {
            var calendar = this.date.calendar();
            if (this.description) {
                return calendar + " at " + this.description;
            }
            return calendar;
        };

        return service;
    }
})();
;
(function () {
	'use strict';

	var serviceId = 'linkService';

	angular.module('main').factory(serviceId, ['$http', '$window', linkService]);

	function linkService($http, $window) {
		var locationId = togoorder.locationId;

		return {
			openLinkInNewWindow: openLinkInNewWindow,
			callOpenLink: callOpenLink
		};

		function openLinkInNewWindow(link, windowName) {
		    if (!callOpenLink(link)) {
		        var w = $window.open("about:blank", windowName || "_blank", "menubar=no,scrollbars=yes,toolbar=no,titlebar=no,height=500, width=750, status=no");
		        w.opener = null;
		        w.location = link;
		    }
		}

		function callOpenLink(link) {
			return togoorder.callInjectedStrategy('openLink', link);
		}
	}
})();;
(function () {
'use strict';

var serviceId = 'loyaltyService';

angular.module('main').factory(serviceId, ['$http', '$q', 'api', 'merchantLocationService', loyaltyService]);

	function loyaltyService($http, $q, api, merchantLocationService) {
		var locationId = togoorder.locationId;
		var merchantId = togoorder.merchantId;

		return {
			assertMerchantHasLoyalty: assertMerchantHasLoyalty,
			createCard: createCard,
			updateCard: updateCard,
			addCard: addCard,
			getOffers: getOffers,
			getPointsBalance: getPointsBalance,
			getOffersEligibleForRedemption: getOffersEligibleForRedemption,
			optInToOffer: optInToOffer,
			optOutOfOffer: optOutOfOffer,
			getPunchCards: getPunchCards,
			getDishoutComPrefs: getDishoutComPrefs,
			updateDishoutComPrefs: updateDishoutComPrefs,
            wipeOffers: wipeOffers,
            validateOffers: validateOffers
		};

		function addCard(cardRequest) {
			return api.post('api/Loyalty/', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function updateCard(cardRequest) {
			return api.post('services/Loyalty/Update', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function getPointsBalance() {
			return api.get('api/Loyalty/GetPointsBalance?locationId=' + locationId + "&merchantId=" + merchantId)
				.then(function(response) {
					return response.data;
				});
		}

		function createCard(newCardRequest) {
			return api.put('api/Loyalty/', newCardRequest)
				.then(function(response) {
					return response.data;
				});

		}

		function assertMerchantHasLoyalty() {
			return merchantLocationService.getById(locationId).then(function(location) {
				if (!location.LoyaltyProfile) {
					throw new Error("Loyalty is not supported for this Merchant");
				}
			});
		}

		function getOffers(offerListRequest) {
			offerListRequest.LocationId = locationId;

			return api.post('api/Loyalty/GetOfferList', offerListRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function getOffersEligibleForRedemption(offerListRequest) {
			offerListRequest.LocationId = locationId;

			return api.post('api/Loyalty/GetOfferListEligibleForRedemption', offerListRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function getPunchCards(punchCardRequest) {
			punchCardRequest.LocationId = locationId;

			return api.post('api/Loyalty/GetPunchCards', punchCardRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function getDishoutComPrefs(request) {
			return api.post('api/Loyalty/GetDishoutComPrefs', request)
				.then(function(response) {
					return response.data;
				});
		}

		function updateDishoutComPrefs(prefs) {
			return api.post('api/Loyalty/UpdateDishoutComPrefs', prefs)
				.then(function(response) {
					return response.data;
				});
		}

		function optInToOffer(optRequest) {
			optRequest.LocationId = locationId;
			return api.post('api/Loyalty/OptIn', optRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function optOutOfOffer(optRequest) {
			optRequest.LocationId = locationId;
			return api.post('api/Loyalty/OptOut', optRequest)
				.then(function(response) {
					return response.data;
				});
		}

        function wipeOffers() {
            var wipeRequest = {
				merchantId,
                locationId
            };
            return api.post('api/Loyalty/WipeOffers', wipeRequest)
                .then(function(response) {
                    return response.data;
                });
        }

        function validateOffers(request) {
            request.LocationId = locationId;
            request.MerchantId = merchantId;

            return api.post('api/Loyalty/ValidateOffers', request)
                .then(function(response) {
                    return response.data;
                });
        }
	}
})();;
(function () {
'use strict';

var serviceId = 'mealPlanService';

angular.module('main').factory(serviceId, ['$http', '$q', 'api', mealPlanService]);

	function mealPlanService($http, $q, api) {
		var locationId = togoorder.locationId;

		return {
			addCard: addCard,
			getBalance: getBalance,
			redeem: redeem
		};

		function addCard(cardRequest) {
			cardRequest.LocationId = locationId;
			return api.post('services/MealPlan/AddCard', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function getBalance(cardRequest) {
			cardRequest.LocationId = locationId;
			return api.post('services/MealPlan/Balance', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}

		function redeem(cardRequest) {
			cardRequest.LocationId = locationId;
			return api.post('services/MealPlan/Redeem', cardRequest)
				.then(function(response) {
					return response.data;
				});
		}

	}
})();;
(function () {
    'use strict';

    var serviceId = 'menuService';

	angular.module('main').factory(serviceId, ['$http', '$log', '$q', '$cacheFactory', 'api', 'events', menuService]);

	function menuService($http, $log, $q, $cacheFactory, api, events) {
		var menuUrls = [];

        return {
            getMenu: _.memoize(getMenu),
            getTopLevelMenuItems: getTopLevelMenuItems,
            getAllMenuItems: getAllMenuItems,
			getIncludedGroups: getIncludedGroups,
			getItemById: getItemById,
			clearMenuCache: clearMenuCache 
        };

        function getIncludedGroups(menuItemId, menuId, locationId) {
            var url = 'api/MenuItem/' + menuItemId + '?menuId=' + menuId + '&locationId=' + locationId;
            return api.get(url, { cache: true }).then(function (response) {
                return response.data;
            });
		}

		function clearMenuCache() {
			var cache = $cacheFactory.get('$http');
			_.each(menuUrls, function (u) {
				cache.remove(u);
			});
			menuUrls = [];
		}

        function getMenu(menuId) {
            var url = 'api/Menu/' + menuId;
			return api.get(url, { cache: true }).then(function (response) {
				menuUrls.push(response.config.url);
                //debugger;
                return response.data;
            });
        }

        function getTopLevelMenuItems(menu) {
            return _.reduceRight(menu.MenuSections, function(memo, section) {
                 return memo.concat(section.MenuItems);
            }, []);
        }

        function getAllModifiers(item, list) {
            _.each(item.IncludedGroups, function (includedGroup) {
                list = list.concat(includedGroup.MenuItems);
                _.each(includedGroup.MenuItems, function(mi) {
                    list = getAllModifiers(mi, list);
                });
            });
            return list;
        }

        function getAllMenuItems(menu) {
            var list = [];
            _.each(getTopLevelMenuItems(menu), function(item) {
                list.push(item);
                list = getAllModifiers(item, list);
            });

            return list;
		}

		function getItemById(menu, itemId) {
			return _.findWhere(getTopLevelMenuItems(menu), { Id: itemId });
		}

    }
})();;
(function () {
	'use strict';

	var serviceId = 'menuWorkflowService';

	angular.module('main').factory(serviceId, ['$http', '$q', '$state', '$stateParams', '$timeout', '$log', 'events', 'common', 'notify', 'merchantLocationService', orderTypeWorkflowService]);

	function orderTypeWorkflowService($http, $q, $state, $stateParams, $timeout, $log, events, common, notify, merchantLocationService) {
		var locationId = togoorder.locationId,
			merchantLocation = {};

		return {
			goToFirstState: goToFirstState,
			getFirstStateName: getFirstStateName,
			goToSections: goToSections,
			initializeRouteState: initializeRouteState,
			mustNavigateToSection: mustNavigateToSection,
			showItemDetail: showItemDetail,
			showSections: showSections,
			isSectionsState: isSectionsState,
			isSectionItemsState: isSectionItemsState,
			continueOrdering: continueOrdering,
			getItemIncludedItemsTemplate: getItemIncludedItemsTemplate
		};

		function isSectionsState(current) {
			return current.name === 'menu.sections' || current.name === 'days.menus.menu.sections';
		}

		function isSectionItemsState(current) {
			return current.name === 'menu.sections.items' || current.name === 'days.menus.menu.sections.items';
		}

		function showSections(stateParams, options, cart) {
			if (togoorder.merchantLocation.MenuWorkflow === 1) {
				stateParams.day = cart.day;
				return $state.go('days.menus.menu.sections.items', stateParams, options);
			}
			return $state.go('menu.sections.items', stateParams, options);
		}

		function goToSections(stateParams, options) {
			if (togoorder.merchantLocation.MenuWorkflow === 1) {
				return $state.go('days.menus.menu.sections', stateParams, options);
			} else {
				return $state.go('menu.sections', stateParams, options);
			}
		}

		function initializeRouteState(replaceOptions) {

			if (!$state.is('menu.sections') &&
				!$state.is('menu.sections.items') &&
				!$state.is('days.menus.menu.sections') &&
				!$state.is('days.menus.menu.sections.items')) {

                if (togoorder.merchantLocation.MenuWorkflow === 1) {
					$state.go('days.menus.menu.sections', $stateParams, replaceOptions);
				} else {
					$state.go('menu.sections', $stateParams, replaceOptions);
				}

			}
		}

		function continueOrdering(cart) {
			var order = cart.order;
			goToSections({
				merchantLocationId: locationId,
				menuId: order.MenuId,
				orderType: order.OrderType,
				fulfillmentType: order.FulfillmentType,
				day: cart.day
			});
		}

		function showItemDetail() {
			if (togoorder.merchantLocation.MenuWorkflow === 1) {
				$state.go('days.menus.menu.itemDetail', $stateParams);
			} else {
				$state.go('menu.itemDetail', $stateParams);
			}
		}

		function mustNavigateToSection() {
			if (togoorder.merchantLocation.MenuSectionUiType === 2) {
				// Horizontal (CKE)
				return false;
			}
            if (togoorder.merchantLocation.MenuSectionUiType === 3) {
                // ExpandedWithHorizontalNav
                return false;
            }
			return !$state.is('menu.sections.items')
				&& !$state.is('days.menus.menu.sections.items');
		}

		function getFirstStateName() {
			if (togoorder.merchantLocation.MenuWorkflow === 1) {
				return "days";
			}
			return 'chooseMenu';
		}

		function goToFirstState(stateParams, options) {
			$state.go(getFirstStateName(), stateParams, options);
		}

		function groupHasSubGroups(includedGroup) {
			return _.some(includedGroup.MenuItems,
				function(menuItem) {
					return menuItem.IncludedGroups && menuItem.IncludedGroups.length;
				});
		}

		function getItemIncludedItemsTemplate(includedGroup) {
			
			if (!includedGroup.MenuItems) {
				return undefined;
			}

			const menuItemUiTypeMap = {
				0: 'app/menu/itemIncludedItems.expanded.html',
				1: 'app/menu/itemIncludedItems.collapsed.html'
			};

			var canUseCollapsed = !groupHasSubGroups(includedGroup);

			if (!canUseCollapsed) {
				return menuItemUiTypeMap[0];
			}

			let itemUiTypeMap = menuItemUiTypeMap[togoorder.merchantLocation && togoorder.merchantLocation.MenuItemUiType] || menuItemUiTypeMap[0];
			
			return itemUiTypeMap;
		}


	}
})();;
(function() {
	'use strict';

	var serviceId = 'merchantLocationService';

	angular.module('main').factory(serviceId,
		['$http', '$q', '$log', 'hoursOfOperationService', 'orderTypeViewModelServiceFactory', merchantLocationService]);

	function merchantLocationService($http, $q, $log, hoursOfOperationService, orderTypeViewModelServiceFactory) {

		var giftCardProviderTypes = { Dishout: 0, Focus: 1 };
		var mealPlanProviderTypes = { Dishout: 0 };
		var loyaltyProviderTypes = { Heartland: 0, Synergy: 1, Logix: 2, Dishout: 3, Togo: 4, Square: 6 };

		return {
			getOrderTypes: getOrderTypes,
			getOrderTypeRules: getOrderTypeRules,
			getMenus: getMenus,
			getDaysWithMenus: getDaysWithMenus,
			getById: getById,
			validateLocationIsActive: validateLocationIsActive,
			validateCanOrder: validateCanOrder,
			getCanOrder: getCanOrder,
			getEarliestDayOrderable: getEarliestDayOrderable,
			requiresBirthdate: requiresBirthdate,
			requiresEmail: requiresEmail,
			requiresCallbackNumber: requiresCallbackNumber,
			getConvenienceFeeLabel: getConvenienceFeeLabel,
			getSurchargeLabel: getSurchargeLabel
		};

		function getMerchantLocation(merchantLocationId) {
			var tgo = togoorder || {};
			if (!tgo.merchantLocation || tgo.merchantLocation.Id !== merchantLocationId) {
				$log.warn("locationId " + merchantLocationId + " requested.");
				return $q.reject();
			}
			var x = $q.defer();

			x.resolve(tgo.merchantLocation);

			return x.promise;
		}

		function getConvenienceFeeLabel() {
			let convenienceFeeLabel = 'Convenience Fee'; 

			if (togoorder.merchantLocation.ConvenienceFeeLabel != '' && togoorder.merchantLocation.ConvenienceFeeLabel != null && togoorder.merchantLocation.ConvenienceFeeLabel != 'undefined') {
				convenienceFeeLabel = togoorder.merchantLocation.ConvenienceFeeLabel;
			} 

			return convenienceFeeLabel;
		}

		function getSurchargeLabel() {
			let surchargeLabel = 'Surcharge';

			if (togoorder.merchantLocation.SurchargeLabel != '' && togoorder.merchantLocation.SurchargeLabel != null && togoorder.merchantLocation.SurchargeLabel != 'undefined') {
				surchargeLabel = togoorder.merchantLocation.SurchargeLabel;
			}

			return surchargeLabel;
		}

		function validateLocationIsActive() {
			return getMerchantLocation(togoorder.locationId).then(function(location) {
				if (!location.IsActive) {
					throw new Error("This Location Is Not Active");
				}
			});
		}

		function requiresBirthdate() {
			return getMerchantLocation(togoorder.locationId).then(function(location) {
				return false;
				//var hasLoyalty = !!(location.LoyaltyProfile || location.MealPlanProfile || location.GiftCardProfile);

				//if (!hasLoyalty) {
				//	return false;
				//}

				//var loyaltyProviderType = (location.LoyaltyProfile && location.LoyaltyProfile.LoyaltyProviderType);
				//var mealPlanProviderType = (location.MealPlanProfile && location.MealPlanProfile.MealPlanProviderType);
				//var giftCardProviderType = (location.GiftCardProfile && location.GiftCardProfile.GiftCardProviderType);

				//return (mealPlanProviderType === mealPlanProviderTypes.Dishout || giftCardProviderType === giftCardProviderTypes.Dishout || loyaltyProviderType === loyaltyProviderTypes.Dishout);

			});
		}

		function requiresCallbackNumber() {
			return getMerchantLocation(togoorder.locationId).then(function (location) {
				var hasLoyalty = !!(location.LoyaltyProfile || location.MealPlanProfile || location.GiftCardProfile);

				if (!hasLoyalty) {
					return false;
				}

				var loyaltyProviderType = (location.LoyaltyProfile && location.LoyaltyProfile.LoyaltyProviderType);
				var mealPlanProviderType = (location.MealPlanProfile && location.MealPlanProfile.MealPlanProviderType);
				var giftCardProviderType = (location.GiftCardProfile && location.GiftCardProfile.GiftCardProviderType);

				//Square? Midax?
				return (mealPlanProviderType === mealPlanProviderTypes.Dishout || giftCardProviderType === giftCardProviderTypes.Dishout || loyaltyProviderType === loyaltyProviderTypes.Dishout || loyaltyProviderType === loyaltyProviderTypes.Square);
			});
		}
		
		function requiresEmail() {
			return getMerchantLocation(togoorder.locationId).then(function (location) {
				var hasLoyalty = !!(location.LoyaltyProfile || location.GiftCardProfile || location.MealPlanProfile);
				return hasLoyalty;
			});
		}

		function OrderingNotAllowedException() {
			this.name = 'OrderingNotAllowed';
			this.message = 'So Sorry. We can\'t take your order right now.';
		}

		function getCanOrder() {
            return getOrderTypes(togoorder.locationId).then(function(data) {
				var canOrder = data.orderTypeViewModels.length;
				return !!canOrder;
			});

		}

		function validateCanOrder() {
			return getCanOrder().then(function(canOrder) {
				if (!canOrder) {
					throw new OrderingNotAllowedException();
				}
			});
		}

		function getById(id) {
			return getMerchantLocation(id);
		}

		function getOrderTypes(merchantLocationId) {
			return getMerchantLocation(merchantLocationId).then(function(location) {
				var orderTypeViewModelService = orderTypeViewModelServiceFactory.getLocationInstance();
				var orderTypeViewModels = orderTypeViewModelService.getOrderTypeViewModels(location);
				return {
					mobileMessage: location.MobileMessage,
					orderTypeViewModels: orderTypeViewModels
				};
			});
		}


		function getHoursOfOperationDisplay(hoursOfOperation) {
			return hoursOfOperationService.getHoursOfOperationDisplay(hoursOfOperation);
		}

		function getOrderTypeRules(merchantLocationId, orderType) {
			return getMerchantLocation(merchantLocationId).then(function(data) {
				var orderTypeViewModels = _.filter(data.OrderTypeViewModels,
					function(ot) {
						return !!(ot.MenuIds && ot.MenuIds.length) && ot.OrderType == orderType;
					});
				return orderTypeViewModels;
			});
		}

		function getEarliestDayOrderable(merchantLocationId, orderType, fulfillmentType) {
			return getMerchantLocation(merchantLocationId).then(function(data) {
				var orderTypeViewModel = _(data.OrderTypeViewModels).find(function(otvm) {
					return otvm.OrderType == orderType && otvm.FulfillmentType == fulfillmentType;
				});
				return orderTypeViewModel.EarliestDayOrderable;
			});
		}

		function getMenus(merchantLocationId, orderType, fulfillmentType) {
			return getMerchantLocation(merchantLocationId)
				.then(function(location) {
					var orderTypeViewModel = _(location.OrderTypeViewModels).find(function(otvm) {
						return otvm.OrderType == orderType && otvm.FulfillmentType == fulfillmentType;
					});
					return {
						menuSelections: orderTypeViewModel &&
							_.map(orderTypeViewModel.MenuIds,
								function(menuId) {
									var menu = location.Menus[menuId];
									menu.HoursOfOperationDisplay = getHoursOfOperationDisplay(menu.HoursOfOperation);
									return menu;
								})
					};
				});
		}

		function getDaysWithMenus(merchantLocationId, orderType, fulfillmentType) {
			return getMerchantLocation(merchantLocationId).then(function(location) {
				var days = location.AvailableDays;
				return _.map(days,
					function(day) {
						var fulfillmentTypes = day.OrderTypeMenuIds[orderType];
						var menuIds = fulfillmentTypes && fulfillmentTypes[fulfillmentType];
						var menus = _.map(menuIds || [],
							function(menuId) {
								var menu = location.Menus[menuId];
								return menu;
							});
						var dayWithMenus = {
							weekDay: day.WeekDay,
							dayOfWeek: day.DayOfWeek,
							hoursOfOperationDisplay: getHoursOfOperationDisplay(day.HoursOfOperation),
							menus: menus
						};
						return dayWithMenus;
					});
			});
		}

	}
})();;
(function() {
    "use strict";

    var serviceId = "merchantMessageService";

    angular.module("main").factory(serviceId, ["$http", "$q", "timeService", "merchantLocationService", "api", "notify", merchantMessageService]);

    function merchantMessageService($http, $q, timeService, merchantLocationService, api, notify) {
        var messages;
        return {
            showMessages: showMessages
        };

        function showMessages() {
            return getMessages().then(function () {
                var p = $q.when();
                _.each(messages.Messages, function (message) {
                    if (message.HasBeenSeen) {
                        return;
                    }
                    if (!togoorder.callInjectedStrategy('showMerchantMessage', message)) {
                        p = p.then(function () {
                            return notify.dialog({
                                dialogTitle: '',
                                dialogMessage: message.Message,
                                closeButtonText: 'Got It'
                            }).then(function() {
                                acknowledgeMessage(message.MessageId);
                            });
                        });
                    }
                });
                return p;
            });
        }

        function acknowledgeMessage(messageId) {
            api.put('api/MerchantMessage/', {MessageId: messageId});
        }

        function getMessages() {
            var url = "api/MerchantMessage/";
            return api.get(url, {
                cache: false,
                params: {
                    locationId: togoorder.locationId
                }
            }).then(function (response) {
                return messages = response.data;
            });
        }
    }
})();;
(function () {
    'use strict';

    var serviceId = 'orderHistoryService';

    angular.module('main').factory(serviceId, ['$http', '$q', '$log', '$state', '$window', '$uibModal', 'common', 'events', 'api', 'cartService', 'deliveryService', 'merchantLocationService', 'userService', orderHistoryService]);

    function orderHistoryService($http, $q, $log, $state, $window, $modal, common, events, api, cartService, deliveryService, merchantLocationService, userService) {
        return {
            getOrderById: getOrderById,
            selectOrder: selectOrder,
            showOrderHistory: showOrderHistory
        };

        function selectOrder(order, merchantLocation) {
            var promise = (order.DeliveryAddress && order.DeliveryAddress.AddressLine1) ?
                    deliveryService.cacheDeliveryAddress(order.DeliveryAddress) :
                    $q.when(1);

            return promise.then(function () {
                return cartService.setOrder(order, merchantLocation);
            }).then(function () {
                //go home first to make sure the 'activate' gets called on the menu.sections controller
                return $state.go('locationHome');
            }).then(function () {
                return $state.go('menu.sections', { orderType: order.OrderType, fulfillmentType: order.FulfillmentType, menuId: order.MenuId });
            }).then(function () {
                common.broadcast(events.presentCart);
            });
        }

        function showOrderHistory(merchantLocation) {
            var url = 'api/User/OrderHistory';
            return api.get(url, { cache: false, params: { merchantLocationId: merchantLocation.Id } }).then(function(response) {
                $modal.open({
                    templateUrl: 'app/orderHistory/orderHistoryList.html',
                    controller: 'orderHistoryList as vm',
                    size: 'md',
                    resolve: {
                        candidateOrders: function() { return response.data.CandidateOrders; }
                    }
                }).result.then(function(o) {
                    return selectOrder(o, merchantLocation);
                });
            });
        }

        function waitForAuthentication() {
            var defer = $q.defer();

            var checkAuthenticated = function() {
                if (userService.checkIsAuthenticated()) {
                    defer.resolve();
                } else {
                    $window.setTimeout(checkAuthenticated, 300);
                }
            }

            checkAuthenticated();
            return defer.promise;
        }

        function getOrderById(orderId) {

            return waitForAuthentication().then(function() {
                var url = 'api/User/PastOrder';
                var orderPromise = api.get(url, { cache: false, params: { orderId: orderId } });

                var merchantLocationPromise = merchantLocationService.getById(togoorder.locationId);

                return $q.all([orderPromise, merchantLocationPromise]).then(function (responses) {
                    var order = responses[0].data.Order;
                    return selectOrder(order, responses[1]);
                });
            });
        }
    }
})();;
(function () {
	'use strict';

	var serviceId = 'orderPricingService';

	angular.module('main').factory(serviceId,
		['$rootScope', '$http', '$q', '$log', '$window', 'menuService', orderPricingService]);

	function orderPricingService($rootScope, $http, $q, $log, $window, menuService) {
		var applicationId = togoorder.applicationId;
		let memoizedPriceOrder = _.memoize(priceOrder, priceOrderHashFunction);

		return {
			rePriceOrder: rePriceOrder,
			applyPricingRules: applyPricingRules,
			getSelectionCount: getSelectionCount
		};

		function getSelectionCount(orderItems) {
			var reduce = _.reduce(orderItems,
				function (cnt, orderItem) {
					if (orderItem.Items.length) {
						return cnt + orderItem.Items[0].countEquivalent;
					}
					return cnt + 1;
				},
				0);
			return reduce;

		}

		function applyPricingRules(order, menu) {
			var allMenuItems = menuService.getAllMenuItems(menu);
			var allOrderItems = getAllOrderItems(order);

			applyPizzaToppingPricingRule(allMenuItems, allOrderItems);
		}

		function applyPizzaToppingPricingRule(allMenuItems, allOrderItems) {
			var foundItems = {};
			allOrderItems = _.sortBy(allOrderItems,
				function (oi) {
					return 0 - ((oi.parentOrderItem && oi.parentOrderItem.ItemPrice) || 0);
				});
			_.each(allOrderItems,
				function (oi) {
					var orderItem = oi.orderItem;
					var menuItem = _.findWhere(allMenuItems,
						{
							Id: orderItem.ItemId,
							GroupInstanceId: orderItem.GroupInstanceId
						});

					if (menuItem) {
						if (oi.parentOrderItem) {
							oi.parentOrderItem.unModifyPrice(menuItem.PricingRule);
						}

						if (menuItem.PricingRule === 'PizzaToppingHalf') {
							if (foundItems[oi.grandParentOrderItem.UniqueId]) {
								oi.parentOrderItem.modifyPrice(0, menuItem.PricingRule);
								foundItems[oi.grandParentOrderItem.UniqueId] = false;
							} else {
								foundItems[oi.grandParentOrderItem.UniqueId] = true;
							}
						}
					}
				});
		}

		function getAllOrderItems(orderItemContainer, parentOrderItem, grandParentOrderItem) {
			var list = [];
			_.each(orderItemContainer.Items,
				function (oi) {
					list.push({
						orderItem: oi,
						parentOrderItem: parentOrderItem,
						grandParentOrderItem: grandParentOrderItem
					});
					_.each(getAllOrderItems(oi, oi, parentOrderItem),
						function (subOi) {
							list.push(subOi);
						});
				});
			return list;
		}

		function copyItems(dst, src) {
			var srcItems = src.Items;
			var dstItems = dst.Items;
			_.each(srcItems,
				function (srcItem, i) {

					var destinationItems = _.where(dstItems, { ItemId: srcItem.ItemId });

					if (destinationItems.length > 1) {
						destinationItems = _.where(destinationItems, { UniqueId: srcItem.UniqueId });
					}

					_.each(destinationItems,
						function (dstItem) {
							if (!dstItem) {
								$log.debug('item ' + srcItem.ItemId + ' not found. Size?');
								return;
							}
							if (dstItem.OriginalItemPrice === undefined) {
								dstItem.OriginalItemPrice = dstItem.ItemPrice;
							}
							dstItem.ItemPrice = srcItem.ItemPrice;
							copyItems(dstItem, srcItem);
						});
				});
		}

		function priceOrderHashFunction() {
			return angular.toJson(arguments);
		}

		function rePriceOrder(order, getUpsellItems) {
			order.isFetchingPrice = true;

			return memoizedPriceOrder(order, getUpsellItems).then(function (priceResult) {
				var pricedOrder = priceResult.Order;

				order.pricedOrder = pricedOrder;

				copyItems(order, pricedOrder);

				return priceResult;
			},
				function (e) {
					order.pricedOrder = undefined;
					throw e;
				})['finally'](function () {
					order.isFetchingPrice = false;
				});
		}


		function priceOrder(order, getUpsellItems) {
			var payment = order.Payment;
			var loyaltyPayment = (payment.LoyaltyPayment)
				? {
					LoyaltyAccountNumber: payment.LoyaltyPayment.AccountNumber,
					LoyaltyPaymentAmount: order.LoyaltyPaymentAmount
				}
				: null;
			var offerPayment = (payment.OfferPayment) ? {
				LoyaltyAccountNumber: payment.OfferPayment.AccountNumber,
				OfferPaymentAmount: order.OfferPaymentAmount
			} : null;
			var externalPromotion = (payment.ExternalPromotion) ? {
				ExternalPromotionPaymentAmount: payment.ExternalPromotion.ExternalPromotionPaymentAmount,
				PaymentAuthCode: payment.ExternalPromotion.PaymentAuthCode,
				PromoCode: payment.ExternalPromotion.PromoCode,
				PosOfferCode: payment.ExternalPromotion.PosOfferCode,
				IsPreTax: payment.ExternalPromotion.IsPreTax
			} : null;

			var priceRequest = {
				Order: order,
				IsUpsellItemsIncluded: getUpsellItems,
				CartPromotion: order.Promotion,
				ApplicationId: applicationId,
				LoyaltyPayment: loyaltyPayment,
				OfferPayment: offerPayment,
				ExternalPromotion: externalPromotion
			};

			return $http.post(togoorder.apiUrl + 'api/OrderService/Price', priceRequest)
				.then(function (result) {
					return result && result.data;
				});
		}

	}
})();;
(function () {
    'use strict';

    var serviceId = 'orderService';

    angular.module('main').factory(serviceId, ['$rootScope', '$http', '$q', '$log', '$window', 'orderPricingService', 'menuService', 'common', 'events', 'Order', 'OrderItem', 'crmService', orderService]);

    function orderService($rootScope, $http, $q, $log, $window, orderPricingService, menuService, common, events, Order, OrderItem, crmService) {

        var applicationId = togoorder.applicationId,
	        locationId = togoorder.locationId,
            orderSummary = {};

        var onBeforeUnloadHandler = function (event) {
            var json = angular.toJson(orderSummary);
            $window.sessionStorage['orderSummary'] = json;
        };

        if ($window.addEventListener) {
            $window.addEventListener('beforeunload', onBeforeUnloadHandler);
        } else {
            $window.onbeforeunload = onBeforeUnloadHandler;
        }

        try {
            var orderSummaryJson = $window.sessionStorage['orderSummary'];
            if (orderSummaryJson) {
                orderSummary = angular.fromJson(orderSummaryJson);
            }
        } catch (e) {
            $log.error("Local Storage error", e);
        }


        return {
            hydrateOrder: hydrateOrder,
            getOrderSummary: getOrderSummary,
            placeOrder: placeOrder,
            rePriceOrder: rePriceOrder
		};

		function rePriceOrder(order, merchantLocation, getUpsellItems) {
			order.PickupTimeZoneId = merchantLocation.TimeZoneId;
			order.MerchantId = merchantLocation.MerchantId;
			return orderPricingService.rePriceOrder(order, getUpsellItems);
		}

        function placeOrder(order, payment, merchantLocation, isDeferredPayment, pickupInfo, eligibleOptedOffers, deliveryServicePickupTimeUtc) {
            order.PickupTimeZoneId = merchantLocation.TimeZoneId;
            order.MerchantId = merchantLocation.MerchantId;
			order.PricedOrder = order.pricedOrder;
            order.DeliveryServicePickupTimeUtc = deliveryServicePickupTimeUtc;

            var checkoutRequestNewCardPayment = payment.NewCreditCard.IsSelected ? payment.NewCreditCard : {};
            var checkoutRequestSavedCardPayment = payment.ExistingCreditCard.IsSelected ? payment.ExistingCreditCard : {};
            var loyaltyPayment = (payment.LoyaltyPayment) ? {
                 LoyaltyAccountNumber: payment.LoyaltyPayment.AccountNumber,
                 LoyaltyPaymentAmount: order.LoyaltyPaymentAmount
            } : null;
            var mealPlanPayment = (payment.MealPlanPayment) ? {
	            MealPlanAccountNumber: payment.MealPlanPayment.AccountNumber,
	            MealPlanPaymentAmount: order.MealPlanPaymentAmount
            } : null;
            var giftCardPayment = (payment.GiftCardPayment) ? {
                 GiftCardAccountNumber: payment.GiftCardPayment.AccountNumber,
                GiftCardPaymentAmount: order.GiftCardPaymentAmount,
                GiftCardPinNumber: payment.GiftCardPayment.PinNumber
            } : null;
			var offerPayment = (payment.OfferPayment) ? {
				LoyaltyAccountNumber: payment.OfferPayment.AccountNumber,
                OfferPaymentAmount: order.OfferPaymentAmount
            } : null;
            var externalPromotion = (payment.ExternalPromotion) ? {
	            ExternalPromotionPaymentAmount: payment.ExternalPromotion.ExternalPromotionPaymentAmount,
                PaymentAuthCode: payment.ExternalPromotion.PaymentAuthCode,
                PromoCode: payment.ExternalPromotion.PromoCode,
                PosOfferCode: payment.ExternalPromotion.PosOfferCode,
                ReferenceNumber: payment.ExternalPromotion.ReferenceNumber,
				IsPreTax: payment.ExternalPromotion.IsPreTax
            } : null;
            var digitalWalletPayment = (payment.DigitalWallet) ? {
                DigitalWalletType: payment.DigitalWallet.DigitalWalletType,
                Token: payment.DigitalWallet.DigitalWalletToken,
                CardNetwork: payment.DigitalWallet.CardNetwork,
                Last4: payment.DigitalWallet.Last4
            } : null;

			var checkoutRequest = {
				Order: order,
				CartPromotion: order.Promotion,
				CreditCardPayment: checkoutRequestNewCardPayment,
				SavedCreditCardPayment: checkoutRequestSavedCardPayment,
				CardHolder: payment.CardHolder,
				ApplicationId: applicationId,
				LoyaltyPayment: loyaltyPayment,
				MealPlanPayment: mealPlanPayment,
				GiftCardPayment: giftCardPayment,
				IsDeferredPayment: isDeferredPayment,
				EligibleOptedOffers: eligibleOptedOffers,
                OfferPayment: offerPayment,
                ExternalPromotion: externalPromotion,
                DigitalWalletPayment: digitalWalletPayment
            };

            if (checkoutRequest.Order.Customer.FirstName == null && checkoutRequest.Order.Customer.LastName == null) {
                checkoutRequest.Order.Customer.FirstName = "";
                checkoutRequest.Order.Customer.LastName = ""
            }

            checkoutRequest.Order.Customer.FirstName = checkoutRequest.Order.Customer.FirstName.replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"');
            checkoutRequest.Order.Customer.LastName = checkoutRequest.Order.Customer.LastName.replace(/[\u2018\u2019]/g, "'").replace(/[\u201C\u201D]/g, '"');

            return $http.post(togoorder.apiUrl + 'api/OrderService/Checkout', checkoutRequest)
                .then(function (result) {
                    common.broadcast(events.orderPlaced, { order: order, orderId: result.data.OrderId });
			        orderSummary =
                    {
                        'locationId': locationId,
                        'orderId': result.data.OrderId,
                        'orderUrlId': result.data.UrlId,
                        'tableNumber': order.TableNumber,
						'vehicleColor': order.VehicleColor,
						'vehicleModel': order.VehicleModel,
                        'deliveryAddress': order.DeliveryAddress,
                        'pickupDate': order.PickupDateTime,
                        'orderType': order.OrderType,
                        'fulfillmentType': order.FulfillmentType,
                        'pickupLocation': (pickupInfo && _.filter([pickupInfo.description, pickupInfo.siteAddress], angular.identity).join(" | ")) || merchantLocation.AddressSummary,
                        'items': order.Items
                    };
                    crmService.usedReward(checkoutRequest.EligibleOptedOffers);
                    crmService.purchase(checkoutRequest.Order, result.data.OrderId);
                    crmService.applyPromo(order.Promotion.Code);
			        common.broadcast(events.promotionIsSelected, {promotion: order.Promotion});
			        return result;
			    });
        }

        function getOrderSummary() {
            if (!orderSummary || !orderSummary.locationId || orderSummary.locationId !== locationId) {
                return $q.reject();
            }
            return $q.when(orderSummary);
        }

        function hydrateOrder(orderJson) {
            if (!orderJson) return null;
            var orderData = (typeof orderJson === 'string') ?
                angular.fromJson(orderJson) :
                orderJson;

            var menuId = orderData.MenuId;
            return menuService.getMenu(menuId).then(function(menu) {
	            var order = angular.extend(new Order(0, 0), orderData);

	            hydrateOrderItems(menu, order);
	            return order;
            });
        }

		function hydrateOrderItems(menu, data, parentOrderItem) {
            _.each(data.Items, function (orderItemData, i, items) {
				items[i] = angular.extend(new OrderItem({ CountEquivalent: 1 }, parentOrderItem), orderItemData);
				
                if (!parentOrderItem) {
                    // it's a parent item
                    var item = menuService.getItemById(menu, items[i].ItemId);
                    items[i].PrimaryImageUrl = item.PrimaryImageUrl;
                }
				hydrateOrderItems(menu, items[i], items[i]);
            });
        }
    }
})();
;
    (function () {
    'use strict';

    var serviceId = 'orderTypeRuleService';

    angular.module('main').factory(serviceId, ['$log', '$state', '$stateParams', 'common', 'events', 'notify', 'merchantLocationService', 'menuWorkflowService', orderTypeRuleService]);

    function orderTypeRuleService($log, $state, $stateParams, common, events, notify, merchantLocationService, menuWorkflowService) {
        var locationId = togoorder.locationId;

        return {
            validateOrderTypeRule: validateOrderTypeRule,
            validateOrderTypeRuleMenu: validateOrderTypeRuleMenu,
            getDisplayOrderTypes: getDisplayOrderTypes,
            switchToOrderTypeRule: switchToOrderTypeRule,
            getOrderTypeRule: getOrderTypeRule
        };

        function getOrderTypeRule(orderType, fulfillmentType, merchantLocation) {
            orderType = +orderType;
            fulfillmentType = +fulfillmentType;

            var orderTypeRule = _.find(merchantLocation.OrderTypeViewModels, function (ot) {
                return ot.OrderType === orderType && ot.FulfillmentType === fulfillmentType;
            });
            if (!orderTypeRule) {
                $log.error('No OrderTypeViewModel found for OrderType: ' + orderType + ', FulfillmentType: ' + fulfillmentType);
                notify.error('Invalid Order Type');
            }

            return orderTypeRule;
        }

        function getDisplayOrderTypes(filter) {
            return merchantLocationService.getOrderTypes(locationId)
	            .then(function (data) {
                    var orderTypeViewModels = data.orderTypeViewModels;
	                if (filter) {
	                    orderTypeViewModels = _.filter(orderTypeViewModels, filter);
	                }
	                return orderTypeViewModels;
	            });
        }

        function switchToOrderTypeRule(newOrderTypeRule, menuId, isMenuSwitchProblematic) {
            merchantLocationService.getMenus(locationId, newOrderTypeRule.OrderType, newOrderTypeRule.FulfillmentType).then(function (data) {
                var menus = data.menuSelections;
                var menu = _.some(menus, { MenuId: menuId });
                if (menu) {
                    menuWorkflowService.goToSections({
                        merchantLocationId: locationId,
                        menuId: menuId,
                        orderType: newOrderTypeRule.OrderType,
                        fulfillmentType: newOrderTypeRule.FulfillmentType
                    }, { location: 'replace' }).then(function () {
                        common.broadcast(events.presentCart);
                    });
                } else if (isMenuSwitchProblematic) {
                    var result = notify.dialog({
                        dialogTitle: 'Switch to ' + newOrderTypeRule.Description + '?',
                        dialogMessage: newOrderTypeRule.Description + ' has a different menu. If you switch, you\'ll lose your cart.',
                        dismissButtonText: 'Cancel',
                        closeButtonText: 'Switch'
                    });
                    result.then(function () {
                        $stateParams.menuId = null;
                        menuWorkflowService.goToFirstState({ orderType: newOrderTypeRule.OrderType, fulfillmentType: newOrderTypeRule.FulfillmentType });
                    });
                } else {
                    $stateParams.menuId = null;
                    menuWorkflowService.goToFirstState({ orderType: newOrderTypeRule.OrderType, fulfillmentType: newOrderTypeRule.FulfillmentType });
                }
            });
        }

        function validateOrderTypeRule(orderType, fulfillmentType) {
            return merchantLocationService.getById(locationId).then(function (location) {
                orderTypeRuleValidation(location, orderType, fulfillmentType);
            });
        }

        function validateOrderTypeRuleMenu(orderType, fulfillmentType, menuId) {
            return merchantLocationService.getById(locationId).then(function (location) {
                var rule = orderTypeRuleValidation(location, orderType, fulfillmentType);
                orderTypeRuleMenuValidation(rule, menuId);
            });
        }

        function orderTypeRuleValidation(location, orderType, fulfillmentType) {
            var rule = _.find(location.OrderTypeViewModels, function (ot) {
                return ot.OrderType == orderType && ot.FulfillmentType == fulfillmentType;
            });
            if (!rule) {
                $log.error('This Location does not support Order Type ' + orderType + ' with Fulfillment Type ' + fulfillmentType);
                throw new OrderTypeRuleMissingException();
            }
            return rule;
        }

        function orderTypeRuleMenuValidation(rule, menuId) {
            if (!_.some(rule.MenuIds, function (id) {
                    return id == menuId;
            })) {
                $log.error('Order Type ' + rule.OrderType + ' and Fulfillment Type ' + rule.FulfillmentType + ' are not compatible with Menu ' + menuId);
                throw new OrderTypeRuleMenuMismatchException();
            }
        }

        function OrderTypeRuleMissingException() {
            this.name = 'OrderTypeRuleMissing';
        }

        function OrderTypeRuleMenuMismatchException() {
            this.name = 'OrderTypeRuleMenuMismatch';
        }
    }
})();;
(function () {
    'use strict';

    var serviceId = 'orderTypeWorkflowService';

	angular.module('main').factory(serviceId, ['$http', '$q', '$state', '$timeout', '$log', 'events', 'common', 'notify', 'deliveryService', 'merchantLocationService', 'menuWorkflowService', 'orderTypeViewModelServiceFactory', 'orderTypeRuleService', orderTypeWorkflowService]);

	function orderTypeWorkflowService($http, $q, $state, $timeout, $log, events, common, notify, deliveryService, merchantLocationService, menuWorkflowService, orderTypeViewModelServiceFactory, orderTypeRuleService) {
        var locationId = togoorder.locationId,
	        orderTypeViewModelService = orderTypeViewModelServiceFactory.getLocationInstance(),
            address = {},
            tableNumberWorkflow = angular.extend(orderTypeViewModelService.tableNumberData,
                {
                    state: 'getTableNumber',
                    verify: verifyTableNumber,
                    displayTemplate: 'app/tableNumber/displayTemplate.html',
                    getData: getTableNumber,
                    pickupDateTimePrefix: '',
                    isPickupDateSelectable: false,
                    payLaterText: 'Pay at the register',
					deliveryZones: null,
					promptTemplate: null
				}),
            deliveryAddressWorkflow = angular.extend(orderTypeViewModelService.deliveryAddressData,
                {
                    state: 'getDeliveryAddress',
                    verify: verifyAddress,
                    displayTemplate: 'app/deliveryAddress/displayTemplate.html',
                    getData: getDeliveryAddress,
                    pickupDateTimePrefix: 'Delivery',
                    isDelivery: true,
                    isPickupDateSelectable: true,
                    payLaterText: 'Pay at delivery',
					deliveryZones: null,
                    promptTemplate: null
                }),
            normalWorkflow = angular.extend(orderTypeViewModelService.normalData,
                {
                    state: menuWorkflowService.getFirstStateName(),
                    verify: verifyNothing,
                    displayTemplate: '',
                    getData: function () { return $q.when(true); },
                    pickupDateTimePrefix: 'Pickup',
                    isPickupDateSelectable: true,
                    payLaterText: 'Pay at the register',
					deliveryZones: null,
					promptTemplate: null,
					isDefault: true
                }),
            cateringForPickupWorkflow = angular.extend(orderTypeViewModelService.cateringForPickupData,
                normalWorkflow,
                { 
                    useCalendar: true, 
                    orderTypeDescription: orderTypeViewModelService.cateringForPickupData.orderTypeDescription,
                    displayTemplate: 'app/cateringForPickup/displayTemplate.html',
	                isDefault:false
                }),
            cateringForDeliveryWorkflow = angular.extend(orderTypeViewModelService.cateringForDeliveryData,
                deliveryAddressWorkflow,
                {
                    useCalendar: true,
                    orderTypeDescription: orderTypeViewModelService.cateringForDeliveryData.orderTypeDescription,
                    displayTemplate: 'app/cateringForDelivery/displayTemplate.html'
                }),
			takeoutCurbsideWorkflow = angular.extend(orderTypeViewModelService.takeoutCurbsideData, normalWorkflow,
		        {
					orderTypeDescription: orderTypeViewModelService.takeoutCurbsideData.orderTypeDescription,
					payLaterText: 'Pay at the store',
					promptTemplate: 'app/curbside/takeoutCurbsidePromptTemplate.html',
					displayTemplate: '',
                    isDefault: false
				}),
	        driveThruWorkflow = angular.extend(orderTypeViewModelService.driveThruData, normalWorkflow,
		        {
			        orderTypeDescription: orderTypeViewModelService.driveThruData.orderTypeDescription,
			        extendForVehicleInformation: orderTypeViewModelService.driveThruData.extendForVehicleInformation,
			        payLaterText: 'Pay at the drive-thru',
			        displayTemplate: 'app/driveThru/displayTemplate.html',
			        isDefault: false
		        }),
	        driveThruWithVehicleInfoWorkflow = angular.extend(orderTypeViewModelService.driveThruWithVehicleInfoData, normalWorkflow,
		        {
			        orderTypeDescription: orderTypeViewModelService.driveThruWithVehicleInfoData.orderTypeDescription,
			        extendForVehicleInformation: orderTypeViewModelService.driveThruData.extendForVehicleInformation,
			        payLaterText: 'Pay at the drive-thru',
			        promptTemplate: 'app/driveThru/vehiclePromptTemplate.html',
			        displayTemplate: 'app/driveThru/displayTemplate.html',
			        isDefault: false
		        }),
	        curbsideWorkflow = angular.extend(orderTypeViewModelService.curbsideData, normalWorkflow,
		        {
			        orderTypeDescription: orderTypeViewModelService.curbsideData.orderTypeDescription,
			        payLaterText: 'Pay at the store',
			        promptTemplate: 'app/curbside/curbsidePromptTemplate.html',
			        displayTemplate: 'app/curbside/displayTemplate.html'
		        }),
	        carsideWorkflow = angular.extend(orderTypeViewModelService.carsideData, normalWorkflow,
		        {
			        orderTypeDescription: orderTypeViewModelService.carsideData.orderTypeDescription,
			        payLaterText: 'Pay at the store',
			        promptTemplate: 'app/carside/carsidePromptTemplate.html',
			        displayTemplate: 'app/carside/displayTemplate.html',
			        isDefault: false
                }),
            fulfillmentTypeMap = orderTypeViewModelService.fulfillmentTypeMap;

        return {
            getPayLaterText: getPayLaterText,
            goToNextState: goToNextState,
            getDisplayTemplate: getDisplayTemplate,
            getDisplayData: getDisplayData,
            getPickupDateTimePrefix: getPickupDateTimePrefix,
            meetsRequirementsForOrderType: meetsRequirementsForOrderType,
            isDelivery: isDelivery,
            isPickupDateSelectable: isPickupDateSelectable,
            isCalendarRequired: isCalendarRequired,
			getPromptTemplate: getPromptTemplate,
			getOrderTypeDescription: getOrderTypeDescription,
            getTakeoutCurbsideDescription: getTakeoutCurbsideDescription,
			setOrderTypeRule: setOrderTypeRule
		};

		function setOrderTypeRule(orderTypeRule, hasVehicleInformation) {
			let workflow = getWorkflow(orderTypeRule.OrderType, orderTypeRule.FulfillmentType);

            if (workflow.extendForVehicleInformation) {
	            workflow.extendForVehicleInformation(orderTypeRule, hasVehicleInformation);
            }
		}

		function getTableNumber(vm) {
            vm.onChangeTableNumberRequest = function () {
                common.broadcast(events.changeTableNumberRequest);
            };
            return deliveryService.getTableNumber(locationId).then(function (tn) {
                return vm.tableNumber = tn;
            });
        }

        function getDeliveryAddress(vm) {
            return deliveryService.getDeliveryAddress().then(function (da) {
	            vm.onChangeDeliveryAddressRequest = function() {
		            common.broadcast(events.changeDeliveryAddressRequest);
	            };
                return vm.address = da;
            });
        }

        function getWorkflow(orderType, fulfillmentType) {
            if (!orderType) { throw new Error('orderType required'); }
            if (!fulfillmentType) { throw new Error('fulfillmentType required'); }
            var workflow = (fulfillmentTypeMap[fulfillmentType] || fulfillmentTypeMap.otherwise);
            if (!workflow.state) {
                return workflow[orderType] || workflow.otherwise;
            }
            return workflow;
        }

        function getDisplayData(orderType, fulfillmentType, vm) {
            var workflow = getWorkflow(orderType, fulfillmentType);
            var fn = workflow.getData;
            return fn(vm, locationId).then(null, function () {
                $log.error("Display data not found for order type: " + workflow.orderTypeDescription, orderType, fulfillmentType);
            });
		}

		function getPromptTemplate(orderType, fulfillmentType) {
			return getWorkflow(orderType, fulfillmentType).promptTemplate;
		}

        function getPayLaterText(orderType, fulfillmentType) {
            return getWorkflow(orderType, fulfillmentType).payLaterText;
        }

        function getDisplayTemplate(orderType, fulfillmentType) {
            return getWorkflow(orderType, fulfillmentType).displayTemplate;
        }

        function getPickupDateTimePrefix(orderType, fulfillmentType) {
            return getWorkflow(orderType, fulfillmentType).pickupDateTimePrefix;
        }

        function goToNextState(orderType, fulfillmentType, goOptions) {
            var nextState = getWorkflow(orderType, fulfillmentType).state;
            $timeout(function () {
                $state.go(nextState, { orderType: orderType, fulfillmentType: fulfillmentType }, goOptions);
            }, 10);
        }

        function verifyTableNumber(orderType, fulfillmentType) {
            return deliveryService.getTableNumber(locationId)
                .then(null, function (reason) {
                    var nextState = getWorkflow(orderType, fulfillmentType).state;
                    throw new common.exceptions.RedirectRequiredException(reason, { name: nextState, params: { orderType: orderType, fulfillmentType: fulfillmentType } });
                });
        }

        function verifyAddress(orderType, fulfillmentType) {

            return deliveryService.getDeliveryAddress()
                .then(function (da) {
                    return address = da;
                })
                .then(function () {
                    var orderTypeRule = orderTypeRuleService.getOrderTypeRule(orderType, fulfillmentType, togoorder.merchantLocation);
                    if (orderTypeRule.DeliveryServiceHasOwnZones)
                        return $q.reject('skip-zones-bro');

                    return deliveryService.getDeliveryZones(orderType, fulfillmentType);
                })
                .then(function (deliveryZones) {
                    if (!deliveryZones || !deliveryZones.length) {
                        return $q.reject('We are currently having an issue with delivery. So sorry.');
                    }
                    return deliveryService.checkAddressIsInAnyDeliveryArea(address, deliveryZones)
                        .then(function () {
                            return $q.when(true);
                        }, function () {
                            return $q.reject('Your address is not in the delivery area');
                        });
                })
                .then(null, function (reason) {
                    if (reason === 'skip-zones-bro')
                        return $q.when(true);
                    
                    var nextState = getWorkflow(orderType, fulfillmentType).state;
                    throw new common.exceptions.RedirectRequiredException(reason, { name: nextState, params: { orderType: orderType, fulfillmentType: fulfillmentType } });
                });
        }

        function isDelivery(orderType, fulfillmentType) {
            var workflow = getWorkflow(orderType, fulfillmentType);
            return !!workflow.isDelivery;
		}

		function getTakeoutCurbsideDescription(orderType, fulfillmentType, vehicleModel, vehicleColor) {
			var workflow = getWorkflow(orderType, fulfillmentType);
			if (workflow.isDefault) {
				if (vehicleModel) {
					return orderTypeViewModelService.curbsideDescription;
				}
				return orderTypeViewModelService.takeoutDescription;
			}
		}

        function getOrderTypeDescription(orderType, fulfillmentType) {
            var workflow = getWorkflow(orderType, fulfillmentType);
            return workflow.orderTypeDescription;
        }

        function isPickupDateSelectable(orderType, fulfillmentType) {
            var workflow = getWorkflow(orderType, fulfillmentType);
            return !!workflow.isPickupDateSelectable;
        }
        function isCalendarRequired(orderType, fulfillmentType) {
            var workflow = getWorkflow(orderType, fulfillmentType);
            return !!workflow.useCalendar;
        }

        function verifyNothing(orderType, fulfillmentType) {
            return $q.when(1);
        }

        function meetsRequirementsForOrderType(orderType, fulfillmentType) {
            var workflow = getWorkflow(orderType, fulfillmentType);
            return workflow.verify(orderType, fulfillmentType);
        }

    }
})();;
(function () {
    'use strict';

    var serviceId = 'orderValidationService';

    angular.module('main').factory(serviceId, ['$http', '$log', '$q', 'api', 'events', 'menuService', 'orderPricingService', orderValidationService]);

    function orderValidationService($http, $log, $q, api, events, menuService, orderPricingService) {
        
        return {
            validateOrder: validateOrder
        };


        function validateOrder(order, menu, merchantLocation) {
	        var validations = validateOrderTypeRule(order, menu, merchantLocation);
            if (validations.fatals) {
                return validations;
            }
            var menuItems = menuService.getTopLevelMenuItems(menu);
            function validateItems() {
				_.each(order.Items, function (topOrderItem) {
					if (!topOrderItem.shouldShow) {
						return;
					}
                    topOrderItem.errors = null;
                    validateMenuItems(order, menuItems, topOrderItem, topOrderItem, order, '', validations);
                });
            }

            validateItems();

            if (validations.warnings && validations.warnings.missing) {
                //do it again incase we removed one that was required.
                validateItems();
            }

            order.LastValidated = new Date();

            return validations;
        }


        function validateMenuItems(order, menuItems, topOrderItem, orderItem, orderItemContainer, itemContainerDescription, validations) {
            var menuItem = _.findWhere(menuItems, { Id: orderItem.ItemId });

            if (!menuItem) {
                addMissingWarning(validations, orderItem, itemContainerDescription);
                $log.info('removing item from order', orderItem);
                orderItemContainer.deleteItem(orderItem);
            } else {
	            if (menuItem.MaximumCount) {
		            var itemsWithItemId = _.where(orderItemContainer.Items, { ItemId: orderItem.ItemId });
		            var totalCount = _.reduce(itemsWithItemId,
			            function(memo, oi) {
				            return memo + oi.Quantity;
			            },
			            0);
		            if (totalCount > menuItem.MaximumCount) {
			            $log.info('removing item from order', orderItem);
			            orderItemContainer.deleteItem(orderItem);
		            }
	            }

	            var addPriceWarning = moment(order.LastValidated).isBefore(moment().subtract(12, 'h'));

                if (menuItem.Price < orderItem.ItemPrice && (!orderItem.OverridePrice)) {
                    if (addPriceWarning) {
                        addNewPriceWarning(validations, 'newLowerPrice', orderItem, menuItem, itemContainerDescription);
                    }
                    orderItem.ItemPrice = menuItem.Price;
                } else if (menuItem.Price > orderItem.ItemPrice && (!orderItem.OverridePrice)) {
                    if (addPriceWarning) {
                        addNewPriceWarning(validations, 'newHigherPrice', orderItem, menuItem, itemContainerDescription);
                    }
                    orderItem.ItemPrice = menuItem.Price;
                }
                var newItemContainerDescription = itemContainerDescription;
                if (newItemContainerDescription) newItemContainerDescription += ' > ';
                newItemContainerDescription += orderItem.ItemName;

                _.each(menuItem.IncludedGroups, function (includedGroup) {
                    var newDesc = newItemContainerDescription;
                    if (includedGroup.GroupDescription && includedGroup.GroupDescription !== ' ') {
                        if (newDesc) newDesc += ' > ';
                        newDesc += includedGroup.GroupDescription;
                    }

                    var orderItemItems = orderItem.Items;
                    var optionSelections = _.filter(orderItemItems, function (oi) {
                        return oi.GroupInstanceId === includedGroup.GroupInstanceId;
                    });

                    var selectionCount = orderPricingService.getSelectionCount(optionSelections);

                    if (includedGroup.MinChoices > 1 && selectionCount < includedGroup.MinChoices) {
	                    topOrderItem.errors = topOrderItem.errors || {};
	                    topOrderItem.errors.min = includedGroup;
	                    addError(validations, 'min', { includedGroup: includedGroup, groupDescription: newDesc });
                    }
                    else if (includedGroup.IsRequired && !selectionCount) {

                        topOrderItem.errors = topOrderItem.errors || {};
                        topOrderItem.errors.required = includedGroup;
                        addError(validations, 'required', { includedGroup: includedGroup, groupDescription: newDesc });
                    }
                    if (includedGroup.MaxChoices < selectionCount) {
                        topOrderItem.errors = topOrderItem.errors || {};
                        topOrderItem.errors.max = includedGroup;
                        addError(validations, 'max', { includedGroup: includedGroup, groupDescription: newDesc });
                    }

                    _.each(optionSelections, function (oi) {
                        validateMenuItems(order, includedGroup.MenuItems, topOrderItem, oi, orderItem, newDesc, validations);
                    });
                });
                _.each(orderItem.Items, function(oi) {
                    var groupInstanceId = oi.GroupInstanceId;
                    var includedGroup = _.find(menuItem.IncludedGroups, function(ig) {
                        return parseInt(ig.GroupInstanceId, 10) === parseInt(groupInstanceId, 10);
                    });
                    if (!includedGroup) {
                        addMissingGroupWarning(validations, oi, newItemContainerDescription);
                        $log.info('removing item from order (missing group)', oi);
                        orderItem.deleteItem(oi);
                    }
                });
            }
        }

        function validateOrderTypeRule(order, menu, merchantLocation) {
            var orderTypeViewModel = _.find(merchantLocation.OrderTypeViewModels, function (ot) {
                return ot.OrderType == order.OrderType && ot.FulfillmentType == order.FulfillmentType;
            });
            if (!orderTypeViewModel) {
                return {
                    fatals: {
                        missingOrderType: [{
                            orderType: order.OrderType,
                            fulfillmentType: order.FulfillmentType
                        }]
                    }
                };
            }
            if (_.some(orderTypeViewModel.MenuIds, function (id) { return id === menu.Id; })) {
                return {};
            }
            return {
                fatals: {
                    menuOrderTypeMismatch: [{
                        menu: menu,
                        orderType: order.OrderType,
                        fulfillmentType: order.FulfillmentType
                    }]
                }
            };
        }

        function addWarning(validations, type, warning) {
            validations.warnings = validations.warnings || {};
            validations.warnings[type] = validations.warnings[type] || [];
            validations.warnings[type].push(warning);
        }
        function addMissingWarning(validations, orderItem, parentItemName) {
            $log.info('missing ' + orderItem.ItemName);
            addWarning(validations, 'missing', {
                itemName: orderItem.ItemName,
                parentItemName: parentItemName
            });
        }
        function addMissingGroupWarning(validations, orderItem, parentItemName) {
            $log.info('missing group for ' + orderItem.ItemName);
            addWarning(validations, 'missingGroup', {
                itemName: orderItem.ItemName,
                parentItemName: parentItemName
            });
        }
        function addNewPriceWarning(validations, type, orderItem, menuItem, parentItemName) {
            $log.info('price change for ' + orderItem.ItemName);
            addWarning(validations, type, {
                itemName: orderItem.ItemName,
                oldPrice: orderItem.ItemPrice,
                newPrice: menuItem.Price,
                parentItemName: parentItemName
            });
        }
        function addError(validations, type, value) {
            $log.info('validation error: ' + type, value);
            validations.errors = validations.errors || {};
            validations.errors[type] = validations.errors[type] || [];
            validations.errors[type].push(value);
        }


    }
})();;
(function () {
	'use strict';

	var serviceId = 'paymentGatewayProfileSettingsService';

	angular.module('main').factory(serviceId, ['$http', '$q', 'api', paymentGatewayProfileSettingsService]);

	function paymentGatewayProfileSettingsService($http, $q, api) {
		var locationId = togoorder.locationId;

		return {
			isAddressRequiredAtCheckout: isAddressRequiredAtCheckout,
			isDigitalWalletProfile: isDigitalWalletProfile
		};

		function isAddressRequiredAtCheckout(locationId) {
			return api.post('services/PaymentGatewayProfileSettings/IsAddressRequiredAtCheckout/' + locationId.locationId)
				.then(function (response) {
					return response.data;
				});
		}

		function isDigitalWalletProfile(locationId) {
			return api.post('services/PaymentGatewayProfileSettings/IsDigitalWalletProfile/' + locationId.locationId)
				.then(function (response) {
					return response.data;
				});
		}


	}
})();;
(function () {
	'use strict';

	var serviceId = 'promoCodeService';

	angular.module('main').factory(serviceId, ['$http', '$q', 'api', 'merchantLocationService', promoCodeService]);

	function promoCodeService($http, $q, api, merchantLocationService) {
		var locationId = togoorder.locationId;

		return {
			getPromotion: getPromotion
		};

		function getPromotion(promo, menuId) {
			promo.locationId = locationId;
			promo.menuId = menuId;
			return api.post('services/Promotion/Lookup', promo)
		        .then(function(response) {
		            return response.data;
		        });
		}

	}
})();;
(function () {
	'use strict';

	var serviceId = 'routeStateService';

	angular.module('main').factory(serviceId, ['$state', routeStateService]);

	function routeStateService($state) {

		var backRoutes = [];

		return {
			addState: addState,
			goBack: goBack,
			hasAppBack: hasAppBack
		};

		function hasAppBack() {
			return _.some(backRoutes, function(route) {
				return route.name !== $state.current.name;
			});
		}

		function goBack() {
			if (backRoutes.length) {
				var route = backRoutes.pop();
				while (route && route.name === $state.current.name) {
					route = backRoutes.pop();
				}
				if (route) {
					$state.go(route.name, route.params);
				}
			}
		}

		function addState() {
			var args = _.toArray(arguments);
			if (args.length) {
				var route = {
					name: args[0].current.name,
					params: args[0].params
				};
				backRoutes.push(route);
				return this;
			}
			return backRoutes;
		}
	}
})();;
(function() {
    'use strict';

    angular.module('main').value('staticData', getStaticData());

    function getStaticData() {
        return {
            timeZones: [
                "Central Standard Time",
                "Eastern Standard Time",
                "Mountain Standard Time",
                "US Mountain Standard Time",
                "Pacific Standard Time",
                "Hawaiian Standard Time",
                "Atlantic Standard Time",
                "SA Western Standard Time",
                "West Pacific Standard Time"
            ],
            states: [
                { name: "Alabama", value: "AL" },
                { name: "Alaska", value: "AK" },
                { name: "Alberta", value: "AB" },
                { name: "American Samoa", value: "AS" },
                { name: "Arizona", value: "AZ" },
                { name: "Arkansas", value: "AR" },
                { name: "British Columbia", value: "BC" },
                { name: "California", value: "CA" },
                { name: "Colorado", value: "CO" },
                { name: "Connecticut", value: "CT" },
                { name: "Delaware", value: "DE" },
                { name: "District of Columbia", value: "DC" },
                { name: "Federated States of Micronesia", value: "FM" },
                { name: "Florida", value: "FL" },
                { name: "Georgia", value: "GA" },
                { name: "Guam", value: "GU" },
                { name: "Hawaii", value: "HI" },
                { name: "Idaho", value: "ID" },
                { name: "Illinois", value: "IL" },
                { name: "Indiana", value: "IN" },
                { name: "Iowa", value: "IA" },
                { name: "Kansas", value: "KS" },
                { name: "Kentucky", value: "KY" },
                { name: "Louisiana", value: "LA" },
                { name: "Maine", value: "ME" },
                { name: "Manitoba", value: "MB" },
                { name: "Marshall Islands", value: "MH" },
                { name: "Maryland", value: "MD" },
                { name: "Massachusetts", value: "MA" },
                { name: "Michigan", value: "MI" },
                { name: "Minnesota", value: "MN" },
                { name: "Mississippi", value: "MS" },
                { name: "Missouri", value: "MO" },
                { name: "Montana", value: "MT" },
                { name: "Nebraska", value: "NE" },
                { name: "Nevada", value: "NV" },
                { name: "New Brunswick", value: "NB" },
                { name: "New Hampshire", value: "NH" },
                { name: "New Jersey", value: "NJ" },
                { name: "New Mexico", value: "NM" },
                { name: "New York", value: "NY" },
                { name: "Newfoundland and Labrador", value: "NL" },
                { name: "North Carolina", value: "NC" },
                { name: "North Dakota", value: "ND" },
                { name: "Northern Mariana Islands", value: "MP" },
                { name: "Northwest Territories", value: "NT" },
                { name: "Nova Scotia", value: "NS" },
                { name: "Nunavut", value: "NU" },
                { name: "Ohio", value: "OH" },
                { name: "Oklahoma", value: "OK" },
                { name: "Ontario", value: "ON" },
                { name: "Oregon", value: "OR" },
                { name: "Palau", value: "PW" },
                { name: "Pennsylvania", value: "PA" },
                { name: "Prince Edward Island", value: "PE" },
                { name: "Puerto Rico", value: "PR" },
                { name: "Quebec", value: "QC" },
                { name: "Rhode Island", value: "RI" },
                { name: "Saskatchewan", value: "SK" },
                { name: "South Carolina", value: "SC" },
                { name: "South Dakota", value: "SD" },
                { name: "Tennessee", value: "TN" },
                { name: "Texas", value: "TX" },
                { name: "Utah", value: "UT" },
                { name: "Vermont", value: "VT" },
                { name: "Virgin Islands", value: "VI" },
                { name: "Virginia", value: "VA" },
                { name: "Washington", value: "WA" },
                { name: "West Virginia", value: "WV" },
                { name: "Wisconsin", value: "WI" },
                { name: "Wyoming", value: "WY" },
                { name: "Yukon", value: "YT" }
            ]
        };
    }

})();;
(function() {
	"use strict";

	const serviceId = "timeService";

	angular.module("main").factory(serviceId, ["$http", "$q", "$log", "api", timeService]);

	function timeService($http, $q, $log, api) {
		return {
			getNowForTimeZoneId: getNowForTimeZoneId,
			getBrowserTimeZoneOffset: getBrowserTimeZoneOffset,
			initialize: initialize
		};

		function initialize() {
			moment.updateLocale("en",
				{
					calendar: {
						lastDay: "[Yesterday]",
						sameDay: "[Today]",
						nextDay: "[Tomorrow]",
						lastWeek: "[Last] dddd",
						nextWeek: "dddd",
						sameElse: "MMMM D"
					}
				});
		}

		function getBrowserTimeZoneOffset() {
			return moment().utcOffset();
		}

		function getNowForTimeZoneId(timeZoneId) {
			const url = "api/Time/Now";
			return api.get(url, { cache: false, params: { timeZoneId: timeZoneId } }).then(function(response) {
				const utcNow = moment.utc(response.data.UtcNow);

				const nowForLocation = utcNow.clone().utcOffset(response.data.Offset);

				if (nowForLocation.hour() < 2) {
					moment.updateLocale("en",
						{
							calendar: {
								lastDay: "[This Morning]",
								sameDay: "[Later Today] (dddd)",
								nextDay: "dddd",
								lastWeek: "[Last] dddd",
								nextWeek: "dddd",
								sameElse: "MMMM D"
							}
						});
				}

				return {
					utcNow: utcNow,
					localNow: nowForLocation,
					localCalendarDate: moment(response.data.LocalCalendarDate)
				};
			});
		}
	}
})();;
(function () {
	'use strict';

	angular.module('main').service('userService', ['$http', '$q', '$log', '$window', 'notify', 'events', 'common', 'api', 'User', userService]);

	function userService($http, $q, $log, $window, notify, events, common, api, User) {
		var nextState = {
			name: '',
			params: {},
			error: ''
		},
			getUserWithLoyaltyOnce,
			getUserBasicInfoOnce,
			cachedUserWithLoyalty,
			locationId = $window.togoorder.locationId,
			togoorder = $window.togoorder;

		var service = this;

		service.getUserData = getUserData;
		service.authenticate = authenticate;
		service.signUp = signUp;
		service.removeAuthentication = removeAuthentication;
		service.isAuthenticated = isAuthenticated;
		service.checkIsAuthenticated = checkIsAuthenticated;
		service.isAGuest = isAGuest;
		service.getNextState = getNextState;
		service.setNextState = setNextState;
		service.clearNextState = clearNextState;
		service.getPaymentMethods = getPaymentMethods;
		service.getUserWithLoyalty = getUserWithLoyalty;
		service.getUser = getUser;
		service.deletePaymentMethod = deletePaymentMethod;
		service.login = login;
		service.setUserAsGuest = setUserAsGuest;
		service.resetPassword = resetPassword;
		service.changePassword = changePassword;
		service.changeEmailOptIn = changeEmailOptIn;
		service.changeName = changeName;
		service.changeEmail = changeEmail;
		service.changeBirthdate = changeBirthdate;
		service.changeCallbackNumber = changeCallbackNumber;
		service.clearGuest = clearGuest;
		service.deleteUser = deleteUser;
		service.getUserMerchants = getUserMerchants;
		service.getUserSpecialRecommendations = getUserSpecialRecommendations;
		service.getUserSavedDeliveryAddresses = getUserSavedDeliveryAddresses;
		service.saveUserSavedDeliveryAddresses = saveUserSavedDeliveryAddresses;
		service.updateUserSavedDeliveryAddresses = updateUserSavedDeliveryAddresses;
		service.deleteUserSavedDeliveryAddresses = deleteUserSavedDeliveryAddresses

		function setHttpAuthHeader() {
			$http.defaults.headers.common.Authorization = 'Bearer ' + togoorder.userData.bearerToken;
			$http.defaults.headers.common.MerchantId = togoorder.merchantId;
		}

		function invalidateCache() {
			getUserWithLoyaltyOnce = _.throttle(getUserWithLoyaltyApiCall, 2000, { trailing: false });
			getUserBasicInfoOnce = _.throttle(getUserBasicInfoApiCall, 60000, { trailing: false });
			cachedUserWithLoyalty = undefined;
		}

		function getUserData() {
			return togoorder.userData;
		}

		function signUp(user) {
			return api.post('api/User', user)
				.then(function () {
					invalidateCache();
					notify.success('Successfully created new account!');
				}, function (response) {
					invalidateCache();
					response = response || {};
					$log.error(response);
					var data = response.data || {};
					var errorMessage = data.Message || 'An error occurred. Please try again or contact us for help.';
					return $q.reject(errorMessage);
				});
		}

		function getPaymentMethods(locationId) {
			return api.get('api/User/PaymentMethods/' + locationId)
				.then(function (response) {
					return response.data;
				});
		}

		function deletePaymentMethod(paymentMethod) {
			var cacheName = 'api/User/PaymentMethods?id=' + paymentMethod.Id;
			return api.delete(cacheName)
				.then(function (response) {
					return response.data;
				});
		}

		function deleteUser() {
			var cacheName = 'api/User/delete/';
			return api.delete(cacheName)
				.then(function (response) {
					return response.data;
				});
		}

		function getUserMerchants() {
			return cachedUserWithLoyalty = api.get('api/UserMerchants')
				.then(function (response) {
					var merchants = response.data;
					return merchants;
				});
        }

		function getUserWithLoyalty(merchantId, fresh) {
			if (fresh || !getUserWithLoyaltyOnce) {
				invalidateCache();
			}

			return getUserWithLoyaltyOnce(merchantId);
		}

		function getUser(merchantId, fresh) {
			if (fresh || !getUserBasicInfoOnce) {
				invalidateCache();
			}

			return getUserBasicInfoOnce(merchantId);
		}

		function getUserWithLoyaltyApiCall(merchantId) {
			return cachedUserWithLoyalty = api.get('api/UserInfo/' + merchantId + "/" + locationId)
				.then(function (response) {
					var user = response.data;
					return new User(user);
				}, function (response) {
					// if we're here, they've deleted their account somewhere else and they need to log in again
					removeAuthentication();
					setNextState('logout', undefined, undefined);
				});
		}

		function getUserBasicInfoApiCall(merchantId) {
			if (cachedUserWithLoyalty) {
				return cachedUserWithLoyalty;
			}
			return api.get('api/UserBasicInfo/' + merchantId + "/" + locationId)
				.then(function (response) {
					var user = response.data;
					return new User(user);
				}, function (response) {
					// if we're here, they've deleted their account somewhere else and they need to log in again
					removeAuthentication();
					setNextState('logout', undefined, undefined);
				});
		}

		function changeBirthdate(birthdate) {
			var request = {
				birthdate: birthdate,
				merchantId: togoorder.merchantId
			};

			return api.put('api/User', request)
				.then(function (response) {
					return response.data;
				})['finally'](invalidateCache);
		}

		function changeName(firstName, lastName) {
			var request = {
				firstName: firstName,
				lastName: lastName,
				merchantId: togoorder.merchantId
			};

			return api.put('api/User', request)
				.then(function (response) {
					return response.data;
				})['finally'](invalidateCache);
		}

		function changeEmail(email) {
			var request = {
				email: email,
				merchantId: togoorder.merchantId
			};

			return api.put('api/User', request)
				.then(function (response) {
		            togoorder.userData.username = email;
					return response.data;
				})['finally'](invalidateCache);
		}

		function changeCallbackNumber(callbackNumber) {
			var request = {
				callbackNumber: callbackNumber,
				merchantId: togoorder.merchantId
			};

			return api.put('api/User', request)
				.then(function (response) {
					return response.data;
				})['finally'](invalidateCache);
		}

		function changePassword(oldPassword, newPassword, confirmNewPassword) {
			var request = {
				currentPassword: oldPassword,
				password: newPassword,
				confirmPassword: confirmNewPassword,
				merchantId: togoorder.merchantId
			};

			return api.put('api/User', request)
				.then(function (response) {
					return response.data;
				})['finally'](invalidateCache);
		}

        function changeEmailOptIn(optIn) {
            var request = {
                emailOptIn: optIn,
                merchantId: togoorder.merchantId
            };

            return api.put('api/User', request)
                .then(function (response) {
                    return response.data;
                })['finally'](invalidateCache);
        }

		function resetPassword(req) {
			req.merchantId = togoorder.merchantId;
			return api.post('services/account/sendPasswordLink', req)
				.then(function (result) {
					notify.success('Email Sent!');
					return result;
				}, function (response) {
					$log.error(response);
					var data = response.data;
					var errorMessage = data.Message || 'An error occurred. Please try again or contact us for help.';
					return $q.reject(errorMessage);
				});
		}

		function authenticate(username, password, persistData) {
			var config = {
				method: 'POST',
				url: 'token',
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded',
					'LocationId': togoorder.locationId
				},
				data: `grant_type=password&username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}&client_id=${togoorder.merchantId}`
			};

			return api.http(config)
				.then(function (response) {
					var data = response.data;
					saveData(data.userName, data.access_token, new Date(data['.expires']), persistData, true);
					setHttpAuthHeader();
					clearGuest();
					return data;
				}, function (response) {
					var data = response && response.data;
					$log.error(response);
					var errorMessage = (data && data.error_description) || 'Unable to contact server; please, try again later.';
					return $q.reject(errorMessage);
				})['finally'](invalidateCache);
		}

		function getUserSpecialRecommendations(menuId){
			return api.get('api/UserSpecialRecomendation/' + locationId + "/" + menuId)
				.then(function (response) {
					return response.data;
				});
		}

		function NoAuthenticationException(message) {
			this.name = 'AuthenticationRequired';
			this.message = message;
		}

		function NextStateUndefinedException(message) {
			this.name = 'NextStateUndefined';
			this.message = message;
		}

		function AuthenticationExpiredException(message) {
			this.name = 'AuthenticationExpired';
			this.message = message;
		}

		function AuthenticationRetrievalException(message) {
			this.name = 'AuthenticationRetrieval';
			this.message = message;
		}

		function isAGuest() {
			return togoorder.merchantLocation.IsOnlyGuestCheckout || $.cookie('is_guest');
		}

		function setUserAsGuest() {
			$.cookie('is_guest', true, { expires: 1 });
			return $q.when(1);
		}

		function checkIsAuthenticated() {
			if (togoorder.userData.isAuthenticated &&
				(togoorder.merchantId === togoorder.userData.merchantId) &&
				!isAuthenticationExpired(togoorder.userData.expirationDate)) {
				setHttpAuthHeader();
				return true;
			}
			return false;
		}

		function isAuthenticated() {
			if (checkIsAuthenticated()) {
				return true;
			} else {
				try {
					retrieveSavedData();
				} catch (e) {
					throw new NoAuthenticationException('Authentication not found');
				}
				setHttpAuthHeader();
				return true;
			}
		};

		function getNextState() {
			if (nextState.name === '') {
				throw new NextStateUndefinedException('No state data was set');
			} else {
				return nextState;
			}
		};

		function setNextState(name, params, error) {
			nextState.name = name;
			nextState.params = params;
			nextState.error = error;
		};

		function clearNextState() {
			nextState.name = '';
			nextState.params = {};
			nextState.error = '';
		};

		function isAuthenticationExpired(expirationDate) {
			var now = new Date();
			expirationDate = new Date(expirationDate);
			return expirationDate - now <= 0;
		}

		function clearUserData() {
			togoorder.clearUserData();
		}

		function removeAuthentication() {
			clearGuest();
			clearUserData();
			invalidateCache();
			$http.defaults.headers.common.Authorization = null;
		}

		function saveData(userName, token, expiration, isSavedSession, isWebLogin) {
			clearGuest();
			togoorder.setAuthCookie(userName, token, expiration, isSavedSession, isWebLogin);
		}

		function clearGuest() {
			$.removeCookie('is_guest');
		}

		function retrieveSavedData() {
			var savedData = togoorder.getSavedUserData();
			if (!savedData) {
				throw new AuthenticationRetrievalException('No authentication data exists');
			} else if (savedData.merchantId !== togoorder.merchantId) {
				throw new AuthenticationRetrievalException('Authorized for another merchant.');
			} else {
				if (isAuthenticationExpired(savedData.expirationDate)) {
					throw new AuthenticationExpiredException('Authentication token has already expired');
				}
			}
		}

		function login(callback, allowGuestCheckout) {
			common.broadcast(events.securityAuthorizationRequired, {
				callback: function () {
					invalidateCache();
					if (callback) callback.apply(this, arguments);
				},
				allowGuestCheckout: allowGuestCheckout
			});
		}

		function getUserSavedDeliveryAddresses() {
			return api.get('api/User/SavedDeliveryAddresses/' + togoorder.merchantId)
			.then(function (response) {
				return response.data;
			});
		}

		function saveUserSavedDeliveryAddresses(address) {

			var addressToSave = {...address};

			addressToSave.merchantId = togoorder.merchantId;
			addressToSave.AddressLine1 = address.displayString;

			return api.post('api/User/SavedDeliveryAddresses', addressToSave)
				.then(function () {
					invalidateCache();
					notify.success('Address successfully saved!');
				}, function (response) {
					invalidateCache();
					response = response || {};
					$log.error(response);
					var data = response.data || {};
					var errorMessage = data.Message || 'An error occurred. Please try again or contact us for help.';
					return $q.reject(errorMessage);
				});
		}

		function updateUserSavedDeliveryAddresses(address){
			address.merchantId = togoorder.merchantId;
			address.IsRecentlyUsed = true;
			
			return api.put('api/User/SavedDeliveryAddresses', address)
				.then(function (response) {
					return response.data;
				})['finally'](invalidateCache);
		}

		function deleteUserSavedDeliveryAddresses(address){
			return api.delete('api/User/SavedDeliveryAddresses/' + address.Id)
				.then(function () {
					invalidateCache();
					notify.success('Address successfully deleted!');
				});;
		}
	}
})();;
(function () {
    'use strict';

    var serviceId = 'usualOrderService';

    angular.module('main').factory(serviceId, ['$http', '$q', '$log', '$state', '$uibModal', 'events', 'api', 'orderHistoryService', usualOrderService]);

    function usualOrderService($http, $q, $log, $state, $modal, events, api, orderHistoryService) {
        return {
            begin: begin
        };

        function selectOrder(order, merchantLocation) {
            return orderHistoryService.selectOrder(order, merchantLocation);
        }

        function begin(merchantLocation) {
            var url = 'api/User/UsualOrder';
            return api.get(url, { cache: false, params: { merchantLocationId: merchantLocation.Id } }).then(function (response) {
                var order = response.data.Order;
                if (order) {
                    selectOrder(order, merchantLocation);
                } else {
                    $modal.open({
                        templateUrl: 'app/orderHistory/usualOrderAlert.html',
                        controller: 'usualOrderAlert as vm',
                        size: 'md',
                        resolve: {
                            candidateOrders: function () { return response.data.CandidateOrders; }
                        }
                    }).result.then(function(o) {
                        selectOrder(o, merchantLocation);
                    });
                }
            });
        }
    }
})();;
(function () {
	'use strict';

	var controllerId = 'orderHistoryList';
	angular.module('common').controller(controllerId, ['$uibModalInstance', 'candidateOrders', orderHistoryList]);

	function orderHistoryList($modalInstance, candidateOrders) {
		var vm = this;

		vm.candidateOrders = candidateOrders;
	    vm.selectOrder = selectOrder;
		vm.dismiss = dismiss;

		function selectOrder(order) {
		    $modalInstance.close(order);
		}

		function dismiss() {
		    $modalInstance.dismiss();
		}
	}

})();;
(function () {
	'use strict';

	var controllerId = 'usualOrderAlert';
	angular.module('common').controller(controllerId, ['$uibModalInstance', 'candidateOrders', usualOrderAlert]);

	function usualOrderAlert($modalInstance, candidateOrders) {
		var vm = this;

		vm.candidateOrders = candidateOrders;
	    vm.selectOrder = selectOrder;
		vm.dismiss = dismiss;

		function selectOrder(order) {
		    $modalInstance.close(order);
		}

		function dismiss() {
		    $modalInstance.dismiss();
		}
	}

})();;
(function () {
	'use strict';

	var controllerId = 'excludedItemsDialog';
	angular.module('common').controller(controllerId, ['$uibModalInstance', 'excludedItems', 'pickupDate', 'dayOfWeek', excludedItemsDialog]);

	function excludedItemsDialog($modalInstance, excludedItems, pickupDate, dayOfWeek) {
		var vm = this;

		vm.excludedItems = excludedItems;
		vm.dismiss = dismiss;
		vm.pickupDate = pickupDate.getDisplayDay();
		vm.dayOfWeek = dayOfWeek;

		function dismiss() {
			$modalInstance.dismiss();
		}
	}

})();;
(function () {
	'use strict';

	var controllerId = 'itemPromptDialog';
	angular.module('common').controller(controllerId, ['$uibModalInstance', 'itemPrompts', itemPromptDialog]);

	function itemPromptDialog($modalInstance, itemPrompts) {
		var vm = this;

		vm.itemPrompts = itemPrompts;
	    vm.selectItem = selectItem;
		vm.dismiss = dismiss;
		vm.getItemImageStyle = getItemImageStyle;

		function getItemImageStyle(itemPrompt) {
			return itemPrompt.ImageUrl ? ("background-image: url('" + itemPrompt.ImageUrl + "')") : "";
		}

		function selectItem(itemPrompt) {
			$modalInstance.close(itemPrompt);
		}

		function dismiss() {
		    $modalInstance.dismiss();
		}
	}

})();;
(function () {
	'use strict';

	var controllerId = 'unavailableItemDialog';
	angular.module('common').controller(controllerId, ['$uibModalInstance', 'unavailableItems', unavailableItemDialog]);

	function unavailableItemDialog($modalInstance, unavailableItems) {
		var vm = this;

		vm.unavailableItems = unavailableItems;
		vm.dismiss = dismiss;

		function dismiss() {
			$modalInstance.dismiss();
		}
	}

})();;
(function() {
	"use strict";

	var controllerId = "cart";
	angular.module("main")
		.controller(controllerId,
			[
				"$q", "$rootScope", "$scope", "$log", "$state", "$stateParams", "$filter", "$window", "spinner",
				"notify", "common", "events", "cartService", "merchantLocationService", "orderTypeWorkflowService",
				"orderTypeRuleService", "userService", "loyaltyService", "crmService", cartController
			]);

	function cartController($q,
		$rootScope,
		$scope,
		$log,
		$state,
		$stateParams,
		$filter,
		$window,
		spinner,
		notify,
		common,
		events,
		cartService,
		merchantLocationService,
		orderTypeWorkflowService,
		orderTypeRuleService,
		userService,
		loyaltyService,
		crmService	) {
		var vm = this,
			menuId = parseInt($stateParams.menuId, 10),
			orderType = $stateParams.orderType,
			fulfillmentType = $stateParams.fulfillmentType,
			merchantLocationId = togoorder.locationId,
			cartItemValidationCallback = function() {};
		vm.editItem = editItem;
		vm.removeItem = removeItem;
		vm.beginCheckOut = beginCheckOut;
		vm.orderTypeDisplay = orderTypeWorkflowService.getOrderTypeDescription(orderType, fulfillmentType);
		vm.deliveryTemplate = orderTypeWorkflowService.getDisplayTemplate(orderType, fulfillmentType);
		vm.merchantLocation = {};
		vm.checkoutButtonText = "Check Out";
		vm.isAuthenticated = togoorder.userData.isAuthenticated;
		vm.signUp = signUp;
		vm.checkoutAsGuest = checkoutAsGuest;
		vm.orderTypes = [];
		vm.eligibleOffers = [];
		vm.hasOffers = false;
		vm.switchToOrderType = switchToOrderType;
		vm.isOrderTypeChoicesOpen = false;
		Object.defineProperty(vm,
			"cart",
			{
				get: function() {
					return cartService.getCart();
				}
			});
		vm.safeClassName = common.safeClassName;
		vm.messages = common.messages;
		vm.user = {};
		vm.isScrollable = isScrollable;
		vm.bestLoyalty = {};
		vm.getItems = getItems;
		vm.getItemImageStyle = getItemImageStyle;
		vm.isCartItemSelected = false;
		vm.convenienceFeeLabel = merchantLocationService.getConvenienceFeeLabel();
		vm.surchargeLabel = merchantLocationService.getSurchargeLabel();

		initialize();

		function initialize() {
			if (!vm.isAuthenticated) {
				vm.checkoutButtonText =
					(togoorder.mobileStrategy.unauthenticatedCheckoutButtonText) || vm.checkoutButtonText;
			}
			$q.all([getMerchantLocation(), getOrderTypes(), getDeliveryData(), cartService.waitForInitialization()]);

			$scope.$on(events.cartItemIsValidChanged,
				function(e, isValid, callback) {
					vm.isCartItemInvalid = !isValid;
					cartItemValidationCallback = callback;
				});

			$(".cart-contents").on("scroll",
				function() {
					if (!isScrollable()) {
						$(".cart-footer").removeClass("cart-box-shadow-top");
					} else {
						$(".cart-footer").addClass("cart-box-shadow-top");
					}
					if (isScrollable() && $(".cart-contents").scrollTop() === 0) {
						$(".cart-header").removeClass("cart-box-shadow-bottom");
					} else {
						$(".cart-header").addClass("cart-box-shadow-bottom");
					}
				});
		}

		function getItems() {
			return _.where(vm.cart.order.Items, { shouldShow: true });
		}

		function getDeliveryData() {
			orderTypeWorkflowService.getDisplayData(orderType, fulfillmentType, vm);
		}

		function switchToOrderType(newOrderType) {
			vm.isOrderTypeChoicesOpen = false;
			orderTypeRuleService.switchToOrderTypeRule(newOrderType, menuId, vm.cart.hasOrderItems());
		}

		function getOrderTypes() {
			var filter = function(ot) {
				return ot.OrderType != orderType || ot.FulfillmentType != fulfillmentType;
			};
			return orderTypeRuleService.getDisplayOrderTypes(filter)
				.then(function(data) {
					vm.orderTypes = data;
				});
		}

		function getMerchantLocation() {
			return merchantLocationService.getById(merchantLocationId)
				.then(function(data) {
					vm.merchantLocation = data;
					var loyaltyProviderType = vm.merchantLocation.LoyaltyProfile &&
						vm.merchantLocation.LoyaltyProfile.LoyaltyProviderType;

					var providersWithOffers = [4,3,6];
					vm.hasOffers = (providersWithOffers.indexOf(loyaltyProviderType) > -1);

					return data;
				}).then(function() {
					return !vm.isAuthenticated
						? false
						: getUser();
				});
		}

		function getUser() {
			if (!vm.hasOffers) {
				return userService.getUser(vm.merchantLocation.MerchantId)
					.then(function(user) {
						vm.user = user;
						return user;
					});
			}
			return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId)
				.then(function(user) {
					vm.user = user;
					vm.bestLoyalty = vm.user.Loyalties && vm.user.Loyalties.length && vm.user.Loyalties[0];
					return getOffersEligibleForRedemption();
				});
		}

		function editItem(orderItem, $event) {
			common.broadcast(events.cartItemSelected, orderItem);
			$event.preventDefault();
			$event.stopPropagation();
		}

		function removeItem(orderItem, $event) {
			vm.cart.order.deleteItem(orderItem);
			cartService.saveCart();
			var notifyMessage = orderItem.ItemName + " removed from cart!";
			notify.success(notifyMessage);
			crmService.removeItemFromCart(orderItem);
			common.broadcast(events.itemRemoved, { menuItem: orderItem, locationId: vm.merchantLocation.Id, type: "OrderItem" });
			$event.preventDefault();
			$event.stopPropagation();
		}

		function beginCheckOut() {
			common.broadcast(events.beginCheckOut, {});
			userService.clearGuest();
			doCheckout();
		}

		function doCheckout() {
			var message = "";
			if (vm.isCartItemInvalid) {
				cartItemValidationCallback();
			} else if (!vm.cart.isAboveMinimumOrder()) {
				message = "There is a minimum order of " +
					$filter("currency")(vm.cart.minimumOrder) +
					" for " +
					vm.orderTypeDisplay +
					".";
			} else if (!vm.cart.isBelowMaximumOrder()) {
				message = validateMaximumOrder(false);
			}

			if (message) {
				notify.dialog("Almost!", message);
				notify.warning("Almost! " + message);
			} else {
				$state.go("checkout", { merchantLocationId: vm.cart.order.LocationId });
			}
		}

		function checkoutAsGuest() {
			userService.setUserAsGuest()
				.then(function() {
					doCheckout();
				});
		}

		function getOffersEligibleForRedemption() {
			vm.cart.order.SelectedOfferAmount = 0;

			if (vm.hasOffers &&
				vm.cart.order.Items &&
				vm.isAuthenticated &&
				vm.bestLoyalty.AccountNumber &&
				vm.bestLoyalty.AccountNumber.length > 0) {

				var request = {
					merchantId: vm.merchantLocation.MerchantId,
					cardNumber: vm.bestLoyalty.AccountNumber,
					cartOrder: vm.cart.order
				};
				debugMessage("cart.getOffersEligibleForRedemption request:");
				debugMessage(request);
				return loyaltyService.getOffersEligibleForRedemption(request).then(function(data) {
					debugMessage("cart.getOffersEligibleForRedemption data:");
					debugMessage(data);
					_.each(data.Offers,
						function(v, k) {
							v.OptedIn = v.Optable ? v.OptedIn : true;
						});
					vm.eligibleOffers = _.filter(data.Offers,
						function(offer) {
							return vm.cart.order.Total + vm.cart.order.PromoSavings - vm.cart.order.OfferDiscount >=
								offer.Threshold;
						});
				});
			}
			debugMessage("cart.getOffersEligibleForRedemption returning false");
			return false;
		}

		function debugMessage(message) {
			if (vm.debugging) {
				console.log(message);
			}
		}

		function signUp() {
			common.broadcast(events.signUpRequested, {});
		}

		function isScrollable() {
			var cartContentsEl = $(".cart-contents");
			if (!cartContentsEl || !cartContentsEl.length) {
				return false;
			}
			return Math.ceil(cartContentsEl.scrollTop() + cartContentsEl.innerHeight()) < cartContentsEl[0].scrollHeight;
		}

		function getItemImageStyle(menuItem) {
			return menuItem.PrimaryImageUrl ? ("background-image: url('" + menuItem.PrimaryImageUrl + "')") : "";
		}

		function validateMaximumOrder(displayMessage) {
			var message = "";
			if (!vm.cart.isBelowMaximumOrder()) {
				// default message 1
				message = "There is a maximum order of " + $filter("currency")(vm.cart.maximumOrder) + " for " + vm.orderTypeDisplay + ".";
				// append default message 2 or Custom message
				if (!vm.merchantLocation.MaximumOrderExceededMsg) {
					message = message + " Please call the restaurant directly to place this order";
				} else { message = message + " " + vm.merchantLocation.MaximumOrderExceededMsg; }
				// append location phone
				if (vm.merchantLocation.IsMaximumOrderExceededPhone && vm.merchantLocation.Phone) {
					message = message + " (" + vm.merchantLocation.Phone + ").";
				} //else { message = message + "."; }
				if (displayMessage) {
					notify.dialog("Almost!", message);
					notify.warning("Almost! " + message);
				}
			}
			return message;
        }

		$scope.$on("$stateChangeStart",
			function (event, next, current) {
				cartService.saveCart();
				getOffersEligibleForRedemption();
			});

		$scope.$on("$stateChangeSuccess",
			function (event, next, current) {
				if (!vm.isCartItemSelected) {
					validateMaximumOrder(true);
				}
				vm.isCartItemSelected = false;
			});

		$scope.$on(events.cartItemSelected,
			function (e, isValid, callback) {
				vm.isCartItemSelected = true;
			});
	}
})();;
(function () {
    'use strict';

    var controllerId = 'staticCart';
    angular.module('main')
        .controller(controllerId, ['$rootScope', '$log', '$state', '$stateParams', '$uibModal', '$filter', '$q', 'notify', 'common', 'events', 'cartService', 'merchantLocationService', staticCart]);

    function staticCart($rootScope, $log, $state, $stateParams, $modal, $filter, $q, notify, common, events, cartService, merchantLocationService) {
        var vm = this,
            merchantLocationId = togoorder.locationId;
        vm.goToMenu = goToMenu;
        vm.removePromotion = removePromotion;
        vm.messages = common.messages;
        vm.getItems = getItems;
        vm.getItemImageStyle = getItemImageStyle;
        vm.merchantLocation = {};
        vm.convenienceFeeLabel = merchantLocationService.getConvenienceFeeLabel();
        vm.surchargeLabel = merchantLocationService.getSurchargeLabel();


        Object.defineProperty(vm, 'cart', {
            get: function () {
                return cartService.getCart();
            }
        });

        initialize();

        function initialize() {
            $q.all([getMerchantLocation()]);
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(merchantLocationId)
                .then(function (data) {
                    vm.merchantLocation = data;
                    return data;
                });
        }

        function getItems() {
            return _.where(vm.cart.order.Items, { shouldShow: true });
        }

        function removePromotion() {
            vm.cart.order.Promotion = {};
            common.broadcast(events.promoCodeRemoved);
        }

        function goToMenu() {
            $state.go('menu.sections', {
                merchantLocationId: vm.cart.order.LocationId,
                orderType: vm.cart.order.OrderType,
                fulfillmentType: vm.cart.order.FulfillmentType,
                menuId: vm.cart.order.MenuId
            });
        }

        function getItemImageStyle(menuItem) {
            return menuItem.PrimaryImageUrl ? ("background-image: url('" + menuItem.PrimaryImageUrl + "')") : "";
        }

    }
})();;
(function () {
	'use strict';

	var controllerId = 'includedGroupValidator';
	angular.module('main').factory(controllerId, ['orderPricingService', includedGroupValidator]);

	function includedGroupValidator(orderPricingService) {
		return {
			validate: validate
		};

		function validate(includedGroup, selectedOrderItems) {
		    var selectionCount = orderPricingService.getSelectionCount(selectedOrderItems);
		    if (includedGroup.IsRequired && includedGroup.MinChoices === 1) {
		        if (!selectionCount) {
					return { "required": includedGroup };
				}
			}
		    if (selectionCount < includedGroup.MinChoices) {
			    return { "min": includedGroup };
		    }
		    if (includedGroup.MaxChoices < selectionCount) {
				return { "max": includedGroup };
			}
			return {};
		}
	}

})();;
(function () {
    'use strict';

    var serviceId = 'menuItemDetailViewModel';

    angular.module('main').factory(serviceId, ['$rootScope', '$document', '$timeout', '$q', '$window', 'common', 'events', 'notify', 'menuService', 'menuWorkflowService', 'orderValidationService', 'cartService', 'orderPricingService', 'linkService', 'OrderItem', 'OrderItemExt',
        menuItemDetailViewModel]);

    function menuItemDetailViewModel($rootScope, $document, $timeout, $q, $window, common, events, notify, menuService, menuWorkflowService, orderValidationService, cartService, orderPricingService, linkService, OrderItem, OrderItemExt) {

        var selectedMenuItem = null,
            currentOrderItem = {},
            isValidationVisible = null,
            isFloatingButtons = undefined,
            isMenuItemLoaded = undefined,
            remainingIncludedGroups = 0,
            hasClonableGroups = undefined,
            menu = {},
            merchantLocation = {},
            $scope,
            isPreview = false,
            currentPotentialIncludedGroup,
            itemUiType = togoorder.merchantLocation && togoorder.merchantLocation.MenuItemUiType,
            root = document.compatMode === 'BackCompat' ? document.body : document.documentElement;

        var service = {
            initialize: initialize,
            listenToValidChanged: listenToValidChanged,
            addItemToCart: addItemToCart,
            showValidation: showValidation,
            backToSection: backToSection,
            selectNewMenuItem: selectNewMenuItem,
            doneEditingItem: doneEditingItem,
            backToMenuSections: backToMenuSections,
            deleteItemFromCart: deleteItemFromCart,
            useCheckboxes: useCheckboxes,
            useRadioButtons: useRadioButtons,
            useSelectList: useSelectList,
            useMultiSelectList: useMultiSelectList,
            getItemDisplayName: getItemDisplayName,
            initializeUi: initializeUi,
            getSelectedItemsSummary: getSelectedItemsSummary,
            getSelectListItems: getSelectListItems,
            setCurrentItem: setCurrentItem,
            getIncludedGroups: getIncludedGroups,
            getNumberString: getNumberString,
            showOptions: showOptions,
            toggleOptions: toggleOptions,
            getIncludedGroupItemsClass: getIncludedGroupItemsClass,
            footnoteClick: footnoteClick
        };

        Object.defineProperty(service, 'hasClonableGroups', {
            get: function () {
                return hasClonableGroups;
            },
            set: function (val) {
                hasClonableGroups = val;
            }
        });

        Object.defineProperty(service, 'remainingIncludedGroups', {
            get: function () {
                return remainingIncludedGroups;
            },
            set: function (val) {
                remainingIncludedGroups = val;
            }
        });

        Object.defineProperty(service, 'menu', {
            get: function () {
                return menu;
            },
            set: function (val) {
                menu = val;
            }
        });

        Object.defineProperty(service, 'merchantLocation', {
            get: function () {
                return merchantLocation;
            },
            set: function (val) {
                merchantLocation = val;
            }
        });

        Object.defineProperty(service, 'isFloatingButtons', {
            get: function () {
                return isFloatingButtons;
            },
            set: function (val) {
                isFloatingButtons = val;
            }
        });

        Object.defineProperty(service, 'isMenuItemLoaded', {
            get: function () {
                return isMenuItemLoaded;
            },
            set: function (val) {
                isMenuItemLoaded = val;
            }
        });

        Object.defineProperty(service, 'selectedMenuItem', {
            get: function () {
                return selectedMenuItem;
            },
            set: function (val) {
                selectedMenuItem = val;
            }
        });

        Object.defineProperty(service, 'currentOrderItem', {
            get: function () {
                return currentOrderItem;
            },
            set: function (val) {
                currentOrderItem = val;
            }
        });

        Object.defineProperty(service, 'isValidationVisible', {
            get: function () {
                return isValidationVisible;
            },
            set: function (val) {
                isValidationVisible = val;
            }
        });


        function getIncludedGroups(menuItem) {
            if (!menuItem) {
                return [];
            }
            var menuItemIncludedGroups = menuItem.IncludedGroups;

            if (menuItem === selectedMenuItem && service.currentOrderItem && service.currentOrderItem.clonedIncludedGroups && menuItemIncludedGroups) {
                var allItems = menuItemIncludedGroups.concat(service.currentOrderItem.clonedIncludedGroups);
                allItems = _(allItems).chain().sortBy(function (i) {
                    return (i.GroupInstanceId + "").length;
                }).sortBy(function (i) {
                    return i.SortIndex;
                }).value();
                return allItems;
            }
            return menuItemIncludedGroups;
        }

        function getNumberString(num) {
            return {
                1: "One",
                2: "Two",
                3: "Three",
                4: "Four",
                5: "Five",
                6: "Six",
                7: "Seven",
                8: "Eight",
                9: "Nine",
                10: "Ten"
            }[num] ||
                num;
        }

        Object.defineProperty(service, 'cart', {
            get: isPreview ? function () { return {} } : function () {
                return cartService.getCart();
            }
        });


        $rootScope.$on(events.itemQuantityChanged, function (event, args) {

            var itemQuantity = args.quantity;
            var minimumCount = args.minimumCount || 1;

            $timeout(function () {
                var isChanging = false;
                do {
                    var clonableGroups = _.filter(getIncludedGroups(service.selectedMenuItem),
                        function (g) {
                            return g.ItemCountCloneAtMultipleOf > 0;
                        });

                    var groupGroups = _.groupBy(clonableGroups,
                        function (g) {
                            return parseInt(g.GroupInstanceId, 10);
                        });


                    _.each(groupGroups,
                        function (grouping) {
                            var cloneProspect = grouping[grouping.length - 1];

                            var itemCountCloneAtMultipleOf = cloneProspect.ItemCountCloneAtMultipleOf;

                            var baseItemQuantity = itemQuantity - minimumCount;

                            baseItemQuantity = Math.max(0, baseItemQuantity - cloneProspect.ItemCountThresholdForClone + 1 + itemCountCloneAtMultipleOf);

                            var requiredGroupCopies = Math.max(1, Math.ceil(baseItemQuantity / itemCountCloneAtMultipleOf));

                            var currentLength = grouping.length;
                            if (requiredGroupCopies > currentLength) {
                                service.currentOrderItem.addIncludedGroupClone(cloneProspect);
                                isChanging = true;
                            } else if (requiredGroupCopies < currentLength) {
                                service.currentOrderItem.removeIncludedGroupClone(cloneProspect);
                                isChanging = true;
                            } else {
                                isChanging = false;
                            }
                        });

                } while (isChanging);

            },
                100);
        });

        function cloneIncludedGroup(includedGroup) {
            var copy = angular.copy(includedGroup);
            copy.GroupInstanceId = "0" + copy.GroupInstanceId;
            copy.SortIndex++;
            _.each(copy.MenuItems,
                function (mi) {
                    mi.GroupInstanceId = copy.GroupInstanceId;
                });

            return copy;
        }

        return service;

        function restoreMenuItems() {
            var promises = [];
            if (service.cart && service.cart.order) {
                _.each(service.cart.order.Items, function (orderItem) {
                    var menuItem = findMenuItemInCache(orderItem.ItemId);
                    if (!menuItem) {
                        return;
                    }
                    var p = getMenuItem(menuItem.Id).then(function (data) {
                        menuItem.IncludedGroups = (data && data.IncludedGroups) || [];
                    });
                    promises.push(p);
                });
            }
            return $q.all(promises);
        }

        function selectNewMenuItem(menuItemId) {
            var menuItem = findMenuItemInCache(menuItemId);

            if (!menuItem) {
                return;
            }
            selectMenuItem(false, menuItem);
        }

        function initialize(scope, menuArg, merchantLocation) {
            $scope = scope;
            menu = menuArg;
            service.merchantLocation = merchantLocation;

            service.footnote = $("#menu-item-footnote").html();

            $scope.$on(events.cartItemSelected, editItem);
            
            return restoreMenuItems();
        }

        function footnoteClick($event) {
            let target = $($event.target);
            if (target.is('a')) {
                var link = target.prop("href");
                var linkTarget = target.prop("target");
                if (linkTarget === 'popup') {
                    linkService.openLinkInNewWindow(link, target);
                    $event.preventDefault();
                    $event.stopPropagation();
                    $event.stopImmediatePropagation();
                    return false;
                } else if (linkService.callOpenLink(link)) {
                    $event.preventDefault();
                    $event.stopPropagation();
                    $event.stopImmediatePropagation();
                    return false;
                } //else just let the default action happen
            }
        }

        function saveCart() {
            return cartService.saveCart();
        }

        function getItemDisplayName(menuItem) {
            var displayName = menuItem.Name;
            if (menuItem.Price && !menuItem.PriceIsExtra) {
                displayName += ' ' + common.monetize(menuItem.Price).toFixed(2);
            }
            if (menuItem.Price && menuItem.PriceIsExtra) {
                displayName += ' (' + common.monetize(menuItem.Price).toFixed(2) + ' Extra)';
            }
            if (menuItem.NutritionInfo) {
                displayName += ' ' + menuItem.NutritionInfo;
            }
            return displayName;
        }


        function addItemToCart(isValid) {
            if (isValid) {
				cartService.addOrderItem(service.currentOrderItem);

                orderPricingService.applyPricingRules(service.cart.order, menu);
                orderValidationService.validateOrder(service.cart.order, menu, service.merchantLocation);

                saveCart();
                notifyItemSuccess("added to cart!");
                service.backToMenuSections();
            } else {
                service.showValidation();
                service.addItemToCart = _.once(addItemToCart);
            }
        }

        function deleteItemFromCart() {
            service.cart.order.deleteItem(service.currentOrderItem);
            saveCart();
            notifyItemSuccess("removed from cart!");

            common.broadcast(events.itemRemoved, { menuItem: service.currentOrderItem, locationId: service.merchantLocation.Id, type: "OrderItem" });

            service.backToMenuSections();
        }


        function doneEditingItem(isValid) {
            if (isValid) {
                orderPricingService.applyPricingRules(service.cart.order, menu);
                orderValidationService.validateOrder(service.cart.order, menu, service.merchantLocation);

                saveCart();
                notifyItemSuccess("edited!");
                service.backToMenuSections();
            } else {
                service.showValidation();
            }
        }

        function backToMenuSections() {
            $window.history.back();
        }


        function notifyItemSuccess(verb) {
            var isMultiple = service.currentOrderItem.Quantity > 1;
            var notifyMessage = isMultiple ? service.currentOrderItem.Quantity + "X " : "";
            notifyMessage += service.selectedMenuItem.Name + " " + verb;
            notify.success(notifyMessage);
        }

        function backToSection() {
            $window.history.back();
        }

        function listenToValidChanged(form) {
            $scope.$watch(function () {
                return form.$valid;
            }, _.debounce(function (isValid) {
                $rootScope.$apply(function () {
                    var isFormInScope = !!form.quantity;
                    if (isFormInScope) {
                        service.currentOrderItem.errors = isValid ? null : form.$error;
                    } else {
                        service.currentOrderItem.errors = service.currentOrderItem.errors || {};
                    }
                    common.broadcast(events.cartItemIsValidChanged,
                        isValid,
                        function () {
                            if (!isValid && isFormInScope) {
                                service.showValidation();
                            }
                        });

                });
            }, 100, false));
        }

        function showValidation() {
            service.isValidationVisible = true;
            notify.dialog('Almost Perfect!', 'Please review the sections marked with a star.');
            notify.warning("Almost Perfect! Please review the sections marked with a star.");
            scrollToFirstValidation();
        }

        function scrollToFirstValidation() {

        }

        function getMenuItem(menuItemId) {
            return menuService.getIncludedGroups(menuItemId, menu.Id, service.merchantLocation.Id);
        }

        function selectMenuItem(isSelectedMenuItemInCart, menuItem, orderItem) {

            service.isFloatingButtons = false;
            service.isMenuItemLoaded = false;

            menuItem.isInCart = isSelectedMenuItemInCart;

            var includedGroups = menuItem.IncludedGroups;
            menuItem.IncludedGroups = undefined;
            var includedGroupsDefer = $q.defer();
            if (includedGroups) {
                includedGroupsDefer.resolve();
            } else {
                includedGroups = [];
                service.remainingIncludedGroups = 1;
                getMenuItem(menuItem.Id).then(function (data) {
                    includedGroups = data.IncludedGroups;
                    includedGroupsDefer.resolve();
                });
            }
            includedGroupsDefer.promise.then(function () {
                service.remainingIncludedGroups = includedGroups.length;
                $timeout(function () {
                    menuItem.IncludedGroups = includedGroups;

                    var p = $q.when();

                    _.each(menuItem.IncludedGroups, function (includedGroup) {

                        var includedGroupMenuItems = includedGroup.MenuItems;
                        includedGroup.MenuItems = undefined;
                        p = p.then(function () {
                            var d = $q.defer();
                            $timeout(function () {
                                includedGroup.MenuItems = includedGroupMenuItems;
                                service.remainingIncludedGroups--;
                                d.resolve();
                            }, 1);
                            return d.promise;
                        });
                    });
                    p.then(function () {
                        $timeout(onMenuItemUiLoaded, 1);
                    });

                    if (orderItem) {
                        setCurrentItem(orderItem, menuItem);
                    } else {
                        setCurrentItem(new OrderItem(menuItem), menuItem);
                    }
                    //	setCurrentItem(orderItem || new OrderItem(menuItem));

                    service.addItemToCart = _.once(addItemToCart);

                }, 150);

            });

            service.selectedMenuItem = menuItem;

            common.broadcast(events.itemIsViewed, { menuItem: service.selectedMenuItem, locationId: service.merchantLocation.Id, type: "MenuItem" });

            service.isValidationVisible = false;
            menuWorkflowService.showItemDetail();
        }

        function setCurrentItem(orderItem, menuItem) {
            service.currentOrderItem = orderItem;
            service.currentOrderItemExt = new OrderItemExt(service.currentOrderItem);

            menuItem.hasClonableGroups = _.some(menuItem.IncludedGroups, function (g) {
                return g.ItemCountCloneAtMultipleOf > 0;
            });
        }

        function findMenuItemInCache(menuItemId) {
            return menuService.getItemById(menu, menuItemId);
        }

        function editItem(e, orderItem) {
            var selectedMenuItem = findMenuItemInCache(orderItem.ItemId);
            if (!selectedMenuItem) {
                return;
            }
            selectMenuItem(true, selectedMenuItem, orderItem);
        }

        function onMenuItemUiLoaded() {
            if (!isPreview) {
                orderValidationService.validateOrder(service.cart.order, menu, service.merchantLocation);
                setIsFloatingButtons();
            }
        }

        function initializeUi() {
            setIsFloatingButtons();
        }

        function setIsFloatingButtons(stop) {
            var isVerticalScrollbar = root.scrollHeight > root.clientHeight;

            if (!stop) {
                $timeout(function () {
                    setIsFloatingButtons(1);
                }, 1);
            } else {
                service.isMenuItemLoaded = true;
            }
            service.isFloatingButtons = isVerticalScrollbar;
        }

        function includedGroupMenuItemsHasSubItems(includedGroup) {
            return _.some(includedGroup.MenuItems, function (mi) {
                return mi.IncludedGroups && mi.IncludedGroups.length;
            });
        }

        function useCheckboxes(includedGroup) {
            if (!includedGroup.MenuItems) {
                return undefined;
            }
            return !service.useRadioButtons(includedGroup) && !service.useSelectList(includedGroup) && !service.useMultiSelectList(includedGroup);
        }
        function useRadioButtons(includedGroup) {
            if (!includedGroup.MenuItems) {
                return undefined;
            }
            return includedGroup.IsRequired &&
                includedGroup.MaxChoices === 1 &&
                (includedGroup.MenuItems.length <= 10 || itemUiType === 1);
        }
        function useSelectList(includedGroup) {
            if (!includedGroup.MenuItems) {
                return undefined;
            }
            return includedGroup.IsRequired &&
                includedGroup.MaxChoices === 1 &&
                !useRadioButtons(includedGroup);
        }
        function useMultiSelectList(includedGroup) {
            if (!Modernizr.touch) {
                return false;
            }
            if (!$window.hasNiceMultiselectControlProbably()) {
                return false;
            }
            if (!includedGroup.MenuItems) {
                return undefined;
            }
            if (service.useRadioButtons(includedGroup) || service.useSelectList(includedGroup)) {
                return false;
            }
            if (includedGroup.MenuItems.length < 10) {
                return false;
            }
            if (itemUiType === 1) {
                return false;
            }
            if (includedGroupMenuItemsHasSubItems(includedGroup)) {
                return false;
            }
            return true;
        }
        function getSelectedItemsSummary(orderItems) {
            if (!orderItems || orderItems.length < 2) {
                return '';
            }
            var names = _.pluck(orderItems, 'ItemName');
            if (names.length === 2) {
                return names[0] + ' and ' + names[1];
            } else if (names.length === 3) {
                return names[0] + ', ' + names[1] + ', and ' + names[2];
            }
            return names[0] + ', ' + names[1] + ', and ' + (names.length - 2) + ' others';
        }
        function getSelectListItems(items) {
            if (_.isArray(items) && !items.isDisabledItemPresent) {
                items.isDisabledItemPresent = true;
                items.unshift({
                    Name: '',
                    disabled: true,
                    Id: 0
                });
            }
            return items;
        }

        function getIncludedGroupItemsClass(includedGroup, potentialIncludedGroup, groupUi) {
            return {
                'radio-buttons': (includedGroup && includedGroup.HasSizes),
                'invalid': !(potentialIncludedGroup && potentialIncludedGroup.isValid()),
                'included-group-scrollable-items': (includedGroup && includedGroup.MenuItems && includedGroup.MenuItems.length > 10 && !includedGroup.IsRequired),
                'included-group-expanded': groupUi && groupUi.isDisplayed,
                'selection-required': includedGroup.IsRequired,
                'selection-optional': !includedGroup.IsRequired
            };
        }

        function showOptions($event, potentialIncludedGroup, show) {
            currentPotentialIncludedGroup = potentialIncludedGroup;
            toggleOptions(show);
            $event.preventDefault();
            $event.stopPropagation();
        }

        function keyDown(event) {
            if (event.keyCode === 27) {
                $timeout(function () {
                    toggleOptions(false);
                });
            }
        }

        function toggleOptions(show) {
            if (currentPotentialIncludedGroup) {
                currentPotentialIncludedGroup.ui.isDisplayed = show;
            }
            if (show) {
                //to prevent double scrollbars...
                $('body').addClass('included-group-open');
                $document.on('keydown', keyDown);
            } else {
                $('body').removeClass('included-group-open');
                $document.off('keydown', keyDown);
            }
        }
    }
})();;
(function () {
    'use strict';

    var controllerId = 'menu';
    angular.module('main')
        .controller(controllerId, ['$log', '$timeout', '$window', '$q', '$scope', '$stateParams', '$state', '$uibModal', 'events', 'browserInfo', 'common', 'notify', 'menuService', 'cartService', 'merchantLocationService', 'routeStateService', 'orderValidationService', 'orderPricingService', 'menuWorkflowService', 'menuItemDetailViewModel', 'storageService', 'Order', 'userService', menu]);

    function menu($log, $timeout, $window, $q, $scope, $stateParams, $state, $modal, events, browserInfo, common, notify, menuService, cartService, merchantLocationService, routeStateService, orderValidationService, orderPricingService, menuWorkflowService, menuItemDetailViewModel, storageService, Order, userService) {
        var vm = this,
            menuId = parseInt($stateParams.menuId, 10),
            orderType = $stateParams.orderType,
            day = $stateParams.day,
            fulfillmentType = $stateParams.fulfillmentType,
            isPreview = !!$stateParams.isPreview,
            merchantLocationId = togoorder.locationId,
            forceOpenCart = false,
            isAutoScrolling = false;

        vm.log = $log.debug;
        vm.selectSection = selectSection;
        vm.alwaysFloatCart = false;
        vm.appBack = routeStateService.goBack;
        vm.hasAppBack = routeStateService.hasAppBack;
        vm.showAppBackButton = false;
        vm.isSectionsInFocus = true;
        vm.showCartSidebar = showCartSidebar;
        vm.isCartInPage = true;
        vm.hideCartSidebar = hideCartSidebar;
        vm.selectNewMenuItem = selectNewMenuItem;
        vm.isSidebarVisible = false;
        vm.merchantLocation = {};
        vm.menu = {};
        vm.replaceOptions = { location: 'replace' };
        vm.sectionReplaceOptions = vm.replaceOptions;
        vm.isPreview = isPreview;
        Object.defineProperty(vm, 'cart', {
            get: isPreview ? function () { return {}; } : function () {
                return cartService.getCart();
            }
        });
        vm.onResized = onResized;
        vm.item = menuItemDetailViewModel;
        vm.getItemImageStyle = getItemImageStyle;
        vm.safeClassName = common.safeClassName;
        vm.getTopCssClasses = getTopCssClasses;
        vm.canIncreaseQuantity = canIncreaseQuantity;
        vm.canDecreaseQuantity = canDecreaseQuantity;
        vm.getItemIncludedItemsTemplate = menuWorkflowService.getItemIncludedItemsTemplate;
        vm.getItemCount = getItemCount;
        vm.menuSections = undefined;
        vm.getSectionItems = getSectionItems;
        vm.getSelectedSection = getSelectedSection;
        vm.selectedSectionIndex = 0;
        vm.expandedSectionCarouselLoaded = expandedSectionCarouselLoaded;
        vm.shouldShowItemLabels = shouldShowItemLabels;
        vm.getItemLabels = getItemLabels;
        vm.flickityExpandedSectionsOptions = {
            cellSelector: '.carousel-cell',
            cellAlign: 'left',
            wrapAround: false,
            percentPosition: false,
            resize: true,
            pageDots: false,
            groupCells: '0%',
            accessibility: true,
            on: {
                'staticClick': expandedSectionCarouselClick
            }
        };
        vm.flickitySectionsOptions = {
            cellSelector: '.carousel-cell',
            cellAlign: 'left',
            wrapAround: false,
            percentPosition: false,
            resize: true,
            pageDots: false,
            asNavFor: '#section-items',
            groupCells: '80%',
            accessibility: true
        };
        vm.flickityItemsOptions = {
            cellSelector: '.selected-menu-section',
            groupCells: false,
            cellAlign: 'left',
            wrapAround: false,
            percentPosition: false,
            resize: true,
            pageDots: false,
            prevNextButtons: false,
            adaptiveHeight: true,
            dragThreshold: 80,
            fade: !Modernizr.touch,
            on: {
                'change': flickityChange
            }
        };
        vm.flickitySectionBrandsOptions = {
            cellSelector: '.carousel-cell',
            cellAlign: 'center',
            freeScroll: false,
            wrapAround: false,
            percentPosition: false,
            resize: true,
            contain: true,
            pageDots: false,
            groupCells: true,
            draggable: false,
            accessibility: true,
            on: {
                'staticClick': function (x, y, z, i) {
                    $timeout(function () {
                        vm.selectedSectionBrandIndex = i;
                        initializeSections(vm.menu.SectionBrands[i]);
                        if (!browserInfo.isSize("xs")) {
                            vm.selectSection(vm.menuSections[0].Id, vm.sectionReplaceOptions);
                        } else {
                            menuWorkflowService.goToSections($stateParams);
                        }
                    });
                }
            }
        };

        vm.selectedSectionBrand = undefined;

        var _selectedSectionBrandIndex = 0;
        Object.defineProperty(vm, 'selectedSectionBrandIndex', {
            get: function () {
                return _selectedSectionBrandIndex;
            }, set: function (val) {
                _selectedSectionBrandIndex = val;
                vm.selectedSectionBrand = vm.menu.SectionBrands[val];
            }
        });

        activate();

        function flickityChange(index) {
            //keep the URL up-to-date when the flickity control changes.
            var selectedSection = vm.menuSections[index];
            selectSection(selectedSection.Id, vm.replaceOptions);
        }

        function activate() {
            var isUserSpecificRecommendationsPromtToBeShown =
                togoorder.merchantLocation.UserSpecificRecommendations === true &&
                userService.checkIsAuthenticated() &&
                $.parseJSON(sessionStorage.getItem('WasUserSpecificRecommendationsPromtShown')) !== true;

            if (isUserSpecificRecommendationsPromtToBeShown) {
                userService.getUserSpecialRecommendations(menuId)
                    .then(x => new Promise(resolve => setTimeout(() => resolve(x), 1000)))
                    .then(function (data) {
                        if (data.RecentItems?.length >= 1) {
                            var modalInstance = $modal.open({
                                templateUrl: 'app/userSpecificRecommendations/userSpecificRecommendationsPromt.html',
                                controller: 'userSpecificRecommendationsPromt as vm',
                                resolve: {
                                    menuId: menuId,
                                    recomendationItems: data
                                }

                            }).result.then(function (menuItemId) {
                                selectNewMenuItem(menuItemId);
                            })['finally'](function () {
                                sessionStorage.setItem('WasUserSpecificRecommendationsPromtShown', true);
                            });
                        }
                    })
            }
            storageService.set('lastSectionIndex', 0);

            menuWorkflowService.initializeRouteState(vm.replaceOptions);

            common.activateController([getMenu(), getMerchantLocation().then(initializeCart)])
                .then(dataCheck)
                .then(initializeUi)
                .then(initializeMenuItemService)
                .then(validateOrder);

            $scope.$on(events.cartItemSelected, function (e, orderItem) {
                hideCartSidebar();
            });


            $scope.$on(events.presentCart, function (e) {
                forceOpenCart = true;
                if (!vm.isCartInPage) {
                    vm.showCartSidebar();
                }
            });
        }

        function setupScrollSpy() {
            //WARNING: DOM STUFF
            var memoizedGetExpandedSectionNavBottom = _.memoize(getExpandedSectionNavBottom);
            $(window).scroll(_.throttle(function () {
                if (isAutoScrolling) {
                    return;
                }
                var winTop = $(self).scrollTop() + memoizedGetExpandedSectionNavBottom() + 50;
                var sectionContainers = $('h1.section-title');
                var current = 0;

                _.forEach(sectionContainers, function (item, i) {
                    item = $(item);
                    if (!item.offset) {
                        return;
                    }
                    var fromTop = $(item).offset().top - winTop;
                    if (fromTop < 0) {
                        current = i;
                    }
                });

                if (current !== vm.selectedSectionIndex) {
                    $timeout(function () {
                        vm.selectedSectionIndex = current;
                    });
                }
            }, 250));
        }

        function expandedSectionCarouselLoaded() {
            setupScrollSpy();

            var lastSectionIndex = +storageService.get('lastSectionIndex');
            if (lastSectionIndex) {
                vm.selectedSectionIndex = lastSectionIndex;
                scrollToSection();
            }
        }

        function expandedSectionCarouselClick(event, pointer, cellElement, cellIndex) {
            if (cellIndex === undefined) {
                return;
            }
            vm.selectedSectionIndex = cellIndex;

            scrollToSection();
        }

        function getExpandedSectionNavBottom() {
            let carouselNav = $("#expanded-section-nav-carousel");
            var navScreenPosition = parseInt(carouselNav.css('top'));
            var navHeight = carouselNav.height();
            return navScreenPosition + navHeight;
        }

        function scrollToSection() {
            if (isAutoScrolling) {
                return;
            }
            var selectedSection = getSelectedSection();
            if (!selectedSection) {

                return;
            }
            isAutoScrolling = true;
            $timeout(function () {
                if (vm.selectedSectionIndex < 0) {
                    return;
                }

                //WARNING: DOM STUFF
                var sectionElement = $($("h1.section-title")[vm.selectedSectionIndex]);

                if (!sectionElement.offset || !sectionElement.offset()) {
                    return;
                }

                var newScrollTop = sectionElement.offset().top - getExpandedSectionNavBottom();

                console.log('newScrollTop', newScrollTop);
                if (vm.selectedSectionIndex === 0) {
                    newScrollTop = 0;
                }

                $('html,body').animate(
                    { scrollTop: newScrollTop },
                    1000,
                    'swing',
                    function () {
                        $timeout(function () { isAutoScrolling = false; }, 100);
                    });
            }, 4);
        }

        function getSectionItems(section) {
            if (!section) {
                return undefined;
            }
            if (section.Branding) {
                _.each(section.MenuItems, function (i) {
                    i.SectionBrand = section.Branding;
                });
            }
            return section.MenuItems;
        }

        function canIncreaseQuantity(menuItem) {
            return menuItem && menuItem.MaximumCount && vm.item.currentOrderItem.Quantity >= menuItem.MaximumCount;
        }

        function canDecreaseQuantity(menuItem) {
            return menuItem && vm.item.currentOrderItem.Quantity <= menuItem.MinimumCount;
        }

        function getItemImageStyle(menuItem) {
            return menuItem.PrimaryImageUrl ? ("background-image: url('" + menuItem.PrimaryImageUrl + "')") : "";
        }

        function initializeMenuItemService() {
            return menuItemDetailViewModel.initialize($scope, vm.menu, vm.merchantLocation);
        }


        function initializeCart() {
            if (isPreview) return cartService.noCart();
            return cartService.initializeCart(menuId, orderType, fulfillmentType, vm.merchantLocation, day)
                .then(null, function () {
                    $window.setTimeout(function () {
                        $state.go('locationHome', {}, { location: 'replace' });
                    }, 0);
                });
        }

        $scope.$on("$stateChangeSuccess", function (event, current, previous) {
            if (current.name === 'menu.sections' || current.name === 'days.menus.menu.sections') {
                initializeUi();
            }
        });

        function initializeSections(sectionBrand) {
            if (!sectionBrand && vm.menu.SectionBrands && vm.menu.SectionBrands.length) {
                sectionBrand = vm.menu.SectionBrands[getSelectedSectionBrandIndex()];
            }
            vm.menuSections = _.filter(vm.menu.MenuSections, function (s) {
                if (s.IsHidden) {
                    return false;
                }
                if (vm.merchantLocation.IsSectionBranding && sectionBrand) {
                    return s.Branding === sectionBrand;
                }
                return true;
            });
        }

        function initializeUi() {
            if (browserInfo.isSize("xs")) {
                vm.sectionReplaceOptions = undefined;
            }

            vm.alwaysFloatCart = vm.merchantLocation.CartUiBehavior === 1;
            initializeSections();

            if (vm.menuSections && vm.menuSections.length) {
                if ($stateParams.sectionId) {
                    vm.selectedSectionIndex = getSelectedSectionIndex($stateParams.sectionId);
                } else if (!browserInfo.isSize("xs") && menuWorkflowService.mustNavigateToSection()) {
                    $timeout(function () {
                        //why $timeout? If it gets here too quick, the 'replace' is too aggressive
                        selectSection(vm.menuSections[0].Id, vm.replaceOptions);
                    },
                        100);
                }
                if ($stateParams.sectionId && vm.menu.SectionBrands && vm.menu.SectionBrands.length) {
                    vm.selectedSectionBrandIndex = getSelectedSectionBrandIndex();
                }
            }

            initializeResizeEvent();

            return true;
        }

        function getSelectedSectionIndex(sectionId) {
            return _.findIndex(vm.menuSections, function (ms) {
                return ms.Id === $stateParams.sectionId;
            });
        }

        function getSelectedSectionBrandIndex() {
            var selectedSectionBrandIndex = 0;
            var selectedSection = getSelectedSection();
            if (selectedSection && selectedSection.Branding && vm.menu.SectionBrands && vm.menu.SectionBrands.length) {
                var sectionBrandIndex = _.findIndex(vm.menu.SectionBrands,
                    function (sb) {
                        return sb === selectedSection.Branding;
                    });
                if (sectionBrandIndex > 0) {
                    selectedSectionBrandIndex = sectionBrandIndex;
                }
            }
            return selectedSectionBrandIndex;
        }

        function onResized() {
            initializeSidebar();
            menuItemDetailViewModel.initializeUi();
        }

        function initializeSidebar() {
            vm.isCartInPage = (!vm.alwaysFloatCart) && (browserInfo.isSize("md") || browserInfo.isSize("lg"));
            $window.setTimeout(function () {
                $('.ui.sidebar').sidebar({ overlay: true }).show();
                if (vm.isCartInPage) {
                    $('.cart-tab').hide();
                } else {
                    $('.cart-tab').show();
                    if (forceOpenCart) {
                        forceOpenCart = false;
                        $window.setTimeout(function () {
                            showCartSidebar();
                        }, 4);
                    }
                }
            }, 750);
        }


        function initializeResizeEvent() {
            // Hmmm. Hacky.
            var onResizingHandler = _.debounce(function () {
                $timeout(function () { vm.isCartInPage = !vm.alwaysFloatCart; });
            }, 333, true);
            var onResizedHandler = _.debounce(function () {
                $timeout(vm.onResized);
            }, 334, false);

            $($window).resize(function () {
                onResizingHandler();
                onResizedHandler();
            });
            vm.onResized();
        }

        function dataCheck() {
            if (vm.menu.MerchantId !== vm.merchantLocation.MerchantId) {
                vm.menu = {};
                $log.warn("Merchant Location ID mismatch");
                throw new Error("Something went wrong.");
            }
            return true;//orderService.rePriceOrder(vm.cart.order);
        }

        function getMenu() {
            return menuService.getMenu(menuId)
                .then(function (data) {
                    vm.menu = data;
                    return data;
                });
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(merchantLocationId)
                .then(function (data) {
                    vm.merchantLocation = data;
                    return data;
                });
        }

        function selectSection(sectionId, options) {
            $stateParams.sectionId = sectionId;
            vm.showAppBackButton = false;

            vm.selectedSectionIndex = getSelectedSectionIndex(sectionId);

            menuWorkflowService.showSections($stateParams, options, vm.cart);
        }

        function getSelectedSection() {
            if (!vm.menuSections || !vm.menuSections.length) {
                return null;
            }
            if (vm.selectedSectionIndex >= 0) {
                common.broadcast(events.menuIsShown, {});
                return vm.menuSections[vm.selectedSectionIndex];
            }
            let section = _.find(vm.menuSections, function (ms) {
                return ms.Id === +$stateParams.sectionId;
            });

            common.broadcast(events.menuIsShown, {});
            return section;
        }

        function addItemsFromStorage(isSectionSelected) {
            var itemsToAdd = cartService.getStoredItemsToAdd();
            //TODO: For now, only helps with the FIRST one in the list.
            if (itemsToAdd && itemsToAdd.length) {
                var item = itemsToAdd[0];
                if (isSectionSelected) {
                    cartService.removeStoredItemsToAdd(item);
                    selectNewMenuItem(item.id);
                } else {
                    selectSection(item.sectionId, vm.sectionReplaceOptions);
                }
            }
        }

        function selectNewMenuItem(menuItemId) {
            storageService.set('lastSectionIndex', vm.selectedSectionIndex);
            menuItemDetailViewModel.selectNewMenuItem(menuItemId);
            hideCartSidebar();
        }

        $scope.$on('$stateChangeStart', function (event, next) {
            menuItemDetailViewModel.toggleOptions(false);
            vm.hideCartSidebar();
        });

        $scope.$on("$stateChangeSuccess", function (event, current, currentParams, previous, previousParams) {
            if (current.name === 'menu.sections' || current.name === 'days.menus.menu.sections') {
                vm.isSectionsInFocus = true;
                menuItemDetailViewModel.selectedMenuItem = null;
                vm.showAppBackButton = vm.hasAppBack();
                addItemsFromStorage(false);
            } else if (current.name === 'menu.sections.items' || current.name === 'days.menus.menu.sections.items') {
                vm.isSectionsInFocus = false;
                menuItemDetailViewModel.selectedMenuItem = null;
                vm.showAppBackButton = vm.hasAppBack() && !browserInfo.isSize("xs");
                addItemsFromStorage(true);
            }

        });

        function showCartSidebar(isSwiping) {
            if (!isSwiping || !togoorder.isCartOpenWithSwipeDisabled) {
                $('.ui.sidebar').sidebar('show');
                vm.isSidebarVisible = true;
            }
        }

        function hideCartSidebar() {
            $('.ui.sidebar').sidebar('hide');

            vm.isSidebarVisible = false;
        }

        function validateOrder() {
            var order = vm.cart.order;

            if (!order || order.constructor !== Order) return;
            orderPricingService.applyPricingRules(vm.cart.order, vm.menu);
            var validations = orderValidationService.validateOrder(order, vm.menu, vm.merchantLocation);

            if (validations.fatals) {
                cartService.destroy();
                $modal.open({
                    templateUrl: 'app/menu/menuChangesAlert.html',
                    controller: 'menuChangesAlert as vm',
                    size: 'md',
                    resolve: {
                        validations: function () { return validations; }
                    }
                }).result['finally'](function () {
                    $state.go('locationHome');
                });
                return;
            }
            if (validations.errors || validations.warnings) {
                $modal.open({
                    templateUrl: 'app/menu/menuChangesAlert.html',
                    controller: 'menuChangesAlert as vm',
                    size: 'md',
                    resolve: {
                        validations: function () { return validations; }
                    }
                });
            }
        }

        function getTopCssClasses() {
            return ['menu-' + menuId];
        }

        function getItemCount() {
            if (!vm.cart || !vm.cart.order) {
                return 0;
            }
            return (cartService.getItems(vm.cart.order) || []).length;
        }

        function shouldShowItemLabels(menuItem) {
            return !!menuItem && menuItem.ItemLabels && menuItem.ItemLabels.length > 0;
        }

        function getItemLabels(menuItem) {
            return menuItem.ItemLabels.join(', ');
        }
    }
})();
;
(function () {
    'use strict';

    angular.module('main')
        .controller('menuSectionItems', ['$log', '$timeout', '$scope', '$stateParams', '$state', '$uibModal', '$window', 'events', 'browserInfo', 'common', 'notify', 'menuService', 'cartService', menuSectionItems]);

    function menuSectionItems($log, $timeout, $scope, $stateParams, $state, $modal, $window, events, browserInfo, common, notify, menuService, cartService) {
        var vm = this,
            menuId = parseInt($stateParams.menuId, 10),
            sectionId = parseInt($stateParams.sectionId, 10),
            menuController = $scope.vm;// because of data-ng-controller="menu as vm"

        vm.merchantLocation = {};
        vm.backToMenuSections = backToMenuSections;

        activate();

        Object.defineProperty(vm, "selectedSection",
            {
                get: function () {
                    return menuController.getSelectedSection();
                }
            });

        function activate() {
            common.activateController([cartService.waitForInitialization()], 'menuSectionItems')
                .then(function () {
                });
        }

        function backToMenuSections() {
            $window.history.back();
        }
    }
})();;
(function () {
	'use strict';

	angular.module('main')
		.controller('menuSections', ['$scope', '$q', '$state', '$stateParams', '$timeout', 'common', 'merchantLocationService', 'menuService', 'cartService', 'menuWorkflowService', menuSections]);

	function menuSections($scope, $q, $state, $stateParams, $timeout, common, merchantLocationService, menuService, cartService, menuWorkflowService) {
		var vm = this,
			menuId = parseInt($stateParams.menuId, 10),
			orderType = parseInt($stateParams.orderType, 10),
			merchantLocationId = togoorder.locationId,
			menuController = $scope.vm;// because of data-ng-controller="menu as vm"

		vm.isUserSpecificRecommendationsOn = false;

		vm.menu = {};
		vm.selectedSectionId = null;
		activate();

		function activate() {
			common.activateController([getMenu(), getMerchantLocation(), cartService.waitForInitialization()], 'menuSections');
		}

		Object.defineProperty(vm, 'selectedFlickityIndex', {
			get: function () {
				var id = getSectionIndex();
				return id;
			},
			set: function (val) {
				//console.log('vm.selectedSectionId', vm.selectedSectionId, val);
			}
		});


		//for back-button usability:
		$scope.$on("$stateChangeSuccess", function (event, current, previous) {
			if (menuWorkflowService.isSectionsState(current)) {
				vm.selectedSectionId = null;
				if (vm.menu && vm.menu.Id === menuId) {
					common.activateController([], 'menuSections');
				}
			} else if (menuWorkflowService.isSectionItemsState(current)) {
				vm.selectedSectionId = parseInt($stateParams.sectionId, 10);
			}
		});

		function getSectionIndex() {
			if (!vm.selectedSectionId) {
				return 0;
			}
			let findIndex = _.findIndex(menuController.menuSections, function (ms) {
				return ms.Id === vm.selectedSectionId;
			});
			return findIndex;
		}

		function getMenu() {
			return menuService.getMenu(menuId)
				.then(function (data) {
					vm.menu = data;
					return data;
				});
		}

		function getMerchantLocation() {
			return merchantLocationService.getById(merchantLocationId);
		}
	}

})();;
(function () {
	'use strict';

	var controllerId = 'menuChangesAlert';
	angular.module('common').controller(controllerId, ['$uibModalInstance', '$log', 'common', 'orderTypeWorkflowService', 'validations', menuChangesAlert]);

	function menuChangesAlert($modalInstance, $log, common, orderTypeWorkflowService, validations) {
		var vm = this;

		vm.validations = validations;
		vm.close = $modalInstance.close;
		vm.getOrderTypeDescription = getOrderTypeDescription;
	    vm.maxErrorLevel = vm.validations.fatals ? "fatals" : (vm.validations.errors ? "errors" : "warnings");
	    vm.safeClassName = common.safeClassName;


	    function getOrderTypeDescription(orderType, fulfillmentType) {
	        try {
                if (!orderType || !fulfillmentType) {
                    return ' one or more of the selected items';
                }
	            return orderTypeWorkflowService.getOrderTypeDescription(orderType, fulfillmentType);
	        } catch (e) {
	            $log.warn(e, arguments);
	            return '';
	        }
	    }
	}

})();;
(function () {
	'use strict';

	var controllerId = 'chooseMenu';
	angular.module('main')
		.controller(controllerId, ['$scope', '$q', '$window', '$timeout', '$log', '$stateParams', '$state', 'common', 'notify', 'merchantLocationService', 'routeStateService', chooseMenu]);

	function chooseMenu($scope, $q, $window, $timeout, $log, $stateParams, $state, common, notify, merchantLocationService, routeStateService) {
		var vm = this,
			merchantLocationId = togoorder.locationId,
			fulfillmentType = $stateParams.fulfillmentType,
			orderType = $stateParams.orderType;

		sessionStorage.setItem('is_unlistedOnly', $stateParams.unlistedOnly === 'unlistedOnly');

		vm.menuSelections = undefined;
		vm.selectMenu = selectMenu;
		vm.appBack = routeStateService.goBack;
		vm.hasAppBack = routeStateService.hasAppBack;
		vm.merchantLocation = {};
		vm.getMenuClass = getMenuClass;

		activate();

		function activate() {
			common.activateController([getMerchantLocation().then(getMenus)], controllerId);
		}

		function getMenuClass(menuSelection) {
			if (!menuSelection) {
				return undefined;
			}
			var className = "m-" + common.safeClassName(menuSelection.MenuName);
			
			return className;
		}

		function getMerchantLocation() {
			return merchantLocationService.getById(merchantLocationId)
				.then(function (data) {
					vm.merchantLocation = data;
					return data;
				});
		}

		function getMenus() {
			return merchantLocationService.getMenus(merchantLocationId, orderType, fulfillmentType).then(function (data) {
				vm.menuSelections = data.menuSelections;
				if (vm.menuSelections &&
					vm.menuSelections.length === 1 &&
					vm.merchantLocation.BypassMenuAndHoursScreen) {
					//defer.reject();
					$timeout(function () {
						//why $timeout? If it gets here too quick, the 'replace' is too aggressive
						vm.selectMenu(vm.menuSelections[0], { location: 'replace' });
					}, 4);
				} else {
					routeStateService.addState($state);
				}

				return data.menuSelections;
			});
		}

		function selectMenu(menuSelection, options) {
			$state.go('menu.sections', {
				merchantLocationId: merchantLocationId,
				menuId: menuSelection.MenuId,
				orderType: orderType,
				fulfillmentType: fulfillmentType
			}, options);
		}

	}
})();;
(function () {
	'use strict';

	var controllerId = 'days';
	angular.module('main')
		.controller(controllerId, ['$scope', '$q', '$window', '$timeout', '$log', '$stateParams', '$state', 'common', 'notify', 'merchantLocationService', 'routeStateService', days]);

	function days($scope, $q, $window, $timeout, $log, $stateParams, $state, common, notify, merchantLocationService, routeStateService) {
		var vm = this,
	        merchantLocationId = togoorder.locationId,
	        fulfillmentType = $stateParams.fulfillmentType,
	        orderType = $stateParams.orderType,
	            dayToSelect;

		vm.days = undefined;
		vm.appBack = routeStateService.goBack;
		vm.hasAppBack = routeStateService.hasAppBack;
		vm.selectDay = selectDay;
		vm.selectedDay = $stateParams.day;
		vm.isSelectedDay = isSelectedDay;
	    vm.getHoursOfOperationDisplay = getHoursOfOperationDisplay;
	    vm.merchantLocation = {};

		activate();

		function activate() {
		    common.activateController([getMerchantLocation(), getDaysWithMenus(), getEarliestDayOrderable()], controllerId)
		        .then(function() {
		            if (!$stateParams.day) {
		                var selectedDay = _.findWhere(vm.days, { weekDay: dayToSelect });
		                if (selectedDay) {
		                    $timeout(function () {
		                        //why $timeout? If it gets here too quick, the 'replace' is too aggressive
		                        selectDay(selectedDay, { location: 'replace' });
		                    }, 4);
		                }
		            } else {
		                routeStateService.addState($state);
		            }


		        });
		}

		function getEarliestDayOrderable() {
		    merchantLocationService.getEarliestDayOrderable(merchantLocationId, orderType, fulfillmentType).then(function(data) {
		        dayToSelect = data;
		    });
		}

		function getHoursOfOperationDisplay(d) {
		    if (d) {
		        return d;
		    }
		    return "";
		}

		function selectDay(day, options) {
            if (!day.menus || !day.menus.length) {
                return;
            }
            if (vm.selectedDay === day.weekDay) {
                return;
            }
		    vm.selectedDay = day.weekDay;
		    $stateParams.day = day.weekDay;
		    $state.go('days.menus', $stateParams, options);
	    }

		function isSelectedDay(day) {
	        return vm.selectedDay === day.weekDay;
	    }

	    function getMerchantLocation() {
		    return merchantLocationService.getById(merchantLocationId)
				.then(function (data) {
				    vm.merchantLocation = data;
				    return data;
				});
		}

	    function getDaysWithMenus() {
	        return merchantLocationService.getDaysWithMenus(merchantLocationId, orderType, fulfillmentType)
	            .then(function(data) {
	                vm.days = data;
	            });
	    }


	}
})();;
(function () {
    'use strict';

    angular.module('main')
        .controller('daysMenus', ['$log', '$q', '$scope', '$stateParams', '$state', '$uibModal', '$timeout', 'events', 'browserInfo', 'common', 'notify', 'menuService', 'cartService', 'merchantLocationService', daysMenus]);

    function daysMenus($log, $q, $scope, $stateParams, $state, $modal, $timeout, events, browserInfo, common, notify, menuService, cartService, merchantLocationService) {
        var vm = this,
            weekDay = $stateParams.day,
            orderType = $stateParams.orderType,
            fulfillmentType = $stateParams.fulfillmentType,
            merchantLocationId = togoorder.locationId;

        vm.merchantLocation = {};
        vm.days = [];
        vm.menus = undefined;
        vm.selectMenu = selectMenu;
        activate();

        function activate() {
            common.activateController([getMerchantLocation(), getDaysWithMenus()], 'daysMenus')
                .then(function () {
                    var selectedDay = _.find(vm.days, function (day) {
                        return day.weekDay === weekDay;
                    });
                    if (selectedDay) {
                        vm.menus = selectedDay.menus;

                        if (vm.menus && vm.menus.length === 1) {
                            $timeout(function() {
                                //why $timeout? If it gets here too quick, the 'replace' is too aggressive
                                selectMenu(vm.menus[0], { location: 'replace' });
                            }, 4);
                        }
                    }
                });
        }

        function selectMenu(menuSelection, options) {
            $stateParams.menuId = menuSelection.MenuId;
            $state.go('days.menus.menu.sections', $stateParams, options);
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(merchantLocationId)
				.then(function (data) {
				    vm.merchantLocation = data;
				    return data;
				});
        }

        function getDaysWithMenus() {
            return merchantLocationService.getDaysWithMenus(merchantLocationId, orderType, fulfillmentType)
                .then(function(data) {
                    vm.days = data;
                });
        }

    }
})();;
(function () {
    'use strict';

    var controllerId = 'getDeliveryAddress';
    angular.module('main')
		.controller(controllerId, ['$scope', '$q', '$window', '$timeout', '$log', '$stateParams', '$state', '$uibModal', 'common', 'events', 'deliveryService', 'routeStateService', 'merchantLocationService', 'notify', 'Address', 'orderTypeWorkflowService', 'orderTypeRuleService', 'cartService', 'menuWorkflowService', 'userService', getDeliveryAddress]);

	function getDeliveryAddress($scope, $q, $window, $timeout, $log, $stateParams, $state, $modal, common, events, deliveryService, routeStateService, merchantLocationService, notify, Address, orderTypeWorkflowService, orderTypeRuleService, cartService, menuWorkflowService, userService) {
        var vm = this,
            merchantLocationId = togoorder.locationId,
            orderType = $stateParams.orderType,
            fulfillmentType = $stateParams.fulfillmentType,
            menuId = $stateParams.menuId,
            _orderTypeRule = {};

        vm.address = new Address();
        vm.orderTypes = [];
        vm.orderTypeDisplay = orderTypeWorkflowService.getOrderTypeDescription(orderType, fulfillmentType);
        vm.instructionsField = '';
        vm.switchToOrderType = switchToOrderType;
        vm.enterDeliveryAddress = enterDeliveryAddress;
        vm.merchantLocation = {};
        vm.isAddressInDeliveryArea = false;
        vm.appBack = routeStateService.goBack;
        vm.hasAppBack = routeStateService.hasAppBack;
        vm.deliveryZones = [];
        vm.safeClassName = common.safeClassName;

        vm.isAddressForSaving = true;
        vm.savedAddressChanged = savedAddressChanged;
        vm.deleteSavedAddress = deleteSavedAddress;
        vm.freeSavedAddress = freeSavedAddress;
        vm.selectedSavedAddress = "";

        if(userService.getUserData()?.isAuthenticated === true) {
            userService.getUserSavedDeliveryAddresses()
                .then(function (data) {
                    vm.savedAddresses = data.sort((a,b)=> a.IsRecentlyUsed ? -1 : 1);
                    return data;
                });
        }

        //Required inputs messaging....
        vm.mandatoryText = { name: "required input", url: "shared/mandatoryMsg.html"}
        
        activate();

        function activate() {

            vm.isAuthenticated = userService.getUserData()?.isAuthenticated === true;

            common.activateController([
                getMerchantLocation().then(function () {
                    getCachedDeliveryAddress();
                    getCurrentOrderType();
                }),
                getOrderTypes(),
                initializeDeliveryZones()
            ], controllerId)
                .then(function () {
                    initializeMap(vm.address.getGoogleAddress());
                    routeStateService.addState($state);

                    $(document).on({
                        'DOMNodeInserted': function () {
                            $('.pac-item, .pac-item span', this).addClass('needsclick');
                        }
                    }, '.pac-container');
                });
        }

        $scope.$on('$destroy', function() {
            $('#pac-input').off('click', '**');
        });

        function getCurrentOrderType() {
            _orderTypeRule = orderTypeRuleService.getOrderTypeRule(orderType, fulfillmentType, vm.merchantLocation);
            vm.instructionsField = _orderTypeRule.InstructionsField;
        }

        function initializeDeliveryZones() {
            return deliveryService.getDeliveryZones(orderType, fulfillmentType).then(function (dz) {
                vm.deliveryZones = dz;
            });
        }

        function getOrderTypes() {
            var filter = function (ot) {
                return ot.OrderType != orderType || ot.FulfillmentType != fulfillmentType;
            };
            return orderTypeRuleService.getDisplayOrderTypes(filter)
                .then(function (data) {
                    vm.orderTypes = data;
                });
        }
        
        function switchToOrderType(newOrderType) {
            vm.isOrderTypeChoicesOpen = false;
            cartService.loadCurrent().then(function (cart) {
                orderTypeRuleService.switchToOrderTypeRule(newOrderType, menuId, cart.hasOrderItems());
            }, function () {
                orderTypeRuleService.switchToOrderTypeRule(newOrderType, menuId, false);
            });
        }

        function verifyAddressIsInDeliveryArea() {

            if (_orderTypeRule.DeliveryServiceHasOwnZones) {
                // Using a delivery service with separately managed delivery areas, check their api
                return deliveryService.checkAddressIsInDeliveryServiceArea(_orderTypeRule, vm.address, 0.0)
                    .then(function () {
                        vm.isAddressInDeliveryArea = true;
                        notify.success("Your address is in the delivery area! You're good to go.");
                        return true;
                    }, function () {
                        vm.isAddressInDeliveryArea = false;
                        deliveryAreaAlert();
                    })
                    .catch(function(ex) {
                        debugger;
                        $log.error(ex);
                    });
            } else {
                // Check the normal togo managed zones
                return deliveryService.checkAddressIsInAnyDeliveryArea(vm.address, vm.deliveryZones)
                    .then(function () {
                        vm.isAddressInDeliveryArea = true;
                        notify.success("Your address is in the delivery area! You're good to go.");
                        return true;
                    }, function () {
                        vm.isAddressInDeliveryArea = false;
                        deliveryAreaAlert();
                    })
                    .catch(function(ex) {
                        debugger;
                        $log.error(ex);
                    });
            }
        }

        function ifHasSiblingLocations() {
            var tgo = togoorder || {};
            return tgo.merchantLocation.MerchantLocationCount > 1;
        }

        function deliveryAreaAlert() {
            notify.dialog({
                dialogTitle: 'Oh No!',
                dialogMessage: 'So Sorry. This address is not in our delivery area.',
                buttonActions: ifHasSiblingLocations() ? [
                    {
						action: function () {
							common.broadcast(events.navigateToMerchantHome, {});
                        },
                        text: "Find Another Location"
                    }] : [],
                cssClass: 'delivery-area-alert'
            });
        }

        function invalidAddressAlert() {
            notify.warning("Sorry. We need an address with a valid street number.");
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(merchantLocationId)
                .then(function (data) {
                    vm.merchantLocation = data;
                    return data;
                });
        }
        
        function getCachedDeliveryAddress() {
            deliveryService.getDeliveryAddress()
                .then(function (address) {
                    vm.address = address;

                }, function () {
                    return $q.when(1);
                });
        }

        function enterDeliveryAddress(formValid) {
            if (formValid) {
                if (!vm.isAddressInDeliveryArea) {
                    deliveryAreaAlert();
                } else {
                    deliveryService.cacheDeliveryAddress(vm.address)
                        .then()
                        .then(function () {
                            if (menuId) {
                                $state.go('menu.sections', $stateParams);
                            } else {
                                menuWorkflowService.goToFirstState($stateParams);
                            }
                        }).catch(function(ex) {
                            debugger;
                            $log.error(ex);
                        });
                }
                if(vm.isAddressForSaving && !vm.selectedSavedAddress && vm.isAuthenticated) {
                    userService.saveUserSavedDeliveryAddresses(vm.address);
                }

                if(vm.selectedSavedAddress && vm.isAuthenticated){
                    userService.updateUserSavedDeliveryAddresses(vm.selectedSavedAddress);
                }
            } else {
                var message = 'There\'s a problem. Please correct any problems AND be sure your address is found on the map.';
                notify.dialog('Almost Perfect!', message);
                notify.warning(message);
            }
        }

        function average(values) {
            var sum = _.reduce(values, function (agg, value) {
                return agg + value;
            }, 0);
            return sum / values.length;
        }

        function initializeMap(initialValue, doItNow) {

            if (!doItNow) {
                setTimeout(function () {
                    initializeMap(initialValue, true);
                }, 100);

                return;
            }

            var $input = $('.mapboxgl-ctrl-geocoder--input');
            
            var coords = { lat: 0, lng: 0 };

            var priciestZone = null;
            if (vm.deliveryZones && vm.deliveryZones.length) {
                priciestZone = vm.deliveryZones[vm.deliveryZones.length - 1];
            }

            if (priciestZone && priciestZone.Coordinates && priciestZone.Coordinates.length) {
                coords.lat = average(_.map(priciestZone.Coordinates, function (coord) {
                    return coord.Lat;
                }));
                coords.lng = average(_.map(priciestZone.Coordinates, function (coord) {
                    return coord.Lng;
                }));
            }

            if (!coords.lat && !coords.lng) {
                coords = { lat: 36.1866405, lng: -86.7852454 };
            }

            mapboxgl.accessToken = togoorder.MapBoxApiKey;
            const map = new mapboxgl.Map({
                container: 'map-canvas',
                style: 'mapbox://styles/mapbox/streets-v11',
                center: [coords.lng, coords.lat],
                zoom: 10
            });
            map.addControl(new mapboxgl.NavigationControl());

            const geocoder = new MapboxGeocoder({
                accessToken: mapboxgl.accessToken,
                mapboxgl: mapboxgl,
                marker: false,
                placeholder: 'Delivery Address*',
                types: 'address,poi',
                language: 'en-US'
            });

            var popUp = new mapboxgl.Popup();
            var marker = new mapboxgl.Marker().setLngLat([0, -29]);

            map.on('load', function() {
            
                //  Add a marker with a popup at the result's coordinates
                geocoder.on('result', function(e) {
                    popUp.remove();
                    marker.remove();

                    //console.log('geocoder input', $input.val());
                    document.activeElement.blur();  // ios devices don't close the keyboard, close it here

                    vm.address = new Address();
                    var address = onPlaceChanged(e.result);
                });

                if (initialValue) {
                    //geocoder.setInput(initialValue);
                    geocoder.query(initialValue);
                }
 
                // ios devices don't set & query when user taps an address in the geocoder, do it here
                $('#pac-input').on('click', '.suggestions li a', function() {
                    var $suggestion = $(this);
                    var title = $suggestion.find('div.mapboxgl-ctrl-geocoder--suggestion-title').text();
                    var addr = $suggestion.find('div.mapboxgl-ctrl-geocoder--suggestion-address').text();
                    var addrString = `${title},${addr}`;
                    //alert(addrString);
                    geocoder.query(addrString);
                });
            });
 
            document.getElementById('pac-input').replaceChildren();
            document.getElementById('pac-input').appendChild(geocoder.onAdd(map));

            function onPlaceChanged(place) {
                $timeout(function () {
                    vm.isAddressInDeliveryArea = false;
                });

                var newAddress = vm.address.parseMapBoxFeature(place);
                //console.log('newAddress', newAddress);

                if (newAddress instanceof Address) {
                    
                    popUp.setHTML(`<div><strong>Deliver Here?</strong><br>${newAddress.displayString}`);

                    $timeout(function() {
                        vm.address = newAddress;
                        verifyAddressIsInDeliveryArea();
                        populateSavedAddressData();
                    });
                } else {
                    popUp.setHTML(`<div><strong>What Street Number?</strong><br>${newAddress.displayString}`);
                    invalidAddressAlert();
                }

                if (Array.isArray(place.geometry.coordinates) && place.geometry.coordinates.length) {
                    marker = new mapboxgl.Marker()
                        .setLngLat(place.geometry.coordinates)
                        .setPopup(popUp)
                        .addTo(map)
                        .togglePopup();
                }
                
                return newAddress;
            }
        }

        function savedAddressChanged(address) {
            vm.address.AddressLine2 = "";
            vm.address.ExtraInstructions = "";
            vm.address.SpecialInstructions = "";

            if(address) {
                vm.selectedSavedAddress = address;

                initializeMap(address.AddressLine1, true);
            }
        }

        function populateSavedAddressData(){
            if(vm.selectedSavedAddress) {
                if(vm.selectedSavedAddress.AddressLine1 !== vm.address.displayString) {
                    freeSavedAddress();
                }
                else{
                    vm.address.AddressLine2 = vm.selectedSavedAddress.AddressLine2;
                    vm.address.ExtraInstructions = vm.selectedSavedAddress.ExtraInstructions;
                    vm.address.SpecialInstructions = vm.selectedSavedAddress.SpecialInstructions;
                }
            }
        }

        function deleteSavedAddress(address, $event) {
            $event.preventDefault();
            $event.stopPropagation();
            
            var result = notify.dialog({
                dialogTitle: 'Are you sure?',
                dialogMessage: `The saved address ${address.AddressLine1} will be deleted?`,
                dismissButtonText: 'Yes',
                closeButtonText: 'No',
                cssClass: 'delivery-area-alert'
            });

            result.then(function () {
            },
                function () {
                    userService.deleteUserSavedDeliveryAddresses(address).then(function(){
                        const index = vm.savedAddresses.indexOf(address);
                        vm.savedAddresses.splice(index, 1);
                    });
                });
        }

        function freeSavedAddress() {
            vm.selectedSavedAddress = "";
        };
    }
})();;
(function () {
	'use strict';

	var controllerId = 'getTableNumber';
	angular.module('main')
		.controller(controllerId, ['$scope', '$q', '$window', '$timeout', '$log', '$stateParams', '$state', '$uibModal', 'common', 'deliveryService', 'notify', 'routeStateService', 'merchantLocationService', 'orderTypeWorkflowService', 'orderTypeRuleService', 'cartService', 'menuWorkflowService', getTableNumber]);

	function getTableNumber($scope, $q, $window, $timeout, $log, $stateParams, $state, $modal, common, deliveryService, notify, routeStateService, merchantLocationService, orderTypeWorkflowService, orderTypeRuleService, cartService, menuWorkflowService) {
	    var vm = this,
	        merchantLocationId = togoorder.locationId,
	        orderType = $stateParams.orderType,
	        fulfillmentType = $stateParams.fulfillmentType,
	        menuId = $stateParams.menuId,
		    orderTypeViewModels;

		vm.tableNumber = "";
		vm.orderTypes = [];
		vm.orderTypeDisplay = orderTypeWorkflowService.getOrderTypeDescription(orderType, fulfillmentType);
	    vm.switchToOrderType = switchToOrderType;
		vm.enterTableNumber = enterTableNumber;
		vm.back = back;
	    vm.safeClassName = common.safeClassName;

		activate();

		function activate() {
		    common.activateController([getCachedTableNumber(), getOrderTypes()], controllerId)
		    .then(function () {
		    	routeStateService.addState($state);
		    });
		}

		function getCachedTableNumber() {
		    deliveryService.getTableNumber(merchantLocationId)
				.then(function (tableNumber) {
					vm.tableNumber = tableNumber;
				});
		}

		function back() {
			routeStateService.goBack();
		}

		function getOrderTypes() {
		    var filter = function (ot) {
		        return ot.OrderType != orderType || ot.FulfillmentType != fulfillmentType;
		    };
		    return orderTypeRuleService.getDisplayOrderTypes(filter)
                .then(function (data) {
                    vm.orderTypes = data;
                });
		}

		function switchToOrderType(newOrderType) {
		    vm.isOrderTypeChoicesOpen = false;
		    cartService.loadCurrent().then(function (cart) {
		        orderTypeRuleService.switchToOrderTypeRule(newOrderType, menuId, cart.hasOrderItems());
		    }, function () {
		        orderTypeRuleService.switchToOrderTypeRule(newOrderType, menuId, false);
		    });
		}

		function enterTableNumber(formValid) {
			if (vm.tableNumber) {
			    deliveryService.cacheTableNumber(vm.tableNumber, merchantLocationId)
					.then(function () {
				    if (menuId) {
				        $state.go('menu.sections', $stateParams);
				    } else {
				        menuWorkflowService.goToFirstState($stateParams);
				    }
				});
			} else {
			    var message = 'We really need your table number so your food can find its way to you.';
			    notify.dialog('Almost Perfect!', message);
				notify.warning(message);
			}
		}
	}
})();;
(function () {
	'use strict';


	var controllerId = "setTableNumber";
	angular.module('main').controller(controllerId, ['$rootScope', '$state', '$stateParams', '$location', '$timeout', 'common', 'spinner', 'events', 'deliveryService', 'menuWorkflowService', setTableNumber]);

	function setTableNumber($rootScope, $state, $stateParams, $location, $timeout, common, spinner, events, deliveryService, menuWorkflowService) {
		var vm = this,
			merchantLocationId = togoorder.locationId;

		vm.tableNumber = $stateParams.tableNumber;
		activate();

		function activate() {

			common.activateController([], controllerId);

			deliveryService.cacheTableNumber(vm.tableNumber, merchantLocationId)
				.then(function () {
					$timeout(function () {
						spinner.spinnerShow();
					}, 500);
					$timeout(function () {
						menuWorkflowService.goToFirstState({ orderType: 1, fulfillmentType: 2 });
					}, 2000);
				});
		}

	}

})();;
(function () {
	'use strict';

	var controllerId = 'chooseOrderType';
	angular.module('main')
		.controller(controllerId, ['$scope', '$http', '$q', '$timeout', '$log', '$stateParams', '$state', 'common', 'notify', 'spinner', 'merchantLocationService', 'routeStateService', 'usualOrderService', 'menuWorkflowService', 'cartService', chooseOrderType]);

	function chooseOrderType($scope, $http, $q, $timeout, $log, $stateParams, $state, common, notify, spinner, merchantLocationService, routeStateService, usualOrderService, menuWorkflowService, cartService) {
		var vm = this,
			merchantLocationId = togoorder.locationId;
		vm.orderTypeViewModels = undefined;
		vm.mobileMessage = "";
		vm.selectOrderType = selectOrderType;
		vm.orderMyUsual = orderMyUsual;

		activate();

        function activate() {
			if (!merchantLocationId) {
				$state.go('home');
			} else {
			    common.activateController([getOrderTypes()], controllerId)
			        .then(function () {
			            routeStateService.addState($state);
			        });
			}
		}

        function getOrderTypes() {
	        return merchantLocationService.getOrderTypes(merchantLocationId)
	            .then(function (data) {
	                vm.mobileMessage = data.mobileMessage;
	                vm.orderTypeViewModels = data.orderTypeViewModels;
	            });
	    }

	    function selectOrderType(orderTypeViewModel) {
	        // If user has cart items and is switching menu warn of loss of cart

            cartService.loadCurrent().then(function(cart) {
                if (cart && cart.hasOrderItems()) {
                    var menuId = cart.order.MenuId;

                    merchantLocationService.getMenus(merchantLocationId, orderTypeViewModel.OrderType, orderTypeViewModel.FulfillmentType).then(function (data) {
                        var menus = data.menuSelections;
                        var menu = _.some(menus, { MenuId: menuId });
                        if (menu) {
                            // Not a menu swtich, proceed
                            menuWorkflowService.goToFirstState({ orderType: orderTypeViewModel.OrderType, fulfillmentType: orderTypeViewModel.FulfillmentType });
                        } else {
                            var result = notify.dialog({
                                dialogTitle: 'Switch to ' + orderTypeViewModel.Description + '?',
                                dialogMessage: orderTypeViewModel.Description + ' has a different menu. If you switch, you\'ll lose your cart.',
                                dismissButtonText: 'Cancel',
                                closeButtonText: 'Switch'
                            });
                            result.then(function () {
                                cartService.destroy();
                                menuWorkflowService.goToFirstState({ orderType: orderTypeViewModel.OrderType, fulfillmentType: orderTypeViewModel.FulfillmentType });
                            });
                        }
                    });
                } else {
                    // No cart items, no worries, proceed
                    menuWorkflowService.goToFirstState({ orderType: orderTypeViewModel.OrderType, fulfillmentType: orderTypeViewModel.FulfillmentType });
                }
            },
            function() {
                // Must have a cart from a different location in storage, effectively an empty cart, proceed
                cartService.destroy();
                menuWorkflowService.goToFirstState({ orderType: orderTypeViewModel.OrderType, fulfillmentType: orderTypeViewModel.FulfillmentType });
            });
		}

		function orderMyUsual() {
		    spinner.spinnerShow();
		    merchantLocationService.getById(merchantLocationId).then(function(merchantLocation) {
		        usualOrderService.begin(merchantLocation)['finally'](function () {
		            spinner.spinnerHide();
		        });
		    });
		}
	}
})();;
(function () {
    'use strict';

    var controllerId = 'loyaltyHome';
    angular.module('main')
		.controller(controllerId, ['$scope', '$q', '$timeout', '$log', '$stateParams', '$state', 'common', 'events', 'notify', 'spinner', 'merchantLocationService', 'userService', 'routeStateService', loyaltyHome]);

    function loyaltyHome($scope, $q, $timeout, $log, $stateParams, $state, common, events, notify, spinner, merchantLocationService, userService, routeStateService) {
        var vm = this,
			merchantLocationId = togoorder.locationId;

        vm.mobileMessage = "";
        vm.isLoggedIn = isLoggedIn;
        vm.signIn = signIn;
        vm.goToRewards = goToRewards;
        vm.goToOffers = goToOffers;
        vm.goToPunchCards = goToPunchCards;
        vm.isAchEnabled = isAchEnabled;
        vm.goToAchEnrollment = goToAchEnrollment;
        vm.getAchStatusLabel = getAchStatusLabel;
        vm.goToPersonalInfo = goToPersonalInfo;
        vm.signUp = signUp;
        vm.goToUpdateCard = goToUpdateCard;

        activate();

        function activate() {
            if (!merchantLocationId) {
                $state.go('home');
            } else {
                common.activateController([getOrderTypes()], controllerId)
			        .then(function () {
			            routeStateService.addState($state);
			        }).then(function () {
			            getMerchantLocation();
			        }).then(function () {
			            getUser();
			        });
            }
        }

        function goToRewards() {
            $state.go("loyaltyAdmin", {}, null);
        }

        function goToOffers() {
            $state.go("offers", {}, null);
        }

        function goToPunchCards() {
            $state.go("punchCards", {}, null);
        }

        function goToAchEnrollment() {
            $state.go("achEnrollment", {}, null);
        }

        function goToPersonalInfo() {
            $state.go("personalInfo", {}, null);
        }

        function goToUpdateCard() {
            $state.go("loyaltyAdmin", {updating:true}, null);
        }

        function isLoggedIn() {
            return userService.checkIsAuthenticated();
        }

        function signIn() {
            common.broadcast(events.securityAuthorizationRequired, {});
        }

        function signUp() {
            common.broadcast(events.securityAuthorizationRequired, { goSignUp: true });
        }

        function getOrderTypes() {
            return merchantLocationService.getOrderTypes(merchantLocationId)
	            .then(function (data) {
	                vm.mobileMessage = data.mobileMessage;
	            });
        }

        function getMerchantLocation() {
            return merchantLocationService.getById(merchantLocationId)
				.then(function (data) {
				    vm.merchantLocation = data;
				});
        }

        function isAchEnabled() {
            return vm.merchantLocation.LoyaltyProfile.IsAchEnabled;
        }

        function getUser() {
            if (!userService.checkIsAuthenticated()) {
                return $q.when();
            }
            return userService.getUserWithLoyalty(vm.merchantLocation.MerchantId)
                    .then(function(user) {
                        vm.user = user;
                    });
            
        }

        function getAchStatusLabel() {
            var status = "Not Enrolled";
            if (vm.user && vm.user.Loyalties && vm.user.Loyalties.length) {
                switch (vm.user.Loyalties[0].AchStatus) {
                    case 1:
                        status = "Pending";
                        break;
                    case 2:
                        status = "Verification Required";
                        break;
                    case 3:
                        status = "Active";
                        break;
                    case 4:
                        status = "Suspended";
                        break;
                }
            }
            return status;
        }
    }
})();;
(function () {
    'use strict';

    var controllerId = 'feedbackSurvey';
    angular.module('main')
        .controller(controllerId, ['$scope', '$state', '$uibModalInstance', '$uibModal', '$window', 'common', 'events', 'spinner', 'notify', 'feedbackService', 'survey', feedbackSurveyController]);

    function feedbackSurveyController($scope, $state, $modalInstance, $modal, $window, common, events, spinner, notify, feedbackService, survey) {
        var vm = this;

        vm.cancel = cancel;
        vm.submitFeedback = submitFeedback;

        vm.survey = survey;

        initialize();

        function initialize() {
            vm.survey.Comments = '';
            _.each(vm.survey.FeedbackQuestions,
                function(question) {
                    question.Value = 0;
                });
            spinner.spinnerHide();
        }

        function submitFeedback() {
            var feedback = {
                FeedbackSurveyId: vm.survey.Id,
                FeedbackQuestionResponses: [],
                Comments: vm.survey.Comments,
                LocationId: togoorder.merchantLocation.Id
            };
            _.each(vm.survey.FeedbackQuestions,
                function(question) {
                    feedback.FeedbackQuestionResponses.push({ FeedbackQuestionId: question.Id, Value: question.Value });
                });
            spinner.spinnerShow();
            feedbackService.saveFeedback(feedback).then(function(response) {
                spinner.spinnerHide();
                cancel();
                //console.log(JSON.stringify(response));
                notify.dialog('Thank you', "Thank you for your feedback.");
            });
        }

        function cancel() {
            $modalInstance.dismiss();
            $modalInstance = null;
        }

        $scope.$on('$stateChangeStart', function (e, toState, toParams, fromState, fromParams) {
            if ($modalInstance) { //modal is open
                cancel();
            }
        });


    }
})();;
(function () {
    'use strict';

    var common = angular.module('common');
    
    common.filter('giftCardMask', function() {
        return function(cardNumber) {

            if (cardNumber.length < 5)
                return cardNumber;

            var lastFour = cardNumber.substr(cardNumber.length - 4, 4);

            if (cardNumber.length >= 14) {
                var firstSix = cardNumber.substr(0, 6);
                return `${firstSix}${'x'.repeat(cardNumber.length - 10)}${lastFour}`;
            } else {
                return `${'x'.repeat(cardNumber.length - 4)}${lastFour}`;
            }
        };
    });

})();;
(function () {
	'use strict';

	var controllerId = 'userSpecificRecommendationsPromt';
	angular.module('main').controller(controllerId, ['$uibModalInstance', 'recomendationItems', userSpecificRecommendationsPromtController]);

	function userSpecificRecommendationsPromtController($modalInstance, recomendationItems) {
		var vm = this;
		vm.recomendationItems = recomendationItems;
		vm.close = $modalInstance.close;
		vm.dismiss = $modalInstance.dismiss;
	}
})();;
