// Generated by CoffeeScript 1.4.0
(function() {
  var $, cardFromNumber, cardFromType, cards, defaultFormat, formatBackCardNumber, formatBackExpiry, formatCardNumber, formatExpiry, formatForwardExpiry, formatForwardSlash, hasTextSelected, luhnCheck, reFormatCardNumber, restrictCVC, restrictCardNumber, restrictExpiry, restrictNumeric, setCardType,
    __slice = [].slice,
    __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; },
    _this = this;

  $ = jQuery;

  $.payment = {};

  $.payment.fn = {};

  $.fn.payment = function() {
    var args, method;
    method = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
    return $.payment.fn[method].apply(this, args);
  };

  defaultFormat = /(\d{1,4})/g;

  cards = [
    {
      type: 'maestro',
      pattern: /^(5018|5020|5038|6304|6759|676[1-3])/,
      format: defaultFormat,
      length: [12, 13, 14, 15, 16, 17, 18, 19],
      cvcLength: [3],
      luhn: true
    }, {
      type: 'dinersclub',
      pattern: /^(36|38|30[0-5])/,
      format: defaultFormat,
      length: [14],
      cvcLength: [3],
      luhn: true
    }, {
      type: 'laser',
      pattern: /^(6706|6771|6709)/,
      format: defaultFormat,
      length: [16, 17, 18, 19],
      cvcLength: [3],
      luhn: true
    }, {
      type: 'jcb',
      pattern: /^35/,
      format: defaultFormat,
      length: [16],
      cvcLength: [3],
      luhn: true
    }, {
      type: 'unionpay',
      pattern: /^62/,
      format: defaultFormat,
      length: [16, 17, 18, 19],
      cvcLength: [3],
      luhn: false
    }, {
      type: 'discover',
      pattern: /^(6011|65|64[4-9]|622)/,
      format: defaultFormat,
      length: [16],
      cvcLength: [3],
      luhn: true
    }, {
      type: 'mastercard',
      pattern: /^5[1-5]/,
      format: defaultFormat,
      length: [16],
      cvcLength: [3],
      luhn: true
    }, {
      type: 'amex',
      pattern: /^3[47]/,
      format: /(\d{1,4})(\d{1,6})?(\d{1,5})?/,
      length: [15],
      cvcLength: [3, 4],
      luhn: true
    }, {
      type: 'visa',
      pattern: /^4/,
      format: defaultFormat,
      length: [13, 14, 15, 16],
      cvcLength: [3],
      luhn: true
    }
  ];

  cardFromNumber = function(num) {
    var card, _i, _len;
    num = (num + '').replace(/\D/g, '');
    for (_i = 0, _len = cards.length; _i < _len; _i++) {
      card = cards[_i];
      if (card.pattern.test(num)) {
        return card;
      }
    }
  };

  cardFromType = function(type) {
    var card, _i, _len;
    for (_i = 0, _len = cards.length; _i < _len; _i++) {
      card = cards[_i];
      if (card.type === type) {
        return card;
      }
    }
  };

  luhnCheck = function(num) {
    var digit, digits, odd, sum, _i, _len;
    odd = true;
    sum = 0;
    digits = (num + '').split('').reverse();
    for (_i = 0, _len = digits.length; _i < _len; _i++) {
      digit = digits[_i];
      digit = parseInt(digit, 10);
      if ((odd = !odd)) {
        digit *= 2;
      }
      if (digit > 9) {
        digit -= 9;
      }
      sum += digit;
    }
    return sum % 10 === 0;
  };

  hasTextSelected = function($target) {
    var _ref;
    if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== $target.prop('selectionEnd')) {
      return true;
    }
    if (typeof document !== "undefined" && document !== null ? (_ref = document.selection) != null ? typeof _ref.createRange === "function" ? _ref.createRange().text : void 0 : void 0 : void 0) {
      return true;
    }
    return false;
  };

  reFormatCardNumber = function(e) {
    var _this = this;
    return setTimeout(function() {
      var $target, value;
      $target = $(e.currentTarget);
      value = $target.val();
      value = $.payment.formatCardNumber(value);
      return $target.val(value);
    });
  };

  formatCardNumber = function(e) {
    var $target, card, digit, length, re, upperLength, value;
    digit = String.fromCharCode(e.which);
    if (!/^\d+$/.test(digit)) {
      return;
    }
    $target = $(e.currentTarget);
    value = $target.val();
    card = cardFromNumber(value + digit);
    length = (value.replace(/\D/g, '') + digit).length;
    upperLength = 16;
    if (card) {
      upperLength = card.length[card.length.length - 1];
    }
    if (length >= upperLength) {
      return;
    }
    if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {
      return;
    }
    if (card && card.type === 'amex') {
      re = /^(\d{4}|\d{4}\s\d{6})$/;
    } else {
      re = /(?:^|\s)(\d{4})$/;
    }
    if (re.test(value)) {
      e.preventDefault();
      return $target.val(value + ' ' + digit);
    } else if (re.test(value + digit)) {
      e.preventDefault();
      return $target.val(value + digit + ' ');
    }
  };

  formatBackCardNumber = function(e) {
    var $target, value;
    $target = $(e.currentTarget);
    value = $target.val();
    if (e.meta) {
      return;
    }
    if (e.which !== 8) {
      return;
    }
    if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {
      return;
    }
    if (/\d\s$/.test(value)) {
      e.preventDefault();
      return $target.val(value.replace(/\d\s$/, ''));
    } else if (/\s\d?$/.test(value)) {
      e.preventDefault();
      return $target.val(value.replace(/\s\d?$/, ''));
    }
  };

  formatExpiry = function(e) {
    var $target, digit, val;
    digit = String.fromCharCode(e.which);
    if (!/^\d+$/.test(digit)) {
      return;
    }
    $target = $(e.currentTarget);
    val = $target.val() + digit;
    if (/^\d$/.test(val) && (val !== '0' && val !== '1')) {
      e.preventDefault();
      return $target.val("0" + val + " / ");
    } else if (/^\d\d$/.test(val)) {
      e.preventDefault();
      return $target.val("" + val + " / ");
    }
  };

  formatForwardExpiry = function(e) {
    var $target, digit, val;
    digit = String.fromCharCode(e.which);
    if (!/^\d+$/.test(digit)) {
      return;
    }
    $target = $(e.currentTarget);
    val = $target.val();
    if (/^\d\d$/.test(val)) {
      return $target.val("" + val + " / ");
    }
  };

  formatForwardSlash = function(e) {
    var $target, slash, val;
    slash = String.fromCharCode(e.which);
    if (slash !== '/') {
      return;
    }
    $target = $(e.currentTarget);
    val = $target.val();
    if (/^\d$/.test(val) && val !== '0') {
      return $target.val("0" + val + " / ");
    }
  };

  formatBackExpiry = function(e) {
    var $target, value;
    if (e.meta) {
      return;
    }
    $target = $(e.currentTarget);
    value = $target.val();
    if (e.which !== 8) {
      return;
    }
    if (($target.prop('selectionStart') != null) && $target.prop('selectionStart') !== value.length) {
      return;
    }
    if (/\d(\s|\/)+$/.test(value)) {
      e.preventDefault();
      return $target.val(value.replace(/\d(\s|\/)*$/, ''));
    } else if (/\s\/\s?\d?$/.test(value)) {
      e.preventDefault();
      return $target.val(value.replace(/\s\/\s?\d?$/, ''));
    }
  };

  restrictNumeric = function(e) {
    var input;
    if (e.metaKey || e.ctrlKey) {
      return true;
    }
    if (e.which === 32) {
      return false;
    }
    if (e.which === 0) {
      return true;
    }
    if (e.which < 33) {
      return true;
    }
    input = String.fromCharCode(e.which);
    return !!/[\d\s]/.test(input);
  };

  restrictCardNumber = function(e) {
    var $target, card, digit, value;
    $target = $(e.currentTarget);
    digit = String.fromCharCode(e.which);
    if (!/^\d+$/.test(digit)) {
      return;
    }
    if (hasTextSelected($target)) {
      return;
    }
    value = ($target.val() + digit).replace(/\D/g, '');
    card = cardFromNumber(value);
    if (card) {
      return value.length <= card.length[card.length.length - 1];
    } else {
      return value.length <= 16;
    }
  };

  restrictExpiry = function(e) {
    var $target, digit, value;
    $target = $(e.currentTarget);
    digit = String.fromCharCode(e.which);
    if (!/^\d+$/.test(digit)) {
      return;
    }
    if (hasTextSelected($target)) {
      return;
    }
    value = $target.val() + digit;
    value = value.replace(/\D/g, '');
    if (value.length > 6) {
      return false;
    }
  };

  restrictCVC = function(e) {
    var $target, digit, val;
    $target = $(e.currentTarget);
    digit = String.fromCharCode(e.which);
    if (!/^\d+$/.test(digit)) {
      return;
    }
    val = $target.val() + digit;
    return val.length <= 4;
  };

  setCardType = function(e) {
    var $target, allTypes, card, cardType, val;
    $target = $(e.currentTarget);
    val = $target.val();
    cardType = $.payment.cardType(val) || 'unknown';
    if (!$target.hasClass(cardType)) {
      allTypes = (function() {
        var _i, _len, _results;
        _results = [];
        for (_i = 0, _len = cards.length; _i < _len; _i++) {
          card = cards[_i];
          _results.push(card.type);
        }
        return _results;
      })();
      $target.removeClass('unknown');
      $target.removeClass(allTypes.join(' '));
      $target.addClass(cardType);
      $target.toggleClass('identified', cardType !== 'unknown');
      return $target.trigger('payment.cardType', cardType);
    }
  };

  $.payment.fn.formatCardCVC = function() {
    this.payment('restrictNumeric');
    this.on('keypress', restrictCVC);
    return this;
  };

  $.payment.fn.formatCardExpiry = function() {
    this.payment('restrictNumeric');
    this.on('keypress', restrictExpiry);
    this.on('keypress', formatExpiry);
    this.on('keypress', formatForwardSlash);
    this.on('keypress', formatForwardExpiry);
    this.on('keydown', formatBackExpiry);
    return this;
  };

  $.payment.fn.formatCardNumber = function() {
    this.payment('restrictNumeric');
    this.on('keypress', restrictCardNumber);
    this.on('keypress', formatCardNumber);
    this.on('keydown', formatBackCardNumber);
    this.on('keyup', setCardType);
    this.on('paste', reFormatCardNumber);
    return this;
  };

  $.payment.fn.restrictNumeric = function() {
    this.on('keypress', restrictNumeric);
    return this;
  };

  $.payment.fn.cardExpiryVal = function() {
    return $.payment.cardExpiryVal($(this).val());
  };

  $.payment.cardExpiryVal = function(value) {
    var month, prefix, year, _ref;
    value = value.replace(/\s/g, '');
    _ref = value.split('/', 2), month = _ref[0], year = _ref[1];
    if ((year != null ? year.length : void 0) === 2 && /^\d+$/.test(year)) {
      prefix = (new Date).getFullYear();
      prefix = prefix.toString().slice(0, 2);
      year = prefix + year;
    }
    month = parseInt(month, 10);
    year = parseInt(year, 10);
    return {
      month: month,
      year: year
    };
  };

  $.payment.validateCardNumber = function(num) {
    var card, _ref;
    num = (num + '').replace(/\s+|-/g, '');
    if (!/^\d+$/.test(num)) {
      return false;
    }
    card = cardFromNumber(num);
    if (!card) {
      return false;
    }
    return (_ref = num.length, __indexOf.call(card.length, _ref) >= 0) && (card.luhn === false || luhnCheck(num));
  };

  $.payment.validateCardExpiry = function(month, year) {
    var currentTime, expiry, prefix, _ref;
    if (typeof month === 'object' && 'month' in month) {
      _ref = month, month = _ref.month, year = _ref.year;
    }
    if (!(month && year)) {
      return false;
    }
    month = $.trim(month);
    year = $.trim(year);
    if (!/^\d+$/.test(month)) {
      return false;
    }
    if (!/^\d+$/.test(year)) {
      return false;
    }
    if (!(parseInt(month, 10) <= 12)) {
      return false;
    }
    if (year.length === 2) {
      prefix = (new Date).getFullYear();
      prefix = prefix.toString().slice(0, 2);
      year = prefix + year;
    }
    expiry = new Date(year, month);
    currentTime = new Date;
    expiry.setMonth(expiry.getMonth() - 1);
    expiry.setMonth(expiry.getMonth() + 1, 1);
    return expiry > currentTime;
  };

  $.payment.validateCardCVC = function(cvc, type) {
    var _ref, _ref1;
    cvc = $.trim(cvc);
    if (!/^\d+$/.test(cvc)) {
      return false;
    }
    if (type) {
      return _ref = cvc.length, __indexOf.call((_ref1 = cardFromType(type)) != null ? _ref1.cvcLength : void 0, _ref) >= 0;
    } else {
      return cvc.length >= 3 && cvc.length <= 4;
    }
  };

  $.payment.cardType = function(num) {
    var _ref;
    if (!num) {
      return null;
    }
    return ((_ref = cardFromNumber(num)) != null ? _ref.type : void 0) || null;
  };

  $.payment.formatCardNumber = function(num) {
    var card, groups, upperLength, _ref;
    card = cardFromNumber(num);
    if (!card) {
      return num;
    }
    upperLength = card.length[card.length.length - 1];
    num = num.replace(/\D/g, '');
    num = num.slice(0, +upperLength + 1 || 9e9);
    if (card.format.global) {
      return (_ref = num.match(card.format)) != null ? _ref.join(' ') : void 0;
    } else {
      groups = card.format.exec(num);
      if (groups != null) {
        groups.shift();
      }
      return groups != null ? groups.join(' ') : void 0;
    }
  };

}).call(this);

var currentWizardPage = 0;
var directionIsForward = true;
var angularScopeRef;
var BOOKING_REQUEST_KEY = 'bookingRequest';
var BOOKING_REQUEST_POST_SEND_KEY = "thebookingrequestwesenttotheserver";
var lastValidateBookingOk = true; // On first load set to true so we can move next
var CURRENT_PAGE_KEY = "currentPage";
var historyStateNotSetup = true;
var todo = null;


if (!String.prototype.startsWith) {
    String.prototype.startsWith = function (searchString, position) {
        position = position || 0;
        return this.indexOf(searchString, position) === position;
    };
}

//GetFullUrl("/bookings#?currentPage=0");

/*
// This method adjusts the dates which appear to be UTC, and converts them into the user's
// local time zone (so the date going to the server looks to the user to be the date they entered)
function getBookingRequestForPost(bookingRequest) {
    var asString = JSON.stringify(bookingRequest);
    var req = JSON.parse(asString);
    ////////
    // Do this if you want to send the date and time as the user entered it
    ////////
    //req.EntryDate = toServerDateFormat(new Date(req.EntryDate)); // seems req.EntryDate is already a string at this point
    //req.ExitDate = toServerDateFormat(new Date(req.ExitDate));
    // You don't need to do anything with the time

    ////////////////
    // Or do this if you want to send UTC
    // You actually don't need to do anything with the date but you need this code to adjust the time to UTC
    /////
    var entryDate = new Date(req.EntryDate);    
    entryDate.setHours(req.EntryHour);
    entryDate.setMinutes(req.EntryMinute);

    var exitDate = new Date(req.ExitDate);
    exitDate.setHours(req.ExitHour);
    exitDate.setMinutes(req.ExitMinute);

    req.EntryDate = toUtcServerDateFormat(entryDate);
    req.EntryHour = entryDate.getUTCHours();
    req.EntryMinute = entryDate.getUTCMinutes();

    req.ExitDate = toUtcServerDateFormat(exitDate);
    req.ExitHour = exitDate.getUTCHours();
    req.ExitMinute = exitDate.getUTCMinutes();

    return req;
}

function toUtcServerDateFormat(e) {
    return e.getUTCFullYear() + "-" + pad(e.getUTCMonth() + 1, 2) + "-" + pad(e.getUTCDate(), 2);
}

function toServerDateFormat(e) {
    return e.getFullYear() + "-" + pad(e.getMonth() + 1, 2) + "-" + pad(e.getDate(), 2);
}

function pad(num, size) {
    var s = num + "";
    while (s.length < size) s = "0" + s;
    return s;
}
*/

function getBookingRequestForPost(bookingRequest) {

    var asString = JSON.stringify(bookingRequest);
    var req = JSON.parse(asString);
    req.EntryDate = toServerDateFormat(new Date(req.EntryDate)); // seems req.EntryDate is already a string at this point
    req.ExitDate = toServerDateFormat(new Date(req.ExitDate));

    return req;
}

function toServerDateFormat(e) {
    return e.getFullYear() + "-" + pad(e.getMonth() + 1, 2) + "-" + pad(e.getDate(), 2);
}

function pad(num, size) {
    var s = num + "";
    while (s.length < size) s = "0" + s;
    return s;
}


function GetFullUrl(url) {
    if (window.parkAltoGlobal.defaults.baseUrl && window.parkAltoGlobal.defaults.baseUrl != null) {
        return window.parkAltoGlobal.defaults.baseUrl + url;
    }
    else {
        return url;
    }
}

var app = angular.module('BookingEngine', ['ui', "google-maps", 'ngCookies', 'ngSanitize', 'angular-google-analytics']).run(function ($rootScope, $location, $templateCache, $cookieStore) {
    $rootScope.pageLoading = false;

    var bookingForm = ParkAltoForms.BookingForm;

    // register listener to watch route changes
    $rootScope.$on("$locationChangeStart", function (event, next, current) {
        //      alert('$locationChangeStart');
        //event.preventDefault();
        //var parameters = next.search();
        //alert(JSON.stringify(parameters));

        //        angularScopeRef.displayCurrentPage(curPage);

    });

    // Load the template cache
    $(bookingForm.Controls).each(function (idx, ctrl) {
        if (ctrl.Template && ctrl.Template != null) {
            $templateCache.put(ctrl.TemplateUrl, ctrl.Template);
        }
    });
    $(bookingForm.Pages).each(function (idx, ctrl) {
        if (ctrl.Template && ctrl.Template != null) {
            $templateCache.put(ctrl.TemplateUrl, ctrl.Template);
        }
    });
    if (bookingForm.Templates) {
        $(bookingForm.Templates).each(function (idx, ctrl) {
            if (ctrl.Template && ctrl.Template != null) {
                $templateCache.put(ctrl.TemplateUrl, ctrl.Template);
            }
        });
    }

});

app.config(function (AnalyticsProvider) {
    // Set a single account
    //AnalyticsProvider.setAccount(window.parkAltoGlobal.defaults.websiteGACode);
        AnalyticsProvider.setAccount(window.parkAltoGlobal.defaults.websiteGACode);
		AnalyticsProvider.trackPages(false);
        AnalyticsProvider.useECommerce(true, false);
    // Enable e-commerce module (ecommerce.js)

});

/*
todo = app; // get the directives working by doing this.

Date.prototype.addHours = function (h) {   
    var dt = new Date();
    dt.setTime(this.getTime() + (h * 60 * 60 * 1000));
    return dt;
};

app.filter('utcdate', function ($filter) {
    var angularDateFilter = $filter('date');
    return function (theDate, dateFormat) {
        if (theDate) {
            var dateUtc = null;
            if (!theDate instanceof Date) {
                try {
                    dateUtc = new Date(Date.parse(theDate));
                } catch (err) {
                }
            }
            else {
                dateUtc = theDate;
            }
            if (dateUtc) {
                dateUtc = dateUtc.addHours( (new Date().getTimezoneOffset()*-1) / 60);
                return angularDateFilter(dateUtc, dateFormat);
            }
        }
        return null;
    };
});
*/

function GetBookingBundleController($scope, $http) {
    $scope.bundleData = bundles;
}



Object.byString = function (o, s) {
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    s = s.replace(/^\./, '');           // strip a leading dot
    var a = s.split('.');
    while (a.length) {
        var n = a.shift();
        if (n in o) {
            o = o[n];
        } else {
            return;
        }
    }
    return o;
};

function getNotNullValue(ori, obj) {
    if (ori == null) {
        return obj;
    } else {
        return ori;
    }
}


app.controller('PassengerCountCtrl',
    ['$rootScope', '$scope', '$http', '$timeout', '$location', '$cookieStore',
   function ($rootScope, $scope, $http, $timeout, $locationProvider, $cookieStore) {
       var field = $scope.field;
       var fieldVal = $scope.bookingRequest[field.BookingProperty] = [];
       if (field.BookingPropertySecond) {
           $scope.bookingRequest[field.BookingPropertySecond] = fieldVal;
       }
       $(field.MemberTypes).each(function (idx, membertype) {
           //adult defualt 1, child 0
           if (idx == 0) {
               fieldVal[idx] = { PartyMemberTypeId: membertype.Id, MemberCount: 1 };
           } else {
               fieldVal[idx] = { PartyMemberTypeId: membertype.Id, MemberCount: 0 };
           }
       });
       $scope.passengerCounts = fieldVal;
       $scope.counts = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

   }]
);

app.controller('BookingEngineCtrl',
    ['$rootScope', '$scope', '$http', '$timeout', '$location', '$cookieStore', 'Analytics',
    function ($rootScope, $scope, $http, $timeout, $locationProvider, $cookieStore, Analytics) {
        $scope.Forms = ParkAltoForms;
        $scope.FormSubmitAttempted = new Object();
        $scope.Global_DateTimeFormat = window.parkAltoGlobal.formats.websiteDatetimeFormat;
        $scope.Global_DateFormat = window.parkAltoGlobal.formats.websiteDateFormat;
        $scope.Global_TimeFormat = window.parkAltoGlobal.formats.websiteTimeFormat;
        $scope.bookingRequestedLoaded = false;
        $scope.parkingDiscount = 0;
        // Ideally this would not be hard coded.
        // Its job is to make sure ExitDate is on or after Entry Date
        $scope.$watch('bookingRequest.EntryDate', function (newVal, oldVal) {
            if ($scope.bookingRequestedLoaded) {
                if (oldVal && newVal && (newVal instanceof Date) && (oldVal instanceof Date) && newVal.getTime() != oldVal.getTime()) {//if (oldVal != newVal && oldVal != null) { This is not compare the actual time but the object reference
                    var exitDate = $scope.bookingRequest.ExitDate;
                    console.log('exitDate', exitDate);
                    var oldEntryDate = new Date(oldVal.getFullYear(), oldVal.getMonth(), oldVal.getDate());
                    var oldexitDate = new Date(exitDate.getFullYear(), exitDate.getMonth(), exitDate.getDate());
                    var timeDiff = Math.abs(oldexitDate.getTime() - oldEntryDate.getTime());
                    var diffDays = Math.ceil(timeDiff / (1000 * 3600 * 24));//date diff
                    var newEndDate = new Date(newVal.getFullYear(), newVal.getMonth(), newVal.getDate() + diffDays);
                    $scope.bookingRequest.ExitDate = newEndDate;
                }
            }
        });
        $scope.$watch('bookingRequest.DropoffMembers', function (newVal, oldVal) {
            //when from home page to booking page reload, the PassengerCountCtrl's scope is gone.
            //$scope.bookingRequest.DropoffMembers becomes undefined. then $scope.bookingRequest.DropoffMembers[0].MemberCount is gone.
            //this steps equals put the init value back.
            if (oldVal != newVal && typeof newVal == 'undefined') {
                $scope.bookingRequest.DropoffMembers = oldVal;
            }
        });
        $scope.$watch('bookingRequest.PickupMembers', function (newVal, oldVal) {
            if (oldVal != newVal && typeof newVal == 'undefined') {
                $scope.bookingRequest.PickupMembers = oldVal;
            }
        });

        $scope.$watch('currentPage', function () {

        });
        //update shopping basket, if cardnumber may match the surcharge fee defined.
        $scope.$watch('bookingRequest.CardNumber', function (newVal, oldVal) {
            if (oldVal != newVal) {
                var type = getCreditCardType(newVal);
                if (type != "unknown") {
                    console.info("CC type:" + type);
                    $($scope.bundleData.PaymentPolicies).each(function (idx, pp) {
                        if (pp.PaymentPolicyId == $scope.bookingRequest.PaymentPolicyId) {
                            $(pp.CreditCardTypes).each(function (idx, ccType) {
                                if (type == ccType.Key) {
                                    $scope.selectedBundle.TransactionFeePercent = ccType.TransactionFeePercent;
                                    $scope.selectedBundle.TransactionFeePercentTitle = ccType.Key;
                                    $scope.calculateShoppingBasket();
                                }
                            });
                        }
                    });
                }
            }
        });


        var clearstore = $locationProvider.search()['clearstore'];
        if (clearstore) {
            alert('about to clear all store');
            clearAll();
        }

        $rootScope.$on("$locationChangeSuccess", function (event, next, current) {

            //event.preventDefault();
            var curPage = parseInt($locationProvider.search()[CURRENT_PAGE_KEY]);
            if (!isNaN(curPage)) {
                //console.log("curPage", curPage);
                angularScopeRef.displayCurrentPage(curPage);
            } else if ($scope.isBookingPage) {
                angularScopeRef.getCurrentPage();
            }

        });
        ///e.g. bookings?entryDateTime=2016-9-9T05%3A00&exitDateTime=2016-9-9T14%3A00&bookingBundleId=d6c71c04-2089-4755-a833-3e3780ca2cac
        ///find which step it would be.
        $scope.getCurrentPage = function () {
            var isParaValid = true;
            var isEntryValid = false;
            var isExitValid = false;
            var isBundleValid = false;

            var promoCode = unescape((window.location.search.split('promoCode=')[1] || '').split('&')[0]);
            //console.log("promoCode", promoCode);
            if (promoCode.length > 0) {
                $scope.bookingRequest['PromoCode'] = promoCode;
            }

            var entryDateTime = unescape((window.location.search.split('entryDateTime=')[1] || '').split('&')[0]);
            //console.log("entryDateTime", entryDateTime);
            if (entryDateTime.length > 0) {
                var parsedStr = entryDateTime.split('T');
                if (parsedStr.length == 2) {
                    $scope.bookingRequest['EntryDate'] = parsedStr[0];
                    var entryTime = parsedStr[1];;
                    var parsedEntryTime = entryTime.split(':');
                    $scope.bookingRequest['EntryHour'] = parsedEntryTime[0];
                    $scope.bookingRequest['EntryMinute'] = parsedEntryTime[1];
                    console.log(" EntryDate", $scope.bookingRequest['EntryDate']);
                    console.log(" EntryHour", $scope.bookingRequest['EntryHour']);
                    console.log(" EntryMinute", $scope.bookingRequest['EntryMinute']);
                    isEntryValid = true;
                } else { isParaValid = false; }
            }

            var exitDateTime = unescape((window.location.search.split('exitDateTime=')[1] || '').split('&')[0]);
            //console.log("exitDateTime", exitDateTime);
            if (exitDateTime.length > 0) {
                var parsedStr = exitDateTime.split('T');
                if (parsedStr.length == 2) {
                    var exitTime = parsedStr[1];;
                    var parsedEnitTime = exitTime.split(':');

                    $scope.bookingRequest['ExitDate'] = parsedStr[0];
                    $scope.bookingRequest['ExitHour'] = parsedEnitTime[0];
                    $scope.bookingRequest['ExitMinute'] = parsedEnitTime[1];
                    console.log(" ExitDate", $scope.bookingRequest['ExitDate']);
                    console.log(" ExitHour", $scope.bookingRequest['ExitHour']);
                    isExitValid = true;
                } else { isParaValid = false; }
            }
            var bookingBundleId = unescape((window.location.search.split('bookingBundleId=')[1] || '').split('&')[0]);
            //console.log("bookingBundleId", bookingBundleId);
            if (bookingBundleId.length > 0) {
                isBundleValid = true;
            }
            if (isParaValid && isExitValid && isEntryValid && isBundleValid) {
                //var myuserSelectedBundle = {};
                //myuserSelectedBundle.BookingBundleId = bookingBundleId
                //$scope.selectBundle(1, myuserSelectedBundle);
                $scope.bookingRequest.preSelectedBundleId = bookingBundleId;
                $scope.bookingRequest.BundleId == bookingBundleId;
                $scope.moveNext(1);
            }
            else if (isParaValid && isExitValid && isEntryValid) {
                $scope.moveNext(0);
            } else if (isParaValid) {
                //console.log("displayCurrentPage");
                angularScopeRef.displayCurrentPage(0);
            }
        }


        $scope.resetMap = function (lat, lng) {
            var ll = new google.maps.LatLng(lat, lng);
            setTimeout(function () {
                google.maps.event.trigger($scope.location.map, 'resize');
                $scope.location.map.setCenter(ll);
                var marker = new google.maps.Marker({
                    map: $scope.location.map,
                    position: ll
                });
                $scope.location.markers = [marker, ];
            }, 100);
        };
        $scope.updateMonthYearModel = function (monthValue, yearValue, bookingRequest, field) {
            bookingRequest[field.BookingProperty] = monthValue + "_" + yearValue;
        };

        $scope.displayCurrentPage = function (currentPage) {
            // var currentPage = parseInt(store.get(CURRENT_PAGE_STORE_KEY));

            // Make sure all but the page we need are hidden
            ////$($scope.pageData.Pages).each(function (idx, page) {
            ////    jQuery("#page" + idx).hide();
            ////});
            ////alert('display page ' + "#page" + currentPage);
            ////jQuery("#page" + currentPage).show();
            $scope.currentWizardPage = currentPage;

            if ($scope.pageData.Pages.length > currentPage) {
                var page = $scope.pageData.Pages[currentPage];
                //console.log('page.MissingDataExpression', page.MissingDataExpression);
                if (page.MissingDataExpression) {
                    var missingRequiredData = eval(page.MissingDataExpression);
                    //console.log('missingRequiredData', missingRequiredData);
                    if (missingRequiredData) { //Handle Missing Data when directly go to currentpage=3, i.e.
                        //alert(missingRequiredData);
                        console.log("missingRequiredData=" + missingRequiredData);
                        window.location = GetFullUrl("/bookings#?currentPage=0");
                    }
                }
            }

            //   store.set(CURRENT_PAGE_STORE_KEY, currentPage);
            jQuery('html, body').animate({
                scrollTop: 100
            }, 200);
            $scope.calculateShoppingBasket();

        };

        $scope.testTime = function () {
            $http({
                method: 'POST',
                data: null,
                url: '/api/BookingRest/TestTimeZone', cache: false
            }).success(function (data) {
                //alert(JSON.stringify(data));
                alert(data);
            });
        };

        $scope.configureWizard = function (pages) {
            // Set default values into the model based on the screen definition    
            $(pages).each(function (idx, page) {
                if (page.Sections) {
                    $(page.Sections).each(function (idx, section) {
                        if (section.Rows) {
                            $(section.Rows).each(function (idx, row) {
                                if (row.Fields) {
                                    $(row.Fields).each(function (idx, field) {
                                        var currentFieldValue = $scope.bookingRequest[field.BookingProperty];
                                        if (field.DefaultValueExpression) {
                                            if (currentFieldValue == null) { // Only set it if empty
                                                currentFieldValue = eval(field.DefaultValueExpression);
                                                //console.log('currentFieldValue', currentFieldValue);
                                                $scope.bookingRequest[field.BookingProperty] = currentFieldValue;

                                            }
                                        }
                                        // Make sure data types are right (e.g. fix issues where we've loaded a date serialized to a string
                                        if (currentFieldValue != null) {
                                            if (field.DataType == 'Date') {
                                                if (!(currentFieldValue instanceof Date)) {// if (typeof (currentFieldValue) != 'Date') {
                                                    //console.log("currentFieldValue", currentFieldValue);
                                                    var theDt = new Date(currentFieldValue);
                                                    //theDt.setMinutes(theDt.getMinutes() + theDt.getTimezoneOffset());
                                                    $scope.bookingRequest[field.BookingProperty] = theDt; // Make sure its actually a date
                                                }
                                            }
                                        }
                                        if (field.BookingProperty === 'DropOffLocationId') {
                                            field.disabled = false;
                                            if (field.PickUpDropOffLocations.length == 1) {
                                                $scope.bookingRequest[field.BookingProperty] = field.PickUpDropOffLocations[0].Id;
                                                field.disabled = true;
                                            }
                                        }
                                        if (field.BookingProperty === 'PickupLocationId') {
                                            field.disabled = false;
                                            if (field.PickUpDropOffLocations.length == 1) {
                                                $scope.bookingRequest[field.BookingProperty] = field.PickUpDropOffLocations[0].Id;
                                                field.disabled = true;
                                            }
                                        }
                                    });
                                }
                            });
                        }
                    });
                }
            });
        };




        $scope.changePassword = function (form) {
            //$scope.FormSubmitAttempted['ChangePasswordForm'] = true;
            $scope.formSubmitAttempted = true;
            $scope.submitRemoteFormAction(form, '/api/BookingRest/ChangePassword', $scope.changePasswordUser, function () {
                $scope.changePasswordComplete = true;
            }
            );
        };

        $scope.setupBookingDefaults = function (user) {

            if ($scope.bookingRequest == null) {
                $scope.bookingRequest = new Object();
            }

            var bookingRequest = $scope.bookingRequest;

            if ($locationProvider.$$absUrl.indexOf('agentId=') !== -1) {
                return;
            }

            bookingRequest.Title = user.Title;
            bookingRequest.Firstname = user.Firstname;
            bookingRequest.Lastname = user.Lastname;
            bookingRequest.Email = user.Email;
            bookingRequest.ContactNumber = user.ContactNumber;

            bookingRequest.AddressStreet1 = user.AddressStreet1;
            bookingRequest.AddressStreet2 = user.AddressStreet2;
            bookingRequest.AddressCity = user.AddressCity;
            bookingRequest.AddressZipCode = user.AddressZipCode;
            bookingRequest.AddressState = user.AddressState;

            if (user.PreferredVehicle) {
                var vehicle = user.PreferredVehicle;
                bookingRequest.RegistrationNumber = vehicle.RegistrationNumber;
                bookingRequest.VehicleColor = vehicle.Color;
                bookingRequest.VehicleMake = vehicle.Make;
                bookingRequest.VehicleModel = vehicle.Model;
                if (vehicle.VehicleSizeDetails) {
                    bookingRequest.VehicleSizeId = vehicle.VehicleSizeDetails.VehicleSizeId;
                }
            }
        };
        $scope.loadUserBookings = function () {
            // Replace this with a call to the RestBookingController
            $scope.bookingHistory = new Object();
            // $scope.agentBookingHistory = new Object();
            var parameter = "?scope=me"

            $http({
                method: 'POST',
                data: null,
                url: '/api/BookingRest/FindBookings' + parameter,
                cache: false
            }).success(function (data, status, headers, config) {
                //alert(JSON.stringify(data));
                // if (parameter.length == 0) {
                $scope.bookingHistory.Bookings = data.Bookings;
                if ($scope.loginResult.RelatedAgents && $scope.loginResult.RelatedAgents.length > 0) {
                    $scope.loadAgentsBookings();
                }
                // }else{
                //      $scope.agentBookingHistory.Bookings = data.Bookings;
                //  }

            }).error(function (data, status, headers, config) {
                if (status && status != '401') {
                    if (data.Message) {
                        alert(status + ' ' + data.Message + '\n\n' + data.ExceptionMessage + '\n\n' + data.ExceptionType + '\n\n' + data.StackTrace);
                    }
                } else {
                    window.location = "/Login?redirectUrl=/edit-bookings.aspx";
                }
            });
        };

        $scope.loadAgentsBookings = function () {
            $scope.agentBookingHistory = new Object();
            var parameter = "?agents=all&scope=me";
            $http({
                method: 'POST',
                data: null,
                url: '/api/BookingRest/FindBookings' + parameter,
                cache: false
            }).success(function (data, status, headers, config) {
                //alert(JSON.stringify(data));
                $scope.agentBookingHistory.Bookings = data.Bookings;
            }).error(function (data, status, headers, config) {
                if (status && status != '401') {
                    if (data.Message) {
                        alert(status + ' ' + data.Message + '\n\n' + data.ExceptionMessage + '\n\n' + data.ExceptionType + '\n\n' + data.StackTrace);
                    }
                } else {
                    window.location = "/Login?redirectUrl=/edit-bookings.aspx";
                }
            });




        };




        $scope.FormSubmitAttempted = new Object();
        angularScopeRef = $scope;

        $scope.notEmpty = function (str) {
            return str && str.length > 0;
        };
        $scope.findCustomControlTemplate = function (controlId) {
            var model = ParkAltoForms.BookingForm;
            for (var i = 0; i < model.Controls.length; i++) {
                var ctrl = model.Controls[i];
                if (ctrl.Id == controlId) {
                    return ctrl.TemplateUrl;
                }
            }
        };
        $scope.normalizeId = function (strId) {
            return strId.replace(/\W/g, '');
        };
        $scope.oldBookingRequestValues = new Array();

        $scope.shouldSectionBeVisible = function (section) {
            var visible = true;
            if (section.VisibleExpression) {
                visible = eval(section.VisibleExpression);
            }

            return visible;
        };
        $scope.shouldRowBeVisible = function (row) {
            var visible = true;
            if (row.VisibleExpression) {
                visible = eval(row.VisibleExpression);
            }
            return visible;
        };

        $scope.getUiDateSettings = function (field) {
            var minDate = "0D";
            var dateFormat = 'dd-mm-yy';// window.parkAltoGlobal.formats.entryFormats.dateFormat; // would come from the website config and would be 'dd-mm-yy' for NZ and "mm-dd-yy" for USA
            var currentFormat = window.parkAltoGlobal.formats.websiteDateFormat;
            if (currentFormat.startsWith('mm-') || currentFormat.startsWith('MM-') || currentFormat.startsWith('MM/') || currentFormat.startsWith('mm/')) {
                dateFormat = 'mm-dd-yy';
            } else if (currentFormat.startsWith('yyyy-')) {
                dateFormat = 'yy-mm-dd';
            }
            if (field.BookingProperty === "ExitDate") {
                var entryDate = $scope.getField("EntryDate")
                var entryVal = $scope.bookingRequest[entryDate.BookingProperty];
                if (entryVal) {
                    minDate = entryVal;
                }
            } else if (field.BookingProperty === "EntryDate") {
                var entryDate = $scope.getField("EntryDate")
                var defaultEntryVal = eval(entryDate.DefaultValueExpression);
                if (defaultEntryVal) {
                    minDate = defaultEntryVal;
                }
            }
            return { dateFormat: dateFormat, minDate: minDate };
        }




        $scope.getField = function (id) {
            var fieldFound = null;
            $($scope.pageData.Pages).each(function (idx, page) {
                if (page.Sections) {
                    $(page.Sections).each(function (idx, section) {
                        if (section.Rows) {
                            $(section.Rows).each(function (idx, row) {
                                if (row.Fields) {
                                    $(row.Fields).each(function (idx, field) {
                                        if (field.BookingProperty == id) {
                                            fieldFound = field;
                                            return false;
                                        }
                                    });
                                }
                                if (fieldFound) { return false; }
                            });
                        }
                        if (fieldFound) { return false; }
                    });
                }
            });
            return fieldFound;
        };
        $scope.updatePushedProperties = function (field) {
            var newValue = $scope.bookingRequest[field.BookingProperty];
            if (field.PushToPropertyWhereSame) {
                var currentValue = $scope.oldBookingRequestValues[field.BookingProperty]; // $scope.bookingRequest[field.PushToPropertyWhereSame];
                var previousRelatedPropValue = $scope.oldBookingRequestValues[field.PushToPropertyWhereSame]; // $scope.bookingRequest[field.PushToPropertyWhereSame];
                if (currentValue == null || currentValue == '' || currentValue == previousRelatedPropValue) {
                    $scope.bookingRequest[field.PushToPropertyWhereSame] = newValue;
                    $scope.oldBookingRequestValues[field.PushToPropertyWhereSame] = newValue;
                }
            }
            $scope.oldBookingRequestValues[field.BookingProperty] = newValue;
        };
        $scope.fieldMatchesSelectedValue = function (field, value) {
            var currentValue = $scope.bookingRequest[field.BookingProperty];
            var matches = false;
            if (currentValue == null || currentValue == '') {
                var valueToMatch = null;
                if (field.DefaultValueExpresion) {
                    valueToMatch = eval(field.DefaultValueExpresion);
                }
                else {
                    if (field.DefaultValueProperty) {
                        valueToMatch = $scope.bookingRequest[field.DefaultValueProperty];
                    }
                }
                if (valueToMatch != null) {
                    matches = (valueToMatch == value);
                    if (matches) { // setting a match and selecting the right item does not automaticaly update the model so we must do it
                        //alert('setting value to ' + valueToMatch);
                        $scope.bookingRequest[field.BookingProperty] = valueToMatch;
                        matches = false; // Need to do this other wise it goes crazy and adds new rows to the select list
                    }
                }
            }
            else {
                matches = (currentValue == value);
            }
            return matches;
        };

        $scope.getExitDate = function () {
            return toServerDateFormat(new Date($scope.bookingRequest["ExitDate"]));
        }

        $scope.getExitTime = function () {
            return $scope.bookingRequest["ExitHour"] + ":" + $scope.bookingRequest["ExitMinute"];
        }

        $scope.shouldFieldBeVisible = function (field) {
            if (field.BookingProperty === 'HearUs' && $scope.loggedIn) {
                return false;
            }

            if (field.BookingProperty === 'PaymentPolicyId') {

                var unconfirmedReturns;

                $($scope.bundleData.PaymentPolicies).each(function (idx, pp) {
                    if (pp.UnconfirmedReturns != null) {
                        unconfirmedReturns = pp.UnconfirmedReturns;
                        return false;
                    }
                });

                if (unconfirmedReturns == undefined) {
                    return true;
                } else if (!unconfirmedReturns && $scope.bookingRequest["ExitDateNotConfirmed"] && $scope.bookingRequest["ExitTimeNotConfirmed"]) {
                    console.log("shouldFieldBeVisible PaymentPolicyId true");
                    return true;
                } else if (unconfirmedReturns && ($scope.bookingRequest["ExitDateNotConfirmed"] || $scope.bookingRequest["ExitTimeNotConfirmed"])) {
                    return true;
                } else {
                    console.log("shouldFieldBeVisible PaymentPolicyId false");
                    $scope.bookingRequest[field.BookingProperty] = $scope.getFirstOnlinePaymentPolicyId(field);
                    return false;
                }
            }

            if (field.VisibleExpression) {
                //bookingRequest["DepartureTransportRequired"]=="true"
                //alert(field.VisibleCondition);
                var output = eval(field.VisibleExpression);
                //alert(output);
                return output;
                //InVisibleValue: '',
            }
            return true; // Visible by default
        }; /*
    $scope.isFieldInvalid = function (field) {
        var inputField = $("#" + field.BookingProperty);
        //var bookingDetailForm = $scope["bookingDetailForm"];
        //alert("bookingDetailForm = " + bookingDetailForm);
        //alert(JSON.stringify($scope));
        //var bookingProp = $scope.bookingRequest[field.BookingProperty];
        var isRequired = inputField.$error.required;
        var isDirty = inputField.$dirty;
        return isRequired && isDirty;
    };*/
        // Do we require the username (only forgotton password or login)
        $scope.userNameRequired = false;

        $scope.selectNewCustomer = function () {
            $scope.newCustomerSelected = true;
            $scope.existingCustomerSelected = false;
            storeBookingState();
        };
        $scope.selectExistingCustomer = function () {
            $scope.newCustomerSelected = false;
            $scope.existingCustomerSelected = true;
            storeBookingState();
        };
        $scope.openForgottenPasswordDialog = function () {
            $scope.forgotPasswordComplete = false;
            showForgottenPasswordDialog();
        };
        $scope.submitRemoteFormAction = function (theForm, url, data, completeCallback, formDef, fieldsToValidate) {
            var isValid = true;
            if (theForm) { // Only valid if a form is passed
                isValid = $scope.validateForm(0, theForm, null, formDef, fieldsToValidate);
            }
            try {
                if (isValid) {
                    $rootScope.pageLoading = true;
                    $http({
                        method: 'POST',
                        data: data,
                        url: url, cache: false
                    }).success(function (result) {
                        $rootScope.pageLoading = false;
                        if (result.Success) {
                            completeCallback(result);
                        }
                        else {
                            if (result.Message) {
                                alertify.alert(result.Message);
                            } else {
                                //no email entered.
                                alertify.alert("Please enter your email address");
                                jQuery("#inputEmail").focus();
                            }
                        }
                    }).error(function (data, status, headers, config) {
                        $rootScope.pageLoading = false;
                        console.log(status);
                        if (status && status == '401' && window.parkAltoGlobal.defaults.websiteLoginUrl.length > 0) {
                            alertify.alert(data.Message, function () {
                                window.location = window.parkAltoGlobal.defaults.websiteLoginUrl;
                            });
                        } else {
                            alertify.alert("Error: " + data);
                        }
                    });
                }
            }
            catch (ex) {
                $rootScope.pageLoading = false;
                alert("Error: " + ex);
            }
        };
        $scope.forgotPassword = function (theForm, user) {
            $scope.userNameRequired = true;
            if (user.Username) {
                $scope.forgotPasswordCompleteFormSubmitAttempted = true;
                $scope.submitRemoteFormAction(theForm, '/api/BookingRest/ForgotPassword', user, function (result) {
                    $scope.forgotPasswordComplete = true;
                    alertify.alert(result.Message); // Show them the success message
                }, null,
                ['inputEmail']);
            } else {
                alertify.alert("Please enter your email address");
                jQuery("#inputEmail").focus();
            }
        };

        $scope.logout = function (user) {
            $http({
                method: 'POST',
                data: null,
                url: '/api/BookingRest/Logout', cache: false
            }).then(function (result) {
                $scope.loginResult = null;
                $scope.loggedIn = false;
                window.location = "/";
            });
        };

        $scope.loginFromDedicatedPage = function (user) {
            $scope.login(user, function () {
                //alert('window.location = ' + window.location);
                if (window.location && window.location.href.indexOf('/Login') != -1) {
                    var redirectUrlIndex = window.location.href.indexOf('redirectUrl=');
                    if (redirectUrlIndex != -1) {
                        var redirectUrl = window.location.href.substring(redirectUrlIndex + 12);
                        //alert('redirect to ' + redirectUrl);
                        window.location = redirectUrl;
                    } else {
                        //alert($scope.loginResult.DefaultLoginRedirectUrl);
                        //window.history.back(); // reload the previous page
                        if ($scope.loginResult.DefaultLoginRedirectUrl) {
                            window.location = $scope.loginResult.DefaultLoginRedirectUrl;
                        } else {
                            History.back();
                        }
                    }
                } else {
                    //alert('reload');
                    if ($scope.loginResult.DefaultLoginRedirectUrl) {
                        window.location = $scope.loginResult.DefaultLoginRedirectUrl;
                    } else {
                        window.location.reload(); // reload the current page so the user gets the content they expect
                    }
                }
            });
        };

        $scope.checkUserExists = function (email) {
            $scope.isForceLogin = true;
            if (email) {
                console.log("email", email)
                var user = {};
                user.Username = email
                $http({
                    method: 'POST',
                    data: user,
                    url: '/api/BookingRest/UserExists', cache: false
                }).then(function (result) {
                    console.log(result.data);
                    if (result.data.IsUserExisting) {
                        $scope.user.Username = email;
                        $scope.loginResult = new Object();
                        $scope.loginResult.Message = result.data.Message;
                        $scope.selectExistingCustomer();
                        $scope.isUserExisting = true;
                    } else {
                        $scope.isUserExisting = false;
                    }
                });
            }
        }

        $scope.login = function (user, successCallback) {
            if ($scope.user.Username == undefined || $scope.user.Username.length == 0) {
                $scope.loginResult = new Object();
                $scope.loginResult.Message = "Email cannot be blank";
                return;
            }
            if ($scope.user.Password == undefined || $scope.user.Password.length == 0) {
                $scope.loginResult = new Object();
                $scope.loginResult.Message = "Password cannot be blank";
                return;
            }
            $scope.userNameRequired = true;
            $scope.userPasswordRequired = true;
            $http({
                method: 'POST',
                data: user,
                url: '/api/BookingRest/Login', cache: false
            }).then(function (result) {
                $scope.loginResult = result.data;
                console.log($scope.loginResult);
                if (result.data.Success) {
                    $scope.loggedIn = true;
                    $scope.setupBookingDefaults($scope.loginResult);
                    jQuery("#bookingProcessLogin").hide();
                    jQuery(".pa-bookingdetail").show();
                    jQuery(".pa-contbtn").show();
                    if (successCallback) {
                        successCallback();
                    }

                    // $scope.addPromoCode();
                }/*
                else {                    
                    alert("Username or password are incorrect");
                }*/
            });
        };

        $scope.selectBundle = function (currentPage, userSelectedBundle) {
            $scope.selectedBundle = userSelectedBundle;
            storeBookingState();
            $scope.bookingRequest.BundleId = userSelectedBundle.BookingBundleId;
            recordConversionStep(2);
            $scope.moveNext(currentPage);
        };

        function recordConversionStep(step) {
            if ($scope.bookingRequest.Step >= step) {
                return;
            }
            $scope.bookingRequest.Step = step;
            $http({
                method: 'POST',
                data: getBookingRequestForPost($scope.bookingRequest),
                url: '/api/BookingRest/RecordConversionStep',
                cache: false
            }).success(function (result) {
                if (result) {
                    if (result.indexOf('"') == 0) {
                        result = result.substring(1, result.length - 1);
                    }
                    $scope.bookingRequest.BookingConversionLogId = result;
                }
            });
        }

        function clearAll() {
            clearBookingState();
            store.remove(BOOKING_REQUEST_POST_SEND_KEY);
        }

        function clearBookingState() {
            store.remove(BOOKING_REQUEST_KEY); // Make sure we don't accidently load something old from the scope now we've created a new booking
            store.remove("bundleData");
            store.remove('selectedBundle');
            store.remove('newCustomerSelected');
        }

        function storeBookingState() {
            store.set('selectedBundle', $scope.selectedBundle);
            store.set('bundleData', $scope.bundleData); // Remember this in case they referesh the page
            store.set(BOOKING_REQUEST_KEY, $scope.bookingRequest);
            store.set('newCustomerSelected', $scope.newCustomerSelected);
        }

        function getCreditCardType(accountNumber) {
            //start without knowing the credit card type
            var result = "unknown";
            //first check for MasterCard
            if (/^5[1-5]/.test(accountNumber)) {
                result = "mastercard";
            }
                //then check for Visa
            else if (/^4/.test(accountNumber)) {
                result = "visa";
            }
                //then check for AmEx
            else if (/^3[47]/.test(accountNumber)) {
                result = "amex";
            }
            return result;
        }

        $scope.calculatedTaxes = function (selectedBundle, products) {

            if (selectedBundle == null || selectedBundle.TaxInclusive) {
                return 0;
            }
            var taxes = selectedBundle.StayTaxes;
            for (var i = 0; i < products.length; i++) {
                if (products[i].Quantity > 0) {
                    taxes += products[i].Tax * products[i].Quantity;
                }
            }
            ///need to apply taxes after gettting the total for promo code discount
            //currentTax= tax*(total-discount)/total;
            return taxes;
        }; /*
    * Calculates the total thats due to be paid including taxes
    */
        $scope.calculatedTotal = function (selectedBundle, products) {

            if (selectedBundle == null) {
                return 0;
            }

            var stayPrice = selectedBundle.StayPrice;
            if ($scope.parkingDiscount > 0 && $scope.parkingDiscount < 100) {
                stayPrice = selectedBundle.StayPrice * ((100 - $scope.parkingDiscount) / 100);
            }

            var parkingPrice = stayPrice + selectedBundle.AdminFee;
            for (var i = 0; i < products.length; i++) {
                if (products[i].Quantity > 0) {
                    parkingPrice += products[i].Price * products[i].Quantity;
                }
            }
            var discountPromoTotal = 0;
            if ($scope.bookingRequest.PromoCodeListDiscountTotal) {
                discountPromoTotal = $scope.bookingRequest.PromoCodeListDiscountTotal;
            }
            //apploy promo code discount
            parkingPrice = parkingPrice - discountPromoTotal;

            return parkingPrice;
        };
        $scope.calculatedSurchargeTotal = function (selectedBundle, totalBeforeSurcharge) {

            if (selectedBundle == null) {
                return 0;
            }
            if (selectedBundle.TransactionFeePercent) {
                if (selectedBundle.TransactionFeePercent > 0) {
                    return totalBeforeSurcharge * (selectedBundle.TransactionFeePercent / 100);
                }
            }
            return 0;
        };

        $scope.validateForm = function (currentPage, theForm, passIfNoUiElementFound, formDef, fieldsToValidate) {
            //alert(JSON.stringify(theForm));
            var isValid = theForm.$valid;
            //    alert('validateForm: ' + isValid);

            if (formDef != null) {
                $scope.FormSubmitAttempted[formDef.FormKey] = true;
            }
            else {
                $scope.formSubmitAttempted = true; // catch all form submitted
            }

            var formFieldElements = jQuery("input.ng-invalid,select.ng-invalid"); //.filter(":visible");

            if (!isValid) {
                //alert('invlai');


                //var ele = $("input.ng-invalid,select.ng-invalid").first();
                if (passIfNoUiElementFound && formFieldElements.length == 0) {
                    return true; // If no UI element to highlight the error then pass it
                }
                else {
                    var ele = formFieldElements.first();
                    if (ele != null) {


                        var offset = ele.offset();
                        var fromTop = 100;
                        if (offset) {
                            fromTop = offset.top - 100;
                        }

                        jQuery('html, body').animate({
                            scrollTop: fromTop
                        }, 500);
                        ele./*removeClass("ng-invalid ng-invalid-required ng-pristine").*/addClass("pa-error");
                        ele.focus(); // Set focus on the first field to fail validation


                    }
                }

                //alert(JSON.stringify($scope.bookingRequest));
                /*
                for (var cIdx = 0; cIdx < $scope.bookingRequest; cIdx++) {
                    var ctrl = $scope.bookingRequest[cIdx];
                    alert(ctrl);
                    ctrl.$setValidity('required', true);
                }
    
                for (var cIdx = 0; cIdx < theForm.controls; cIdx++) {
                    var ctrl = theForm.controls[cIdx];
                    alert(ctrl);
                }
                */

                if (fieldsToValidate) { // If the error isn't with one of the fields in this list then consider the form valid
                    var isValid = true;
                    formFieldElements.each(function (idx, ff) {
                        for (var i = 0; i < fieldsToValidate; i++) {
                            if (ff.id == fieldsToValidate[i]) {
                                isValid = false;
                                return;
                            }
                        }
                        if (!isValid) {
                            return;
                        }
                    });
                    return isValid;
                }

                return false;
            }
            return true; // all ok
        };

        // Gets the first payment policy id that is for online payment
        $scope.getFirstOnlinePaymentPolicyId = function (paymentPolicyField) {
            var output = null;
            if (paymentPolicyField != null) {
                var paymentPolicies = paymentPolicyField.PaymentPolicies;
                $(paymentPolicyField.PaymentPolicies).each(function (idx, pp) {
                    if (pp.OnlinePaymentRequired) {
                        output = pp.Id;
                        return;
                    }
                });
            }
            return output;
        };

        $scope.canSelectCreditCard = function (paymentPolicyIdFieldId) {
            if (!$scope.canEnterCreditCard(paymentPolicyIdFieldId)) {
                return false;
            }
            if ($scope.loginResult == null ||
                $scope.loginResult.Success == false ||
                $scope.loginResult.CreditCards == null ||
                $scope.loginResult.CreditCards.length == 0) {
                return false;
            } else {
                for (var i = 0; i < $scope.loginResult.CreditCards.length; i++) {
                    if ($scope.loginResult.CreditCards[i].CardExpiryDate == null) {
                        //alert(JSON.stringify($scope.loginResult.CreditCards[i].CreditCardTypeDetail));
                        $scope.loginResult.CreditCards[i].CardExpiryDate = Date.parse($scope.loginResult.CreditCards[i].CardExpiry);
                    }
                }
            }
            return true;
        };

        $scope.isNewCard = function (paymentPolicyIdFieldId) {
            if (!$scope.canEnterCreditCard(paymentPolicyIdFieldId)) {
                return false;
            }
            if ($scope.bookingRequest.CreditCardId == 'null') {
                return true;
            }
            return false;
        };

        $scope.isPaymentPolicyShown = function (policy) {
            if (policy.OnAccount == null) {
                //alert('null onaccount');
                return true;
            } else if (!Global_isLoggedIn) {
                return false;
            } else if (Global_LoggedInUser.OnAccount == policy.OnAccount) {
                return true;
            } else {
                return false;
            }
        };

        $scope.hasMultiplePaymentPolicies = function (paymentPolicyIdFieldId) {
            var visiablePolicyCount = 0;
            var policies = $scope.getField(paymentPolicyIdFieldId).PaymentPolicies;
            for (var i = 0 ; i < policies.length ; i++) {
                if ($scope.isPaymentPolicyShown(policies[i])) {
                    visiablePolicyCount++;
                    if (visiablePolicyCount > 1) {
                        break;
                    }
                }
            }
            return visiablePolicyCount > 1;
        };

        $scope.isOnAccountCheckPassed = function (paymentPolicyIdFieldId) {
            var paymentPolicy = $scope.getPaymentPolicy(paymentPolicyIdFieldId);
            if (paymentPolicy != null) {
                alert(paymentPolicy);
                if (paymentPolicy.OnAccount == null) {
                    return true;
                }
            }
            return false;
        };

        $scope.getPaymentPolicy = function (paymentPolicyIdFieldId) {
            var paymentPolicyField = $scope.getField(paymentPolicyIdFieldId);
            if (paymentPolicyField != null) {
                var paymentPolicy;
                if (paymentPolicyField.PaymentPolicies.length == 1) {
                    paymentPolicy = paymentPolicyField.PaymentPolicies[0]; // Default to the first one
                } else {
                    var paymentPolicyId = $scope.bookingRequest[paymentPolicyField.BookingProperty];
                    paymentPolicy = $scope.getPaymentPolicyById(paymentPolicyField, paymentPolicyId);

                    if (!paymentPolicy) {
                        paymentPolicy = paymentPolicyField.PaymentPolicies[1];
                    }

                }
                return paymentPolicy;
            }
            return null;
        };

        $scope.canEnterCreditCard = function (paymentPolicyIdFieldId) {
            var paymentPolicy = $scope.getPaymentPolicy(paymentPolicyIdFieldId);
            if (paymentPolicy != null) {
                if (paymentPolicy.OnlinePaymentRequired) {
                    return true;
                }
            }
            return false;
        };

        $scope.getPaymentPolicyById = function (paymentPolicyField, paymentPolicyId) {
            var output = null;
            $(paymentPolicyField.PaymentPolicies).each(function (idx, pp) {
                if (pp.Id == paymentPolicyId) {
                    output = pp;
                    return;
                }
            });
            return output;
        };

        $scope.placeBookingToServer = function (currentPage, theForm) {
            try {
                var isValid = $scope.validateForm(currentPage, theForm);
                $scope.bookingRequest.TotalPrice = $scope.shoppingBasket.totalPayable.toFixed(2); // Send this to the server so it can validate that what was quoted matches what we calculate to take from the card

                if ($locationProvider.$$absUrl.indexOf('agentId=') !== -1) {
                    var str = $locationProvider.$$absUrl;
                    var start = $locationProvider.$$absUrl.indexOf('agentId=') + 8;
                    var end = start + 36;
                    $scope.bookingRequest.AgentId = str.substring(start, end);
                }

                if ($locationProvider.$$absUrl.indexOf('agentId=') !== -1 && $locationProvider.$$absUrl.indexOf('customerId=') == -1) {
                    $scope.bookingRequest.IsCreateNewCustomer = true;
                } else {
                    $scope.bookingRequest.IsCreateNewCustomer = false;
                }

                if (isValid) {

                    if (!$scope.bookingRequest.DepartureTransportRequired) {
                        $scope.bookingRequest.DropOffLocationId = "";
                    }

                    if (!$scope.bookingRequest.ReturnTransportRequired) {
                        $scope.bookingRequest.PickupLocationId = "";
                    }

                    $rootScope.pageLoading = true;
                    $http({
                        method: 'POST',
                        data: getBookingRequestForPost($scope.bookingRequest),
                        url: '/api/BookingRest/PlaceBooking',
                        cache: false
                    }).success(function (result) {
                        $rootScope.pageLoading = false;
                        var data = result;
                        $scope.bookingComplete = data.Success;
                        $scope.bookingErrorMessage = '';
                        $scope.booking = data;
                        $scope.facility = data.Facility;
                        $scope.bookingRequest.BookingConversionLogId = data.BookingConversionLogId;

                        // Send Google Analytics details of the Booking if the Website.GACode contains a Google UA ID.
                        if (window.parkAltoGlobal.defaults.websiteGACode && window.parkAltoGlobal.defaults.websiteGACode.length > 0 && $scope.booking.BookingNumber) {
                            ///function (transactionId, affiliation, total, tax, shipping, city, state, country, currency) ????currency
                            var currencyCode = $scope.bundleData.Bundles[0].PriceBook.Currency.Code;
							/**
                            if (Analytics.configuration.accounts === undefined) {
                                app.config(function (AnalyticsProvider) {
                                    // Set a single account
                                    //AnalyticsProvider.setAccount(window.parkAltoGlobal.defaults.websiteGACode);
                                    AnalyticsProvider.setAccount(window.parkAltoGlobal.defaults.websiteGACode);
                                    AnalyticsProvider.useECommerce(true, false);
                                });
                            }**/
							
							Analytics.addTrans($scope.booking.BookingNumber, '', '' + $scope.shoppingBasket.totalPayable.toFixed(2), '' + $scope.shoppingBasket.totalTax.toFixed(2), '0', '', '', '', currencyCode);
                                    //function (transactionId, sku, name, category, price, quantity)
                                    Analytics.addItem($scope.booking.BookingNumber, 'parking', 'parking from' + $scope.bookingRequest.EntryDate + " to " + $scope.bookingRequest.ExitDate, 'parking', '' + ($scope.selectedBundle.StayPrice + $scope.selectedBundle.AdminFee).toFixed(2), '1');
                                    //console.log("parking item ", $scope.booking.BookingNumber, 'parking from' + $scope.bookingRequest.EntryDate + " to " + $scope.bookingRequest.ExitDate, ($scope.selectedBundle.StayPrice + $scope.selectedBundle.AdminFee).toFixed(2));
                                    if ($scope.bundleData && $scope.bundleData.Products) {
                                        for (var i = 0; i < $scope.bundleData.Products.length; i++) {
                                            console.log("item", $scope.booking.BookingNumber, $scope.bundleData.Products[i].ProductId, $scope.bundleData.Products[i].ProductTitle + ' ' + $scope.bundleData.Products[i].Title, ($scope.bundleData.Products[i].Price * $scope.bundleData.Products[i].Quantity).toFixed(2))
                                            Analytics.addItem($scope.booking.BookingNumber, '' + $scope.bundleData.Products[i].ProductId, $scope.bundleData.Products[i].ProductTitle + ' ' + $scope.bundleData.Products[i].Title, '', '' + ($scope.bundleData.Products[i].Price * $scope.bundleData.Products[i].Quantity).toFixed(2), '' + $scope.bundleData.Products[i].Quantity);
                                        }
                                    }
                                    // Complete transaction
                                    Analytics.trackTrans();
                                    //Send to google
                                    Analytics.pageView();
                                    // Clear transaction
                                    Analytics.clearTrans();

                        }

                        if (data.RedirectUrl && data.RedirectUrl != '') {
                            window.location = data.RedirectUrl; // Send the user to the specified page (likely a hosted payment page)
                        }
                        else {
                            if (data.Success) {
                                $scope.moveToNextPage(currentPage, true);
                                if (data.Facility) {
                                    $scope.carparkFacilityMarkers = [
                                        {
                                            latitude: data.Facility.Latitude, // initial map center latitude
                                            longitude: data.Facility.Longitude // initial map center longitude
                                        }];

                                    $scope.carparkFacilityLatLng = {
                                        latitude: data.Facility.Latitude, // initial map center latitude
                                        longitude: data.Facility.Longitude // initial map center longitude
                                    };
                                    //Google Map Angular-UI Reset
                                    $scope.resetMap(data.Facility.StreetAddress.Latitude, data.Facility.StreetAddress.Longitude);
                                }
                            }
                            else { // Some kind of error
                                if (data.ErrorMessage) {
                                    alertify.alert(data.ErrorMessage);
                                    if (data.TotalTax > 0) { $scope.shoppingBasket.totalTax = data.TotalTax }
                                    if (data.TotalPrice > 0) {//price change error
                                        $scope.shoppingBasket.totalPayable = data.TotalPrice
                                        $scope.selectedBundle.StayPrice = data.TotalPrice;
                                        $scope.selectedBundle.AdminFee = 0;
                                    }
                                }
                                else {
                                    alertify.alert('An error occured');
                                }
                                $rootScope.pageLoading = false;
                            }
                        }

                    }).error(function (ex) {
                        $scope.bookingErrorMessage = 'Error: ' + 'Sorry there was a problem placing your booking';//ex.ExceptionMessage;
                        $rootScope.pageLoading = false;
                        //if (data) $scope.bookingRequest.BookingConversionLogId = data.BookingConversionLogId;
                        alertify.alert($scope.bookingErrorMessage);
                    });
                }
            }
            catch (ex) {
                alertify.alert(ex.toString());
                $rootScope.pageLoading = false;
            }
        }

        $scope.placeBooking = function (currentPage, theForm) {

            if ($scope.isForceLogin && !$scope.loggedIn && $scope.isUserExisting) {
                $scope.checkUserExists($scope.bookingRequest["Email"]);
                return;
            }

            if ($scope.bookingRequest.PromoCode && $scope.bookingRequest.PromoCode.length > 0) {
                //entered promo code, but not applied yet.
                $rootScope.pageLoading = true;
                $http({
                    method: 'POST',
                    data: getBookingRequestForPost($scope.bookingRequest),
                    url: '/api/BookingRest/ValidateRequest', cache: false
                }).then(function (result) {

                    $scope.bookingResult = result.data;
                    if (!$scope.bookingResult.BookingAllowed) { // This booking is not allowed
                        console.log("$scope.bookingResult.BookingNotAllowedTitle:", $scope.bookingResult);
                        //showAlert($scope.bookingResult.BookingNotAllowedMessage, $scope.bookingResult.BookingNotAllowedTitle);
                        alertify.alert($scope.bookingResult.BookingNotAllowedMessage);
                        $rootScope.pageLoading = false;
                        $scope.bookingRequest.PromoCode = "";
                        return;
                    }
                    //update shopping basket with the
                    $scope.parkingDiscount = $scope.bookingResult.parkingDiscount;
                    $scope.bookingRequest.PromoCodeList = $scope.bookingResult.PromoCodeList;
                    $scope.bookingRequest.PromoCodeListDiscountTotal = $scope.bookingResult.PromoCodeListDiscountTotal;
                    $scope.calculateShoppingBasket();
                    $scope.bookingRequest.PromoCode = "";
                    $scope.placeBookingToServer(currentPage, theForm);
                });
            } else {
                $scope.placeBookingToServer(currentPage, theForm);
            }
        };
        $scope.removeProduct = function (productVar) {
            productVar.Quantity = 0;

            var array = new Array();
            for (catIdx = 0; catIdx < $scope.bookingRequest.Products.length; catIdx++) {
                var cat = $scope.bookingRequest.Products[catIdx];

                if (cat.ProductVariantId === productVar.ProductVariantId) {

                } else {
                    array.concat(cat);
                }

            }

            $scope.bookingRequest.Products = array;

            $scope.calculateShoppingBasket();
        };
        $scope.editProduct = function (productVar) {
            //class="pa-vas-page" pa-page-index="{{$index}}"
            //Find which page we edit products on
            var vasPage = jQuery(".pa-vas-page").first();
            var pageIndex = vasPage.attr("pa-page-index");
            if (!isNaN(pageIndex)) {
                $scope.moveTo(currentWizardPage, pageIndex);
            }

            var id = "#productQuantity" + productVar.SafeProductVariantId;
            jQuery(id).focus();

            jQuery('html, body').animate({
                scrollTop: jQuery(id).offset().top
            }, 2000);
        };
        $scope.updateProductQuantity = function (productVar, qty) {
            productVar.Quantity = qty;
            $scope.calculateShoppingBasket();
        };
        $scope.isRowInvalid = function (row) {
            if (!row.RowLevelValidation) { // Its valid if we are not doing row level validation
                return false;
            }
            return $scope.isAnyFieldInRowInvalid(row);
        };


        $scope.isAnyFieldInRowInvalid = function (row) {
            for (i = 0; i < row.Fields.length; i++) {
                var field = row.Fields[i];
                var isInvalid = $scope.isFieldInvalidAndDirty(field);
                if (isInvalid) {
                    return true;
                }
            }
            return false;
        };

        $scope.isFieldValid = function (field) {
            if (!isInValidField(field)) {
                //   ng-scope ng-pristine ng-invalid ng-invalid-required
                //Check the parent <ng-form> tag as until
                // the user types into the field Angular does not update the actual input box
                isInvalid = input.parent().hasClass("ng-invalid");
            }
            return !isInvalid;
        };


        $scope.getErrorMessage = function (form, field) {
            var errMsg = ""
            var isError = false;
            if (form.input.$error.required) {
                errMsg = field.RequiredErrorMessage;
                isError = true;
            }
            else if (form.input.$error.email) {
                errMsg = field.EmailErrorMessage;
                isError = true;
            }
            else if (form.input.$error.creditCardNumber) {
                errMsg = field.InvalidErrorMessage;
                isError = true;
            }
            if (isError) {
                if (!errMsg || errMsg == '') {
                    errMsg = "Error";
                }
            }
            return errMsg;
        };
        $scope.isFieldInvalidAndDirty = function (field, formDefinition) {
            if (field.ChildrenFieldIds != null && field.ChildrenFieldIds.length > 0) {
                for (i = 0; i < field.ChildrenFieldIds.length; i++) {
                    var input = jQuery("#" + field.ChildrenFieldIds[i].Id);
                    if ($scope.formSubmitAttempted ||
                        (formDefinition && $scope.FormSubmitAttempted[formDefinition.FormKey]) ||
                        input.hasClass("ng-dirty")) {
                        if (input.hasClass("ng-invalid")) {
                            return true;
                        }
                    }
                }
            } else {
                var input = jQuery("#" + field.BookingProperty);
                if ($scope.formSubmitAttempted || input.hasClass("ng-dirty")) {
                    var isInvalid = input.hasClass("ng-invalid");
                    return isInvalid;
                }
            }
            return false;
        };

        $scope.isFieldValidAndDirty = function (field) {
            var input = jQuery("#" + field.BookingProperty);
            var isInvalid = input.hasClass("ng-invalid");
            if (input.hasClass("ng-dirty") && !isInvalid) { // Someone has typed in it and its valid            
                return true; // return true to make it green
            }
            // Someone has not typed in it leave it black
            return false;
        };

        $scope.anyDirtyAndInvalid = function (form) {
            for (var x in form) {
                var prop = form[x];
                if (prop && prop.$dirty && prop.$invalid) {
                    console.log(prop);
                    return true;
                }
            }
            return false;
        };

        $scope.removePromoCode = function (idx) {
            $rootScope.pageLoading = true;
            //$scope.bookingRequest
            //var to_delete = $scope.bookingRequest.PromoCodeList[idx];
            $scope.bookingRequest.PromoCodeList.splice(idx, 1);
            $http({
                method: 'POST',
                data: getBookingRequestForPost($scope.bookingRequest),
                url: '/api/BookingRest/ValidateRequest', cache: false
            }).then(function (result) {
                try {
                    $scope.bookingResult = result.data;
                    if (!$scope.bookingResult.BookingAllowed) { // This booking is not allowed
                        //showAlert($scope.bookingResult.BookingNotAllowedMessage);
                        alertify.alert($scope.bookingResult.BookingNotAllowedMessage);
                        return;
                    }
                    //update shopping basket with the
                    $scope.parkingDiscount = $scope.bookingResult.ParkingDiscount;
                    $scope.bookingRequest.PromoCodeList = $scope.bookingResult.PromoCodeList;
                    $scope.bookingRequest.PromoCodeListDiscountTotal = $scope.bookingResult.PromoCodeListDiscountTotal;
                    $scope.calculateShoppingBasket();

                } finally {
                    $rootScope.pageLoading = false;
                }
            });

        }

        $scope.addPromoCode = function () {
            if (!$scope.bookingRequest.PromoCode || $scope.bookingRequest.PromoCode == "") {
                //showAlert("Please enter a promo code");
                alertify.alert("Please enter a promo code");
            } else {
                $rootScope.pageLoading = true;
                $http({
                    method: 'POST',
                    data: getBookingRequestForPost($scope.bookingRequest),
                    url: '/api/BookingRest/ValidateRequest', cache: false
                }).then(function (result) {
                    try {
                        $scope.bookingResult = result.data;
                        if (!$scope.bookingResult.BookingAllowed) { // This booking is not allowed
                            //showAlert($scope.bookingResult.BookingNotAllowedMessage);
                            alertify.alert($scope.bookingResult.BookingNotAllowedMessage);
                            return;
                        }
                        //update shopping basket with the
                        $scope.parkingDiscount = $scope.bookingResult.ParkingDiscount;
                        $scope.bookingRequest.PromoCodeList = $scope.bookingResult.PromoCodeList;
                        $scope.bookingRequest.PromoCodeListDiscountTotal = $scope.bookingResult.PromoCodeListDiscountTotal;
                        $scope.calculateShoppingBasket();

                    } finally {
                        $scope.bookingRequest.PromoCode = "";
                        $rootScope.pageLoading = false;
                    }
                });
            }
        };
        $scope.getInvalidRowMessage = function (row) {
            for (i = 0; i < row.Fields.length; i++) {
                var field = row.Fields[i];
                //ng-invalid ng-invalid-required
                var invalidField = getFirstInvalidField(field);
                if (invalidField != null) {
                    if (invalidField.hasClass("ng-invalid-required")) {
                        return field.RequiredErrorMessage;
                    }
                    else if (invalidField.hasClass("ng-invalid-credit-card-number")) {
                        return field.InvalidErrorMessage;
                    }
                    else if (invalidField.hasClass("ng-invalid-email")) {
                        return field.EmailErrorMessage;
                    }
                }
            }
            return "";
        };

        $scope.yesNoOutput = function (boolValue) {
            if (boolValue)
                return 'Yes';
            else
                return 'No';
        };
        $scope.updateProductQuantityByVariantTitle = function (product, prodByTitle, qty) {
            var variant = $scope.getVariantByTitle(product, prodByTitle.title);
            if (variant != null) {
                variant.Quantity = qty;
            }
        };
        // Unselects all variants
        $scope.clearAllProductSelections = function () {
            for (i = 0; i < $scope.bundleData.ProductCategories.length; i++) {
                var prodCat = $scope.bundleData.ProductCategories[i];
                for (prodIdx = 0; prodIdx < prodCat.Products.length; prodIdx++) {
                    var catProduct = prodCat.Products[prodIdx];
                    for (clearProdIdx = 0; clearProdIdx < prodCat.Products.length; clearProdIdx++) {
                        var prodToClear = prodCat.Products[clearProdIdx];
                        for (clearProdVarIdx = 0; clearProdVarIdx < prodToClear.Variants.length; clearProdVarIdx++) {
                            var prodVarToClear = prodToClear.Variants[clearProdVarIdx];
                            prodVarToClear.Quantity = 0;
                        }
                    }
                }
            }
            for (i = 0; i < $scope.bundleData.Products.length; i++) {
                var prodToClear = $scope.bundleData.Products[prodIdx];
                prodToClear.Quantity = 0;
            }
            $scope.bookingRequest.Products = new Array();
        };
        // Unselects any other variants that are within the same category as this product.
        $scope.clearProductSelectionInProductCategory = function (product) {

            for (i = 0; i < $scope.bundleData.ProductCategories.length; i++) {
                var prodCat = $scope.bundleData.ProductCategories[i];
                for (prodIdx = 0; prodIdx < prodCat.Products.length; prodIdx++) {
                    var catProduct = prodCat.Products[prodIdx];
                    if (product == null || product == catProduct) {
                        //alert("Found the prod");
                        for (clearProdIdx = 0; clearProdIdx < prodCat.Products.length; clearProdIdx++) {
                            var prodToClear = prodCat.Products[clearProdIdx];
                            //alert('prodToClear = ' + prodToClear);
                            //if (prodToClear != product) {
                            //alert('set qty to 0');
                            for (clearProdVarIdx = 0; clearProdVarIdx < prodToClear.Variants.length; clearProdVarIdx++) {
                                var prodVarToClear = prodToClear.Variants[clearProdVarIdx];
                                prodVarToClear.Quantity = 0;
                            }
                            //}
                        }
                        return;
                    }
                }
            }
        };
        $scope.updateProductQuantityByVariantTitleOnlyOneOptionFromCategoryAllowed = function (product, prodByTitle, qty) {
            //alert('HERE ' + (product == null ? "null" : product.Title) + ', ' + prodByTitle.title + ', ' + qty);
            $scope.clearProductSelectionInProductCategory(product);
            var variant = $scope.getVariantByTitle(product, prodByTitle.title);
            if (variant != null) {
                variant.Quantity = qty;
            }
            $scope.calculateShoppingBasket();
        };

        $scope.getVariantByTitle = function (product, title) {
            //            for (i = 0; i < $scope.bundleData.Products.length; i++) {
            //                var variant = $scope.bundleData.Products[i];
            //                if (product.ProductId == variant.ProductId && variant.Title == title) {
            //                    return variant;
            //                }
            //            }

            for (i = 0; i < $scope.bundleData.ProductCategories.length; i++) {
                var prodCat = $scope.bundleData.ProductCategories[i];
                for (prodIdx = 0; prodIdx < prodCat.Products.length; prodIdx++) {
                    var catProduct = prodCat.Products[prodIdx];
                    if (product == catProduct) {
                        //alert("Found the prod, so use this cat");
                        for (clearProdIdx = 0; clearProdIdx < prodCat.Products.length; clearProdIdx++) {
                            var prodToClear = prodCat.Products[clearProdIdx];
                            if (prodToClear == product) {
                                for (clearProdVarIdx = 0; clearProdVarIdx < prodToClear.Variants.length; clearProdVarIdx++) {
                                    var variant = prodToClear.Variants[clearProdVarIdx];
                                    if (product.ProductId == variant.ProductId && variant.Title == title) {
                                        return variant;
                                    }
                                }
                            }
                        }
                    }
                }
            }
        };

        $scope.getAllProductVariants = function () {
            var variants = new Array();
            for (i = 0; i < $scope.bundleData.ProductCategories.length; i++) {
                var cat = $scope.bundleData.ProductCategories[i];
                for (prodIdx = 0; prodIdx < cat.Products.length; prodIdx++) {
                    var catProduct = cat.Products[prodIdx];
                    for (prodVarIdx = 0; prodVarIdx < catProduct.Variants.length; prodVarIdx++) {
                        var variant = catProduct.Variants[prodVarIdx];
                        variants[variants.length] = variant;
                    }
                }
            }
            return variants;
        };

        $scope.moveTo = function (currentPage, newPage) {
            //alert('From page ' + currentPage + ' to page ' + newPage);
            if (currentPage > newPage) {
                var moves = currentPage - newPage;
                //History.go(-moves);
                window.history.back(moves + 1);
            }
            else {
                var moves = newPage - currentPage;
                for (i = 0; i < moves; i++) {
                    $scope.storeCurrentPageState(newPage);
                    //$scope.moveNext(currentPage+0, theForm);
                }
            }
            //$scope.displayCurrentPage();
        };

        /// Only allows moving backwards not forwards
        $scope.moveToIfAllowed = function (currentPage, newPage) {
            if (newPage < currentPage) {
                $scope.moveTo(currentPage, newPage);
            }
        };

        $scope.checkAndGoToPage = function (currentPage, newPage) {
            if ($scope.checkPageAccessible(currentPage, newPage)) {
                window.location = GetFullUrl("/bookings#?currentPage=" + newPage);
            }
        };

        $scope.checkPageAccessible = function (currentPage, newPage) {
            if ($scope.pageData &&
                $scope.pageData.Pages &&
                $scope.pageData.Pages.length > newPage &&
                $scope.pageData.Pages[newPage].CanMoveBackTo == true &&
                newPage < currentPage) {
                return true;
            }
            return false;
        };

        $scope.goToPage = function (currentPage, newPage) {
            if (newPage < currentPage) {
                window.location = GetFullUrl("/bookings#?currentPage=" + newPage);
            }
        };

        $scope.movePrevious = function (currentPage) {
            $scope.userNameRequired = false;
            $scope.userPasswordRequired = false;

            //$scope.currentWizardPage = currentPage-1;
            directionIsForward = false;
            window.history.back();
            /*
            var states = History.savedStates;
            var prevUrlIndex = states.length - 2;
            var prevUrl = states[prevUrlIndex].hash;
            alert(prevUrl);*/
            //History.back();

            //url = State.url,

        };

        $scope.fieldRequired = function (field) {
            if (field.Required) {
                //bookingRequest["DepartureTransportRequired"]=="true"
                //alert(field.VisibleCondition);
                var output = eval(field.Required);
                //alert(output);
                return output;
                //InVisibleValue: '',
            }
            return false; // Visible by default
        };

        $scope.isInvalidInput = function (row, form, formDefinition) {
            if (row.Fields[0].BookingProperty === 'CardNumber') {
                row.RowLevelValidation = undefined;
            }

            var _value = (row && !(row.RowLevelValidation) && form.input && (
                form.input.$error.required
                || form.input.$error.email || form.input.$error.creditCardNumber
                ) && (

                (
                $scope.formSubmitAttempted ||
                (formDefinition != null && $scope.FormSubmitAttempted[formDefinition.FormKey])
                )

                || form.input.$dirty));

            return _value;
        };

        $scope.quantityGreaterThan0 = function (item) {
            if (item.Quantity > 0) {
                return true;
            }
            return false;
        };

        $scope.productQuantityChanged = function (productVar) {
            //alert("productQuantityChanged "  + JSON.stringify(productVar));
            $scope.calculateShoppingBasket();
        };
        $scope.moveToNextPage = function (currentPage, isFormValid) {
            setupHistoryIfMissing();

            //alert('moveToNextPage from page ' + currentPage + ', ' + isFormValid);
            //console.log("moveToNextPage", $scope.bookingRequest.BundleId);
            //console.log("currentPage", currentPage);
            directionIsForward = true;
            // Skip the next page if it should not be visible
            if ($scope.pageData.Pages.length > currentPage + 1) {
                var page = $scope.pageData.Pages[currentPage + 1];
                if (page.VisibleExpression) {

                    var pageVisible = eval(page.VisibleExpression);
                    if (!pageVisible) { // Then skip this page.
                        currentPage++;
                    }
                }
                currentPage++;
                $scope.storeCurrentPageState(currentPage);
                $rootScope.pageLoading = false;

                return true;
            }
            return false; // no movement.
        };

        /// Checks if there are booking product options the user can pick from
        $scope.hasBookingProductOptions = function () {
            if ($scope.bundleData.ProductCategories.length > 0) {
                for (var pCatIdx = 0; pCatIdx < $scope.bundleData.ProductCategories.length; pCatIdx++) {
                    var prodCat = $scope.bundleData.ProductCategories[pCatIdx];
                    for (var pIdx = 0; pIdx < prodCat.Products.length; pIdx++) {
                        var prod = prodCat.Products[pIdx];
                        for (var pvIdx = 0; pIdx < prod.Variants.length; pvIdx++) {
                            var prodVar = prod.Variants[pvIdx];
                            if (prodVar.MinQuantity < prodVar.MaxQuantity) {
                                return true; // Allow it as the max is more than the min.
                            }
                        }
                    }
                }
            }
            return false; // No options the user can choose
        };
        //Hide How Did You Hear About Us for Existing Customers
        $scope.isNewCustomer = function () {
            if ($scope.loggedIn && $scope.loginResult.Success) {
                return false;
            }
            return true;
        };

        $scope.range = function (min, max) {
            var input = [];
            for (var i = min; i <= max; i++) input.push(i);
            return input;
        };
        $scope.storeCurrentPageState = function (currentPage) {
            //store.set(CURRENT_PAGE_STORE_KEY, currentPage);
            //alert('pushing state ==' + currentPage);
            //window.History.pushState({ currentPage: currentPage }, '#currentPage' + currentPage, '#currentPage=' + currentPage);
            $locationProvider.search({ 'currentPage': currentPage });
        };
        // Enters the booking process by redirecting to the booking page
        $scope.enterBookingProcess = function (theForm) {
            // Do basic client side validation
            if (!$scope.validateForm(0, theForm)) {
                return;
            }
            // Clear out any product variants left over from the previous booking - but doesn't stop old prod var selections from appearing!
            $scope.clearAllProductSelections();

            // Store this for when the page reloads
            /*
            if (store.enabled) {
                storeBookingState();
                store.set('startPage', 2); // Go directly to the 2nd page
            }
            else {
                alert('No local storage support.  Please upgrade your browser');
            }*/
            //alert('redirect to bookings');

            $cookieStore.put("booking", JSON.stringify($scope.bookingRequest));
            window.location = GetFullUrl("/bookings#?currentPage=0");
            //moveNext(0, bookingStep1Form)
        };

        $scope.newBooking = function (theForm) {
            $scope.bookingComplete = false; // if we are on the first page the booking can't be complete
            clearBookingState();
            $scope.moveNext(0, theForm);
        };

        $scope.calculateShoppingBasket = function () {
            var shoppingBasket = {};

            $scope.shoppingBasket = shoppingBasket;
            shoppingBasket.totalTax = $scope.calculatedTaxes($scope.selectedBundle, $scope.getAllProductVariants());
            shoppingBasket.hasTax = shoppingBasket.totalTax > 0;
            shoppingBasket.subTotal = $scope.calculatedTotal($scope.selectedBundle, $scope.getAllProductVariants());
            shoppingBasket.total = shoppingBasket.subTotal + shoppingBasket.totalTax;
            //surcharge fees
            shoppingBasket.surchargeTotal = $scope.calculatedSurchargeTotal($scope.selectedBundle, shoppingBasket.total);
            shoppingBasket.total += shoppingBasket.surchargeTotal;
            shoppingBasket.totalPayable = shoppingBasket.total;
        }

        $scope.moveNext = function (currentPage, theForm) {
            $scope.userNameRequired = false;
            $scope.userPasswordRequired = false;
            $scope.calculateShoppingBasket();
            setupHistoryIfMissing();
            // Store it the local storage so if they press F5 they get their page back
            storeBookingState();

            if (arguments.length >= 2) {
                if (!$scope.validateForm(currentPage, theForm)) {
                    return false;
                }
            }

            try {
                $rootScope.pageLoading = true;
                $scope.bookingRequest.Products = new Array();
                /*for (i = 0; i < $scope.bundleData.ProductCategories.length; i++) {
                    var prodCat = $scope.bundleData.ProductCategories[i];
                    for (prodIdx = 0; prodIdx < prodCat.Products.length; prodIdx++) {
                        var product = prodCat.Products[prodIdx];
                        if (product.Quantity > 0) {
                            $scope.bookingRequest.Products.push(product); // Tell the server we want this product
                        }
                    }
                }*/

                if ($scope.bundleData.ProductCategories) {
                    $scope.bookingRequest.Products = new Array();
                    for (catIdx = 0; catIdx < $scope.bundleData.ProductCategories.length; catIdx++) {
                        var cat = $scope.bundleData.ProductCategories[catIdx];
                        for (prodIdx = 0; prodIdx < cat.Products.length; prodIdx++) {
                            var catProduct = cat.Products[prodIdx];
                            for (prodVarIdx = 0; prodVarIdx < catProduct.Variants.length; prodVarIdx++) {
                                var variant = catProduct.Variants[prodVarIdx];
                                if (variant.Quantity > 0) {
                                    $scope.bookingRequest.Products.push(variant); // Tell the server we want this product
                                }
                            }
                        }
                    }
                }

                // Stops a null object being sent and angular deciding to set the content type to text/plain which screws the server thats expecting the JSON content type
                $scope.bookingRequest.RequestId = 123;

                if (lastValidateBookingOk) {
                    if (!hasRequestChanged()) {
                        $rootScope.pageLoading = false;
                        $scope.checkAndMove(currentPage);
                        return;
                    }
                }
            }
            catch (e) {
                alert(e);
                $rootScope.pageLoading = false; // Just in case
            }

            lastValidateBookingOk = false;

            //make sure the mins have two digits
            if ($scope.bookingRequest.EntryMinute.length == 1) {
                $scope.bookingRequest.EntryMinute = "0" + $scope.bookingRequest.EntryMinute;
            }
            if ($scope.bookingRequest.ExitMinute.length == 1) {
                $scope.bookingRequest.ExitMinute = "0" + $scope.bookingRequest.ExitMinute;
            }

            $http({
                method: 'POST',
                data: getBookingRequestForPost($scope.bookingRequest),
                url: '/api/BookingRest/ValidateRequest', cache: false
            }).then(function (result) {
                try {
                    /*
                    if (result.data.RedirectUrl) {
                        alert(result.data.RedirectUrl);
                        window.location = result.data.RedirectUrl;
                        return;
                    }*/
                    $scope.bookingResult = result.data;
                    $scope.parkingDiscount = $scope.bookingResult.ParkingDiscount;
                    //
                    if (result.data.PaymentPolicies.length == 1) {
                        $scope.bookingRequest.PaymentPolicyId = result.data.PaymentPolicies[0].PaymentPolicyId;
                    }
                    // Store it the local storage so if they press F5 they get their page back
                    storeBookingState();
                    //will format at UI
                    $scope.bookingRequest.EnteringDateTime = result.data.EnteringDateTime;
                    $scope.bookingRequest.LeavingDateTime = result.data.LeavingDateTime;
                    if (!$scope.bookingResult.BookingAllowed) { // This booking is not allowed
                        //showAlert($scope.bookingResult.BookingNotAllowedMessage, $scope.bookingResult.BookingNotAllowedTitle);
                        alertify.alert($scope.bookingResult.BookingNotAllowedMessage);
                        return;
                    }

                    $scope.bundleData.Bundles = result.data.PossibleBundles;
                    //if pre entered the bookingbundleId (quickquote), set the $scope.selectedBundle
                    //console.log('preSelectedBundleId', $scope.bookingRequest.preSelectedBundleId);
                    if ($scope.bookingRequest.preSelectedBundleId) {
                        if ($scope.bookingRequest.BundleId === undefined && $scope.bookingRequest.preSelectedBundleId.length > 0) {
                            $scope.bookingRequest.BundleId = $scope.bookingRequest.preSelectedBundleId;
                        }
                        if ($scope.bookingRequest.preSelectedBundleId.length > 0 && $scope.selectedBundle === undefined) {

                            angular.forEach(result.data.PossibleBundles, function (item, key) {

                                if ($scope.bookingRequest.preSelectedBundleId == item.BookingBundleId) {
                                    $scope.selectedBundle = item;
                                    storeBookingState();
                                    recordConversionStep(2);
                                }
                            });
                        }
                    }
                    //console.log('selectedBundle', $scope.selectedBundle);
                    //console.log('bookingRequest BundleId', $scope.bookingRequest.BundleId);
                    $scope.bundleData.ProductCategories = result.data.ProductCategories;
                    $scope.bundleData.PaymentPolicies = result.data.PaymentPolicies;
                    // Copy all items into a flattened products array
                    $scope.bundleData.Products = new Array();
                    for (i = 0; i < $scope.bundleData.ProductCategories.length; i++) {
                        var prodCat = $scope.bundleData.ProductCategories[i];
                        var prodCatVariants = new Array();
                        var uniqueProdCatVariantTitles = new Array();
                        if (!prodCat.WebBookingTemplate) {
                            prodCat.WebBookingTemplate = "/BookingEngine/VAS/BookingEngineVASTemplate.html";
                        }
                        for (prodIdx = 0; prodIdx < prodCat.Products.length; prodIdx++) {
                            var product = prodCat.Products[prodIdx];
                            //alert(JSON.stringify(product.Variants));
                            //$scope.bundleData.Products.push.apply($scope.bundleData.Products, product.Variants);
                            prodCatVariants.push.apply(prodCatVariants, product.Variants);
                        }
                        $scope.bundleData.Products.push.apply($scope.bundleData.Products, prodCatVariants);
                        //alert(JSON.stringify(prodCatVariants));
                        prodCat.uniqueVariantTitles = new Array();
                        var uniqueProdCatVariantTitles = Enumerable.From(prodCatVariants).Distinct('$.Title').Select("$.Title").ForEach(function (title) {
                            var item = new Object();
                            item.title = title;
                            for (varIndex = 0; varIndex < prodCatVariants.length; varIndex++) {
                                if (prodCatVariants[varIndex].Title == title && prodCatVariants[varIndex].Subtext && prodCatVariants[varIndex].Subtext.length > 0) {
                                    item.subtext = prodCatVariants[varIndex].Subtext;
                                }
                            }
                            prodCat.uniqueVariantTitles.push(item);

                        });

                        //prodCat.uniqueVariantTitles = uniqueProdCatVariantTitles;
                        //alert(JSON.stringify(uniqueProdCatVariantTitles));
                        //uniqueProdCatVariantTitles =

                    }
                    //update shopping basket with the
                    $scope.bookingRequest.PromoCodeList = $scope.bookingResult.PromoCodeList;
                    $scope.bookingRequest.PromoCodeListDiscountTotal = $scope.bookingResult.PromoCodeListDiscountTotal;
                    $scope.calculateShoppingBasket();
                    //alert($scope.bundleData.Products.length);
                    //$scope.bundleData.BasketTotal = 123; // Calculate me

                    $scope.formSubmitAttempted = false;
                    $scope.FormSubmitAttempted = new Object();
                    storeBookingState();
                    store.set(BOOKING_REQUEST_POST_SEND_KEY, $scope.bookingRequest);

                    lastValidateBookingOk = true;

                    // No we have more data set any defaults that have not been set
                    $scope.configureWizard($scope.pageData.Pages);
                    $scope.checkAndMove(currentPage);
                }
                finally {
                    $rootScope.pageLoading = false;
                }
            });

            //alert('Model 4 = ' + model.Pages[0].Sections[0].Fields[0].BookingProperty);
            //            $scope.pageData = model;

            return true;

        };


        $scope.checkAndMove = function (currentPage) {
            if ($scope.bundleData.Bundles && $scope.bundleData.Bundles.length == 1 && currentPage == 0) {
                $scope.selectedBundle = $scope.bundleData.Bundles[0];
                storeBookingState();
                $scope.bookingRequest.BundleId = $scope.bundleData.Bundles[0].BookingBundleId;
                if ($scope.bundleData.Products) {
                    $scope.bookingRequest.Products = new Array();
                    for (prodIdx = 0; prodIdx < $scope.bundleData.Products.length; prodIdx++) {
                        var product = $scope.bundleData.Products[prodIdx];
                        if (product.Quantity > 0) {
                            $scope.bookingRequest.Products.push(product); // Tell the server we want this product
                        }
                    }
                }

                $scope.moveToNextPage(++currentPage, true);
            } else {
                $scope.moveToNextPage(currentPage, true);
            }
        };

        $scope.canEditOrRemoveProduct = function (item) {
            if ($scope.bookingComplete) {
                return false; // booking is complete no changes
            }
            else {
                return item.MinQuantity < item.MaxQuantity; // Can't edit it if you can't change the qty
            }
        };

        $scope.loginResult = Global_LoggedInUser;
        $scope.loggedIn = Global_isLoggedIn;

        //$http.defaults.headers.post["Content-Type"] = "application/json";
        $scope.pageData = ParkAltoForms.BookingForm;
        $scope.registrationWizardModel = ParkAltoForms.RegistrationForm;

        if ($locationProvider.search()[CURRENT_PAGE_KEY]) {
            $scope.bookingRequest = store.get(BOOKING_REQUEST_KEY); //  Only load from the data store if they are into the booking process not if they get here by clicking from another page
        }

        $scope.selectedBundle = store.get('selectedBundle');

        $scope.forgottonPasswordUser = new Object();
        $scope.forgotPasswordCompleteFormSubmitAttempted = false;

        $scope.changePasswordComplete = false;
        $scope.changePasswordUser = new Object();
        $scope.updateProfileComplete = false;

        var newCustomerSelected = store.get("newCustomerSelected");
        if (typeof (newCustomerSelected) != 'undefined') {
            $scope.newCustomerSelected = newCustomerSelected;
            $scope.existingCustomerSelected = !$scope.newCustomerSelected;
        }
        else {
            $scope.newCustomerSelected = false;
            $scope.existingCustomerSelected = false;
        }

        //alert(JSON.stringify($scope.bookingRequest));
        if ($scope.bookingRequest == null) {
            $scope.bookingRequest = new Object();
            if (Global_isLoggedIn) { // We are logged in and the user has refreshed the page.  Go to the server t                
                $scope.setupBookingDefaults($scope.loginResult);
            }
        }
        if ($scope.bookingRequest.CreditCardId == null) {
            $scope.bookingRequest.CreditCardId = 'null';
        }

        $scope.bookingRequest.DepartureTransportRequired = true;
        $scope.bookingRequest.ReturnTransportRequired = true;

        //Google Map Angular-UI Initalization
        $scope.location = [];
        $scope.mapOptions = {
            zoom: 15,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };


        $scope.onMapIdle = function () { };
        $scope.showMarkerInfo = function (marker) { $scope.location.myInfoWindow.open($scope.location.map, marker); };



        $scope.bundleData = store.get('bundleData');
        if ($scope.bundleData == null) {
            $scope.bundleData = new Object();
            $scope.bundleData.ProductCategories = new Array();
            $scope.bundleData.Products = new Array();
            $scope.bundleData.Bundles = new Array();
        }

        $scope.booking = new Object();
        $scope.user = new Object();
        $scope.forgotPasswordComplete = false;

        $scope.changePasswordRequest = new Object();
        $scope.updateProfileRequest = new Object();
        $scope.newUserAccount = new Object();
        $scope.bookingComplete = false;
        $scope.isMapElementHidden = true;

        $scope.currentWizardPage = 0;
        $scope.isBookingPage = false;
        $scope.configureWizard($scope.pageData.Pages);
        $scope.configureWizard($scope.registrationWizardModel);

        $scope.calculateShoppingBasket();

        $scope.carparkFacilityMarkers = [
            {
                latitude: 0, // initial map center latitude
                longitude: 0 // initial map center longitude
            }];

        $scope.carparkFacilityLatLng = {
            latitude: 0, // initial map center latitude
            longitude: 0 // initial map center longitude
        };




        /**
        * Checks if anything has changed about the request we last sucessfully sent to the server.  If it has not then we can skip the server call as 
        * the user moves through the wizard
        */
        function hasRequestChanged() {
            var stored = store.get(BOOKING_REQUEST_POST_SEND_KEY);
            if (stored) {
                var storedJSON = JSON.stringify(stored, function (key, val) {
                    if (key == '$$hashKey') {
                        return undefined;
                    }
                    return val;
                });

                var newJSON = JSON.stringify($scope.bookingRequest, function (key, val) {
                    if (key == '$$hashKey') {
                        return undefined;
                    }
                    return val;
                });
                var changed = storedJSON != newJSON;
                return changed;
            }
            return true;
        }

        function setupHistoryIfMissing() {
            if (historyStateNotSetup) {
                historyStateNotSetup = false;
                var curPage = parseInt($locationProvider.search()[CURRENT_PAGE_KEY]);
                if (isNaN(curPage)) {
                    //                History.pushState({ currentPage: startPage }, 'currentPage' + startPage, '?currentPage=' + startPage); // REMEMBER TO CHECK IF WE NEEED THIS
                    $scope.storeCurrentPageState(0);
                }
            }
        }

        $scope.displayOrQuote = function () {
            //var startPage = parseInt(store.get('startPage'));
            var bookingReq = $cookieStore.get("booking");
            if (bookingReq) {
                $scope.bookingRequestedLoaded = false;
                $cookieStore.remove("booking");
                clearBookingState(); // remove previous selections made such as new /
                $scope.existingCustomerSelected = false;
                $scope.newCustomerSelected = false;
                $scope.bookingRequest = JSON.parse(bookingReq);
                $scope.configureWizard($scope.pageData.Pages); // Fix up the dates in bookingRequest turning the string values back into dates
                $scope.$apply();
                $(".btn-quote").click();
                $scope.bookingRequestedLoaded = true;
            }




        };

        $timeout(function () {
            $scope.displayOrQuote();
        }, 300);
        $scope.bookingRequestedLoaded = true;
    }]

);


function getFirstInvalidField(field) {
    if (field.ChildrenFieldIds != null && field.ChildrenFieldIds.length > 0) {
        for (i = 0; i < field.ChildrenFieldIds.length; i++) {
            var input = jQuery("#" + field.ChildrenFieldIds[i].Id);
            if (input.hasClass("ng-invalid")) {
                return input;
            }
        }
    }
    else {
        var ele = jQuery("#" + field.BookingProperty);
        if (ele.hasClass("ng-invalid")) {
            return ele;
        }
    }
    return null;
}

function isInValidField(field) {
    var invalidField = getFirstInvalidField(field);
    if (invalidField != null) {
        return true;
    }
    else {
        return false;
    }
}


app.directive("paBookingfield", function () {
    return {
        //scope: { paBookingfield: '@', formfieldid: '@', required: '=', ngModel: '=', ngDisabled: '=', inputclass: '@' },
        scope: {}, // Create an isolateed scope
        link: function (scope, element, attrs, ctrl) {
            var theScope = scope.$parent;
            scope.field = theScope.getField(attrs.paBookingfield); //alert(scope.field);
            scope.fieldTemplateUrl = theScope.findCustomControlTemplate(scope.field.Control);
            //alert(scope.fieldTemplateUrl);
        },
        template: '<div ng-include="fieldTemplateUrl"></div>',
        /*
        template: function (elem, attrs) {
            return '<div ng-include="fieldTemplateUrl" ng-transclude></div>';
        },*/
        replace: true
    };
});
app.directive("paFormScope", function ($compile, $rootScope, $http) {
    return {
        scope: true, // { form: '=paFormScope' }, // Create an isolateed scope
        compile: function (elem, attrs, transclude) {
            //var contents = elem.contents().remove();
            //alert(contents);
            var compiledContents;

            return function (scope, lElem, lAttrs) {
                //childScope = scope.$new(),
                childElement = angular.element('<div></div>');
                var scopeToLookAt = null;
                var theScope = scope.$parent;

                // do the transclusion.

                transclude(scope, function (clone, innerScope) {
                    //append the transcluded element.
                    //innerScope.bookingRequest = new Object();
                    //innerScope.bookingRequest.LookAt = 'Look at me NOW';
                    //innerScope.bookingRequest = childScope.bookingRequest;
                    scopeToLookAt = innerScope;
                    scopeToLookAt.currentPage = 0;





                    var childScope = innerScope;

                    childScope.formDefinition = Object.byString(theScope, attrs.paFormScope);
                    childScope.bookingRequest = new Object();
                    childScope.currentPage = 0;

                    if (attrs.initializeFrom) {
                        childScope.sourceObject = Object.byString(window, attrs.initializeFrom);
                        if (childScope.sourceObject) { // Copy from the web user object into the bookingRequest object that backs the form
                            for (var k in childScope.sourceObject) scopeToLookAt.bookingRequest[k] = childScope.sourceObject[k];
                        }
                    }


                    childScope.moveToNextPage = function (currentPage, formDef, isFormValid) {
                        directionIsForward = true;
                        // Skip the next page if it should not be visible
                        if (formDef.Pages.length > currentPage + 1) {
                            var page = formDef.Pages[currentPage + 1];
                            if (page.VisibleExpression) {
                                var pageVisible = eval(page.VisibleExpression);
                                if (!pageVisible) { // Then skip this page.
                                    currentPage++;
                                }
                            }
                            currentPage++;
                            scopeToLookAt.currentPage = currentPage;
                            //$scope.storeCurrentPageState(currentPage);
                            $rootScope.pageLoading = false;
                            return true;
                        }
                        return false; // no movement.
                    };


                    childScope.evalInChildScope = function (thefunc, theForm, formDefinition) {
                        eval("this." + thefunc);
                    };

                    childScope.handleSubmitEvent = function (page, theForm, formDefinition) {
                        if (page.Button && page.Button.OnClick) {
                            var args = [page.Button.OnClick, theForm, formDefinition];
                            childScope.evalInChildScope.apply(this, args); // Do this so we can use updateProfile with out prefixing it with childScope.                            
                        }
                        else {
                            childScope.register(theForm, formDefinition); // default action
                        }
                    };
                    childScope.updateProfile = function (theForm, formDef) {
                        return childScope.postToUrl(theForm, formDef, '/api/BookingRest/UpdateProfile');
                    };

                    childScope.register = function (theForm, formDef) {
                        return childScope.postToUrl(theForm, formDef, '/api/BookingRest/Register');
                    };

                    childScope.postToUrl = function (theForm, formDef, url) {
                        var isValid = theScope.validateForm(0, theForm, null, formDef);
                        if (isValid) {
                            $rootScope.pageLoading = true;
                            try {
                                if (formDef.FormKey) {
                                    scopeToLookAt.bookingRequest.FormKey = formDef.FormKey;
                                }
                                else {
                                    scopeToLookAt.bookingRequest.FormKey = null;
                                }

                                $http({
                                    method: 'POST',
                                    data: getBookingRequestForPost(scopeToLookAt.bookingRequest),
                                    url: url, cache: false
                                }).success(function (result) {
                                    $rootScope.pageLoading = false;
                                    if (result) {
                                        theScope.loginResult = result.User;
                                        theScope.loggedIn = result.IsLoggedIn;

                                        if (result.Success) {                                            
                                            if (!childScope.moveToNextPage(scopeToLookAt.currentPage, formDef, true) && result.RedirectUrl) {
                                                window.location = result.RedirectUrl; // No page to move to so redirect
                                            }
                                            else {
                                                if (result.Message) {
                                                    alertify.alert(result.Message);
                                                }
                                            }                                            
                                        }
                                        else {
                                              alertify.alert(result.Message);
                                        }
                                    }
                                    else {
                                        alertify.alert("Sorry, an error occured.");
                                    }
                                }).error(function (data, status, headers, config) {
                                    $rootScope.pageLoading = false;
                                    if (status && status == '401' && window.parkAltoGlobal.defaults.websiteLoginUrl.length > 0) {
                                        alertify.alert(data.Message, function () {
                                            window.location = window.parkAltoGlobal.defaults.websiteLoginUrl;
                                        });
                                    } else {
                                        alertify.alert("Sorry, an error occured");
                                    }
                                });
                            }
                            catch (ex) {
                                $rootScope.pageLoading = false;
                            }
                        }
                    };







                    childElement.append($compile(clone)(innerScope));
                    //childElement.append($compile(clone)(childScope));
                });
                lElem.append(childElement);

                /*
                if (!compiledContents) {
                    compiledContents = $compile(contents, transclude);
                }
                compiledContents(scope, function (clone, scope) {
                    lElem.append(clone);
                });
                */

            };
        },
        /*
        template: function (elem, attrs) {
            return '<div ng-include="fieldTemplateUrl" ng-transclude></div>';
        },*/
        replace: true,
        priority: 1010,
        transclude: true
    };
});
app.directive("passwordVerify", function () {
    return {
        require: "ngModel",
        scope: {
            passwordVerify: '='
        },
        link: function (scope, element, attrs, ctrl) {
            scope.$watch(function () {
                var combined;

                if (scope.passwordVerify || ctrl.$viewValue) {
                    combined = scope.passwordVerify + '_' + ctrl.$viewValue;
                }
                return combined;
            }, function (value) {
                if (value) {
                    ctrl.$parsers.unshift(function (viewValue) {
                        var origin = scope.passwordVerify;
                        if (origin !== viewValue) {
                            ctrl.$setValidity("passwordVerify", false);
                            return undefined;
                        } else {
                            ctrl.$setValidity("passwordVerify", true);
                            return viewValue;
                        }
                    });
                }
            });
        }
    };
});
app.directive('postRender', ['$timeout', function ($timeout) {
    var def = {
        restrict: 'A',
        terminal: true,
        transclude: true,
        link: function (scope, element, attrs) {
            $timeout(scope.displayOrQuote, 0); //Calling a scoped method
        }
    };
    return def;
}]);
app.directive("creditCardNumber", function () {
    return {
        require: "ngModel",
        link: function (scope, elm, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                var valid = true;
                if (viewValue != null && viewValue != '') {
                    valid = $.payment.validateCardNumber(viewValue);
                }
                ctrl.$setValidity('creditCardNumber', valid);
                return viewValue;
            });
        }
    };
});
app.directive('controlgroup', function () {
    return {
        require: '^form',
        restrict: 'E',
        transclude: true,
        scope: { 'formSubmitAttempted': '=' },
        link: function ($scope, element, attrs, formCtrl) {
            
            //alert('invoking controller');
            
            $scope.formCtrl = formCtrl;

        },
        controller: function ($scope, $element) {
            var fields = $scope.fields = [];

           
            this.addField = function (field, element, attrs) {
                fields.push({ field: field, element: element, attrs: attrs });
            };


            $scope.isAnyFieldInRowRequiredInvalid = function () {
                //console.log('isAnyFieldInRowInvalid called');
                for (i = 0; i < fields.length; i++) {
                    var field = fields[i];

                    var isInvalid = $scope.isFieldInvalidAndDirty(field);
                    if (isInvalid) {
                        return true;
                    }
                }
                return false;
            };

            $scope.isFieldInvalidAndDirty = function (field) {
                //alert(field.formfieldid);
                ///////////var fieldEle = jQuery("#" + field.field.formfieldid);
                //alert($scope.formSubmitAttempted);
                //alert(field.$dirty);
                //var fieldEle = $(field.element[0]);
                //var debugHtml = fieldEle.html();
                var attrs = fields.attrs;
                //var formField = $scope.$parent.theForm[field.field.formfieldid];
                //var formField = field.field;
                var formField = $scope.formCtrl[field.field.formfieldid];
                if (!formField) {
                    //throw Error("The form field " + field.field.formfieldid + " was not found");
                }
                if (formField) {
                    var isDirty = formField.$dirty; // fieldEle.hasClass('ng-dirty')

                    if ($scope.formSubmitAttempted || isDirty) {
                        //var isInvalid = fieldEle.hasClass('ng-invalid-required');
                        //var isInvalid = formField.$error.required;
                        var isInvalid = formField.$invalid;
                        return isInvalid;
                    }
                }
                return false;
            };
            $scope.isAnyFieldInRowCurrencyInvalid = function () {
                for (i = 0; i < fields.length; i++) {
                    var field = fields[i];
                    var fieldEle = jQuery("#" + field.field.formfieldid);
                    var isCurrencyInvalid = fieldEle.hasClass('ng-invalid-currency');
                    if (isCurrencyInvalid) {
                        return true;
                    }
                }
                return false;
            };
            $scope.isAnyFieldInRowErrorMessageInvalid = function () {
                for (i = 0; i < fields.length; i++) {
                    var field = fields[i];
                    var fieldEle = jQuery("#" + field.field.formfieldid);
                    var iserrormessageInvalid = fieldEle.hasClass('ng-invalid-errormessage');
                    if (iserrormessageInvalid) {
                        return true;
                    }
                }
                return false;
            };
            $scope.getErrorMessage = function () {
                for (i = 0; i < fields.length; i++) {
                    var field = fields[i];
                    //ng-invalid ng-invalid-required
                    var message = $(field.element[0]).attr('errormessage')
                    if (message) {
                        return message;
                    }
                }
                return "";
            };
            $scope.getInvalidRowMessage = function (row) {
                for (i = 0; i < row.Fields.length; i++) {
                    var field = row.Fields[i];
                    //ng-invalid ng-invalid-required
                    var isInvalid = $(field.element[0]).hasClass("ng-invalid");

                    if (isInvalid) {
                        return field.RequiredErrorMessage;
                    }
                }
                return "";
            };
        },
        template:
             '<div ng-class="{\'control-group\' : true, \'pa-notvalid  pa-error\': isAnyFieldInRowRequiredInvalid() || isAnyFieldInRowCurrencyInvalid() || isAnyFieldInRowErrorMessageInvalid()}">' +
                 '<div ng-transclude></div>' +
                 '<span class="pa-error-msg"  ng-show="isAnyFieldInRowRequiredInvalid()">At least 1 field is required.</span>' +
                 '<span class="pa-error-msg"  ng-show="isAnyFieldInRowCurrencyInvalid()">Please input number.</span>' +
                  '<span class="pa-error-msg"  ng-show="isAnyFieldInRowErrorMessageInvalid()">{{getErrorMessage()}}</span>' +
                 '' +
             '</div>',
        replace: true
    };
}).directive('formfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element);
        }
        ,
        template:
            '<div class="inline" ng-transclude>' +
    '<label class="control-label">{{title}}:</label>' +
'</div>',
        replace: true
    };
}).directive('textformfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=', inputclass: '@', type:'@' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
            //tabsCtrl.$setViewValue("Hello");

            //   scope.$on('kickOffValidations', function () {
            //ctrl.$setValidity('emails', true);
            //field.$setViewValue(field.$value); // MAke it dirty
            //tabsCtrl.$setDirty();
            // });
            //this.$setValidity(true);
        }
           ,
        template: function (elem, attrs) {
            return '<div class="inline">' +
    '<label class="control-label" for="' + attrs.formfieldid + '">{{title}}:</label>' +
    '<input ' +
            ' ng-transclude="" ' +
            ' placeholder="{{title}}" type="{{type}}" novalidate="yes" ' +
    'name="' + attrs.formfieldid + '" ' +
    'id="' + attrs.formfieldid + '" ng-required="isrequired" ' +
    '  ng-model="ngModel" class="{{inputclass}}" ng-disabled="ngDisabled" ' + ' />' + '</div>';

        },
        replace: true
    };
}).directive('textformfieldwebsite', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=', inputclass: '@' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
        },
        template: function (elem, attrs) {
            return '<div class="inline">' +
            '<label class="control-label" for="' + attrs.formfieldid + '">{{title}}:</label>' +
            '<input ng-transclude="" ' +            
            ' placeholder="{{title}}" type="text" novalidate="yes" ' +
            'name="' + attrs.formfieldid + '" ' +
            'id="' + attrs.formfieldid + '" ng-required="isrequired" ' +
            '  ng-model="ngModel" class="{{inputclass}}" ng-disabled="ngDisabled" ' + ' />' +
            '  <a href="http://{{ngModel}}">{{ngModel}} <i class="icon-external-link"></i></a></div>';
        },
        replace: true
    };
}).directive('dropdownformfield', function ($compile) {

    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: {
            title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', emptyoption: '@', options: '=', 'class': '@',
            ngDisabled: '=', ngoptiontext: '@', ngoptionid: '@'
        },
        link: function (scope, element, attrs, tabsCtrl) {
            var templateStart = '<div class="inline">' +
            '<label class="control-label" for="' + attrs.formfieldid + '">{{title}}:</label>' +
            '<select ' +
            ' ng-transclude="" ' +
            ' ng-required="isrequired" class="{{class}}" name="' + attrs.formfieldid + '"  id="' +
            attrs.formfieldid + '" ng-model="ngModel"  ng-disabled="ngDisabled" ';
            var templateOptions = 'ng-options="option.id as option.text for option in options"';
            var templateEnd = '</select></div>';


            if (typeof (attrs.ngoptiontext) != 'undefined') {
                var idPart = 'option.' + attrs.ngoptiontext;
                if (typeof (attrs.ngoptionid) != 'undefined') {
                    idPart = 'option.' + attrs.ngoptionid;
                }
                var textPart = 'option.' + attrs.ngoptiontext;

                templateOptions = 'ng-options="' + idPart + ' as ' + textPart + ' for option in options"';
            }
            var template = templateStart + templateOptions + ">" + templateEnd; //Do not allow null at the top
            if (typeof (attrs.emptyoption) != 'undefined') {
                template = templateStart + templateOptions + '><option value="">' + attrs.emptyoption + '</option>' + templateEnd; //allow empty option on the top
            }

            element.html(template).show();
            $compile(element.contents())(scope);
            tabsCtrl.addField(scope, element);
            if (!attrs["class"]) {
                attrs["class"] = 'input-mini'; // Default class for form fields
            }
        },
        replace: true
    }

}).directive('dropdownformfieldselect2', function ($compile) {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: {
            title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', emptyoption: '@', options: '=', 'class': '@',
            ngDisabled: '=', ngoptiontext: '@', ngoptionid: '@', select2init: '='
        },
        link: function (scope, element, attrs, tabsCtrl) {
            var templateStart = '<div class="inline">' +
            '<label class="control-label" for="' + attrs.formfieldid + '">{{title}}:</label>' +
            '<select ' +
            ' ng-transclude="" ' +
            ' ng-required="isrequired" class="{{class}}" name="' + attrs.formfieldid + '"  id="' + attrs.formfieldid + '" ng-model="ngModel"  ng-disabled="ngDisabled" ui-select2="select2init" >';
            var templateOptions = '<option ng-repeat="option in options" value="{{option.id}}">{{option.text}}</option>';
            var templateEnd = '</select></div>';


            if (typeof (attrs.ngoptiontext) != 'undefined') {
                var idPart = 'option.' + attrs.ngoptiontext;
                if (typeof (attrs.ngoptionid) != 'undefined') {
                    idPart = 'option.' + attrs.ngoptionid;
                }
                var textPart = 'option.' + attrs.ngoptiontext;
                templateOptions = '<option ng-repeat="option in options" value="{{' + idPart + '}}">{{' + textPart + '}}</option>';
            }

            var template = templateStart + templateOptions + templateEnd; //Do not allow null at the top
            if (typeof (attrs.emptyoption) != 'undefined') {
                template = templateStart + '<option value="">' + attrs.emptyoption + '</option>' + templateOptions + templateEnd; //allow empty option on the top
            }

            element.html(template).show();
            $compile(element.contents())(scope);
            tabsCtrl.addField(scope, element);
            if (!attrs["class"]) {
                attrs["class"] = 'input-mini'; // Default class for form fields
            }
        },
        replace: true
    };
}).directive('dropdowntitleformfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', titles: '=', options: '=', ngDisabled: '=' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element);
        }
        ,
        template:
            '<div class="inline" ng-transclude>' +
    '<label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
    '<select ' +
                ' ng-transclude="" ' +
        ' ng-required="isrequired" class="input-mini" name="title"  ng-model="ngModel" ng-disabled="ngDisabled" ' +
    'ng-options="option.title as option.title for option in titles" />' + '</div>',
        replace: true
    };
}).directive('persontitleformfield', ['$rootScope', 'datacontext', function ($rootScope, datacontext) {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', options: '=', ngDisabled: '=' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element);
            scope.datacontext = datacontext;
        }
        ,
        template:
            '<div class="inline">' +
    '<label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
    '<select ' +
         ' ng-transclude="" ' +
        ' ng-required="isrequired" class="input-mini" name="title"  ng-model="ngModel" ng-disabled="ngDisabled" ' +
    'ng-options="option.title as option.title for option in datacontext.optionListPersonTitles"></select>' + '</div>',
        replace: true
    };
}]).directive('emailformfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=' },
        link: function (scope, element, attrs, tabsCtrl) {
            scope.emailRegEx = /^(([^<>()[\]\\.,;:\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,}))$/;
            tabsCtrl.addField(scope, element);
        }
           ,
        template: function (elem, attrs) {
            return '<div class="inline">' +
    '<label class="control-label" for="' + attrs.formfieldid + '">{{title}}:</label>' +
    '<input class="input-medium" placeholder="Email" type="text" novalidate ' +
                ' ng-transclude="" ' +
    'valid-email ' +
    'name="' + attrs.formfieldid + '"  ' +
    'id="' + attrs.formfieldid + '" ng-required="isrequired" ' +
    '  ng-model="ngModel"  ng-disabled="ngDisabled" ' + ' />' + '</div>';
        },
        replace: true
    };
}).directive('validEmail', function () {
    return {
        require: "ngModel",
        link: function (scope, elm, attrs, ctrl) {
            var regex = /^(([^<>()[\]\\.,;:\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,}))$/;
            ctrl.$parsers.unshift(function (viewValue) {
                var valid = true;
                if (viewValue != null && viewValue != '') {
                    valid = regex.test(viewValue);
                }
                ctrl.$setValidity('validEmail', valid);
                return viewValue;
            });
        }
    };
}).directive('myRequired', function () {
    return {
        require: "ngModel",
        link: function (scope, elm, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                var valid = viewValue && viewValue.length > 0;
                ctrl.$setValidity('myRequired', valid);
                return viewValue;
            });
        }
    };
}).directive('vehicleformfield', function ($compile) {

    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', emptyoption: '@', options: '=', 'class': '@', ngDisabled: '=' },
        link: function (scope, element, attrs, tabsCtrl) {
            var templateStart = '' +
'<label class="control-label" for="' + attrs.formfieldid + '">{{title}}:</label>' +
'<select ng-required="isrequired" class="{{class}}" name="' + attrs.formfieldid + '"  id="' + attrs.formfieldid + '" ng-model="ngModel"  ng-disabled="ngDisabled" ui-select2>' +
'<option ng-repeat="custVehicle in options" value="{{custVehicle.vehicle.vehicleId}}">[{{custVehicle.vehicle.registrationNumber}} ]  {{custVehicle.vehicle.color}} {{custVehicle.vehicle.make}} {{custVehicle.vehicle.model}}</option>';
            var templateEnd = '</select>';

            var template = templateStart + templateEnd; //Do not allow null at the top
            element.html(template).show();
            $compile(element.contents())(scope);
            tabsCtrl.addField(scope, element);
            if (!attrs["class"]) {
                attrs["class"] = 'input-mini'; // Default class for form fields
            }
        },
        replace: true
    };
}).directive('dateformfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
        }
           ,
        template:
            '<div class="inline datepicker">' +
    '<label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
    '<input class="input-medium" placeholder="{{title}}" type="text" novalidate ' +
    'name="{{formfieldid}}"  ' +
    ' ng-transclude="" ' +
    'id="{{formfieldid}}" ng-required="isrequired"' +
    '  ng-model="ngModel"  ng-disabled="ngDisabled" ' + '  bs-datepicker /><a class="add-on" data-toggle="datepicker"></a>' + '</div>',
        replace: true
    };
}).directive('timeformfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
        }
           ,
        template:
            '<div class="inline timepicker">' +
    '<label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
    '<input class="input-medium" placeholder="{{title}}" type="text" ' +
    ' ng-transclude="" ' +
    'name="{{formfieldid}}"  ' +
    'id="{{formfieldid}}" ng-required="isrequired" ' +
    '  ng-model="ngModel"  ng-disabled="ngDisabled" ' + '  bs-timepicker /><a class="add-on" data-toggle="timepicker"></a>' + '</div>',
        replace: true
    };
}).directive('bookingdateformfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=', errormessage: '@', clearvalidationid: '@', mintarget: '=', maxtarget: '=', timecomparetarget: '=', datecomparetarget: '=' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
        }
        ,
        template:
            '<div class="inline datepicker">' +
    '<label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
    '<input class="input-medium" placeholder="{{title}}" type="text" novalidate ' +
    'name="{{formfieldid}}"  ' +
    ' ng-transclude="" ' +
    'id="{{formfieldid}}" ng-required="isrequired" errormessage="{{errormessage}}" clearvalidationid="{{clearvalidationid}}" mintarget="mintarget" maxtarget="maxtarget" timecomparetarget="comparetarget" datecomparetarget="datecomparetarget" ' +
    '  ng-model="ngModel"  ng-disabled="ngDisabled" ' + '  bs-datepicker bookingdatetimecompare /><a class="add-on" data-toggle="datepicker"></a>' + '</div>',
        replace: true
    };
}).directive('bookingtimeformfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=', errormessage: '@', clearvalidationid: '@', mintarget: '=', maxtarget: '=', timecomparetarget: '=', datecomparetarget: '=' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
        }
           ,
        template:
            '<div class="inline timepicker">' +
    '<label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
    '<input class="input-medium" placeholder="{{title}}" type="text" ' +
    'name="{{formfieldid}}"  ' +
    ' ng-transclude="" ' +
    'id="{{formfieldid}}" ng-required="isrequired"  errormessage="{{errormessage}}" clearvalidationid="{{clearvalidationid}}" mintarget="mintarget" maxtarget="maxtarget" timecomparetarget="comparetarget" datecomparetarget="datecomparetarget"' +
    '  ng-model="ngModel"  ng-disabled="ngDisabled" ' + '  bs-timepicker bookingdatetimecompare /><a class="add-on" data-toggle="timepicker"></a>' + '</div>',
        replace: true
    };
}).directive('currency', function () {
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function (scope, elem, attrs, ctrl) {
            //Default.
            //allow user input onblur, to do the validation first, then set the viewvalue.
            elem.unbind('input').unbind('keydown').unbind('change');
            elem.bind('blur', function () {
                var viewValue = elem.val();
                var validateResult = validateNumber(viewValue, attrs, ctrl)

                if (validateResult) {
                    scope.$apply(function () {
                        ctrl.$setViewValue(elem.val());
                    });
                }
            });

        }
    };
}).directive('currencyfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=', max: '@', min: '@', errormessage: '@' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
        },
        template:
        '<div class="inline">' +
        '<label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
        '<div class="input-prepend"><span class="add-on">$</span><input class="input-miut" ' +
        ' ng-transclude="" ' + ' type="text" name="{{formfieldid}}"  id="{{formfieldid}}" ng-required="isrequired" ng-model="ngModel" ' +
        'ng-disabled="ngDisabled" max="{{max}}" min="{{min}}" errormessage="{{errormessage}}"  currency></input>' +
        '</div></div>',
        replace: true
    };
}).directive('quanityfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=', max: '@', min: '@', errormessage: '@' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
        },
        template:
        '<div class="inline">' +
        '<label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
        '<div><input ' +
        ' ng-transclude="" ' + ' class="input-miut" placeholder="{{title}}" type="text" novalidate="yes" name="{{formfieldid}}"  id="{{formfieldid}}" ng-required="isrequired" ng-model="ngModel" ng-disabled="ngDisabled" ' +
        ' max="{{max}}" min="{{min}}" errormessage="{{errormessage}}" currency></input>' +
        '</div></div>',
        replace: true
    };
}).directive('bookingdatetimecompare', function () {
    return {
        require: 'ngModel',
        restrict: 'A',
        link: function (scope, elem, attrs, ctrl) {
            ctrl.$parsers.unshift(function (viewValue) {
                var errormessage = attrs.errormessage;
                var maxtarget = scope.maxtarget;
                var mintarget = scope.mintarget;
                var comparingtarget = scope.comparetarget;
                var datecomparetarget = scope.datecomparetarget;
                var timecomparetarget = scope.timecomparetarget;
                var datetimetocompare;
                /*
                  var mytimeDateTime = moment("2000-01-01 " + mytime, "YYYY-MM-DD hh:mm A");
                var te = new Date(mydate.getFullYear(), mydate.getMonth(), mydate.getDate(), mytimeDateTime.hours(), mytimeDateTime.minutes(), 0, 0);
                return new Date(mydate.getFullYear(), mydate.getMonth(), mydate.getDate(), mytimeDateTime.hours(), mytimeDateTime.minutes(), 0, 0);
                */

                //get the changing value
                if (timecomparetarget) {
                    //current viewValue is from datepicker, require the timepicker's value
                    var mytimeDateTime = moment("2000-01-01 " + timecomparetarget, "YYYY-MM-DD hh:mm A");
                    datetimetocompare = new Date(viewValue.getFullYear(), viewValue.getMonth(), viewValue.getDate(), mytimeDateTime.hours(), mytimeDateTime.minutes(), 0, 0);
                } else if (datecomparetarget) {
                    //current viewValue is from timepicker, require the datepicker's value
                    var mytimeDateTime = moment("2000-01-01 " + viewValue, "YYYY-MM-DD hh:mm A");
                    datetimetocompare = new Date(datecomparetarget.getFullYear(), datecomparetarget.getMonth(), datecomparetarget.getDate(), mytimeDateTime.hours(), mytimeDateTime.minutes(), 0, 0);
                } else {
                    datetimetocompare = viewValue;
                }

                if (mintarget) {
                    //giving mintarget to compare
                    if (datetimetocompare < mintarget) {
                        ctrl.$setValidity('errormessage', false);
                        return undefined;
                    }
                }
                if (maxtarget) {
                    if (datetimetocompare > maxtarget) {
                        ctrl.$setValidity('errormessage', false);
                        return undefined;
                    }
                }
                ctrl.$setValidity('errormessage', true);
                return viewValue;
            });
        }
    };
}).directive('textareaformfield', function () {
    return {
        require: '^controlgroup',
        restrict: 'E',
        transclude: true,
        scope: { title: '@', formfieldid: '@', isrequired: '=', ngModel: '=', ngDisabled: '=', 'class': '@' },
        link: function (scope, element, attrs, tabsCtrl) {
            tabsCtrl.addField(scope, element, attrs);
        }
           ,
        template:
    '<div><label class="control-label" for="{{formfieldid}}">{{title}}:</label>' +
    '<textarea  class="{{class}}" rows="5" name="{{formfieldid}}" ' +
    'id="{{formfieldid}}" ng-required="isrequired" ' +
    'ng-model="ngModel"  ng-disabled="ngDisabled"></textarea></div>',
        replace: true
    };
}).directive('fileChange', function () {
    var linker = function ($scope, element, attributes) {
        // onChange, push the files to $scope.files.
        element.bind('change', function (event) {
            var files = event.target.files;
            $scope.$apply(function () {
                for (var i = 0, length = files.length; i < length; i++) {
                    $scope.files.push(files[i]);
                }
            });
        });
    };
    return {
        restrict: 'A',
        link: linker
    };
}).directive('statusbadge', function () {
    return {
        require: 'ngModel',
        restrict: 'E',
        transclude: true,
        template:
    '<div class="cus-status clearfix" ng-hide="isNew"><h3><span class="label label-success" ' +
    'ng-hide="isDeleted">Active</span><span class="label label-important" ng-show="isDeleted">Deleted</span></h3></div>',
        replace: true
    };
});

function validateNumber(viewValue, attrs, ctrl) {
    var FLOAT_REGEXP = /^(?=.+)(?:[1-9]\d*|0)?(?:\.\d+)?$/;
    var regex = new RegExp(FLOAT_REGEXP);
    var valid = regex.test(viewValue);
    var errormessage = attrs.errormessage;
    if (!valid) {
        // it is invalid, return undefined (no model update)
        ctrl.$setValidity('currency', false);
        return undefined;
    } else {
        //check if max number
        var maxAttr = attrs.max;
        var myNumber = parseFloat(viewValue);
        if (maxAttr) {
            var maxNumber = parseFloat(maxAttr);
            if (myNumber > maxNumber) {
                ctrl.$setValidity('errormessage', false);
                return undefined;
            }
        }
        var minAttr = attrs.min;
        if (minAttr) {
            var minNumber = parseFloat(minAttr);
            if (minNumber > myNumber) {
                ctrl.$setValidity('errormessage', false);
                return undefined;
            }
        }
    }
    ctrl.$setValidity('currency', true);
    ctrl.$setValidity('errormessage', true);
    return viewValue;
}



