PageSourceSearch

https://www.lexusofbrisbane.com.au/bundles/lexus/mainsite/javascri…OBXRXkQAzSos-PTBoxrwjKQ2-ZlQsVaXzU9qFeWnow1

js lexusofbrisbane.com.au collected 2026-09-28 13:16:46 UTC 711,480 bytes, 16,362 lines download raw bytes

1/* Minification failed. Returning unminified contents.
2(2418,36-37): run-time error JS1003: Expected ':': ,
3(2418,48-49): run-time error JS1003: Expected ':': ,
4(2418,61-62): run-time error JS1003: Expected ':': }
5(2620,40-41): run-time error JS1195: Expected expression: )
6(2620,43-44): run-time error JS1195: Expected expression: >
7(2622,22-23): run-time error JS1195: Expected expression: ,
8(2622,28-29): run-time error JS1003: Expected ':': )
9(2623,17-18): run-time error JS1002: Syntax error: }
10(2625,72-73): run-time error JS1004: Expected ';': {
11(2632,18-19): run-time error JS1195: Expected expression: )
12(2634,72-73): run-time error JS1004: Expected ';': {
13(2638,18-19): run-time error JS1195: Expected expression: )
14(2647,62-63): run-time error JS1004: Expected ';': {
15(2689,17-18): run-time error JS1028: Expected identifier or string: .
16(2689,19-20): run-time error JS1010: Expected identifier: .
17(2690,25-26): run-time error JS1004: Expected ';': :
18(2690,64-65): run-time error JS1197: Too many errors. The file might not be a JavaScript file: ,
19(2664,5-44): run-time error JS1301: End of file encountered before function is properly closed: function eventTrack(action, properties)
20(2691,14-15): run-time error JS1195: Expected expression: )
21(2691,15-16): run-time error JS1197: Too many errors. The file might not be a JavaScript file: ;
22 */
23/* Minification failed. Returning unminified contents.
24(2396,36-37): run-time error JS1003: Expected ':': ,
25(2396,48-49): run-time error JS1003: Expected ':': ,
26(2396,61-62): run-time error JS1003: Expected ':': }
27(2598,40-41): run-time error JS1195: Expected expression: )
28(2598,43-44): run-time error JS1195: Expected expression: >
29(2600,22-23): run-time error JS1195: Expected expression: ,
30(2600,28-29): run-time error JS1003: Expected ':': )
31(2601,17-18): run-time error JS1002: Syntax error: }
32(2603,72-73): run-time error JS1004: Expected ';': {
33(2610,18-19): run-time error JS1195: Expected expression: )
34(2612,72-73): run-time error JS1004: Expected ';': {
35(2616,18-19): run-time error JS1195: Expected expression: )
36(2625,62-63): run-time error JS1004: Expected ';': {
37(2667,17-18): run-time error JS1028: Expected identifier or string: .
38(2667,19-20): run-time error JS1010: Expected identifier: .
39(2668,25-26): run-time error JS1004: Expected ';': :
40(2668,64-65): run-time error JS1197: Too many errors. The file might not be a JavaScript file: ,
41(2642,5-44): run-time error JS1301: End of file encountered before function is properly closed: function eventTrack(action, properties)
42(2669,14-15): run-time error JS1195: Expected expression: )
43(2669,15-16): run-time error JS1197: Too many errors. The file might not be a JavaScript file: ;
44 */
45(function () {
46  'use strict';
47  angular.module(
48      'Lexus', [
49      'Lexus.Directives',
50      'Lexus.Components',
51      'Lexus.Factories',
52      'Lexus.Services',
53      'Lexus.Filters',
54      'ngTouch',
55      'ngCookies',
56      'ngSanitize',
57      'ngAnimate',
58      'ngTagsInput',
59      'ng-directives-utils.transcludeReplace',
60      'vcRecaptcha',
61      '720kb.datepicker',
62      'ngRoute',
63      'ngDialog',
64      'angularCancelOnNavigateModule',
65      'angulartics',
66      'Lexus.angulartics',
67      'angulartics.debug',
68      'duParallax'
69
70  ]);
71
72    // Namespaces
73  angular.module('Lexus.Directives', []);
74  angular.module('Lexus.Factories', []);
75  angular.module('Lexus.Services', []);
76  angular.module('Lexus.Filters', []);
77  angular.module('Lexus.Components', []);
78})();
79;
80"use strict";
81
82angular.module('Lexus.Filters', [])
83.filter('formatArgs', [function () { // pass url and array of arguments.
84    return function (url, args) {
85
86        var urlReplacers = url.match(/\{[0-9]+\}/g);
87        var replaced = 0;
88        // Loop through arguments and replace url value.
89        angular.forEach(args, function (value, key) {
90            url = url.replace("{" + key + "}", value);
91            replaced++;
92        });
93        //strips off the unused replacers from URL
94        if (urlReplacers.length > replaced) {
95            for (var _t = replaced; _t < urlReplacers.length; _t++) {
96                url = url.replace("/{" + _t + "}", '');
97            }
98        }
99
100        return url;
101    };
102}])
103.filter('format', [function () {
104    return function (value, template) {
105        return template.replace('{0}', value);
106    }
107}])
108.filter('replaceCaseInsensitive', [function () {
109    return function (value, substringValue) {
110        if (typeof (value) != "string") return value;
111        
112        var r = new RegExp(substringValue, "i");
113        return value.replace(r, "");
114    }
115}])
116.filter('sanatize', [function () { // Remove certain characters from variable.
117    return function (value) {
118        if (value !== null) {
119            return value.replace(/[^a-z0-9.\-@\s\/\'\&\u2019\u004
1191-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+/gi, '');
120        }
121    };
122}])
123.filter('lowerCaseFirstLetter', function () {
124    return function (input) {
125        return (!!input) ? input.charAt(0).toLowerCase() + input.substr(1) : '';
126    }
127})
128.filter('currencyNoZero', ['$filter', function ($filter) {
129    return function (input) {
130        return (parseInt(input) === 0) ? '' : $filter('currency')(input);
131    }
132}])
133.filter('currencyNoZeroNoCents', ['$filter', function ($filter) {
134    return function (input) {
135        return (parseInt(input) === 0) ? '' : $filter('currency')(input, undefined, 0);
136    }
137}])
138.filter('currencyRounded', ['$filter', function ($filter) {
139    return function (input) {
140        return $filter('currency')(Math.round(input));
141    }
142}])
143.filter('dateToBE', ['$filter', function ($filter) { // Format FE date for BE.
144    return function (value, format) {
145        if (angular.isDefined(value) && !angular.equals(value, '')) {
146            var date = moment(value, format);
147
148            return {
149                day: date.format('D'),
150                month: date.format('M'),
151                year: date.format('YYYY'),
152                fullDate: date.format('YYYY-M-D')
153            };
154        } else {
155            return {
156                day: '',
157                month: '',
158                year: '',
159                fullDate: ''
160            };
161        }
162    };
163}])
164.filter('dateToUniversal', ['$filter', function ($filter) { // Format BE date for FE.
165    return function (value) {
166        if (angular.isDefined(value) && !angular.equals(value, '')) {
167            var date = moment(value, 'YYYY-MM-DD');
168
169            return date.unix();
170        } else {
171            return '';
172        }
173    };
174}])
175.filter('filterLower', function () {
176    return function (array, value) {
177        return array.filter(function (element, index, array) {
178            if (element.rating <= value.rating) {
179                return element;
180            }
181        });
182    };
183})
184.filter('filterHigher', function () {
185    return function (array, value) {
186        return array.filter(function (element, index, array) {
187            if (element.rating >= value.rating) {
188                return element;
189            }
190        });
191    };
192})
193.filter('removeSpaces', function () {
194    return function (text) {
195        return String(text).replace(/\s+/g, '');
196    }
197})
198.provider('filterWatcher', function() {
199
200        this.$get = ['$window', '$rootScope', function($window, $rootScope) {
201
202            /**
203             * Cache storing
204             * @type {Object}
205             */
206            var $$cache = {};
207
208            /**
209             * Scope listeners container
210             * scope.$destroy => remove all cache keys
211             * bind to current scope.
212             * @type {Object}
213             */
214            var $$listeners = {};
215
216            /**
217             * $timeout without triggering the digest cycle
218             * @type {function}
219             */
220            var $$timeout = $window.setTimeout;
221
222            /**
223             * @description
224             * get `HashKey` string based on the given arguments.
225             * @param fName
226             * @param args
227             * @returns {string}
228             */
229            function getHashKey(fName, args) {
230                function replacerFactory() {
231                    var cache = [];
232                    return function(key, val) {
233                        if (angular.isObject(val) && !(val==null)) {
234                            if (~cache.indexOf(val)) return '[Circular]';
235                            cache.push(val)
236                        }
237                        if($window == val) return '$WINDOW';
238                        if($window.document == val) return '$DOCUMENT';
239                        if (val && val.$evalAsync && val.$watch) return '$SCOPE';
240                        return val;
241                    }
242                }
243                return [fName, JSON.stringify(args, replacerFactory())]
244                  .join('#')
245                  .replace(/"/g,'');
246            }
247
248            /**
249             * @description
250             * fir on $scope.$destroy,
251             * remove cache based scope from `$$cache`,
252             * and remove itself from `$$listeners`
253             * @param event
254             */
255            function removeCache(event) {
256                var id = event.targetScope.$id;
257                forEach($$listeners[id], function(key) {
258                    delete $$cache[key];
259                });
260                delete $$listeners[id];
261            }
262
263            /**
264             * @description
265             * for angular version that greater than v.1.3.0
266             * it clear cache when the digest cycle is end.
267             */
268            function cleanStateless() {
269                $$timeout(function() {
270                    if(!$rootScope.$$phase)
271                        $$cache = {};
272                }, 2000);
273            }
274
275            /**
276             * @description
277             * Store hashKeys in $$listeners container
278             * on scope.$destroy, remove them all(bind an event).
279             * @param scope
280             * @param hashKey
281             * @returns {*}
282             */
283            function addListener(scope, hashKey) {
284                var id = scope.$id;
285                if (angular.isUndefined($$listeners[id])) {
286                    scope.$on('$destroy', removeCache);
287                    $$listeners[id] = [];
288                }
289                return $$listeners[id].push(hashKey);
290            }
291
292            /**
293             * @description
294             * return the `cacheKey` or undefined.
295             * @param filterName
296             * @param args
297             * @returns {*}
298             */
299            function $$isMemoized(filterName, args) {
300                var hashKey = getHashKey(filterName, args);
301                return $$cache[hashKey];
302            }
303
304            /**
305             * @description
306             * store `result` in `$$cache` container, based on the hashKey.
307             * add $destroy listener and return result
308             * @param filterName
309             * @param args
310             * @param scope
311             * @param result
312             * @returns {*}
313             */
314            function $$memoize(filterName, args, scope, result) {
315                var hashKey = getHashKey(filterName, args);
316                //store result in `$$cache` container
317                $$cache[hashKey] = result;
318                // for angular versions that less than 1.3
319                // add to `$destroy` listener, a cleaner callback
320                if(scope && scope.$evalAsync && scope.$watch) {
321                    addListener(scope, hashKey);
322                } else {
323                    cleanStateless();
324                }
325                return result;
326            }
327
328            return {
329                isMemoized: $$isMemoized,
330                memoize: $$memoize
331            }
332        }];
333    })
334.filter('groupBy', [ '$parse', 'filterWatcher', function ( $parse, filterWatcher ) {
335    return function (collection, property) {
336
337        if (!angular.isObject(collection) || angular.isUndefined(property)) {
338            return collection;
339        }
340
341        return filterWatcher.isMemoized('groupBy', arguments) ||
342          filterWatcher.memoize('groupBy', arguments, this,
343            _groupBy(collection, $parse(property)));
344
345        /**
346         * groupBy function
347         * @param collection
348         * @param getter
349         * @returns {{}}
350         */
351        function _groupBy(collection, getter) {
352            var result = {};
353            var prop;
354
355            angular.forEach( collection, function( elm ) {
356                prop = getter(elm);
357
358                if(!result[prop]) {
359                    result[prop] = [];
360                }
361                result[prop].push(elm);
362            });
363            return result;
364        }
365    }
366}]);
367;
368"use strict";
369angular.module('Lexus.Factories')
370.factory('API', ['$http', '$q', function ($http, $q) { // Query API and return JSON.
371    return {
372        get: function (url,callback) {
373            var deferred = $q.defer();
374            
375            $http({ method: 'GET', url: url }).
376            then(function success(response) {
377                if (callback) {
378                    callback();
379                }
380                if (response.data.ErrorMessage) {
381                    deferred.reject(response.data, 200);
382                } else { 
383                    deferred.resolve(response.data);
384                }
385            }, function error(response) {
386                var myresult = { reason: response.data, status: response.status, ErrorMessage: response.statusText };
387                deferred.reject(myresult);
388            });
389
390            return deferred.promise;
391        },
392        post: function (url, data) {
393            var deferred = $q.defer();
394
395            $http({ method: 'POST', url: url, data: data }).
396            then(function success(response) {
397                if (response.data.ErrorMessage) {
398                    deferred.reject(response.data, 200);
399                } else {
400                    deferred.resolve(response.data);
401                }
402            }, function (response) {
403                var myresult = { reason: response.data, status: response.status, ErrorMessage: response.statusText };
404                deferred.reject(myresult);
405            });
406
407            return deferred.promise;
408        },
409        del: function (url, callback) {
410            var deferred = $q.defer();
411
412            $http({ method: 'DELETE', url: url }).
413            then(function success(response) {
414                if (callback) {
415                    callback();
416                }
417                if (response.data.ErrorMessage) {
418                    deferred.reject(response.data, 200);
419                } else {
420                    deferred.resolve(response.data);
421                }
422            }, function error(response) {
423                var myresult = { reason: response.data, status: response.status, ErrorMessage: response.statusText };
424                deferred.reject(myresult);
425            });
426
427            return deferred.promise;
428        }
429    };
430}]);;
431"use strict";
432angular.module('angularCancelOnNavigateModule', [])
433  .config(['$httpProvider', function ($httpProvider) {
434      $httpProvider.interceptors.push('HttpRequestTimeoutInterceptor');
435  }])
436  .run(['$rootScope', 'HttpPendingRequestsService', function ($rootScope, HttpPendingRequestsService) {
437      //$rootScope.$on('$locationChangeSuccess', function (event, newUrl, oldUrl) {
438      //    if (newUrl != oldUrl) {
439      //        HttpPendingRequestsService.cancelAll();
440      //    }
441      //})
442  }]);
443
444angular.module('angularCancelOnNavigateModule')
445  .service('HttpPendingRequestsService',['$q', function ($q) {
446      var cancelPromises = [];
447
448      function newTimeout() {
449          var cancelPromise = $q.defer();
450          cancelPromises.push(cancelPromise);
451          //console.log('%c HttpPendingRequestsService newTimeout() -> cancelPromises: ', 'color: red;', cancelPromises);
452          return cancelPromise.promise;
453      }
454
455      function cancelAll() {
456          console.log('%c HttpPendingRequestsService cancelAll() -> cancelPromises: ', 'color: red;', cancelPromises);
457          angular.forEach(cancelPromises, function (cancelPromise) {
458              cancelPromise.promise.isGloballyCancelled = true;
459              cancelPromise.resolve();
460          });
461          cancelPromises.length = 0;
462      }
463
464      return {
465          newTimeout: newTimeout,
466          cancelAll: cancelAll
467      };
468  }]);
469
470angular.module('angularCancelOnNavigateModule')
471  .factory('HttpRequestTimeoutInterceptor',['$q', 'HttpPendingRequestsService', function ($q, HttpPendingRequestsService) {
472      return {
473          request: function (config) {
474              config = config || {};
475              if (config.timeout === undefined && !config.noCancelOnRouteChange) {
476                  config.timeout = HttpPendingRequestsService.newTimeout();
477              }
478              return config;
479          },
480
481          responseError: function (response) {
482              if (response.config.timeout.isGloballyCancelled) {
483                  return $q.defer().promise;
484              }
485              return $q.reject(response);
486          }
487      };
488  }]);;
489"use strict";
490
491angular.module('Lexus.Factories')
492    .factory('Geolocation', ['$q', '$rootScope', function ($q, $rootScope) {
493        return function () {
494
495            var deferred = $q.defer();
496            var geoLocServiceExists = 'geolocation' in navigator;
497            var timeout;
498
499            var defOptions = {
500                timeout: 8000
501            };
502
503            if (geoLocServiceExists) {
504                //success
505                var geoOnSuccess = function (position) {
506                    if (position) {
507                        window.clearTimeout(timeout);
508                        deferred.resolve({
509                            "lat": position.coords.latitude,
510                            "lng": position.coords.longitude,
511                            "success": true,
512                            "message": "pass"
513                        });
514                    }
515                };
516                //error
517                var geoOnError = function (error) {
518                    window.clearTimeout(timeout);
519                    deferred.reject({
520                        "success": false,
521                        "error": error,
522                        "message": error
523                    }, 200);
524                };
525
526                // start timer. See firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=675533
527                // this will force a failure notice after the above configured timeout, as firefox will
528                // never give us the error
529                timeout = window.setTimeout(geoOnError, defOptions.timeout);
530
531                //getPosition call
532                navigator.geolocation.getCurrentPosition(geoOnSuccess, geoOnError, defOptions);
533
534                // when opening the modal, stop attempting to get the location
535                $rootScope.$on('modal-open', function () {
536                    window.clearTimeout(timeout);
537                });
538            }
539            else {
540                deferred.reject({
541                    "success": false,
542                    "message": "geoLocation is not supported"
543                });
544            }
545
546            return deferred.promise;
547        }
548    }]);;
549"use strict";
550
551angular.module('Lexus.Factories')
552    .factory('Geodecode', ['$q', '$filter', 'API', 'Geolocation', function ($q, $filter, API, Geolocation) {
553        return function () {
554
555
556            var geodeferred = $q.defer();
557
558            var response = {
559                isSuccessfull: false,
560                data: {},
561                message: ''
562            };
563
564            var addressParse = {};
565
566            var onSuccess = function (geoData) {
567                if (geoData && geoData.success) {
568                    var addressDecode = $filter('formatArgs')(Lexus.API.Address.GeoDecode, [Lexus.ID.Site, geoData.lat, geoData.lng]);
569
570                    API.get(addressDecode).then(function (req) {
571
572                        $.each(req.Data.results, function (i, item) {
573                            if (item.types[0] === "postal_code") {
574                                addressParse.postcode = item.address_components[0].short_name;
575                            }
576                            if (item.types[0] === "locality") {
577                                addressParse.suburb = item.address_components[0].short_name;
578                            }
579                            if (item.types[0] === "administrative_area_level_1") {
580                                addressParse.state = item.address_components[0].short_name;
581                            }
582                        });
583
584                        response.isSuccessfull = true;
585                        response.data = addressParse;
586
587                        geodeferred.resolve(response);
588
589                    }, function (data) {
590                        response.message = "failed to get the parsed address";
591                        geodeferred.reject(response);
592                    });
593                }
594                else {
595                    response.message = "failed to get the co-ordinates from geolocation service";
596                    geodeferred.reject(response);
597                }
598            };
599
600            var onError = function (data) {
601                response.message = (data) ? data.message : "geo service unavailable";
602                geodeferred.reject(response);
603            };
604
605            //Invoke Geolocation service
606            var _g = Geolocation();
607            _g.then(onSuccess, onError);
608
609            return geodeferred.promise;
610        }
611    }]);;
612"use strict";
613
614angular.module('Lexus.Services')
615.service('AddressSuggestService', ['$timeout', '$http', 'API', '$filter', function ($timeout, $http, API, $filter) {
616    var self = this;
617    var params = {};
618
619    params.defaults = {
620        siteID: Lexus.ID.Site,
621        term: null
622    };
623
624    params.params = {
625        siteID: params.defaults.siteID,
626        term: params.defaults.term
627    };
628
629    self.GetResults = function (callback) {
630        params.params.searchQueryUrl = $filter('formatArgs')(Lexus.API.Lookup.Suburb, [
631            params.params.siteID,
632            params.params.term
633        ]);
634
635        return API.get(params.params.searchQueryUrl, callback);
636    }
637
638    // Update a search parameter.
639    self.setParam = function (name, value) {
640        params.params[name] = value;
641    };
642
643    // Get a parameter value
644    self.getParam = function (name) {
645        return params.params[name];
646    };
647
648
649    return self;
650}]);;
651"use strict";
652
653angular.module('Lexus.Services')
654.factory('BuildPriceService', ['$q', '$timeout', 'BuildPriceAPIService', function ($q, $timeout, BuildPriceAPIService) {
655    var self = this;
656
657    self.events = {
658        pageNavigation: 'bp-page-navigation',
659        pageNavigationBroadcast: 'bp-page-navigation-broadcast',
660        openPostcodeDialog: 'open-change-postcode',
661        closePostcodeDialog: 'close-change-postcode',
662        postcodeChanged: 'postcode-changed',
663        openPostcodeSlideout: 'open-postcode-slideout',
664        confirmPostcodeFromSlideout: 'confirm-postcode-slideout',
665
666        overviewDataReceived: 'bp-overview-data-received',
667        vehicleDataReceived: 'bp-vehicle-data',
668        enhancementPacksReceived: 'bp-enhancement-pack-data',
669        trimsReceived: 'bp-trim-data',
670        paintsReceived: 'bp-colour-data',
671
672        modelSelected: 'bp-did-select-model',
673        modelSelectedUpdateMenu: 'bp-did-select-model-update-menu',
674        engineVariantSelected: 'bp-did-select-engine-variant',
675        gradeSelected: 'bp-did-select-grade',
676        enhancementPackSelected: 'bp-did-select-enhancement-pack',
677        paintSelected: 'bp-did-select-paint',
678        trimSelected: 'bp-did-select-trim',
679
680        threeSixtyLoad: 'bp-did-360-start',
681        threeSixtyLoaded: 'bp-did-360-done',
682
683        userSelection: 'bp-user-selection',
684
685        priceLoad: 'bp-did-price-start',
686        priceDone: 'bp-did-price-done',
687        priceReceived: 'bp-did-price-succeed',
688        priceError: 'bp-did-price-error',
689
690        onOfferText: 'bp-on-offer-text',
691
692        vehicleDataRelay: 'bp-vehicle-relay',
693        paintDataRelay: 'bp-paint-relay',
694        invalidSelectionError: 'bp-invalid-selection',
695
696        holdPricingCalls: 'bp-hold-pricing-calls',
697        holdPricingCallsRelay: 'bp-hold-pricing-calls-relay',
698
699        gtmEvent: 'bp-gtm-event',
700        onCompareVehicles: 'cv-on-compare-vehicles',
701        compareVehicleActiveSelection: 'cv-vehicle-active-selection',
702
703        triggerSaveBuildPriceDialog: 'trigger-save-build-price',
704        triggerCloseSaveBuildPriceDialog: 'trigger-close-save-build-price',
705        closeSaveBuildPriceDialog: 'close-save-build-price',
706        openSaveBuildPriceDialog: 'open-save-build-price',
707    };
708
709    self.findModelInVehicleData = function (modelName) {
710        var modelObj;
711        for (var btIdx in BuildPriceAPIService.data) {
712            modelObj = BuildPriceAPIService.data[btIdx].models.find(function (model) {
713                return model.uriName === modelName;
714            });
715            if (modelObj) {
716                return modelObj;
717            }
718        }
719    };
720
721    //Remove soon, moved to bp-api
722    self.findEngineVariantInVehicleData = function (variantName) {
723        for (var btIdx in BuildPriceAPIService.data) {
724            for (var mdlIdx in BuildPriceAPIService.data[btIdx].models) {
725                var engineVariantObj = BuildPriceAPIService.data[btIdx].models[mdlIdx].modelEngineVariants.find(function (variant) {
726                    return variant.uriName === variantName;
727                });
728                if (engineVariantObj) {
729                    return engineVariantObj;
730                }
731            }
732        }
733
734        return {
735            grades: []
736        };
737    };
738
739    self.findGradeInVehicleData = function (gradeId) {
740        var gradeObj;
741        for (var btIdx in BuildPriceAPIService.data) {
742            for (var mdlIdx in BuildPriceAPIService.data[btIdx].models) {
743                for (var varIdx in BuildPriceAPIService.data[btIdx].models[mdlIdx].modelEngineVariants) {
744                    gradeObj = BuildPriceAPIService.data[btIdx].models[mdlIdx].modelEngineVariants[varIdx].grades.find(function (grade) {
745                        return grade.id === gradeId;
746                    });
747                    if (gradeObj) {
748                        return gradeObj;
749                    }
750                }
751            }
752        }
753    };
754
755    self.waitCondition = {
756        CHANGED: 0,
757        NOTNULL: 1,
758        ISNULL: 2,
759        NOTEMPTY: 3,
760        ISTRUE: 4,
761        ISSTRICTLYTRUE: 5,
762        ISFALSE: 6,
763        ISSTRICTLYFALSE: 7
764    };
765
766    self.stepIds = {
767        MODEL: "MODEL",
768        GRADE: "GRADE",
769        ENHANCEMENT_PACK: "ENHANCEMENT_PACK",
770        PAINT_TRIM: "PAINT_TRIM",
771        SUMMARY: "SUMMARY"
772    };
773
774    self.routingSettings = {
775        baseUrl: '',
776        argIdx: ['carmodel', 'engineVariant', 'grade', 'enhancementPack', 'colour', 'trim']
777    };
778
779    self.waitFor = function (target, waitCondition) {
780        var defer = $q.defer();
781
782        var currentValue = eval("this." + target);
783        if ((waitCondition === self.waitCondition.NOTNULL && currentValue != null
784            || waitCondition === self.waitCondition.ISNULL && currentValue == null)) {
785            defer.resolve(currentValue);
786        }
787        
788        var unbind = this.$watch(target, function (newValue, oldValue) {
789            switch (waitCondition) {
790                case self.waitCondition.CHANGED:
791                    unbind();
792                    defer.resolve(newValue);
793                    break;
794                case self.waitCondition.NOTNULL:
795                    if (newValue != null) {
796                        unbind();
797                        defer.resolve(newValue);
798                    }
799                    break;
800                case self.waitCondition.ISNULL:
801                    if (newValue == null) {
802                        unbind();
803                        defer.resolve(newValue);
804                    }
805                    break;
806                case self.waitCondition.NOTEMPTY:
807                    if (newValue != null && newValue.length != null && newValue.length > 0) {
808                        unbind();
809                        defer.resolve(newValue);
810                    }
811                    break;
812                case self.waitCondition.ISTRUE:
813                    if (newValue == true) {
814                        unbind();
815                        defer.resolve(newValue);
816                    }
817                    break;
818                case self.waitCondition.ISSTRICTLYTRUE:
819                    if (newValue === true) {
820                        unbind();
821                        defer.resolve(newValue);
822                    }
823                    break;
824                case self.waitCondition.ISFALSE:
825                    if (newValue == false) {
826                        unbind();
827                        defer.resolve(newValue);
828                    }
829                    break;
830                case self.waitCondition.ISSTRICTLYFALSE: 7
831                    if (newValue === false) {
832                        unbind();
833                        defer.resolve(newValue);
834                    }
835                    break;
836                default:
837                    unbind();
838                    defer.reject();
839            }
840        });
841  
842        return defer.promise
843    }
844
845    self.watchDebounce = function(func, wait, immediate) {
846        var timeout;
847
848        var deferred = $q.defer();
849
850        return function () {
851            var context = this, args = arguments;
852            var later = function () {
853                timeout = null;
854                if (!immediate) {
855                    deferred.resolve(func.apply(context, args));
856                    deferred = $q.defer();
857                }
858            };
859            var callNow = immediate && !timeout;
860            if (timeout) {
861                $timeout.cancel(timeout);
862            }
863            timeout = $timeout(later, wait);
864            if (callNow) {
865                deferred.resolve(func.apply(context, args));
866                deferred = $q.defer();                
867            }
868            return deferred.promise;
869        };
870    }
871
872    self.getGradeNameFromId = function (gradeId) {
873        var grade = self.findGradeInVehicleData(gradeId);
874        return grade ? grade.name : "Unknown";
875    }
876
877    self.getColourFromUri = function (colours, colour) {
878        return colours.find(function (currentColour) {
879            return currentColour.uriName == colour;
880        });
881
882    }
883
884    self.emitInvalidSelection = function (invalidSelectionData) {
885
886        console.error("Specification validation failed in " + (invalidSelectionData.controllerName ? invalidSelectionData.controllerName : "scope id " + invalidSelectionData.$scope.$id) + ": '" + invalidSelectionData.invalidValue + "' - value defaulted to '" + invalidSelectionData.defaultValue + "'");
887        invalidSelectionData.$scope.$emit(self.events.invalidSelectionError, invalidSelectionData);
888    }
889
890    self.fragmentReplacementMap = [
891        //orig-regex, orig-replacement, reverse-regex, reverse-replacement
892        [/-/g, "_", /_/g, "-"],
893        [/\s/g, "-", /-/g, " "],
894        [/\//g, "~", /~/g, "/"]
895    ];
896
897    self.pathReplacementMap = [
898        [/\/+/g, "/"]
899    ];
900
901    //Makes replacements in the fragmentReplacementMap
902    self.encodeUriFragment = function (pathFragment) {
903        if (pathFragment == null) return;
904        for (var i = 0; i < self.fragmentReplacementMap.length; i++) {
905            pathFragment = pathFragment.replace(self.fragmentReplacementMap[i][0], self.fragmentReplacementMap[i][1]);
906        }
907        return pathFragment
908    }
909
910    self.decodeUriFragment = function (pathFragment) {
911        if (pathFragment == null) return;
912        for (var i = self.fragmentReplacementMap.length - 1; i >= 0; i--) {
913            pathFragment = pathFragment.replace(self.fragmentReplacementMap[i][2], self.fragmentReplacementMap[i][3]);
914        }
915        return pathFragment;
916    }
917
918    //Makes replacements in the pathReplacementMap
919    self.encodeUriPath = function (path) {
920        if (path == null) return;
921        for (var i = 0; i < self.pathReplacementMap.length; i++) {
922            path = path.replace(self.pathReplacementMap[i][0], self.pathReplacementMap[i][1]);
923        }
924        return path;
925    }
926
927    //Vehicle structure in the format
928    /*
929    vehicle: {
930              carmodel: 'CT',
931              engineVariant: 'CT 200',
932              grade: '',
933              postcode: '3000',
934              enhancementPack: 'Standard',
935              colour: '',
936              trim: ''
937            }
938    */
939    self.getBuildPriceLink = function (vehicle, step) {
940
941        var url = (vehicle.carmodel) ? self.encodeUriFragment(vehicle.carmodel) : "";
942        url = url + "/";
943
944        if (vehicle.engineVariant) {
945
946            url = url + self.encodeUriFragment(vehicle.engineVariant) + "/";
947            if (vehicle.grade) {
948                url = url + self.encodeUriFragment(vehicle.grade) + "/";
949
950                if (vehicle.enhancementPack) {
951                    url = url + vehicle.enhancementPack + "/";
952
953                    if (vehicle.colour) {
954                        url = url + self.encodeUriFragment(vehicle.colour) + "/";
955                    }
956
957                    if (vehicle.trim) {
958                        url = url + self.encodeUriFragment(vehicle.trim) + "/";
959
960                        if (step) {
961                            url = url + "/" + step + "#lx-bp-nav";
962                        }                        
963                    }
964                }
965            }                                    
966        }
967
968        var path = self.encodeUriPath(url);
969
970        return path;
971    }
972
973    return self;
974}]);;
975"use strict";
976
977angular.module('Lexus.Services')
978.factory('BuildPriceAPIService', ['$q', '$timeout', 'VehicleService', function ($q, $timeout, VehicleService) {
979    var self = this;
980    //self.data = [];
981
982    self.isEmpty = function (obj) {
983        for (var prop in obj) {
984            if (obj.hasOwnProperty(prop))
985                return false;
986        }
987
988        return JSON.stringify(obj) === JSON.stringify({});
989    }
990
991    self.waitCondition = {
992        CHANGED: 0,
993        NOTNULL: 1,
994        ISNULL: 2,
995        NOTEMPTY: 3
996    };
997
998    self.waitFor = function (target, waitCondition) {
999        var defer = $q.defer();
1000
1001        var currentValue = eval("this." + target);
1002        if ((waitCondition === self.waitCondition.NOTNULL && currentValue != null
1003            || waitCondition === self.waitCondition.ISNULL && currentValue == null)) {
1004            defer.resolve(currentValue);
1005        }
1006
1007        if (!Object.prototype.watch) {
1008            Object.defineProperty(Object.prototype, "watch", {
1009                enumerable: false
1010                , configurable: true
1011                , writable: false
1012                , value: function (prop, handler) {
1013                    var
1014                      oldval = this[prop]
1015                    , newval = oldval
1016                    , getter = function () {
1017                        return newval;
1018                    }
1019                    , setter = function (val) {
1020                        oldval = newval;
1021                        return newval = handler.call(this, prop, oldval, val);
1022                    }
1023                    ;
1024
1025                    if (delete this[prop]) { // can't watch constants
1026                        Object.defineProperty(this, prop, {
1027                            get: getter
1028                            , set: setter
1029                            , enumerable: true
1030                            , configurable: true
1031                        });
1032                    }
1033                }
1034            });
1035        }
1036
1037        // object.unwatch
1038        if (!Object.prototype.unwatch) {
1039            Object.defineProperty(Object.prototype, "unwatch", {
1040                enumerable: false
1041                , configurable: true
1042                , writable: false
1043                , value: function (prop) {
1044                    var val = this[prop];
1045                    delete this[prop]; // remove accessors
1046                    this[prop] = val;
1047                }
1048            });
1049        }
1050
1051        function isEmpty(obj) {
1052            for (var prop in obj) {
1053                if (obj.hasOwnProperty(prop))
1054                    return false;
1055            }
1056
1057            return JSON.stringify(obj) === JSON.stringify({});
1058        }
1059
1060        var obj = this;
1061        obj.watch(target, function (id, oldValue, newValue) {
1062            switch (waitCondition) {
1063                case self.waitCondition.CHANGED:
1064                    obj.unwatch(target);
1065                    defer.resolve(newValue);
1066                    break;
1067                case self.waitCondition.NOTNULL:
1068                    if (newValue != null) {
1069                        obj.unwatch(target);
1070                        defer.resolve(newValue);
1071                    }
1072                    break;
1073                case self.waitCondition.ISNULL:
1074                    if (newValue == null) {
1075                        obj.unwatch(target);
1076                        defer.resolve(newValue);
1077                    }
1078                    break;
1079                case self.waitCondition.NOTEMPTY:
1080                    //if (newValue != null && newValue.length != null && newValue.length > 0) {
1081                    if (!isEmpty(newValue)) {
1082                        obj.unwatch(target);
1083                        defer.resolve(newValue);
1084                    }
1085                    break;
1086                default:
1087                    obj.unwatch(target);
1088                    defer.reject();
1089            }
1090        });
1091        return defer.promise
1092    }
1093
1094    self.getModels = function (callback) {
1095        return VehicleService.getVehiclesOverview().then(function success(response) {
1096            self.data = response.data;
1097            if (callback && typeof (callback) === "function") {
1098                callback();
1099            }
1100            return response;
1101        }, function failure(response) {
1102            var msg = "Error loading vehicle overview";
1103            console.error ? console.error(msg) : console.log("Error: " + msg);
1104        });
1105
1106    }
1107
1108    self.findEngineVariantInVehicleData = function (engineVariantName) {
1109        //if (!self.data) {
1110        //    console.log("self.findEngineVariantInVehicleData -> self.data: ", self.data);
1111        //    console.log("self.findEngineVariantInVehicleData -> engineVariantName: ", engineVariantName);
1112        //}
1113
1114        for (var btIdx in self.data) {
1115            if (self.data.hasOwnProperty(btIdx)) {
1116                var bodyTypeObj = self.data[btIdx];
1117                for (var mdlIdx in bodyTypeObj.models) {
1118                    if (bodyTypeObj.models.hasOwnProperty(mdlIdx)) {
1119                        var modelObj = bodyTypeObj.models[mdlIdx];
1120                        var engineVariantObj = modelObj.modelEngineVariants.find(function (variant) {
1121                            return variant.uriName === engineVariantName;
1122                        });
1123                        if (engineVariantObj) {
1124                            return engineVariantObj;
1125                        }
1126                    }
1127                }
1128            }
1129        }
1130        
1131    };
1132
1133    self.findModelInVehicleData = function (carmodel) {        
1134        for (var btIdx in self.data) {
1135            if (self.data.hasOwnProperty(btIdx)) {
1136                var bodyTypeObj = self.data[btIdx];
1137                var modelObj = bodyTypeObj.models.find(function (model) {
1138                    return model.uriName === carmodel;
1139                });
1140                if (modelObj) {
1141                    return modelObj;
1142                }
1143            }
1144        }
1145
1146    };
1147
1148    self.getGradeByName = function (engineVariantName, gradeName) {       
1149        if (engineVariantName == null || gradeName == null) return false;
1150        var grades = (self.findEngineVariantInVehicleData(engineVariantName) || {}).grades;
1151        return (grades || []).find(function (item) {
1152            return item.uriName == gradeName;
1153        });
1154
1155    }
1156
1157    self.getGradeByID = function (engineVariantName, gradeId) {
1158        return (self.findEngineVariantInVehicleData(engineVariantName).grades || []).find(function (item) {
1159            return item.id == gradeId;
1160        });
1161    }
1162
1163    self.getVariantByID = function (engineVariantName, gradeId, variantId) {
1164        var grade = self.getGradeByID(engineVariantName, gradeId);
1165        return (grade.variants || []).find(function (item) {
1166            return item.mdmid == variantId;
1167        });
1168    }
1169
1170    self.getVariantByName = function (engineVariantName, gradeId, variantName) {
1171        var grade = self.getGradeByID(engineVariantName, gradeId);
1172        return (grade.variants || []).find(function (item) {
1173            return item.uriName == variantName;
1174        });
1175    }
1176
1177    self.getTrimByID = function (engineVariantName, gradeId, variantId, trimId) {
1178        var variant = self.getVariantByID(engineVariantName, gradeId, variantId);
1179        return (variant.trims.availableTrims || []).find(function (item) {
1180            return item.id == trimId;
1181        });
1182    }
1183
1184    self.getTrimByName = function (engineVariantName, gradeId, variantId, trimName) {
1185        var variant = self.getVariantByID(engineVariantName, gradeId, variantId);
1186       
1187        return (variant.trims.availableTrims || []).find(function (trim) {
1188            return trim.uriName == trimName;
1189        });
1190    }
1191
1192    self.getPaintByID = function (engineVariantName, gradeId, variantId, trimId, materialCode) {
1193        var trim = self.getTrimByID(engineVariantName, gradeId, variantId, trimId);
1194        return (trim.paints.availableColours || []).find(function (item) {
1195            return item.materialCode == materialCode;
1196        });
1197    }
1198
1199    self.getPaintByName = function (engineVariantName, gradeId, variantId, trimId, paintName) {
1200        var trim = self.getTrimByID(engineVariantName, gradeId, variantId, trimId);
1201        return (trim.paints.availableColours || []).find(function (paint) {
1202            return paint.shortName == paintName || paint.uriName == paintName;
1203        });
1204    }
1205
1206    self.getVariants = function (engineVariantName, gradeId, callback) {
1207        var myGrade = self.getGradeByID(engineVariantName, gradeId);
1208
1209        if (myGrade.variants) {
1210
1211            if (callback && typeof (callback) === "function") {
1212                callback(myGrade.variants);
1213            }
1214            return myGrade.variants;
1215        }
1216        else {
1217            return VehicleService.getVariants(gradeId).then(function success(response) {
1218                if (!myGrade.variants) {
1219                    myGrade.variants = response.data;
1220                }
1221                if (callback && typeof (callback) === "function") {
1222                    callback(response.data);
1223                }
1224            }, function failure(response) {
1225                //emit error event?
1226                var msg = "Error loading enhancement packs";
1227                console.error ? console.error(msg) : console.log("Error: " + msg);
1228            });
1229        }
1230        //bpEnPack.variants = response.data;
1231    }
1232
1233    self.getTrims = function (engineVariantName, gradeId, variantId, callback) {
1234        self.getVariants(engineVariantName, gradeId, function(variants) {
1235            var myVariant = self.getVariantByID(engineVariantName, gradeId, variantId);
1236            var myGrade = self.getGradeByID(engineVariantName, gradeId);
1237
1238            if (myVariant.trims) {
1239
1240                var availableTrims = myVariant.trims.availableTrims;
1241                var unavailableTrims = self.GetUnAvailableCollection(myGrade.availableTrims, availableTrims);
1242
1243                myVariant.trims = {
1244                    allTrims: myGrade.availableTrims,
1245                    availableTrims: availableTrims,
1246                    unavailableTrims: unavailableTrims
1247                }
1248
1249                if (callback && typeof (callback) === "function") {
1250                    callback(myVariant.trims.availableTrims, myVariant.trims.unavailableTrims, myVariant.trims.allTrims);
1251                }
1252            }
1253            else {
1254                VehicleService.getTrims(gradeId, variantId).then(function success(response) {
1255                    if (response.data.length == 0) {
1256                        console.warn('getTrims received 0 results for ' + engineVariant + ' with grade ID ' + gradeId);
1257                    }
1258
1259                    var availableTrims = response.data;                    
1260                    var unavailableTrims = self.GetUnAvailableCollection(myGrade.availableTrims, availableTrims);
1261
1262                    if (!myVariant.trims) {
1263                        myVariant.trims = {
1264                            allTrims: myGrade.availableTrims,
1265                            availableTrims: availableTrims,
1266                            unavailableTrims: unavailableTrims
1267                        };
1268                    }
1269
1270                    if (callback && typeof (callback) === "function") {
1271                        callback(myVariant.trims.availableTrims, myVariant.trims.unavailableTrims, myVariant.trims.allTrims);
1272                    }
1273
1274                }, function error(response) {
1275                    console.error('getTrims returned an error');
1276                });
1277
1278            }
1279        });
1280    }
1281
1282    self.getPaints = function (engineVariantName, gradeId, variantId, trimId, callback) {
1283        self.getTrims(engineVariantName, gradeId, variantId, function (trims) {
1284            var myTrim = self.getTrimByID(engineVariantName, gradeId, variantId, trimId);
1285            var myGrade = self.getGradeByID(engineVariantName, gradeId);
1286            if (myTrim.paints) {
1287                if (callback && typeof (callback) === "function") {
1288                    callback(myTrim.paints.availableColours, myTrim.paints.unavailableColours);
1289                }
1290            }
1291            else {
1292                VehicleService.getPaints(gradeId, variantId, trimId).then(function success(response) {
1293                    if (response.data.length == 0) {
1294                        console.warn('getColours received 0 results for ' + trimId);
1295                    }
1296                    
1297                    var availableColours = response.data;
1298                    var unavailableColours = self.GetUnAvailableCollection(myGrade.availablePaints, availableColours);
1299
1300                    if (!myTrim.paints) {
1301                        myTrim.paints = {
1302                            availableColours: availableColours,
1303                            unavailableColours: unavailableColours
1304
1305                        };
1306                    }
1307
1308                    if (callback && typeof (callback) === "function") {
1309                        callback(myTrim.paints.availableColours, myTrim.paints.unavailableColours);
1310                    }
1311                }, function error(response) {
1312                    console.error('getColours returned an error');
1313                });
1314            }
1315
1316
1317        });
1318    }
1319
1320    //Remove intersections with Available Colours from from AvailablePaints
1321    self.GetUnAvailableCollection = function (allCollection, availableCollection) {        
1322        var unavailableCollection = [];
1323        for (var a = 0, lenA = allCollection.length; a < lenA; a++) {
1324            var foundMatch = false;
1325            for (var b = 0, lenB = availableCollection.length; b < lenB; b++) {
1326                if (allCollection[a].code == availableCollection[b].code) {
1327                    foundMatch = true;
1328                }
1329            }
1330            if (!foundMatch) {
1331                unavailableCollection.push(allCollection[a]);
1332            }
1333        }
1334        return unavailableCollection;
1335    }
1336
1337    self.FilterAvailableCollection = function (allCollection, availableCollection) {
1338        var filteredCollection = [];
1339        for (var a = 0, lenA = allCollection.length; a < lenA; a++) {
1340            var foundMatch = false;
1341            for (var b = 0, lenB = availableCollection.length; b < lenB; b++) {
1342                if (allCollection[a].code == availableCollection[b].code) {
1343                    foundMatch = true;
1344                }
1345            }
1346            if (foundMatch) {
1347                filteredCollection.push(allCollection[a]);
1348            }
1349        }
1350        return filteredCollection;
1351    }
1352
1353    self.getUnavailableTrimsBasedOnPaint = function (engineVariantName, gradeId, variantId, trimId, paintName) {
1354        var myGrade = self.getGradeByID(engineVariantName, gradeId);
1355        var paint = self.getPaintByName(engineVariantName, gradeId, variantId, trimId, paintName);
1356
1357        var availableTrims = paint.availableTrims;
1358        var unavailableTrims = self.GetUnAvailableCollection(myGrade.availableTrims, availableTrims);
1359        return {availableTrims: availableTrims, unavailableTrims: unavailableTrims };
1360    }
1361
1362    self.getExteriorImages = function (engineVariantName, gradeId, variantId, trimId, materialCode, callback) {
1363        self.getPaints(engineVariantName, gradeId, variantId, trimId, function (paints) {
1364            var myPaint = self.getPaintByID(engineVariantName, gradeId, variantId, trimId, materialCode);
1365            if (myPaint.images && myPaint.images.exterior) {
1366                if (callback && typeof (callback) === "function") {
1367                    callback(myPaint.images.exterior);
1368                }
1369            }
1370            else {
1371                VehicleService.getVehicleImages(gradeId, variantId, trimId, materialCode).then(function (response) {
1372                    if (!myPaint.images)
1373                    {
1374                        myPaint.images = {};
1375                    }
1376                    if (!myPaint.images.exterior) {
1377                        myPaint.images.exterior = response.data;
1378                    }
1379                    if (callback && typeof (callback) === "function") {
1380                        callback(myPaint.images.exterior);
1381                    }    
1382                });
1383
1384            }
1385        });
1386    };
1387
1388    return self;
1389}]);;
1390"use strict";
1391
1392angular.module('Lexus.Services')
1393    .factory('PreLaunchAPIService', ['$q', '$timeout', 'PreLaunchVehicleService', function ($q, $timeout, PreLaunchVehicleService) {
1394    var self = this;
1395    //self.data = [];     
1396
1397        self.getModels = function (callback) {
1398            return PreLaunchVehicleService.getVehiclesOverview().then(function success(response) {
1399                self.data = response.data;
1400                if (callback && typeof (callback) === "function") {
1401                    callback();
1402                }
1403                return response;
1404            }, function failure(response) {
1405                var msg = "Error loading vehicle overview for pre launch vehicles";
1406                console.error ? console.error(msg) : console.log("Error: " + msg);
1407            });
1408
1409        };
1410
1411        self.findEngineVariantInVehicleData = function (engineVariantName) {
1412            
1413            for (var btIdx in self.data) {
1414                if (self.data.hasOwnProperty(btIdx)) {
1415                    var bodyTypeObj = self.data[btIdx];
1416                    for (var mdlIdx in bodyTypeObj.models) {
1417                        if (bodyTypeObj.models.hasOwnProperty(mdlIdx)) {
1418                            var modelObj = bodyTypeObj.models[mdlIdx];
1419                            var engineVariantObj = modelObj.modelEngineVariants.find(function (variant) {
1420                                return variant.uriName === engineVariantName;
1421                            });
1422                            if (engineVariantObj) {
1423                                return engineVariantObj;
1424                            }
1425                        }
1426                    }
1427                }
1428            }
1429
1430        };
1431
1432        self.getGradesList = function (engineVariantName) {
1433            if (engineVariantName == null) return false;
1434            var grades = (self.findEngineVariantInVehicleData(engineVariantName) || {}).grades;
1435            return (grades || []).map(function (grade) {
1436                return grade.name;
1437            });
1438        };
1439
1440        self.getGradesByEngineVariant = function (engineVariantName) {
1441            if (engineVariantName == null) return false;
1442            var grades = (self.findEngineVariantInVehicleData(engineVariantName) || {}).grades;
1443            return grades || [];
1444        };
1445
1446        self.getInteriorsByGradeName = function (engineVariantName, gradeName) {
1447            if (engineVariantName == null) return false;
1448            var grades = (self.findEngineVariantInVehicleData(engineVariantName) || {}).grades;
1449            var grade = (grades || []).find(function (item) {
1450                return item.name == gradeName;
1451            });
1452            return (grade && grade.baseVariant) ? grade.baseVariant.trims : [];
1453        };
1454
1455        self.getExteriorsByGradeName = function (engineVariantName, gradeName) {
1456            if (engineVariantName == null) return false;
1457            var grades = (self.findEngineVariantInVehicleData(engineVariantName) || {}).grades;
1458            var grade = (grades || []).find(function (item) {
1459                return item.name == gradeName;
1460            });
1461            return (grade && grade.baseVariant) ? grade.baseVariant.paints : [];
1462        };
1463
1464    return self;
1465}]);;
1466"use strict";
1467
1468angular.module('Lexus.Services')
1469    .service('DealersService', ['$timeout', '$filter', '$http', '$rootScope', 'API', function ($timeout, $filter, $http, $rootScope, API)
1470    {
1471        var service = this;
1472        var params = {};
1473        var decodeEmailPart = function (str) {
1474            return typeof atob === "function"
1475                ? decodeURIComponent(atob(str))
1476                : typeof Buffer !== "undefined" && typeof Buffer.from === "function"
1477                    ? decodeURIComponent(Buffer.from(str, 'base64').toString())
1478                    : str;
1479        };
1480        var decodeDealerEmail = function (dealer) {
1481            if (!dealer || !dealer.locations || !dealer.locations.length) { return; }
1482            dealer.locations.forEach(service.DecodeLocationEmail);
1483        };
1484        var decodeDealerResponseEmail = function (data) {
1485            if (!data || !data.Data) { return; }
1486            if (data.Data instanceof Array) {
1487                data.Data.forEach(decodeDealerEmail);
1488            } else {
1489                decodeDealerEmail(data.Data);
1490            }
1491        };
1492
1493        params.defaults = {
1494            siteID: Lexus.ID.Site,
1495            postCode: 'null',
1496            suburb: 'null',
1497            state: 'null',
1498            type: 'All',
1499            dealerID: '',
1500            branchCode: '',
1501            dealerState: ''
1502        };
1503
1504        params.params = {
1505            siteID: params.defaults.siteID,
1506            postCode: params.defaults.postCode,
1507            suburb: params.defaults.suburb,
1508            state: params.defaults.state,
1509            type: params.defaults.type,
1510            dealerID: params.defaults.dealerID,
1511            branchCode: params.defaults.branchCode,
1512            dealerState: params.defaults.dealerState
1513        };
1514
1515        service.DeconstructCompositeID = function (dealerCompositeKey) {
1516            if (dealerCompositeKey.indexOf('-')) {
1517                var parts = dealerCompositeKey.split('-');
1518                return {
1519                    dealerID: parts[0],
1520                    branchCode: parts[1],
1521                    sapCode: parts[2]
1522                };
1523            }
1524            else {
1525                return;
1526            }
1527        };
1528
1529        service.DecodeLocationEmail = function (location) {
1530            if (location && location.elMailFirstPartEncoded && location.elMailLastPartEncoded) {
1531                location.email = decodeEmailPart(location.elMailFirstPartEncoded) + "@" + decodeEmailPart(location.elMailLastPartEncoded);
1532            }
1533            if (location && location.serviceElMailFirstPartEncoded && location.serviceElMailLastPartEncoded) {
1534                location.serviceEmail = decodeEmailPart(location.serviceElMailFirstPartEncoded) + "@" + decodeEmailPart(location.serviceElMailLastPartEncoded);
1535            }
1536        };
1537
1538        service.GetDealerBranches = function () {
1539            params.params.searchQueryUrl = $filter('formatArgs')(Lexus.API.Lookup.DealerBranches, [
1540                params.params.siteID,
1541                params.params.dealerID,
1542                params.params.type
1543            ]);
1544
1545            API.get(params.params.searchQueryUrl)
1546                .then(function (data) {
1547                    decodeDealerResponseEmail(data);
1548                    $rootScope.$broadcast('dealer-search-results-received', data);
1549                    $timeout($rootScope.$broadcast('refresh-gtm', 'Dealer Results'), 0);
1550                }, function (reason) {
1551                    $rootScope.$broadcast('dealer-search-results-error', reason);
1552                });
1553        }
1554
1555        service.GetDealerBranch = function () {
1556            params.params.searchQueryUrl = $filter('formatArgs')(Lexus.API.Lookup.DealerBranchByCode, [
1557                params.params.siteID,
1558                params.params.dealerID,
1559                params.params.branchCode
1560            ]);
1561
1562            API.get(params.params.searchQueryUrl)
1563                .then(function (data) {
1564                    decodeDealerResponseEmail(data);
1565                    $rootScope.$broadcast('dealer-search-results-received', data);
1566                    $timeout($rootScope.$broadcast('refresh-gtm', 'Dealer Results'), 0);
1567                }, function (reason) {
1568                    $rootScope.$broadcast('dealer-search-results-error', reason);
1569                });
1570        };
1571
1572        service.GetResult = function () {
1573
1574            params.params.searchQueryUrl = $filter('formatArgs')(Lexus.API.Lookup.DealerById, [
1575                params.params.siteID,
1576                params.params.dealerID, //This dealerID a composite key ex. 050055-22541-12345
1577                params.params.dealerState
1578            ]);
1579
1580            API.get(params.params.searchQueryUrl)
1581                .then(function (data) {
1582                    decodeDealerResponseEmail(data);
1583                    $rootScope.$broadcast('dealer-search-result-received', data);
1584                    $timeout($rootScope.$broadcast('refresh-gtm', 'Dealer Results'), 0);
1585                }, function (reason) {
1586                    $rootScope.$broadcast('dealer-search-result-error', reason);
1587                });
1588        };
1589
1590        service.GetResults = function () {
1591            params.params.searchQueryUrl = $filter('formatArgs')(Lexus.API.Lookup.DealersByLocation, [
1592                params.params.siteID,
1593                params.params.postCode,
1594                params.params.suburb,
1595                params.params.state,
1596                params.params.type
1597            ]);
1598
1599            return API.get(params.params.searchQueryUrl)
1600                .then(function (data) {
1601                    decodeDealerResponseEmail(data);
1602                    $rootScope.$broadcast('dealer-search-results-received', data);
1603                    $timeout($rootScope.$broadcast('refresh-gtm', 'Dealer Results'), 0);
1604                    return data;
1605                }, function (error) {
1606                    $rootScope.$broadcast('dealer-search-results-error', error);
1607                    throw error;
1608                });
1609        };
1610
1611        // Update a search parameter.
1612        service.setParam = function (name, value) {
1613            params.params[name] = value;
1614        };
1615
1616        // Get a parameter value
1617        service.getParam = function (name) {
1618            return params.params[name];
1619        };
1620
1621        return service;
1622    }]);
1623
1624
1625
1626;
1627"use strict";
1628
1629angular.module('Lexus.Services')
1630.service('FormsService', ['$timeout', '$http', '$rootScope', function ($timeout, $http, $rootScope)
1631{
1632    var self = this;
1633
1634    self.ShowResult = function (element) {
1635      if(!element.classList.contains('lx-form-no-scroll')){
1636          TweenLite.to(window, 2, { scrollTo: { y: element.parentElement.offsetTop, x: 0 }, ease: Power4.easeOut });
1637      }
1638      $rootScope.$broadcast('broadcast.lazyLoadRevalidate');
1639    }
1640
1641    return self;
1642}]);;
1643"use strict";
1644
1645angular.module('Lexus.Services')
1646.service('MouseDetectionService', ['$rootScope', function ($rootScope)
1647{
1648    var self = this;
1649
1650    var MouseDetectionAgent = function() {
1651        this.resetMouseFoundAndBuffer();
1652        this.startWatching();
1653        return this;
1654    };
1655
1656    MouseDetectionAgent.prototype.timestampMsArrayLooksLikeMouseData = function(timestampMsArray, maxBufferSize, mouseFrequencyThresholdHz) {
1657        var bufferFull = timestampMsArray.length === maxBufferSize;
1658        var bufferDurationThresholdMs = maxBufferSize / mouseFrequencyThresholdHz * 1000;
1659        if (bufferFull) {
1660            var firstTimestampMsInBuffer = timestampMsArray[0];
1661            var lastTimestampMsInBuffer = timestampMsArray[timestampMsArray.length - 1];
1662            if (bufferDurationThresholdMs >= lastTimestampMsInBuffer - firstTimestampMsInBuffer) {
1663                return true;
1664            }
1665        }
1666        return false;
1667    };
1668
1669    MouseDetectionAgent.prototype.resetMouseFoundAndBuffer = function() {
1670        this.mouseMoveTimestampMsBuffer = [];
1671        this.mouseFound = false;
1672        $rootScope.$broadcast('did-lose-mouse');
1673    };
1674
1675    MouseDetectionAgent.prototype.tellMouseFound = function() {
1676        this.mouseFound = true;
1677        $rootScope.$broadcast('did-detect-mouse');
1678    };
1679
1680    MouseDetectionAgent.prototype.stopWatching = function() {
1681        $('body').off('mousemove.MouseDetectionAgent');
1682    };
1683
1684    MouseDetectionAgent.prototype.startWatching = function() {
1685        var self = this
1686            , maxBufferSize = 10;
1687        $('body').on('mousemove.MouseDetectionAgent', function() {
1688            if (self.mouseMoveTimestampMsBuffer.length >= maxBufferSize) {
1689                self.mouseMoveTimestampMsBuffer.shift();
1690            }
1691
1692            self.mouseMoveTimestampMsBuffer.push((new Date).valueOf());
1693
1694            if (self.timestampMsArrayLooksLikeMouseData(self.mouseMoveTimestampMsBuffer, maxBufferSize, 10)) {
1695                self.tellMouseFound();
1696                self.stopWatching();
1697            }
1698        })
1699    };
1700
1701    return new MouseDetectionAgent();
1702}]);
1703;
1704"use strict";
1705
1706angular.module('Lexus.Services')
1707    .service('PricingService', ['$filter', 'API', function ($filter, API) {
1708        var params = {
1709            defaults: {
1710                siteID: Lexus.ID.Site,
1711                gradeId: null,
1712                postCode: null,
1713                pricingZone: null,
1714                enablePricingByZone: false
1715            }
1716        };
1717
1718        params.params = {
1719            siteID: params.defaults.siteID,
1720            gradeId: params.defaults.gradeId,
1721            postCode: params.defaults.postCode,
1722            pricingZone: params.defaults.pricingZone,
1723            enablePricingByZone: params.defaults.enablePricingByZone
1724        };
1725
1726        var service = {
1727            driveAwayByGrade: driveAwayByGrade,
1728            driveAwayByEnhancementPack: driveAwayByEnhancementPack,
1729            detailPriceByPaint: detailPriceByPaint,
1730            setParam: setParam,
1731            getParam: getParam
1732        };
1733
1734        return service;
1735
1736        function driveAwayByGrade(gradeID, postCode, pricingZone) {
1737            var endpoint = Lexus.API.Lookup.DriveAwayByGrade;
1738            var location = postCode;
1739
1740            if (params.params.enablePricingByZone === true && pricingZone) {
1741                endpoint = Lexus.API.Lookup.DriveAwayByGradeByZone;
1742                location = pricingZone;
1743            }
1744
1745            var apiUrl = $filter('formatArgs')(endpoint, [
1746                params.params.siteID,
1747                gradeID,
1748                location
1749            ]);
1750            return API.get(apiUrl);
1751        }
1752
1753        function driveAwayByEnhancementPack(gradeID, enhancementPack, postCode, pricingZone) {
1754            var endpoint = Lexus.API.Lookup.DriveAwayByEnhancementPack;
1755            var location = postCode;
1756
1757            if (params.params.enablePricingByZone === true && pricingZone) {
1758                endpoint = Lexus.API.Lookup.DriveAwayByEnhancementPackByZone;
1759                location = pricingZone;
1760            }
1761
1762            var apiUrl = $filter('formatArgs')(endpoint, [
1763                params.params.siteID,
1764                gradeID,
1765                enhancementPack,
1766                location
1767            ]);
1768            return API.get(apiUrl);
1769        };
1770
1771        function detailPriceByPaint(gradeId, variantId, trimId, materialCode, postCode, pricingZone) {
1772            var endpoint = Lexus.API.Lookup.DetailPriceByPaint;
1773            var location = postCode;
1774
1775            if (params.params.enablePricingByZone === true && pricingZone) {
1776                endpoint = Lexus.API.Lookup.DetailPriceByPaintByZone;
1777                location = pricingZone;
1778            }
1779            var apiUrl = $filter('formatArgs')(endpoint, [
1780                params.params.siteID,
1781                gradeId,
1782                variantId,
1783                trimId,
1784                materialCode,
1785                location
1786            ]);
1787
1788            return API.get(apiUrl);
1789        };
1790
1791        // Update a search parameter.
1792        function setParam(name, value) {
1793            params.params[name] = value;
1794        };
1795
1796        // Get a parameter value
1797        function getParam(name) {
1798            return params.params[name];
1799        };
1800    }]);
1801;
1802"use strict";
1803
1804angular.module('Lexus.Services')
1805.service('RepairersService', ['$timeout', '$filter', '$http', '$rootScope', 'API', function Map($timeout, $filter, $http, $rootScope, API) {
1806    var self = this;
1807    var params = {};
1808
1809    params.defaults = {
1810        siteID: Lexus.ID.Site,
1811        state: 'null'
1812    };
1813
1814    params.params = {
1815        siteID: params.defaults.siteID,
1816        state: params.defaults.state
1817    };
1818
1819    self.GetResults = function () {
1820        params.params.searchQueryUrl = $filter('formatArgs')(Lexus.API.Lookup.RepairerByState, [
1821            params.params.siteID,
1822            params.params.state
1823        ]);
1824
1825        API.get(params.params.searchQueryUrl)
1826            .then(function (data) {
1827                $rootScope.$broadcast('repairers-search-results-received', data);
1828            }, function (reason) {
1829                $rootScope.$broadcast('repairers-search-results-error', reason);
1830            });
1831    }
1832
1833    // Update a search parameter.
1834    self.setParam = function (name, value) {
1835        params.params[name] = value;
1836    };
1837
1838    // Get a parameter value
1839    self.getParam = function (name) {
1840        return params.params[name];
1841    };
1842
1843
1844    return self;
1845}]);;
1846"use strict";
1847
1848angular.module('Lexus.Services')
1849.service('UserPreferencesService', ['$filter', '$rootScope', '$cookies', 'API', '$location', '$log',
1850    function ($filter, $rootScope, $cookies, API, $location, $log) {
1851    var self = this;
1852    self.$location = $location;
1853    var params = {};
1854    var version = Lexus.ID.CacheVersion;
1855    var cookieOptions = { domain: '.lexus.com.au', path: '/' };
1856
1857    params.defaults = {
1858        visitor: {
1859            postCode: null,
1860            suburb: null,
1861            state: null,
1862            pricingZone: null,
1863        },
1864        dealer: {
1865            dealerID: null, //This dealerID a composite key ex. 050055-22541-12345
1866            dealerCodeSimple: null,
1867            branchCode: null,
1868            dealerName: null,
1869            dealerType: null,
1870            dealerUrl: null,
1871            dealerState: null
1872        },
1873        vehicle: {
1874            model: null,
1875            grade: null,
1876            engineVariant: null,
1877            enhancementPack: null,
1878            trim: null,
1879            colour: null,
1880            materialCode: null
1881        },
1882        compareVehicles: {
1883            model: null,
1884            bodyType:null,
1885            modelsBrowsed: null,
1886            variantsSelected:null
1887        },
1888        analytics: {
1889            identifier: null
1890        },
1891        languagePref: {
1892            translate: null
1893        },
1894        personalization: {
1895            lifestyle: null,
1896        }
1897    };
1898
1899    params.params = {
1900        visitor: {
1901            postCode: params.defaults.visitor.postCode,
1902            suburb: params.defaults.visitor.suburb,
1903            state: params.defaults.visitor.state,
1904            pricingZone: params.defaults.visitor.pricingZone,
1905        },
1906        dealer: {
1907            dealerID: params.defaults.dealer.dealerID,
1908            dealerCodeSimple: params.defaults.dealer.dealerCodeSimple,
1909            branchCode: params.defaults.dealer.branchCode,
1910            dealerName: params.defaults.dealer.dealerName,
1911            dealerType: params.defaults.dealer.dealerType,
1912            dealerUrl: params.defaults.dealer.dealerUrl,
1913            dealerState: params.defaults.dealer.dealerState
1914        },
1915        vehicle: {
1916            model: params.defaults.vehicle.model,
1917            grade: params.defaults.vehicle.grade,
1918            engineVariant: params.defaults.vehicle.engineVariant,
1919            enhancementPack: params.defaults.vehicle.enhancementPack,
1920            trim: params.defaults.vehicle.trim,
1921            colour: params.defaults.vehicle.colour,
1922            materialCode: params.defaults.vehicle.materialCode
1923        },
1924        compareVehicles: {
1925            model: params.defaults.compareVehicles.model,
1926            bodyType: params.defaults.compareVehicles.bodyType,
1927            modelsBrowsed: params.defaults.compareVehicles.modelsBrowsed,
1928            variantsSelected: params.defaults.compareVehicles.variantsSelected
1929        },
1930        analytics: {
1931            identifier: params.defaults.analytics.identifier
1932        },
1933        languagePref: {
1934            translate: params.defaults.languagePref.translate
1935        },
1936        personalization: {
1937            lifestyle: params.defaults.personalization.lifestyle,
1938        }
1939    };
1940
1941    // **************************************
1942    // Init.
1943    // **************************************
1944
1945    self.init = function () {
1946        var visitor = $cookies.getObject('lexus-visitor' + version);
1947        var dealer = $cookies.getObject('lexus-dealer' + version);
1948        var vehicle = $cookies.getObject('lexus-vehicle' + version);
1949        var compareVehicles = $cookies.getObject('lexus-compare-vehicles' + version);
1950        var analytics = $cookies.getObject('lexus-analytics' + version);
1951        var languagePref = $cookies.getObject('lexus-analytics' + version);
1952        var personalization = $cookies.getObject('lexus-personalization' + version);
1953
1954        if (visitor) params.params.visitor = visitor;
1955        if (dealer) params.params.dealer = dealer;
1956        if (vehicle) params.params.vehicle = vehicle;
1957        if (compareVehicles) params.params.compareVehicles = compareVehicles;
1958        if (analytics) params.params.analytics = analytics;
1959        if (languagePref) params.params.languagePref = languagePref;
1960        if (personalization) params.params.personalization = personalization;
1961
1962    };
1963
1964    $rootScope.$on('preferred-dealer-change', function (event, dealer) {
1965        if (!dealer.dealerID || dealer.dealerID == 0) {
1966            $cookies.remove('lexus-dealer' + version, cookieOptions);
1967        } else {
1968            $cookies.putObject('lexus-dealer' + version, dealer, cookieOptions); 
1969        }
1970    });
1971
1972    self.setVisitorCookie = function (event, visitor) {
1973        //all or nothing / don't write the cookie unless we have postcode, state and suburb (or all are empty) - forms dealing with the user's address need all three
1974        if ((visitor.postCode && visitor.suburb && visitor.state) || (!visitor.postCode && !visitor.suburb && !visitor.state)) {
1975            $cookies.putObject('lexus-visitor' + version, visitor, cookieOptions);
1976        } else {
1977            console.error("Incomplete visitor cookie")
1978        }
1979    };
1980
1981    self.setAnalyticsCookie = function (analytics) {
1982        if (analytics) {
1983            $cookies.putObject('lexus-analytics' + version, analytics);
1984        } else {
1985            console.error("No analytics info.")
1986        }
1987    };
1988
1989    self.getAnalyticsCookie = function (urlParameters) {
1990        var result = $cookies.getObject('lexus-analytics' + version) || params.params.analytics;
1991        if (urlParameters) {
1992            result.identifier = urlParameters.identifier || result.identifier;
1993        }
1994        return result;
1995    } 
1996
1997    self.setLanguagePref = function (languagePref, pageGuid) {
1998        if (languagePref) {
1999            $cookies.putObject('lexus-language-pref-' + pageGuid + version, languagePref);
2000        } else {
2001            console.error("No language preference info.")
2002        }
2003    };
2004
2005    self.getLanguagePref = function (pageGuid) {
2006        var result = $cookies.getObject('lexus-language-pref-' + pageGuid + version) || params.params.languagePref;
2007        return result;
2008    } 
2009
2010    self.setPageVisited = function (event, pageId) {
2011        if (!pageId || pageId == "") {
2012            console.warn("Visit not recorded for page " + window.location.pathname + ", guid is null or empty");
2013            return;
2014        }
2015        var cookieName = 'lexus-page-visit' + version;
2016        var visitedPages = $cookies.getObject(cookieName) || [];
2017        if (visitedPages.find(function (id) { return id === pageId; }) == null) {
2018            visitedPages.push(pageId);
2019            $cookies.putObject(cookieName, visitedPages);
2020        }
2021    };
2022    $rootScope.$on('record-page-visit', self.setPageVisited);
2023
2024    //functionality for these two event handlers has converged except for the casing of postcode, todo: refactor out one of these
2025    $rootScope.$on('visitor-details-cookie-change', self.setVisitorCookie);
2026    $rootScope.$on('visitor-details-change-autocomplete', function (event, visitor) {
2027        if (visitor.postcode) {
2028            visitor.postCode = visitor.postcode;
2029            delete visitor.postcode;
2030        }
2031        self.setVisitorCookie(event, visitor);
2032    });
2033
2034    $rootScope.$on('visitor-details-session-change', function (event, visitor) {
2035        return API.post($filter('formatArgs')(Lexus.API.User.SetPreferences, [Lexus.ID.Site]), visitor);
2036
2037    });
2038
2039    $rootScope.$on('language-pref-change', function (event, languagePref) {
2040        self.setLanguagePref(event, languagePref);
2041    });   
2042
2043    $rootScope.$on('vehicle-preference-change', function (event, vehicle) {
2044        //$log.log('vehicle-preference-change');
2045        //$log.log(vehicle);
2046        $cookies.putObject('lexus-vehicle' + version, vehicle, cookieOptions);
2047    });
2048
2049    $rootScope.$on('compare-vehicle-change', function (event, compareVehicleState) {        
2050        $cookies.putObject('lexus-compare-vehicles' + version, compareVehicleState);
2051    });
2052
2053    $rootScope.$on('personalization-change', function (event, personalization) {
2054        $cookies.putObject('lexus-personalization' + version, personalization);
2055     });
2056
2057    self.getAutocompleteTagFromVisitor = function (visitor) {
2058        return {
2059            text: visitor.suburb + " " + visitor.postCode,
2060            data: {
2061                postcode: visitor.postCode,
2062                suburb: visitor.suburb,
2063                state: visitor.state,
2064                pricingZone: visitor.pricingZone
2065            }
2066        };
2067    }
2068
2069    self.splitAutoCompleteResult = function (suggestion) {
2070        var msg = "splitAutoCompleteResult deprecated: use <postcode-result>.data directly instead of splitting <postcode-result>.text";
2071        console.warn ? console.warn(msg) : console.log(msg);
2072        var visitor = {
2073            postCode: suggestion.slice(suggestion.lastIndexOf(' ') + 1, suggestion.length).trim(),
2074            suburb: suggestion.slice(0, suggestion.lastIndexOf(' ')).trim()
2075        };
2076        return visitor;
2077    }
2078
2079    // Get a parameter value
2080    self.getParam = function (name) {
2081        return params.params[name];
2082    };
2083
2084    self.getLexusDealer = function (urlParameters) {
2085        var result = $cookies.getObject('lexus-dealer' + version) || params.params.dealer;
2086        if (urlParameters) {
2087            result.dealerID = urlParameters.dealerID || result.dealerID;
2088            result.dealerCodeSimple = urlParameters.dealerCodeSimple || result.dealerCodeSimple;
2089            result.dealerName = urlParameters.dealerName || result.dealerName;
2090            result.dealerState = urlParameters.dealerState || result.dealerState;
2091        }
2092        return result;
2093
2094    }
2095
2096    self.getLexusVisitor = function (urlParameters) {
2097        var result = $cookies.getObject('lexus-visitor' + version) || params.params.visitor;
2098        if (urlParameters) {
2099            //overrides for visitor?
2100        }
2101        return result;
2102    }
2103
2104    self.getLexusVehicle = function (urlParameters) {
2105        var result = $cookies.getObject('lexus-vehicle' + version) || params.params.vehicle;
2106        if (urlParameters) {
2107            result.model = urlParameters.model || result.model;
2108            result.grade = urlParameters.grade || result.grade;
2109            result.engineVariant = urlParameters.engineVariant || result.engineVariant;
2110            result.enhancementPack = urlParameters.enhancementPack || result.enhancementPack;
2111            result.trim = urlParameters.trim || result.trim;
2112            result.colour = urlParameters.colour || result.colour;
2113            result.materialCode = urlParameters.materialCode || result.materialCode;
2114        }
2115        return result;
2116    }
2117
2118    self.isPageVisited = function (pageId) {
2119        if (!pageId || pageId == "") {
2120            return false;
2121        }
2122
2123        var cookieName = 'lexus-page-visit' + version;
2124        var pages = $cookies.getObject(cookieName);
2125        if (pages && pages.length > 0) {
2126            return pages.indexOf(function (item) { return item == pageId; }) != null;
2127        }
2128    }
2129
2130    self.getLexusVisitorSession = function () {
2131        return API.get($filter('formatArgs')(Lexus.API.User.GetPreferences, [Lexus.ID.Site]));
2132    }
2133
2134    //#region UTM parameters - cookies disabled - incompatible with Eloqua handling of UTM campaign
2135    //self.setUtmParameters = function (utmParameters) {
2136    //    $cookies.putObject('lexus-utm', utmParameters);
2137    //};
2138
2139    self.getUtmParameters = function () {
2140        //var utmParameters = $cookies.getObject('lexus-utm');
2141        var utmParams = ["utm_campaign", "utm_medium", "utm_source", "utm_content"];
2142        var result = {};
2143        var queryParams = this.$location.search();
2144        for (var idx = 0, pLen = utmParams.length; idx < pLen; idx++) {
2145            var param = utmParams[idx];
2146            if(typeof param == 'string'){
2147                result[param.replace(/utm_/, '')] = queryParams[param];
2148            }
2149        }
2150            
2151        return result;
2152    };
2153    //#endregion
2154
2155    self.getEloquaCustomerGUID = function () {
2156
2157        if (typeof (GetElqCustomerGUID) == "function") {
2158            return GetElqCustomerGUID();
2159        }
2160        else {
2161            return "";
2162        }
2163    }
2164
2165    self.getCompareVehiclesState = function (urlParameters) {
2166        var result = $cookies.getObject('lexus-compare-vehicles' + version) || params.params.compareVehicles;
2167        if (urlParameters) {
2168            result.model = urlParameters.model || result.model;
2169            result.bodyType = urlParameters.bodyType || result.bodyType;
2170            result.modelsBrowsed = urlParameters.modelsBrowsed || result.modelsBrowsed;
2171            result.variantsSelected = urlParameters.variantsSelected || result.variantsSelected;
2172        }
2173        return result;
2174    }
2175
2176    self.getPersonalizationState = function (urlParameters) {
2177         var result = $cookies.getObject('lexus-personalization' + version) || params.params.personalization;
2178         if (urlParameters) {
2179             result.lifestyle = urlParameters.lifestyle || result.lifestyle;             
2180         }
2181         return result;
2182        }
2183
2184    self.init();
2185
2186    return self;
2187}]);
2188
2189;
2190"use strict";
2191
2192angular.module('Lexus.Services')
2193.service('VehicleService', ['$timeout', '$filter', '$http', 'API', '$rootScope', function ($timeout, $filter, $http, API, $rootScope) {
2194    var self = this;
2195    var params = {};
2196
2197    params.defaults = {
2198        siteID: Lexus.ID.Site,
2199        bodyTypeId: null,
2200        modelId: null,
2201        engineVariants: []
2202    };
2203
2204    params.params = {
2205        siteID: params.defaults.siteID,
2206        bodyTypeId: params.defaults.bodyTypeId,
2207        modelId: params.defaults.modelId,
2208        engineVariants: params.defaults.engineVariants
2209    };
2210
2211    self.getBodyTypes = function () {
2212        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleBodyTypes, [
2213            params.params.siteID
2214        ]);
2215        return API.get(apiUrl);
2216    };
2217
2218    self.getVehiclesOverview = function () {
2219        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehiclesOverview, [
2220            params.params.siteID
2221        ]);
2222        return API.get(apiUrl);
2223    };
2224
2225    self.getModels = function (bodyTypeId) {
2226        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleModels, [
2227            params.params.siteID,
2228            bodyTypeId
2229        ]);
2230        return API.get(apiUrl);
2231    };
2232
2233    self.getEngineVariants = function (modelId) {
2234        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleEngineVariants, [
2235            params.params.siteID,
2236            modelId
2237        ]);
2238        return API.get(apiUrl);
2239    };
2240
2241    self.getEngineVariantsWithGrades = function (modelId) {
2242        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleEngineVariantsWithGrades, [
2243            params.params.siteID,
2244            modelId
2245        ]);
2246        return API.get(apiUrl);
2247    };
2248
2249    self.getTrims = function (gradeID, variantId) {
2250        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleTrims, [
2251            params.params.siteID,
2252            gradeID,
2253            variantId
2254        ]);
2255        return API.get(apiUrl);
2256    };
2257
2258    self.getPaints = function (gradeID, variantId, trimId) {
2259        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehiclePaints, [
2260            params.params.siteID,
2261            gradeID,
2262            variantId,
2263            trimId
2264        ]);
2265        return API.get(apiUrl);
2266    };
2267
2268    self.getVariants = function (gradeId) {
2269        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleVariants, [
2270            params.params.siteID,
2271            gradeId
2272        ]);
2273        return API.get(apiUrl);
2274    };
2275
2276    self.getEnhancementPacks = function (gradeId) {
2277        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleEnhancementPacks, [
2278            params.params.siteID,
2279            gradeId
2280        ]);
2281        return API.get(apiUrl);
2282    };
2283
2284    self.getVehicleImages = function (gradeId, variantId, trimId, materialCode) {
2285        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleImages, [
2286            params.params.siteID,
2287            gradeId,
2288            variantId,
2289            trimId,
2290            materialCode
2291        ]);
2292        return API.get(apiUrl);
2293    };
2294
2295    self.getVehiclePanoXMLUrl = function (gradeId, variantId, trimId, materialCode) {
2296        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.PanoXML, [
2297            params.params.siteID,
2298            gradeId,
2299            variantId,
2300            trimId,
2301            materialCode
2302        ]);
2303        return apiUrl;
2304    };
2305
2306
2307
2308    //self.getPaintNTrimDetails = function (variantID) {
2309    //    var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.PaintNTrimByGrade, [
2310    //        params.params.siteID,
2311    //        variantID
2312    //    ]);
2313    //    return API.get(apiUrl);
2314    //}
2315
2316    // Update a search parameter.
2317    self.setParam = function (name, value) {
2318        params.params[name] = value;
2319    };
2320
2321    // Get a parameter value
2322    self.getParam = function (name) {
2323        return params.params[name];
2324    };
2325
2326    return self;
2327}]);
2328;
2329"use strict";
2330
2331angular.module('Lexus.Services')
2332.service('PreLaunchVehicleService', ['$timeout', '$filter', '$http', 'API', '$rootScope', function ($timeout, $filter, $http, API, $rootScope) {
2333    var self = this;
2334    var params = {};
2335
2336    params.defaults = {
2337        siteID: Lexus.ID.Site
2338    };
2339
2340    params.params = {
2341        siteID: params.defaults.siteID
2342    };
2343
2344    self.getVehiclesOverview = function () {
2345        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehiclesOverviewPreLaunch, [
2346            params.params.siteID
2347        ]);
2348        return API.get(apiUrl);
2349    }
2350   
2351    self.getTrims = function (gradeID, variantId) {
2352        var apiUrl = $filter('formatArgs')(Lexus.API.Lookup.VehicleTrims, [
2353            params.params.siteID,
2354            gradeID, 
2355            variantId
2356        ]);
2357        return API.get(apiUrl);
2358    }
2359    
2360    return self;
2361}]);
2362;
2363"use strict";
2364
2365angular.module("Lexus.Services")
2366    .factory("EloquaService", ["UserPreferencesService", function EloquaService(UserPreferencesService)
2367    {
2368        var service = {
2369            attachEloquaFields: AttachEloquaFields
2370        };
2371        return service;
2372
2373        //Takes an object and attaches the corresponding eloqua fields to it
2374        function AttachEloquaFields(toObject) {
2375            var elqCustomerGUID = UserPreferencesService.getEloquaCustomerGUID();
2376
2377            if (toObject) {
2378                toObject.ElqCustomerGUID = elqCustomerGUID;
2379            }
2380            return toObject;
2381        }
2382    }]);
2383
2384
2385;
2386'use strict';
2387
2388angular.module('Lexus.Services').service('ViewportService', [
2389    '$window',
2390    function($window) {
2391        var self = this;
2392
2393        self.isMobile = function() {
2394            return $window.innerWidth < Lexus.Breakpoint.Small
2395        };
2396
2397        self.isDesktop = function() {
2398            return !self.isMobile();
2399        }
2400
2401        return self;
2402    }
2403]);
2404;
2405'use strict';
2406
2407(function() {
2408    angular.module("Lexus.Services").service("FontIconService", function () {
2409        var self = this;
2410
2411        self.generateDataURLfromIcon = function (size, unicode, color) {
2412            var canvas = document.createElement("canvas");
2413            var ctx = canvas.getContext("2d");
2414
2415            canvas.height = size;
2416            canvas.width = size;
2417            ctx.fillStyle = color;
2418            ctx.font = size + "px icomoon";
2419            ctx.textAlign = "center";
2420            ctx.textBaseline = "middle";
2421            ctx.fillText(unicode, size / 2, size / 2);
2422
2423            return canvas.toDataURL("image/png");
2424        };
2425
2426        // hack to fix the above function resulting in wrong icon by icomoon.
2427        self.generateCursorIcon = function () {
2428            // FontIconService.generateDataURLfromIcon(32, '\ue92b', '#f0eee6');
2429            var lightBoxCursor =
2430                "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACf0lEQVRYR+2XvWsUYRCHn9lTxCLs7Z5Fbg/BSlBQsLOw8KPyP9AETTAokoiCSoqIiIgpghEUFVEiiRL1P7DSWFjYCREUbBTk9lJ4u3umENG7kffihSPxdtdE3CZbvbAz83veeb9mJAr8j8AWOn+fsKyBfL77RYzNsl9RNLefRmMiKbZEgf8V6IoJ/hnL6l8hwCSwOSb2/FKAusC07Xp9fzPbtLa1wJ9S6AVyv30WAQT4AOwC6qJM2wWvP23gNHa1qj+psij+BtgKaCsDqMhpQXtRDgA/EB7mHe94muBJNlHo30c5CqxHeK7ItKjeNH6LACIM/Ww0ZnLIFCL7gO+KTjhu6VSSQNz/MCjfEmQA2IDqTB3tW2dZ+1S5vQzAdrxH89XK9oboXYU9BgK4nne9CyuBiAL/KnDWiAu8slROdhWK72qhf6QNoBw1SZBB2/Uem7GBqIua8c6VQrSLA7M5lR4jbuLXAr9H0TutDIyYQd71RttnuQDBE1AD8U2VcafgXUyTibDqXxHhHLARZDanHG6Jt/yjwG/qmt3f8VsCMS8i47ZTvBznUwsrl1TViHd1Em/3jwVopqs6t1ulcQ/YAVoDRvNuaexPEFFQHgZGQGzgrah1wi50v44DTgQwzmFY2SvaXLNtQCDokO2WnrYHrgXlQ4qYne0C71Vk0HGKL5OWLBXAwsapHFTVGwibVPW8Uyg9aA8eVsvHROQayhcROWO7xWdJ4ol7IE2A1dqkzsBqhTr5rwGsZSB1BtqOoauqwx2O4RhK8M+PYaYXUaZX8X95jFrPYmbPcRRkX5CYvgBTE2ZUkjUbkyZApkVp1mV55o1JqzfMrDXLtDnNtD3/BcgX4+AyBu25AAAAAElFTkSuQmCC";
2431
2432            // FontIconService.generateDataURLfromIcon(32, '\ue90f', '#e4e1d7');
2433            var prevCursor =
2434                "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB+klEQVRYR8WXv2sTYRjHP08KbTMUulTQ5HJxKF4ijoKDm4N1UQQHhy5VKhSkHToUB6md1KGDpWCp1IIIOnYoKPgPCI5ic6WDvV7OQrsUOsQfbR654WKWpJfc1ff+gPt87vu+9/wQTukJPPeVoqOq8tEqOrdbYSRt/r7rDvzO6kuFu0BG4ZNll67/F4EQ/iur74CRCN5fkztDjnN46gLV6lae46NV4BpQB9b6ajLWDh5KpXIEu9638hE9S4JeDeEC73trMnESPBWBEH6MrIJcBv4I8iZnO+Nx71aiBKrfN66QkWXgEvBThRWrUHoYF54oAX9784ZI/QUwDNQQnc8Xyo87gXctEOy4txRdQCkAhwLzObs01ym8KwHfc0cFfQ6cAzlQmLVsZ6EbeMcCwbZ7T0WfAmeAPVF5lCs6r7uFdyTge+6kwBzoIPBDkRnLdt4mgccWCLzKrMI0MAB4qpkJq3jhQ1J4LIHAc58pOglkgS3VzFRa8BMF/J3Koij3gX7gK3V9kD9f/pzGl0fvaFmIfK/yRGAG6AP90oOOnbUvbqQJb5uA
2434cYHQzugRRFEbvYT/JAz+hpGE0ULUSMJkKW5KwlwzaiRhsh03kjA5kEQSRkeySMLoUNosYWwsbxyHycUkkjC6mjVLNC+nwHreLt1s1cYTLSbtZoO46/lfr4cXMA1Qm1oAAAAASUVORK5CYII=";
2435
2436            // FontIconService.generateDataURLfromIcon(32, '\ue910', '#e4e1d7');
2437            var nextCursor =
2438                "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAB70lEQVRYR8XXP2jUcBTA8e+7KNcOIggOklxzDsVcBTfBUTu1UztWB20HF5E6FBR1EBEEBQeLlEKhrULp2k51at3dCr1GOrTpXXEQhNKhil6eJJ7YwfRyl+AvY/IL78Pv33tPSHjqO34/wjxoUVQe2mVvNmlslveSAlACviLy2OnxprME+9e/iYBo8N6uP6SqbwAH2Bf0ue32vcwTcSwgClTb+TQoEr4GeoEDgVe2W3maF6IlIEZsb16Vgk6BVIBvikyWXO9BHohUgChQfbt6BUumUS4BPwR5Z7ve7ayI1IAo0Odgo6+BzIFcBhrAguNWRrMg2gLEM1Hfcmj8fAtcA0JgqXgoY2c976ATSNuAKMgX3z/1vVsXgYFm0PfFQ7neCaIjwBHEHDAMFIA1rBO3HKe33s5MdAz4iwij0zECWKAfLXTsnHuxmhaRCfAnyF7gzyh6EziJsG5peCMtIhdAfFcE/gtBx4Eu0E0N5U7pfOVDq5nIDRCfkN3qM1QmgG5gS7Vwr1S+sHIcIldAnD+C6n1FHgGnI5OI3LV7vOUkRO6A5nKMC/oEOANSQxl1yt5q29mw1folfa8FvjmA0SUwugmNHkNjF9HvpGToKm5mRDPJyGg6NlqQGC3JjBalRstyo42J0dbsSHdspjn9X+35L8RtDDDXC10pAAAAAElFTkSuQmCC";
2439
2440            return { lightBoxCursor, prevCursor, nextCursor };
2441        };
2442
2443        return self;
2444    });
2445})();
2446;
2447"use strict";
2448angular.module('Lexus.Directives')
2449.directive('lxAccordion', ['$window', '$timeout', '$filter', '$http', function ($window, $timeout, $filter, $http) {
2450    accordionController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$rootScope'];
2451    return {
2452        scope: true,
2453        restrict: 'A',
2454        controller: accordionController,
2455        controllerAs: 'ac',
2456        bindToController: true,
2457        link: function (scope, element, attrs, filter, http) {
2458        }
2459    };
2460    
2461    function accordionController($scope, $http, $filter, $timeout, $window, $interval, $rootScope) {
2462        var ac = this;
2463        $scope.init = function (collapseOthersOnExpansion, broadcastSelection) {
2464            ac.panels = [];
2465            ac.collapseOthersOnExpansion = collapseOthersOnExpansion;
2466            ac.broadcastSelection = broadcastSelection;
2467        };
2468
2469        $scope.toggleVisible = function (i) {
2470            var becomeVisible = !ac.panels[i].visible;
2471            ac.panels[i].visible = becomeVisible;
2472
2473            // On first open, init content gallery
2474            if (becomeVisible && !ac.panels[i].hasBeenVisible) {
2475                $scope.$broadcast('broadcast.contentGalleryReload', { targetId: ac.panels[i].id });
2476                ac.panels[i].hasBeenVisible = true;
2477            }
2478
2479            if (becomeVisible == true && ac.collapseOthersOnExpansion) {
2480                for (var j = 0; j < ac.panels.length; j++) {
2481                    if (j != i) {
2482                        ac.panels[j].visible = false;
2483                    }
2484                }
2485            }
2486
2487            if (ac.broadcastSelection) {
2488                $rootScope.$broadcast('variant-selected', { selectedVariantIndex: i });
2489            }
2490        };
2491
2492        $scope.initPanel = function (i, initiallyExpanded) {
2493            ac.panels[i] = {};
2494            ac.panels[i].id = i;
2495            ac.panels[i].visible = initiallyExpanded;
2496            ac.panels[i].hasBeenVisible = false;
2497        };
2498
2499        // Check if the URL has a hash link that should set an accordion item open by default.
2500        $scope.expandItemByDefault = function (divId) {
2501            return ($window.location.hash === divId) ? true : false;
2502        };
2503
2504    }
2505}]);
2506
2507
2508;
2509"use strict";
2510angular.module('Lexus.Directives')
2511    .directive('lxAnchorMenuBar', ['$window', function ($window) {
2512        lxAnchorMenuBarController.$inject = ['$scope', '$filter', '$timeout'];
2513        return {
2514            scope: true,
2515            restrict: 'A',
2516            controller: lxAnchorMenuBarController,
2517            controllerAs: 'amc',
2518            bindToController: {
2519                modelName: '@'
2520            },
2521            link: function (scope, element, attrs) {
2522                scope.currentlyScrolledId = '';
2523
2524                if (attrs.anchorMenuItems) {
2525                    scope.anchorMenuItems = angular.fromJson(attrs.anchorMenuItems);
2526
2527                    var transitionDelay = 0;
2528                    for (var i = scope.anchorMenuItems.length - 1; i >= 0; i--) {
2529                        var anchorMenuItem = scope.anchorMenuItems[i];
2530                        anchorMenuItem.transitionDelay = transitionDelay;
2531                        scope.anchorMenuItems[i] = anchorMenuItem;
2532                        transitionDelay += 0.15;
2533                    }
2534                }
2535
2536                angular.element($window).bind("scroll", function () {
2537                    calcCurrentlyScrolledIdComponent($window);
2538                });
2539
2540                function calcCurrentlyScrolledIdComponent(window) {
2541                    var currentlyScrolledIdComponent;
2542                    for (var i = 0; i < scope.anchorMenuItems.length; i++) {
2543                        var anchorMenuItem = scope.anchorMenuItems[i];
2544                        var item = $("#" + anchorMenuItem.anchorId)[0];
2545                        if (!item) continue;
2546                        anchorMenuItem.offsetTop = item.offsetTop;
2547
2548                        if (window.pageYOffset + 73 >= anchorMenuItem.offsetTop
2549                            && (currentlyScrolledIdComponent == null || anchorMenuItem.offsetTop > currentlyScrolledIdComponent.offsetTop)) {
2550                            currentlyScrolledIdComponent = anchorMenuItem;
2551                        }
2552                    }
2553                    scope.currentlyScrolledId = currentlyScrolledIdComponent != null ? currentlyScrolledIdComponent.anchorId : scope.anchorMenuItems[0].anchorId;
2554                }
2555            }
2556        };
2557
2558        function lxAnchorMenuBarController($scope, $filter, $timeout) {
2559            this.mobileMenuOpen = false;
2560            this.menuOpenTimeouts = [];
2561            this.menuCloseTimeouts = [];
2562
2563            this.getCurrentlyScrolledId = function () {
2564                return $scope.currentlyScrolledId;
2565            };
2566
2567            this.getCurrentlyActiveItem = function () {
2568                var item = $filter("filter")($scope.anchorMenuItems, { anchorId: $scope.currentlyScrolledId });
2569                return item != null && item.length > 0 ? item[0].title : '';
2570            };
2571
2572            this.toggleMobileAnchorMenu = function () {
2573                this.mobileMenuOpen = !this.mobileMenuOpen;
2574
2575                if (this.mobileMenuOpen == true) {
2576                    if ($scope.anchorMenuItems) {
2577                        for (var i = 0; i < this.menuOpenTimeouts.length; i++) {
2578                            $timeout.cancel(this.menuOpenTimeouts[i]);
2579                        }
2580
2581                        this.menuOpenTimeouts = [];
2582
2583                        for (var i = 0; i < $scope.anchorMenuItems.length; i++) {
2584                            var timeout = $timeout(fireMenuOpened, 600, true, $scope.anchorMenuItems[i]);
2585                            this.menuOpenTimeouts.push(timeout);
2586                        }
2587                    }
2588                }
2589                else {
2590                    if ($scope.anchorMenuItems) {
2591
2592                        for (var i = 0; i < this.menuCloseTimeouts.length; i++) {
2593                            $timeout.cancel(this.menuCloseTimeouts[i]);
2594                        }
2595
2596                        this.menuCloseTimeouts = [];
2597
2598                        for (var i = 0; i < $scope.anchorMenuItems.length; i++) {
2599                            var timeout = $timeout(fireMenuClosed, 0, true, $scope.anchorMenuItems[i]);
2600                            this.menuCloseTimeouts.push(timeout);
2601                        }
2602                    }
2603                }
2604
2605                $('body').removeClass("modal-open");
2606
2607                if (this.mobileMenuOpen) {
2608                    $('body').addClass("modal-open");
2609                }
2610            };
2611
2612            this.closeMobileAnchorMenu = function () {
2613                this.mobileMenuOpen = false;
2614                $('body').removeClass("modal-open");
2615            };
2616
2617            function fireMenuOpened(anchorMenuItem) {
2618                $scope.$broadcast('anchor-menu-opened', anchorMenuItem);
2619            }
2620
2621            function fireMenuClosed(anchorMenuItem) {
2622                $scope.$broadcast('anchor-menu-closed', anchorMenuItem);
2623            }
2624        }
2625    }]);
2626
2627
2628angular.module('Lexus.Directives')
2629    .directive('lxAnchorMenuItem', ['$rootScope', '$anchorScroll', '$location', function ($rootScope, $anchorScroll, $location) {
2630        return {
2631            scope: true,
2632            restrict: 'A',
2633            require: "^lxAnchorMenuBar",
2634            link: function (scope, element, attrs, controller) {
2635                scope.anchorId = attrs.anchorId;
2636
2637                scope.isActive = function () {
2638                    return controller.getCurrentlyScrolledId() == attrs.anchorId;
2639                };
2640
2641                scope.lazyRerender = function () {
2642                    window.setTimeout(() => {
2643                        $rootScope.$digest();
2644                    }, 1100);
2645                }
2646
2647                scope.$on('anchor-menu-opened', function (event, data) {
2648                    if (scope.anchorId == data.anchorId) {
2649                        element.css({
2650                            transition: "opacity 0.25s ease-out " + data.transitionDelay + "s, transform 0.25s ease-out " + data.transitionDelay + "s"
2651                        });
2652                        element.addClass("menu-open");
2653                    }
2654                });
2655
2656                scope.$on('anchor-menu-closed', function (event, data) {
2657                    if (scope.anchorId == data.anchorId) {
2658                       element.removeClass("menu-open");
2659                    }
2660                });
2661
2662            }
2663        }
2664    }]);
2665;
2666'use strict';
2667
2668angular.module('Lexus.angulartics', ['angulartics'])
2669.config(['$analyticsProvider', function ($analyticsProvider) {
2670
2671    $analyticsProvider.settings.ga = {
2672        userId: null
2673    };
2674
2675    $analyticsProvider.registerPageTrack(function (path) {
2676        var dataLayer = window.dataLayer = window.dataLayer || [];
2677        dataLayer.push({
2678            'event': 'content-view',
2679            'content-name': path,
2680            'userId': $analyticsProvider.settings.ga.userId
2681        });
2682    });
2683
2684    $analyticsProvider.registerEventTrack(eventTrack);
2685
2686    function eventTrack(action, properties) {
2687        var dataLayer = window.dataLayer = window.dataLayer || [];
2688        properties = properties || {};
2689
2690        // Only fire when the GA4 event comes from clicking email or phone.
2691        if ((properties.serviceType === "Sales" || properties.serviceType === "Service") && (properties.label === "Phone" || properties.label === "Email")) {
2692            dataLayer.push({
2693                "event": "find_a_dealer",
2694                "target": properties.category,
2695                "action": properties.action,
2696                "target-properties": properties.label,
2697                "value": properties.value,
2698                "interaction-type": properties.noninteraction,
2699                "model": properties.model,
2700                "driveAwayPrice": properties.driveAwayPrice,
2701                "variant": properties.variant,
2702                "userId": $analyticsProvider.settings.ga.userId,
2703                "salesOrService": properties.serviceType,
2704                "dealerSelected": properties.dealer,
2705                "contactMethod": properties.label,
2706            });
2707        }
2708
2709        if (properties.event === "build_and_price") {
2710            dataLayer.push({
2711                ...properties,
2712                "userId": $analyticsProvider.settings.ga.userId,
2713            });
2714            return;
2715        }
2716
2717        dataLayer.push({
2718            'event': properties.event || 'interaction',
2719            'target': properties.category,
2720            'action': properties.action,
2721            'target-properties': properties.label,
2722            'value': properties.value,
2723            'interaction-type': properties.noninteraction,
2724            'model': properties.model,
2725            'driveAwayPrice': properties.driveAwayPrice,
2726            'variant': properties.variant,
2727            'userId': $analyticsProvider.settings.ga.userId
2728        });
2729
2730    }
2731
2732    $analyticsProvider.registerExceptionTrack(function (error, cause) {
2733
2734        eventTrack(error.toString(), {
2735            'category': 'Exceptions',
2736            'label': error.stack
2737        });
2738
2739    });
2740
2741    $analyticsProvider.registerSetUsername(function (userId) {
2742        $analyticsProvider.settings.ga.userId = userId;
2743    });
2744
2745}]);
2746
2747angular.module('Lexus.Directives')
2748.directive('lxGtm', ['$window', '$timeout', '$filter', '$http', '$analytics', function ($window, $timeout, $filter, $http, $analytics) {
2749    gtmController.$inject = ['$rootScope', '$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$analytics', 'UserPreferencesService'];
2750    return {
2751        scope: true,
2752        restrict: 'A',
2753        controller: gtmController,
2754        controllerAs: 'gtm',
2755        bindToController: true,
2756        link: function (scope, element, attrs, filter, http) {
2757            var helpers = {};
2758            helpers.now = Date.now || function () {
2759                return new Date().getTime();
2760            };
2761            helpers.debounce = function (func, wait, immediate) {
2762                var timeout, args, context, timestamp, result;
2763
2764                var later = function () {
2765                    var last = helpers.now() - timestamp;
2766
2767                    if (last < wait && last >= 0) {
2768                        timeout = setTimeout(later, wait - last);
2769                    } else {
2770                        timeout = null;
2771                        if (!immediate) {
2772                            result = func.apply(context, args);
2773                            if (!timeout) context = args = null;
2774                        }
2775                    }
2776                };
2777
2778                return function () {
2779                    context = this;
2780                    args = arguments;
2781                    timestamp = helpers.now();
2782                    var callNow = immediate && !timeout;
2783                    if (!timeout) timeout = setTimeout(later, wait);
2784                    if (callNow) {
2785                        result = func.apply(context, args);
2786                        context = args = null;
2787                    }
2788
2789                    return result;
2790                };
2791            };
2792            helpers.throttle = function (func, wait) {
2793                var context, args, timeout, throttling, more, result;
2794                var whenDone = helpers.debounce(function () { more = throttling = false; }, wait);
2795                return function () {
2796                    context = this; args = arguments;
2797                    var later = function () {
2798                        timeout = null;
2799                        if (more) func.apply(context, args);
2800                        whenDone();
2801                    };
2802                    if (!timeout) timeout = setTimeout(later, wait);
2803                    if (throttling) {
2804                        more = true;
2805                    } else {
2806                        result = func.apply(context, args);
2807                    }
2808                    whenDone();
2809                    throttling = true;
2810                    return result;
2811                };
2812            };
2813
2814            var currentScrollDepth = 0;
2815
2816            var scrollDepth = function (e) {
2817                //whatever
2818                var topEdge = $window.pageYOffset,
2819                    clientHeight = $window.innerHeight,
2820                    fullHeight = document.body.clientHeight,
2821                    calculatedScrollDepth = ((topEdge / (fullHeight - clientHeight)) * 100).toFixed();
2822
2823                var scrollEvent = function (calculated, current) {
2824                    if (calculated != current) {
2825                        currentScrollDepth = calculated;
2826                        //$analytics.eventTrack('Scroll Event', { category: window.location.pathname, action: 'Page Scroll', label: currentScrollDepth + '%', noninteraction: true });
2827                        $analytics.eventTrack('Scroll Event', { event: 'Scroll', category: window.location.pathname, action: 'Page Scroll', label: currentScrollDepth + '%', noninteraction: true });
2828                    }
2829                };
2830
2831                if ((calculatedScrollDepth - currentScrollDepth) > 10 || (calculatedScrollDepth - currentScrollDepth) < -10) {
2832                    scrollEvent(((calculatedScrollDepth / 10).toFixed() * 10), currentScrollDepth);
2833                }
2834                else if (calculatedScrollDepth == 0) {
2835                    scrollEvent(0, currentScrollDepth);
2836                }
2837                else if (calculatedScrollDepth == 100) {
2838                    scrollEvent(100, currentScrollDepth);
2839                }
2840            };
2841
2842            angular.element($window).bind("scroll", helpers.throttle(scrollDepth, 100));
2843        }
2844    };
2845
2846    function gtmController($rootScope, $scope, $http, $filter, $timeout, $window, $interval, $analytics, UserPreferencesService) {
2847        var gtm = this;
2848        gtm.trackers = [];
2849        gtm.forms = {};
2850        gtm.chatTracked = false;
2851
2852        if (window.Element && !Element.prototype.closest) {
2853            Element.prototype.closest =
2854            function (s) {
2855                var matches = (this.document || this.ownerDocument).querySelectorAll(s),
2856                    i,
2857                    el = this;
2858                do {
2859                    i = matches.length;
2860                    while (--i >= 0 && matches.item(i) !== el) { };
2861                } while ((i < 0) && (el = el.parentElement));
2862                return el;
2863            };
2864        }
2865
2866        if (!Element.prototype.matches) {
2867            Element.prototype.matches =
2868                Element.prototype.matchesSelector ||
2869                Element.prototype.mozMatchesSelector ||
2870                Element.prototype.msMatchesSelector ||
2871                Element.prototype.oMatchesSelector ||
2872                Element.prototype.webkitMatchesSelector ||
2873                function (s) {
2874                    var matches = (this.document || this.ownerDocument).querySelectorAll(s),
2875                        i = matches.length;
2876                    while (--i >= 0 && matches.item(i) !== this) { }
2877                    return i > -1;
2878                };
2879        }
2880
2881        function extractContent(s) {
2882            var span = document.createElement('span');
2883            span.innerHTML = s;
2884            return span.textContent || span.innerText;
2885        };
2886
2887        function checkValue(val, el, type) {
2888            var newVal = val;
2889            switch (val) {
2890                case ':path':
2891                    newVal = window.location.pathname;
2892                    break;
2893                case ':link':
2894                    newVal = el.getAttribute('href');
2895                    break;
2896                case ':parentlink':
2897                    newVal = el.parentElement.getAttribute('href');
2898                    break;
2899            }
2900
2901            if (el.matches('[type]') && el.type.toLowerCase() == 'submit' && type == 'label') {
2902                if (angular.element(el).closest('form').hasClass('ng-invalid'))
2903                    newVal += ' - Errors';
2904            }
2905
2906            return newVal.replace(/\n/g, ' ');
2907        }
2908
2909        function getGtmDetails(el) {
2910            var labelEl = el.closest('[data-gtm-label]');
2911            var categoryEl = el.closest('[data-gtm-category]');
2912            var subCategoryEl = el.closest('[data-gtm-sub-category]');
2913            var priceEl = el.closest('[data-gtm-drive-away-price]');
2914            var dealerEl = el.closest("[data-gtm-dealer]");
2915            //temp change: this is for find a dealer only unless refactor to React component
2916            var findADealerEl = el.closest("[data-gtm-find-a-dealer]");
2917
2918            var label = labelEl ? checkValue(labelEl.dataset.gtmLabel, el, 'label') : false;
2919            var subCategory = subCategoryEl ? checkValue((subCategoryEl) ? subCategoryEl.dataset.gtmSubCategory : '', el, 'subCategory') : false;
2920            var category = categoryEl ? checkValue((categoryEl) ? categoryEl.dataset.gtmCategory : '', el, 'category') : false;
2921            var price = priceEl ? checkValue((priceEl) ? priceEl.dataset.gtmDriveAwayPrice : '', el, 'driveAwayPrice') : false;
2922            var dealer = dealerEl ? checkValue(labelEl.dataset.gtmDealer, el, "dealer") : false;
2923            var serviceType = findADealerEl
2924                ? checkValue(findADealerEl.dataset.gtmFindADealer, el, "findADealer")
2925                : false;
2926
2927            if (label && subCategory && category) {
2928                var tracker = {
2929                    el: el,
2930                    category: category,
2931                    subCategory: subCategory,
2932                    label: label,
2933                    driveAwayPrice: price,
2934                    dealer,
2935                    serviceType,
2936                };
2937                return tracker;
2938            }
2939        }
2940
2941        var pushAnalytics = function (label, data) {
2942            var data = data;
2943            var preferences = UserPreferencesService.getLexusVehicle();
2944            data.model = preferences.model;
2945            data.variant = preferences.engineVariant;
2946
2947            $analytics.eventTrack(label.replace(/\s/gi, '-'), data);
2948        };
2949
2950        var analyticsClick = function (event) {
2951            var el = event.target;
2952            $timeout(function () {
2953                var gtmDetails = getGtmDetails(el);
2954                //$analytics.eventTrack(gtmDetails.subCategory, { category: gtmDetails.category, action: gtmDetails.subCategory, label: gtmDetails.label });
2955                if (gtmDetails) {
2956                    pushAnalytics(gtmDetails.subCategory, {
2957                        category: gtmDetails.category,
2958                        action: gtmDetails.subCategory,
2959                        label: gtmDetails.label,
2960                        driveAwayPrice: gtmDetails.driveAwayPrice,
2961                        dealer: gtmDetails.dealer,
2962                        serviceType: gtmDetails.serviceType,
2963                    });
2964                }
2965            });
2966        };
2967
2968        var analyticsBlur = function (event) {
2969            var el = event.target,
2970                details = el.dataset.gtmFormDetails.split('||');
2971            gtm.forms[details[0]][details[1]].touched = true;
2972        };
2973
2974        var chatClick = function (event) {
2975                pushAnalytics('Live Chat', { category: window.location.pathname, action: 'Live Chat', label: 'Chat Clicked' });
2976        };
2977
2978        var addTrackers = function () {
2979            gtm.labels = document.querySelectorAll('[data-gtm-label]');
2980
2981            for (var e = 0, len = gtm.labels.length; e < len; e++) {
2982                var el = gtm.labels[e];
2983                angular.element(el).off('click', analyticsClick);
2984                angular.element(el).on('click', analyticsClick);
2985            };
2986        };
2987
2988        var addForms = function () {
2989            //Exclude SC from which will have its own GTM supported specifically
2990            var forms = document.querySelectorAll('form:not(.lx-scforms)');
2991            for (var f = 0, len = forms.length; f < len; f++) {
2992                var frm = forms[f];
2993                var formName = frm.closest("[data-gtm-sub-category]")?.dataset.gtmSubCategory.replace(/\W/g, "");
2994                gtm.forms[formName] = [];
2995                var inputs = frm.querySelectorAll('input,select,textarea,button');
2996
2997                for (var i = 0, inputLen = inputs.length; i < inputLen; i++) {
2998                    var el = inputs[i];
2999                    var labelText = el.closest('label');
3000                    if (el.type != 'hidden' && el.type != 'submit' && !el.matches('[style*="display: none"')) {
3001                        var inputLabel = null;
3002                        if (inputLabel == null) inputLabel = (el.closest('label')) ? el.closest('label').innerText : null;
3003                        if (inputLabel == null) inputLabel = (el.closest('.lx-form-text-label')) ? el.closest('.lx-form-text-label').innerText : null;
3004                        if (inputLabel == null)
3005                            inputLabel = el.hasAttribute("id")
3006                                ? document.querySelector("[for=" + el.id + "]")?.innerText
3007                                : el.innerText;
3008
3009                        inputLabel =
3010                            inputLabel &&
3011                            inputLabel
3012                                .replace(/^\n/, "")
3013                                .replace(/^\s*/, "")
3014                                .replace(/(.*)\n(.*)/g, "$1")
3015                                .replace(/\W/g, "-")
3016                                .replace(/-{2,}/g, "-");
3017
3018                        var item = { el: el, label: inputLabel, index: gtm.forms[formName].length, touched: false };
3019                        el.setAttribute('data-gtm-form-details', formName + '||' + item.index);
3020
3021                        angular.element(el).on('blur change input', analyticsBlur);
3022                        gtm.forms[formName].push(item);
3023                    }
3024                }
3025            }
3026
3027            //console.log(gtm.forms);
3028
3029        };
3030
3031        var addChat = function () {
3032            var chatButton = document.querySelector('img[id^=LCM]');
3033
3034            if (chatButton && !gtm.chatTracked) {
3035                angular.element(chatButton).on('click', chatClick);
3036                gtm.chatTracked = true;
3037            }
3038        };
3039
3040        var checkForChat = function (delay, count, limit) {
3041            //console.log('checkForChat(' + delay + ',' + count + ',' + limit + ')');
3042            if (typeof LCM != 'undefined' && LCM) {
3043                console.log(LCM);
3044                addChat();
3045            } else if(count < limit){
3046                setTimeout(function () { checkForChat(delay, count + 1, limit); }, delay);
3047            }
3048
3049        };
3050
3051        $rootScope.$on('open-modal-window', function (event, data) {
3052            var errorWords = ['oops', 'error', 'warning', 'unavailable', 'problem'];
3053            var matchString = new RegExp(errorWords.join('|'), 'gi', '');
3054
3055            var modalType = (data.content.match(matchString)) ? 'Error Message' : 'Dialog';
3056
3057            var eventObject = {
3058                category: window.location.pathname,
3059                action: 'Modal ' + modalType,
3060                label: extractContent(data.content)
3061            };
3062            //console.log(eventObject);
3063            //$analytics.eventTrack('Modal Window', eventObject);
3064            pushAnalytics('Modal Window', eventObject);
3065
3066        });
3067
3068        $scope.$on('gtm-form-success', function (event, data, labelSuffix) {
3069            var category = window.location.pathname;
3070            var action = data; //  Form name
3071            var label = 'Form Submission Success' + (labelSuffix != null ? " - " + labelSuffix : "");
3072            pushAnalytics(category, { category: category, action: action, label: label });
3073        });
3074
3075        $scope.$on('gtm-form-failure', function (event, data) {
3076            var category = window.location.pathname;
3077            var action = data; //  Form name
3078
3079            pushAnalytics(category, { category: category, action: action, label: 'Form Submission Failure' });
3080        });
3081
3082        function pushVehicleAnalytics(area, vehicle, eventData) {
3083
3084            if (vehicle.carmodel != null && vehicle.carmodel != "")
3085                pushAnalytics(area, { category: area, action: 'Model', label: eventData.source + ': ' + vehicle.carmodel });
3086            if (vehicle.engineVariant != null && vehicle.engineVariant != "")
3087                pushAnalytics(area, { category: area, action: 'Variant', label: eventData.source + ': ' + vehicle.engineVariant });
3088            if (vehicle.grade != null && vehicle.grade != "")
3089                pushAnalytics(area, { category: area, action: 'Grade', label: eventData.source + ': ' + vehicle.grade });
3090            if (vehicle.enhancementPack != null && vehicle.enhancementPack != "")
3091                pushAnalytics(area, { category: area, action: 'Enhancement Pack', label: eventData.source + ': ' + vehicle.enhancementPack });
3092            if (vehicle.colour != null && vehicle.colour != "")
3093                pushAnalytics(area, { category: area, action: 'Colour', label: eventData.source + ': ' + vehicle.colour });
3094            if (vehicle.trim != null && vehicle.trim != "")
3095                pushAnalytics(area, { category: area, action: 'Trim', label: eventData.source + ': ' + vehicle.trim });
3096        }
3097
3098        $scope.$on('gtm-buildPrice', function (event, eventData) {
3099            var area = 'Build And Price';
3100            var vehicle = eventData.data;
3101
3102            if (eventData.source.toLowerCase().match(/initial|summary|default/)) {
3103                pushVehicleAnalytics(area, vehicle, eventData);
3104            }
3105
3106            if (eventData.source == "360") {
3107                pushAnalytics(area, { category: area, action: '360', label: 'Vehicle Rotated' });
3108            }
3109
3110            if (eventData.source.toLowerCase().replace(/\s/gi, '') 
3110== "postcoderoadblock") {
3111                if (eventData.data.dialogDisplayed) {
3112                    if (eventData.data.firstVisit) {
3113                        pushAnalytics(area, { category: 'Postcode Roadblock', action: area, label: 'Roadblock displayed' });
3114                    } else {
3115                        pushAnalytics(area, { category: 'Postcode Roadblock', action: area, label: 'Postcode displayed' });
3116                    }
3117                } else {
3118                    pushAnalytics(area, { category: 'Postcode Roadblock', action: area, label: 'Roadblock and Postcode not displayed' });
3119                }
3120            }
3121
3122            // New GA4 event. Only send event on last step
3123            if (eventData.source.toLowerCase() === "summary") {
3124                const dealerName = eventData.data.dealerName;
3125                const data = {
3126                    "event": "build_and_price",
3127                    "model": vehicle.carmodel || "unknown",
3128                    "variant": vehicle.engineVariant || "unknown",
3129                    "grade": vehicle.grade || "unknown",
3130                    "enhancement_pack": vehicle.enhancementPack || "unknown",
3131                    "exterior_colour": vehicle.colour || "unknown",
3132                    "interior_colour": vehicle.trim || "unknown",
3133                    "website": dealerName || "Corporate"
3134                }
3135                $analytics.eventTrack("build_and_price", data);
3136            }
3137        });
3138
3139        $scope.$on('gtm-preLaunch', function (event, eventData) {
3140            var area = 'Pre Launch';
3141
3142            if (eventData.source.toLowerCase().match(/initial|interior|exterior|default/)) {
3143                var vehicle = eventData.data;
3144                pushVehicleAnalytics(area, vehicle, eventData);
3145            }
3146
3147        });
3148
3149        $scope.$on('refresh-gtm', function (event, data) {
3150            console.log('lxGtm: refresh-gtm event received from: ', data);
3151            $timeout(function () { addTrackers(); }, 150, false);
3152        });
3153
3154        $window.addEventListener('beforeunload', function (event) {
3155            var forms = Object.keys(gtm.forms);
3156            if (forms.length > 0) {
3157                forms.forEach(function (formName) {
3158                    var form = gtm.forms[formName];
3159                    var touched = [];
3160                    var filled = '';
3161                    form.forEach(function (item) {
3162                        if (item.touched) {
3163                            touched.push(item);
3164                            filled += (filled.length > 0) ? ' >> ' + item.label : item.label;
3165                        }
3166                    });
3167                    var eventObject = {
3168                        category: window.location.pathname,
3169                        action: formName,
3170                        label: touched.length + '/' + form.length + ' fields: ' + filled
3171                    };
3172                    //console.log(eventObject);
3173                    //$analytics.eventTrack(formName, eventObject);
3174                    pushAnalytics(formName, eventObject);
3175
3176                });
3177            }
3178        });
3179
3180        function init() {
3181            $timeout(function () { addTrackers(); addForms(); }, 0, false);
3182            $timeout(function () { addTrackers(); addForms(); checkForChat(100,0,10); }, 500, false);
3183        }
3184
3185        init();
3186
3187    }
3188}]);
3189
3190;
3191"use strict";
3192/* Directives */
3193angular.module('Lexus.Directives')
3194.directive('lxHero', [function () {
3195    return {
3196        restrict: 'A',
3197        link: function (scope, element, attrs) {
3198            var controls = attrs.controls || false,
3199                _window = angular.element(window),
3200                _header = angular.element('header'),
3201                _bNav = angular.element('.lx-b-nav'),
3202                _cta = angular.element('.lx-cta-bar'),
3203                headerHeight = _header.innerHeight(),
3204                breakpointSrc = 'src-lg',
3205                oldBreakpointSrc,
3206                breakpointHeightAdjust = Lexus.Breakpoint.Small;
3207
3208            function init() {
3209                fullScreenResize();
3210            }
3211
3212            function fullScreenResize() {
3213                var windowHeight = _window.innerHeight(),
3214                    ctaHeight = (element.hasClass('has-cta-bar')) ? 0 : (_cta.length > 0) ? _cta.innerHeight() : 96,
3215                    bNavHeight = (_bNav.length > 0) ? _bNav.innerHeight() : 0,
3216                    imageHeight = windowHeight - headerHeight - ctaHeight - bNavHeight;
3217
3218                if (_window.width() < breakpointHeightAdjust) {
3219                    imageHeight = 261;
3220                }
3221
3222                element.height(imageHeight);
3223            }
3224
3225            _window.resize(function () {
3226                fullScreenResize();
3227            });
3228
3229            _window.ready(function () {
3230                init();
3231            });
3232        }
3233    };
3234}]);
3235;
3236"use strict";
3237/* Directives */
3238angular.module('Lexus.Directives')
3239.directive('lxBackgroundImage', [function () {
3240    return {
3241        restrict: 'A',
3242        link: function (scope, element, attrs) {
3243            var controls = attrs.controls || false,
3244                _window = angular.element(window),
3245                _currentSize = 0,
3246                _newSize = 0;
3247
3248            function init() {
3249                updateBackgroundSize();
3250            }
3251
3252            function updateBackgroundSize() {
3253
3254                if (_window.width() <= Lexus.Breakpoint.ExtraExtraSmall) { _newSize = Lexus.Breakpoint.ExtraExtraSmall; }
3255                else if (_window.width() <= Lexus.Breakpoint.ExtraSmall) { _newSize = Lexus.Breakpoint.ExtraSmall; }
3256                else if (_window.width() <= Lexus.Breakpoint.Small) { _newSize = Lexus.Breakpoint.Small; }
3257                else { _newSize = 1920; }// Lexus.Breakpoint.Medium; }
3258
3259                if (_currentSize != _newSize) {
3260
3261                    var resolvedBackground = attrs.background + '?q=95&w=' + _newSize;
3262
3263                    if (attrs.backgroundFillVertical) {
3264                        resolvedBackground = attrs.background + '?q=95&h=' + element[0].clientHeight;
3265                    }
3266
3267                    element.css({ 'background-image': 'url(' + resolvedBackground + ')'});
3268
3269                    _currentSize = _newSize;
3270
3271                }
3272            }
3273
3274            _window.resize(function () {
3275                updateBackgroundSize();
3276            });
3277
3278            _window.ready(function () {
3279                init();
3280            });
3281        }
3282    };
3283}]);
3284;
3285"use strict";
3286/* Directives */
3287angular.module('Lexus.Directives')
3288    .directive('lxImageResize', [function () {
3289    return {
3290        restrict: 'A',
3291        link: function (scope, element, attrs) {
3292            var controls = attrs.controls || false,
3293                _window = angular.element(window),
3294                _currentSize = 0,
3295                _newSize = 0;
3296
3297            function init() {
3298                updateImageSize();
3299            }
3300
3301            function updateImageSize() {
3302
3303                if (_window.width() <= Lexus.Breakpoint.ExtraExtraSmall) { _newSize = Lexus.Breakpoint.ExtraExtraSmall; }
3304                else if (_window.width() <= Lexus.Breakpoint.ExtraSmall) { _newSize = Lexus.Breakpoint.ExtraSmall; }
3305                else if (_window.width() <= Lexus.Breakpoint.Small) { _newSize = Lexus.Breakpoint.Small; }
3306                else { _newSize = 1200; }
3307
3308                if (_currentSize != _newSize) {
3309                    var resolvedImage = attrs.image + '?q=95&w=' + _newSize;
3310                    attrs.$set("src", resolvedImage);
3311                    _currentSize = _newSize;
3312                }
3313            }
3314
3315            _window.resize(function () {
3316                updateImageSize();
3317            });
3318
3319            _window.ready(function () {
3320                init();
3321            });
3322        }
3323    };
3324}]);
3325;
3326"use strict";
3327/* Directives */
3328angular.module('Lexus.Directives')
3329.directive('lxCarousel', [function () { // Lazy load images.
3330    return {
3331        restrict: 'A',
3332        link: function (scope, element, attrs) {
3333            var controls = attrs.controls || false,
3334                _window = angular.element(window),
3335                _header = angular.element('header'),
3336                _allImages = element.find('.lx-carousel__image'),
3337                //_videos = element.find('.carousel-video'),
3338                //_ytVideos = [],
3339                feautredCarousel = element.parent('.lx-carousel'),
3340                headerHeight = _header.innerHeight(),
3341                breakpointSrc = 'src-lg',
3342                reassignSources = true,
3343                oldBreakpointSrc,
3344                breakpointHeightAdjust = Lexus.Breakpoint.Small,
3345                //hasLoadedYoutubeAPI = false,
3346                bxSlider;
3347
3348            function init() {
3349                fullScreenResize();
3350                imageSrc();
3351                initSlider();
3352            }
3353
3354            // If it is a feature carousel then resize images.
3355            function fullScreenResize() {
3356                var windowHeight = _window.innerHeight(),
3357                    imageHeight = windowHeight - headerHeight - 88;
3358
3359                if (feautredCarousel.length > 0) {
3360                    if (_window.width() < breakpointHeightAdjust) {
3361                        imageHeight = 261;
3362                    }
3363
3364                    element.height(imageHeight);
3365                    _allImages.height(element.height());
3366                }
3367            }
3368
3369            function findElement(arr, propName, propValue) {
3370                for (var i = 0; i < arr.length; i++)
3371                    if (arr[i][propName] == propValue)
3372                        return arr[i];
3373
3374                // will return undefined if not found; you could return a default instead
3375            }
3376
3377            // Resize all video heights, as iframes won't automatically set height
3378            //function videoResize() {
3379            //    if (_videos.length) {
3380            //        var videoHeight = element[0].parentElement.clientHeight * 0.97; // set to 0.97 to prevent height adjust issue
3381            //        for (var i = 0; i < _videos.length; i++) {
3382            //            _videos[i].height = videoHeight;
3383            //        }
3384            //    }
3385            //}
3386
3387            // Pauses all the videos
3388            //function pauseVideos() {
3389            //    if (_ytVideos.length) {
3390            //        angular.forEach(_ytVideos, function (value, key) {
3391            //            if (value.value && value.value.stopVideo) {
3392            //                value.value.stopVideo();
3393            //            }
3394            //        });
3395            //    }
3396            //}
3397
3398            function imageSrc() {
3399                if ((reassignSources || breakpointSrc == 'src') && _window.width() > breakpointHeightAdjust) {
3400                    breakpointSrc = 'src-lg';
3401                    reassignSrc();
3402                } else if ((reassignSources || breakpointSrc == 'src-lg') && _window.width() <= breakpointHeightAdjust) {
3403                    breakpointSrc = 'src';
3404                    reassignSrc();
3405                }
3406                reassignSources = false;
3407            }
3408
3409            function reassignSrc() {
3410                $(_allImages).each(function () {
3411                    var $img = $(this).hasClass('lx-carousel__image') ? $(this) : $(this).find('.lx-carousel__image');
3412                    $img.attr('src', null).attr('src', $img.attr('data-' + breakpointSrc
3412));
3413                    
3414                });
3415            }
3416
3417            //function initYoutubeAPI() {
3418
3419            //    var tag = document.createElement('script');
3420            //    tag.src = "https://www.youtube.com/iframe_api";
3421            //    var firstScriptTag = document.getElementsByTagName('script')[0];
3422            //    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
3423            //    hasLoadedYoutubeAPI = true;
3424            //}
3425
3426            // Load bxslider.
3427            function initSlider() {
3428                var _allImages = element.find('.lx-carousel__image'),
3429                    imagesLength = _allImages.length;
3430
3431                //// retrieve all youtube video iframes
3432                //var _frames = element.find('.carousel-video'),
3433                //    framesLength = _frames.length;
3434
3435                ////Init the YT JS API
3436                //if (!hasLoadedYoutubeAPI) {
3437                //    initYoutubeAPI();
3438                //}
3439
3440                function loadBackgroundImage(slide) {
3441                    if (angular.isDefined(slide) &&
3442                        (angular.isUndefined(slide.css('background-image')) || slide.css('background-image') == 'none' || !angular.equals(breakpointSrc, oldBreakpointSrc))) {
3443                        slide.css('background-image', 'url("' + slide.data(breakpointSrc) + '")');
3444                        slide.find('img').attr('src', slide.data(breakpointSrc)).hide();
3445                    }
3446                };
3447
3448                bxSlider = element.bxSlider({
3449                    mode: 'fade',
3450                    minSlides: 1,
3451                    maxSlides: 1,
3452                    slideMargin: 0,
3453                    captions: true,
3454                    auto: true,
3455                    pause: 5000,
3456                    pager: true,
3457                    autoHover: true,
3458                    nextText: '',
3459                    prevText: '',
3460                    controls: controls,
3461                    onSliderLoad: function (currentIndex) {
3462
3463                        if (imagesLength) {
3464                            var _firstElement = angular.element(_allImages[currentIndex]),
3465                                _secondElement = angular.element(_allImages[currentIndex + 1]);
3466
3467                            // Show first element.
3468                            loadBackgroundImage(_firstElement);
3469                            loadBackgroundImage(_secondElement);
3470                        }
3471                        //videoResize();
3472                    },
3473                    onSlideBefore: function () {
3474                    },
3475                    onSlideAfter: function ($slideElement, oldIndex, newIndex) {
3476                        //When a video comes into view start playing it, when its left pause it again
3477                        //pauseVideos();
3478                        //if ($slideElement.hasClass('slide-video')) {
3479                        //    var video = $slideElement.find('iframe');
3480                        //    if (!findElement(_ytVideos, 'name', video[0].id)) {
3481                        //        var player = new YT.Player(video[0].id, {
3482                        //            events: {
3483                        //                'onReady': function (event) {
3484                        //                    event.target.setVolume(0);
3485                        //                    event.target.playVideo();
3486                        //                },
3487                        //                'onStateChange': function (event) {
3488
3489                        //                    switch (event.data) {
3490                        //                        case YT.PlayerState.PLAYING:
3491                        //                            bxSlider.stopAuto();
3492                        //                            //pause the gallery rotation
3493                        //                            break;
3494                        //                        case YT.PlayerState.PAUSED:
3495                        //                            bxSlider.startAuto();
3496                        //                            //resume gallery rotation
3497                        //                            break;
3498                        //                        case YT.PlayerState.ENDED:
3499                        //                            bxSlider.startAuto();
3500                        //                            //resume gallery rotation
3501                        //                            break;
3502                        //                        default:
3503                        //                            //default co
3503de block
3504                        //                    }
3505                        //                },
3506                        //                'onError': function (data) {
3507                        //                    console.log('Error ' + data);
3508                        //                }
3509                        //            }
3510                        //        });
3511                        //        _ytVideos.push({
3512                        //            name: video[0].id,
3513                        //            value: player
3514                        //        });
3515                        //    }
3516                        //    var ytObj = findElement(_ytVideos, 'name', video[0].id);
3517                        //    //This is used for the second pass on the same video, the above ready callback wont run
3518                        //    //Get player state will not be valid on first pass cause the video isnt inited yet
3519                        //    if (ytObj.value.getPlayerState) {
3520                        //        console.log(ytObj.value.getPlayerState());
3521
3522                        //        switch (ytObj.value.getPlayerState()) {
3523                        //            case -1: //unstarted
3524                        //                ytObj.value.setVolume(0);
3525                        //                ytObj.value.playVideo();
3526                        //                break;
3527                        //            case 0: //ended
3528                        //                break;
3529                        //            case 1: //playing
3530                        //                break;
3531                        //            case 2: //paused
3532                        //                break;
3533                        //            case 3: //buffering
3534                        //                break;
3535                        //            case 5: //video cued
3536                        //                break;
3537                        //            default:
3538                        //        }
3539                        //    }
3540                        //}
3541                        if (imagesLength) {
3542                            var _thisImage = angular.element(_allImages[newIndex]);
3543                            var _nextImage = angular.element(_allImages[newIndex + 1]);
3544                            loadBackgroundImage(_thisImage);
3545                            loadBackgroundImage(_nextImage);
3546                            oldBreakpointSrc = breakpointSrc;
3547                        }
3548                    }
3549                });
3550            }
3551
3552
3553            _window.resize(function () {
3554
3555                //put this on a short timeout to prevent continuous re-initing while resizing
3556                init();
3557                fullScreenResize();
3558                imageSrc();
3559                //videoResize();
3560            });
3561
3562            _window.ready(function () {
3563                init();
3564            });
3565        }
3566    };
3567}]);
3568;
3569'use strict';
3570
3571(function() {
3572    var EVENT_OPEN_LIGHTBOX = 'EVENT_OPEN_LIGHTBOX';
3573    var EVENT_OWL_CHANGED = 'EVENT_OWL_CHANGED';
3574
3575    angular.module('Lexus.Directives').directive('lxImageGalleryCarousel', [
3576        'FontIconService',
3577        '$timeout',
3578        '$window',
3579        function(FontIconService, $timeout, $window) {
3580            controller.$inject = ['$scope', '$attrs'];
3581
3582            return {
3583                scope: true,
3584                restrict: 'A',
3585                link: link,
3586                controller: controller,
3587                controllerAs: 'ImageGalleryCtrl',
3588                bindToController: true,
3589                transclude: true
3590            };
3591
3592            function link(scope, element, attrs, ctrl, transclude) {
3593                transclude(scope, function(clone) {
3594                    element.append(clone);
3595                });
3596
3597                function loadAllImages(callback) {
3598                    var numImagesLoaded = 0;
3599                    var images = element
3600                        .find('.js-owl-carousel')
3601                        .first()
3602                        .find('.item img')
3603                        .toArray();
3604
3605                    for (var i = 0; i < images.length; i++) {
3606                        var img = new Image();
3607                        img.src = images[i].src;
3608                        img.addEventListener('load', function() {
3609                            numImagesLoaded += 1;
3610                            if (numImagesLoaded === images.length) {
3611                                callback();
3612                            }
3613                        });
3614                    }
3615                }
3616
3617                // Fix for owl carousel incorrectly computing the stage width
3618                // when used in conjunction with `autoWidth: true`.
3619                // https://github.com/OwlCarousel2/OwlCarousel2/issues/1139
3620                function fixOwlStageWidth() {
3621                    var stage = element.find('.owl-stage').first();
3622                    stage.width(stage.width() * 2);
3623
3624                    window.addEventListener('load', function() {
3625                        ctrl.$owlCarousel.trigger('refresh.owl.carousel');
3626                    });
3627                }
3628
3629                var owlCarouselOptions = {
3630                    items: 2,
3631                    margin: 40,
3632                    nav: false,
3633                    dots: false,
3634                    smartSpeed: 750,
3635                    autoWidth: true,
3636                    onInitialized: fixOwlStageWidth,
3637                    onRefreshed: fixOwlStageWidth
3638                };
3639                var $items = element
3640                    .find('.js-owl-carousel')
3641                    .first()
3642                    .find('.item');
3643
3644                if (typeof attrs.lxImageGalleryCarouselOptions !== 'undefined') {
3645                    owlCarouselOptions = angular.extend(
3646                        owlCarouselOptions,
3647                        JSON.parse(attrs.lxImageGalleryCarouselOptions)
3648                    );
3649                }
3650
3651                ctrl.numberOfItems = $items.length;
3652                ctrl.hasLightbox = typeof attrs.lxImageGalleryCarouselHasLightbox !== 'undefined';
3653                ctrl.isInsideLightbox = typeof attrs.lxImageGalleryCarouselIsInsideLightbox !== 'undefined';
3654
3655                // If the carousel is inside a lightbox we want to expand each items
3656                // height to fill the maximum vertical space allocated to it via CSS.
3657                function resizeItems() {
3658                    var height = element
3659                        .find('.lx-image-gallery-carousel__owl-body')
3660                        .first()
3661                        .height();
3662
3663                    $items.each(function() {
3664                        $(this).css({
3665                            height: height + 'px',
3666                            width: 'auto'
3667                        });
3668                    });
3669
3670                    if (typeof ctrl.$owlCarousel !== 'undefined') {
3671                        ctrl.$owlCarousel.trigger('refresh.owl.carousel');
3672                    }
3673                }
3674
3675                if (ctrl.isInsideLightbox) {
3676                    $($window).on('resize', resizeItems);
3677                    resizeItems();
3678                }
3679
3680                // If we do not wait for the images to load, on occasion owl carousel
3681                // will not load correctly (only on occasion though).
3682                loadAllImages(function() {
3683                    scope.$apply(function() {
3684                        ctrl.$owlCarousel = element
3685                            .find('.js-owl-carousel')
3686                            .first()
3687                            .owlCarousel(owlCarouselOptions);
3688                        ctrl.owlCarouselInitialised = true;
3689                    });
3690                });
3691
3692                // Apply a custom cursor to items if it this carousel contains a lightbox.
3693                if (ctrl.hasLightbox) {
3694                    var { lightBoxCursor } = FontIconService.generateCursorIcon();
3695
3696                    $items.find('img').each(function() {
3697                        $(this).css("cursor", "url(" + lightBoxCursor + ") 16 16, auto");
3698                    });
3699                }
3700
3701                // Apply a custom cursor to the next/previous buttons if inside lightbox.
3702                if (ctrl.isInsideLightbox) {
3703                    var { prevCursor, nextCursor } = FontIconService.generateCursorIcon();
3704
3705                    element
3706                        .find(".js-lx-image-gallery-carousel-overlay-nav-button-prev")
3707                        .first()
3708                        .css("cursor", "url(" + prevCursor + ") 16 16, auto");
3709                    element
3710                        .find(".js-lx-image-gallery-carousel-overlay-nav-button-next")
3711                        .first()
3712                        .css("cursor", "url(" + nextCursor + ") 16 16, auto");
3713                }
3714            }
3715
3716            function controller($scope, $attrs) {
3717                var $owlCarousel = null;
3718                var ImageGalleryCtrl = this;
3719                ImageGalleryCtrl.sliderValue = 1;
3720
3721                ImageGalleryCtrl.handleNextClick = function() {
3722                    if (!$owlCarousel) return;
3723                    $owlCarousel.trigger('next.owl.carousel');
3724                };
3725
3726                ImageGalleryCtrl.handlePrevClick = function() {
3727                    if (!$owlCarousel) return;
3728                    $owlCarousel.trigger('prev.owl.carousel');
3729                };
3730
3731                ImageGalleryCtrl.handleItemClick = function(event) {
3732                    if (!ImageGalleryCtrl.hasLightbox) return;
3733
3734                    if (!$(event.target).hasClass("lx-image-gallery-carousel__owl-carousel-image")) {
3735                        event.stopPropagation();
3736                        return;
3737                    }
3738
3739                    $scope.$broadcast(EVENT_OPEN_LIGHTBOX, ImageGalleryCtrl.sliderValue);
3740                };
3741
3742                ImageGalleryCtrl.handleSliderChange = function() {
3743                    if (!$owlCarousel) return;
3744                    $owlCarousel.trigger('to.owl.carousel', ImageGalleryCtrl.sliderValue - 1);
3745                };
3746
3747                $scope.$watch(
3748                    'ImageGalleryCtrl',
3749                    function(newValue, oldValue) {
3750                        if (newValue.owlCarouselInitialised && !oldValue.owlCarouselInitialised) init();
3751                    },
3752                    true
3753                );
3754
3755                function init() {
3756                    $owlCarousel = ImageGalleryCtrl.$owlCarousel.owlCarousel();
3757
3758                    $owlCarousel.on('changed.owl.carousel', function(event) {
3759                        ImageGalleryCtrl.sliderValue = event.item.index + 1;
3760
3761                        if (!$scope.$$phase) {
3762                            $scope.$apply();
3763                        }
3764
3765                        if (ImageGalleryCtrl.hasLightbox) {
3766                            $scope.$broadcast(EVENT_OWL_CHANGED, ImageGalleryCtrl.sliderValue);
3767                        }
3768                    });
3769
3770                    $attrs.$observe('lxImageGalleryCarouselSliderValue', function(sliderValue) {
3771                        $owlCarousel.trigger('to.owl.carousel', sliderValue);
3772                    });
3773                }
3774            }
3775        }
3776    ]);
3777
3778    angular.module('Lexus.Directives').directive('lxImageGalleryCarouselLightbox', function() {
3779        ImageGalleryCarouselLightboxController.$inject = ['$scope', '$element'];
3780
3781        return {
3782            scope: true,
3783            restrict: 'A',
3784            controller: ImageGalleryCarouselLightboxController,
3785            controllerAs: 'LightboxCtrl',
3786            bindToController: true
3787        };
3788
3789        function ImageGalleryCarouselLightboxController($scope, $element) {
3790            var LightboxCtrl = this;
3791            var element = $element[0];
3792            var $document = $(document);
3793            var KEYUP_EVENT = 'keyup.lxImageGalleryCarousel';
3794
3795            LightboxCtrl.isOpen = false;
3796            LightboxCtrl.sliderValue = 1;
3797
3798            LightboxCtrl.open = function() {
3799                bodyScrollLock.disableBodyScroll(element);
3800                LightboxCtrl.isOpen = true;
3801                $document.on(KEYUP_EVENT, function(e) {
3802                    if (e.key === 'Escape') {
3803                        LightboxCtrl.isOpen = false;
3804                        bodyScrollLock.enableBodyScroll(element);
3805                        $scope.$apply();
3806                    }
3807                });
3808            };
3809
3810            LightboxCtrl.close = function() {
3811                LightboxCtrl.isOpen = false;
3812                bodyScrollLock.enableBodyScroll(element);
3813                $document.off(KEYUP_EVENT);
3814            };
3815
3816            $scope.$on(EVENT_OPEN_LIGHTBOX, function() {
3817                LightboxCtrl.open();
3818            });
3819
3820            $scope.$on(EVENT_OWL_CHANGED, function(event, sliderValue) {
3821                LightboxCtrl.sliderValue = sliderValue - 1;
3822            });
3823        }
3824    });
3825})();
3826;
3827"use strict";
3828/* Directives */
3829angular.module('Lexus.Directives')
3830    .directive('lxCampaignPromoTile', function () {
3831        CampaignPromoTileController.$inject = ['$scope', '$window', '$rootScope'];
3832        return {
3833            scope: true,
3834            restrict: 'A',
3835            controller: CampaignPromoTileController,
3836            controllerAs: 'cptc',
3837            bindToController: true,
3838            link: function (scope, element, attrs) {
3839            }
3840        };
3841
3842        function CampaignPromoTileController($scope, $window, $rootScope) {
3843            var cptc = this;
3844            cptc.shiftPromoTilesUp = false;
3845
3846            $scope.init = function () {
3847                $scope.showPromoTile = true;
3848            }
3849
3850
3851            $scope.toggleShow = function () {
3852                $scope.showPromoTile = !$scope.showPromoTile;
3853            }
3854
3855            $scope.$on("shift-promo-tiles-up", function (evt, data) {
3856                cptc.shiftPromoTilesUp = data;
3857            });
3858        } 
3859
3860    });
3861;
3862"use strict";
3863/* Directives */
3864angular.module('Lexus.Directives')
3865    .directive('lxCampaignRangeExplore', function () {
3866        CampaignRangeExploreController.$inject = ['$scope', '$window', '$rootScope', '$timeout'];
3867        return {
3868            scope: true,
3869            restrict: 'A',
3870            controller: CampaignRangeExploreController,
3871            controllerAs: 'crec',
3872            bindToController: true,
3873            link: function (scope, element, attrs) {
3874                scope.directiveElement = element;
3875                scope.initControl();
3876            }
3877        };
3878
3879        function CampaignRangeExploreController($scope, $window, $rootScope, $timeout) {
3880            var crec = this;
3881
3882            $scope.init = function (showSplashScreen) {
3883                $scope.showSplashScreen = showSplashScreen;
3884            }
3885
3886            $scope.initControl = function () {
3887                $scope.showCampaignDetails = !$scope.showSplashScreen;
3888                $scope.blurOverlayImage = true;
3889                $scope.animationStarted = false;
3890                if ($scope.showSplashScreen) {
3891                    $('body').addClass("is-locked");
3892                }
3893            }
3894
3895            $scope.attachActivateOnScroll = function () {
3896                //Wait before attaching, the window might still be anchor scrolling
3897                $timeout(function () {
3898                    $scope.scrollEvent();
3899                }, 300);
3900            }
3901
3902            $scope.scrollEvent = function () {
3903                window.onscroll = function () {
3904                    if (!$scope.animationStarted) {
3905                        $scope.animationStarted = true;
3906                        $scope.closeOverlay();
3907                    }
3908                }
3909            }
3910
3911            $scope.scrollIntoView = function (callback) {
3912                TweenLite.to(window, 0, { scrollTo: 0, onComplete: callback });
3913            }
3914
3915            $scope.animationComplete = function () {
3916                $(".lx-campaign-range-explore__overlay-svg").hide();
3917                $(".lx-campaign-range-explore__centered").hide();
3918                $(".lx-campaign-range-explore__background").hide();
3919            }
3920
3921            $scope.fadeoutText = function () {
3922                $(".lx-campaign-range-explore__overlay").hide();
3923
3924                var tl = new TimelineLite();
3925                tl.delay(3);
3926                tl.to($(".lx-campaign-range-explore__centered"), 0.7, { css: { opacity: "0" }, onComplete: $scope.animateSVGShowOffer });
3927                tl.play();
3928            }
3929
3930            $scope.hideBackground = function () {
3931                var t = new TimelineLite();
3932                t.to($(".lx-campaign-range-explore__background"), 1, { css: { opacity: "0" } });
3933                t.play();
3934            }
3935
3936            $scope.animateSVGShowOffer = function () {
3937                $scope.scrollIntoView();
3938                $scope.hideBackground();
3939
3940                $('body').removeClass("is-locked");
3941
3942                var tl = new TimelineLite({ onComplete: $scope.animationComplete });
3943                tl.to("#Poly-1", 3, { y: '200%' }, "showOffer")
3944                    .to("#Poly-2", 3, { y: '-200%' }, "showOffer")
3945                    .to("#Poly-3", 3, { x: '200%' }, "showOffer")
3946                    .to("#Poly-4", 3, { x: '-200%' }, "showOffer")
3947                tl.play();
3948
3949                $timeout(function () { $rootScope.$broadcast('calculate-widths') }, 100);
3950
3951            }
3952
3953            $scope.closeOverlay = function () {
3954                var tl = new TimelineLite();
3955                tl.to($(".lx-campaign-range-explore__overlay-logo, .lx-campaign-range-explore__overlay-heading"), 1.2, { css: { top: "-5%", opacity: "0" } });
3956                tl.play();
3957
3958                var tl2 = new TimelineLite();
3959                tl2.to($(".lx-campaign-range-explore__overlay-animation-outer"), 2, { css: { opacity: "0" } });
3960                tl2.play();
3961
3962                $scope.blurOverlayImage = false;
3963
3964                var tl3 = new TimelineLite();
3965                tl3.to($(".lx-campaign-range-explore__overlay-svg"), 0.8, { css: { opacity: "0.9" } });
3966                tl3.to($(".lx-campaign-range-explore__centered"), 0, { css: { transform: "translateY(15%)" } });
3967                tl3.to($(".lx-campaign-range-explore__centered"), 1, { css: { transform: "translateY(0)", opacity: "0.9" }, onComplete: $scope.fadeoutText });
3968                tl3.play();
3969                $scope.showCampaignDetails = true;
3970            }
3971        }
3972    })
3973    .directive('lxCampaignRangeExploreMainImage', [function () {
3974        return {
3975            restrict: 'A',
3976            link: function (scope, element, attrs) {
3977                var controls = attrs.controls || false,
3978                    _window = angular.element(window)
3979
3980                function init() {
3981                    fullScreenResize();
3982                }
3983
3984                function fullScreenResize() {
3985                    var imageHeight = _window.innerHeight();
3986                    element.height(imageHeight);
3987                }
3988
3989                _window.resize(function () {
3990                    fullScreenResize();
3991                });
3992
3993                _window.ready(function () {
3994                    init();
3995                });
3996            }
3997        };
3998    }])
3999    .directive('lxCampaignRangeExploreHero', ['$window', '$timeout', '$filter', '$http', '$location', '$rootScope', function ($window, $timeout, $filter, $http, $location, $rootScope) {
4000        exploreCampaignRangeController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location'];
4001        return {
4002            scope: true,
4003            restrict: 'A',
4004            controller: exploreCampaignRangeController,
4005            controllerAs: 'er',
4006            bindToController: true,
4007            link: function (scope, element, attrs, filter, http) {
4008            }
4009        };
4010
4011
4012        function exploreCampaignRangeController($scope, $http, $filter, $timeout, $window, $interval, $location) {
4013            var er = this,
4014                _window = angular.element(window),
4015                _currentSize = 0,
4016                _newSize = 0,
4017                _sizeFilter = '',
4018                _panelBkg = angular.element('.lx-campaign-range-explore__image-panel').attr('data-background');
4019
4020            $scope.resolvedImage = null;
4021
4022            function init() {
4023                $scope.updateBackgroundImage();
4024                $scope.model = null;
4025                $scope.modelFullName = null;
4026            }
4027
4028
4029            $scope.previousVehicle = function () {
4030                $rootScope.$broadcast('change-previous-vehicle');
4031            }
4032
4033            $scope.nextVehicle = function () {
4034                $rootScope.$broadcast('change-next-vehicle');
4035            }
4036
4037            $scope.scrollTo = function (anchor) {
4038                TweenLite.to(window, 0.5, { scrollTo: anchor, ease: Power2.easeOut });
4039            }
4040
4041            $rootScope.$on('change-active-vehicle', function (event, args) {
4042                $scope.er.activeVehicle = args;
4043                $scope.model = args.name;
4044                $scope.modelFullName = args.fullName;
4045                $scope.updateBackgroundImage(args.imageSrc);
4046                $scope.updateDisclaimer(args.disclaimer, args.disclaimerIsDarkStyle);
4047            });
4048
4049            $scope.updateDisclaimer = function (text, isDarkStyle) {
4050                $(".lx-campaign-range-explore__info-content").text(text);
4051                $(".lx-campaign-range-explore__info-content").closest(".lx-campaign-range-explore__info-overlay").toggleClass("lx-campaign-range-explore--dark-tooltip", isDarkStyle);
4052            }
4053
4054            $scope.updateModelTitle = function (model) {
4055                if (!model) {
4056                    return;
4057                }
4058
4059                $scope.model = model.name;
4060                $scope.modelFullName = model.fullName;
4061            }
4062
4063            $scope.updateBackgroundImage = function (imageSrc) {
4064
4065                if (_window.width() <= Lexus.Breakpoint.ExtraExtraSmall) { _newSize = Lexus.Breakpoint.ExtraExtraSmall; }
4066                else if (_window.width() <= Lexus.Breakpoint.ExtraSmall) { _newSize = Lexus.Breakpoint.ExtraSmall; }
4067                else if (_window.width() <= Lexus.Breakpoint.Small) { _newSize = Lexus.Breakpoint.Small; }
4068                else { _newSize = Lexus.Breakpoint.Medium; }
4069
4070                if (_currentSize != _newSize) {
4071                    _sizeFilter = '?q=95&w=' + _newSize;
4072                    _currentSize = _newSize;
4073                }
4074
4075                if ($scope.resolvedImage != (imageSrc + _sizeFilter)) {
4076                    $scope.resolvedImage = imageSrc + _sizeFilter;
4077
4078                }
4079
4080            }
4081
4082            _window.resize(function () {
4083                $scope.updateBackgroundImage($scope.er.activeVehicle.imageSrc);
4084            });
4085
4086
4087            $scope.init = function () {
4088                $scope.er = {
4089                    activeVehicle: {
4090                    }
4091                };
4092            }
4093
4094        }
4095    }])
4096    .directive('lxCampaignRangeExploreSlider', ['$window', '$timeout', '$filter', '$http', '$location', '$rootScope', 'UserPreferencesService', 'VehicleService', function ($window, $timeout, $filter, $http, $location, $rootScope, UserPreferencesService, VehicleService) {
4097        exploreRangeSliderController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location', '$rootScope', 'UserPreferencesService', 'VehicleService'];
4098        return {
4099            scope: true,
4100            restrict: 'A',
4101            controller: exploreRangeSliderController,
4102            controllerAs: 'ers',
4103            bindToController: true,
4104            link: function (scope, element, attrs, filter, http) {
4105
4106                var touchStartHandler = function (e) {
4107                    scope.slider.startClientX = e.changedTouches[0].screenX;
4108                }
4109                var touchMovetHandler = function (e) {
4110                    // reference first touch point for this event
4111                    var relativeScrolled_x = e.changedTouches[0].screenX - scope.slider.startClientX;
4112                    var idexesToFly = Math.ceil(Math.abs(relativeScrolled_x) / scope.itemWidth);
4113                    //scrolled right
4114                    if (relativeScrolled_x < 0) {
4115                        //scroll to next element
4116                        scope.slider.jumpToIndex = scope.carouselPos + idexesToFly <= scope.sliderItemCollection.length - 1
4117                            ? scope.carouselPos + idexesToFly : scope.carouselPos;
4118                    }
4119                    //scrolled left
4120                    else {
4121                        //scroll to prev element
4122                        scope.slider.jumpToIndex = scope.carouselPos - idexesToFly >= 0 ? scope.carouselPos - idexesToFly : 0;
4123                    }
4124                }
4125                var touchEndHandler = function (e) {
4126                    if (scope.slider.jumpToIndex == null || (scope.slider.jumpToIndex == scope.carouselPos)) {
4127                        return;
4128                    }
4129                    scope.changeActiveVehicleIndex(scope.slider.jumpToIndex, true, true);
4130                }
4131
4132                function clearTouchEvents() {
4133                    if (scope.carouselSlider[0].removeEventListener) {
4134                        scope.carouselSlider[0].removeEventListener('touchstart', touchStartHandler);
4135                        scope.carouselSlider[0].removeEventListener('touchmove', touchMovetHandler);
4136                        scope.carouselSlider[0].removeEventListener('touchend', touchEndHandler);
4137                    }
4138                }
4139
4140                function addTouchEvents() {
4141                    clearTouchEvents();
4142                    if (scope.carouselSlider[0].addEventListener) {
4143                        scope.carouselSlider[0].addEventListener('touchstart', touchStartHandler, false);
4144                        scope.carouselSlider[0].addEventListener('touchmove', touchMovetHandler, false);
4145                        scope.carouselSlider[0].addEventListener('touchend', touchEndHandler, false);
4146                    }
4147                }
4148
4149                $rootScope.$on('calculate-widths', function (event, args) {
4150                    scope.carouselSlider = element;
4151                    scope.activeMarker = element.find('.lx-campaign-range-explore__marker-js');
4152                    scope.$innerContainer = scope.carouselSlider.children().first('.lx-campaign-range-explore__subpanels');
4153                    scope.itemWidth = scope.$innerContainer.find('.lx-campaign-range-explore__subpanel').first().width();
4154
4155                    scope.outerContainerWidth = scope.carouselSlider.width();
4156                    scope.maxScrollX = scope.$innerContainer.width() - scope.outerContainerWidth;
4157                    scope.slider = {
4158                        startClientX: undefined,
4159                        jumpToIndex: null,
4160                        smoothScrollonClick: false,
4161                    }
4162
4163                    //use native swipe scrolling for touch devices, If its a laptop with touch and wide screen assume they are using K&M
4164                    if (!Modernizr.touchevents || angular.element($window).width() >= Lexus.Breakpoint.Medium) {
4165                        scope.carouselSlider.mousemove(function (event) { scope.smoothScrollTo(event); });
4166                        scope.carouselSlider.mouseleave(function (event) { scope.smoothSnapToNearest(event); });
4167                        clearTouchEvents();
4168                        scope.slider.smoothScrollonClick = false;
4169                    }
4170                    else if (Modernizr.touchevents && angular.element($window).width() < Lexus.Breakpoint.Medium) {
4171                        addTouchEvents();
4172                        scope.slider.smoothScrollonClick = true;
4173                    }
4174
4175                    angular.forEach(scope.$innerContainer.children('.lx-campaign-range-explore__subpanel'), function (value, key) {
4176                        value.className = value.className;
4177                    });
4178                    //scope.smoothSnapToNearest(event);
4179                });
4180
4181                angular.element($window).bind('resize', function () {
4182                    $rootScope.$broadcast('calculate-widths');
4183                });
4184
4185                scope.$on('$destroy', function () {
4186                    clearTouchEvents();
4187                });
4188            }
4189        }
4190
4191
4192        function exploreRangeSliderController($scope, $http, $filter, $timeout, $window, $interval, $location, $rootScope) {
4193            var ers = this;
4194            $scope.urlParameters = $location.search();
4195
4196            $scope.init = function (overscrollPadding, markerRadius) {
4197                $scope.overscrollPadding = overscrollPadding;
4198                $scope.markerRadius = markerRadius;
4199                $scope.carouselTweenTime = 1;
4200                $scope.markerTweenTime = 1;
4201                $scope.easingFunction = Power2.easeOut;
4202                $scope.sliderItemCollection = [];
4203                var dataVehicle = UserPreferencesService.getLexusVehicle($scope.urlParameters);
4204                if (dataVehicle && dataVehicle.model) {
4205                    $scope.changeActiveVehicleIndex($scope.findIndexByModel(dataVehicle.model), true);
4206                }
4207
4208
4209                $rootScope.$on('change-previous-vehicle', function () {
4210                    var newIndex = $scope.carouselPos - 1 >= 0 ? $scope.carouselPos - 1 : $scope.sliderItemCollection.length - 1;
4211                    $scope.changeActiveVehicleIndex(newIndex, true);
4212                    $scope.smoothScrollToIndex(newIndex);
4213                });
4214                $rootScope.$on('change-next-vehicle', function () {
4215                    var newIndex = $scope.carouselPos + 1 <= $scope.sliderItemCollection.length - 1 ? $scope.carouselPos + 1 : 0;
4216                    $scope.changeActiveVehicleIndex(newIndex, true);
4217                    $scope.smoothScrollToIndex(newIndex);
4218                });
4219            }
4220
4221
4222
4223            $scope.findIndexByModel = function (modelName) {
4224                var modelIndex = -1;
4225                if ($scope.sliderItemCollection && $scope.sliderItemCollection.length > 0 && modelName) {
4226                    modelIndex = $scope.sliderItemCollection.findIndex(function (m) { return m.name.toLowerCase() === modelName.toLowerCase() });
4227                }
4228                return (modelIndex == -1 ? 0 : modelIndex);
4229            }
4230
4231            $scope.smoothScrollToIndex = function (index) {
4232                var left = $scope.itemWidth * index;
4233                left = Math.round(left + $scope.overscrollPadding + ($scope.itemWidth / 2) - ($scope.outerContainerWidth / 2));
4234                TweenLite.to($scope.carouselSlider, $scope.carouselTweenTime, { scrollTo: { x: (left) }, ease: $scope.easingFunction });
4235                $rootScope.$broadcast('pause-smooth-scroll-to', '');
4236            }
4237
4238            $rootScope.$on('pause-smooth-scroll-to', function (event, args) {
4239                $rootScope.pauseSmoothScrollTo = true;
4240                setTimeout(function () {
4241                    $rootScope.pauseSmoothScrollTo = false;
4242                }, 1500);
4243            });
4244
4245            $scope.smoothScrollTo = function (event) {
4246                if ($rootScope.pauseSmoothScrollTo === true) {
4247                    return;
4248                }
4249                var offsetX = $scope.carouselSlider.position().left;
4250                var posInPc = (event.clientX - offsetX) / $scope.outerContainerWidth;
4251                $scope.carouselPos = $scope.maxScrollX * posInPc;
4252                TweenLite.to($scope.carouselSlider, $scope.carouselTweenTime, { scrollTo: { x: ($scope.carouselPos) }, ease: $scope.easingFunction });
4253            }
4254
4255            $scope.smoothSnapToNearest = function (event) {
4256                var offset = $scope.overscrollPadding;
4257                var snapWidth = $scope.itemWidth;
4258
4259                var misalignment = ($scope.carouselPos % snapWidth) - offset;
4260                var previousSnapPoint = $scope.carouselPos - misalignment;
4261                var nextSnapPoint = $scope.carouselPos - misalignment + snapWidth;
4262
4263                if (misalignment === 0) {
4264                    return;
4265                } else if (misalignment < (snapWidth / 2)) {
4266                    TweenLite.to($scope.carouselSlider, $scope.carouselTweenTime, { scrollTo: { x: previousSnapPoint }, ease: $scope.easingFunction });
4267                } else {
4268                    TweenLite.to($scope.carouselSlider, $scope.carouselTweenTime, { scrollTo: { x: nextSnapPoint }, ease: $scope.easingFunction });
4269                }
4270            }
4271
4272            $scope.setMarkerPosition = function (failCount) {
4273                var i = failCount || 0;
4274                if (!$scope.activeMarker && i < 10) {
4275                    setTimeout(function () {
4276                        $scope.setMarkerPosition(i + 1);
4277                    }, 100);
4278                    return;
4279                }
4280                var newPosition = Math.round($scope.overscrollPadding + ($scope.carouselPos * $scope.itemWidth) + ($scope.itemWidth * 0.5) - $scope.markerRadius);
4281                $scope.activeMarker.css('left', newPosition + 'px');
4282                //why? $scope.smoothScrollToIndex($scope.carouselPos);
4283            }
4284
4285            $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
4286                $scope.sliderItemCollection[0].style = { "margin-left": $scope.overscrollPadding + "px" };
4287                $scope.sliderItemCollection[$scope.sliderItemCollection.length - 1].style = { "margin-right": $scope.overscrollPadding + "px" };
4288                $timeout(function () { $rootScope.$broadcast('calculate-widths') }, 100);
4289                $scope.setMarkerPosition();
4290
4291                var dataVehicle = UserPreferencesService.getLexusVehicle($scope.urlParameters);
4292                if (dataVehicle && dataVehicle.model) {
4293                    $scope.changeActiveVehicleIndex($scope.findIndexByModel(dataVehicle.model), true);
4294                }
4295
4296            });
4297
4298            this.addSliderItem = function (data) {
4299                $scope.sliderItemCollection[data.index] = data;
4300                $scope.sliderItemCollection[data.index].disabled = false;
4301                $scope.sliderItemCollection[data.index].style = {};
4302                if (data.init) {
4303                    $scope.carouselPos = parseInt(data.index);
4304                    $scope.sliderItemCollection[data.index].active = true;
4305                    $timeout(function () { $rootScope.$broadcast('change-active-vehicle', data) }, 100);
4306                }
4307                else {
4308                    $scope.sliderItemCollection[data.index].active = false;
4309                }
4310            }
4311
4312            $rootScope.$on('campaign-vehicle-preference-change', function (event, args) {
4313                if (args) {
4314                    var vehicleIndex = $scope.findIndexByModel(args.model);
4315                    if (vehicleIndex >= 0) {
4316                        $scope.changeActiveVehicleIndex(vehicleIndex, false);
4317                    }
4318                }
4319
4320            });
4321
4322            $scope.changeActiveVehicleIndex = function (index, broadcastChange, smoothScroll) {
4323                if ($scope.sliderItemCollection.length > 0) {
4324
4325                    $scope.carouselPos = index;
4326                    angular.forEach($scope.sliderItemCollection, function (value, key) {
4327                        value.active = (key == index);
4328                    });
4329                    $scope.setMarkerPosition();
4330                    var newVehicle = $scope.sliderItemCollection[index];
4331
4332                    $scope.updateBackgroundImage(newVehicle.imageSrc);
4333                    $scope.updateModelTitle(newVehicle);
4334
4335                    if (broadcastChange) {
4336                        $scope.updateCookieAlertOthers(newVehicle);
4337                    }
4338
4339                    if (smoothScroll || ($scope.slider && $scope.slider.smoothScrollonClick)) {
4340                        $scope.smoothScrollToIndex(index);
4341                    }
4342
4343                }
4344            }
4345
4346            $scope.updateCookieAlertOthers = function (newVehicle) {
4347
4348                $timeout(function () { $rootScope.$broadcast('change-active-vehicle', newVehicle) }, 100);
4349
4350                $rootScope.$broadcast('campaign-vehicle-preference-change', {
4351                    model: newVehicle.name,
4352                    grade: undefined,
4353                    engineVariant: undefined,
4354                    enhancementPack: undefined
4355                });
4356
4357            }
4358
4359
4360
4361            $rootScope.$on('filter-body-type', function (event, args) {
4362                var newActiveIndex = 0;
4363                var needtoApplyNewIndex = false;
4364                angular.forEach($scope.sliderItemCollection, function (value, key) {
4365                    var isDisabled = !(args == 'all' || args == value.bodyType);
4366                    value.disabled = isDisabled;
4367                    if (isDisabled && value.active) {
4368                        value.active = false;
4369                        needtoApplyNewIndex = true;
4370                    }
4371                });
4372                if (needtoApplyNewIndex) {
4373                    var keepGoing = true;
4374                    angular.forEach($scope.sliderItemCollection, function (value, key) {
4375                        if (keepGoing) {
4376                            if (!value.disabled) {
4377                                value.active = true;
4378                                $scope.smoothScrollToIndex(key);
4379                                keepGoing = false;
4380                                $timeout(function () {
4381                                    $rootScope.$broadcast('change-active-vehicle', value);
4382                                    $scope.changeActiveVehicleIndex(value.index);
4383                                }, 100);
4384                            }
4385                        }
4386                    });
4387                }
4388            });
4389
4390
4391        }
4392    }])
4393    .directive('lxCampaignRangeExploreSliderItem', ['$window', '$timeout', '$filter', '$http', '$location', '$rootScope', function ($window, $timeout, $filter, $http, $location, $rootScope) {
4394        return {
4395            scope: true,
4396            restrict: 'AE',
4397            require: '^lxCampaignRangeExploreSlider',
4398            link: function (scope, element, attrs, exploreRangeSliderController) {
4399                exploreRangeSliderController.addSliderItem(attrs);
4400            }
4401        }
4402
4403
4404    }]);
4405
4406;
4407(function (undefined) {
4408
4409    "use strict";
4410    /* Directives */
4411    angular.module('Lexus.Directives')
4412        .directive('lxCampaignModelRangeEngineVariant', CampaignModelRangeEngineVariantDirective);
4413
4414    CampaignModelRangeEngineVariantDirective.$inject = ['$location', '$rootScope', 'UserPreferencesService'];
4415
4416    function CampaignModelRangeEngineVariantDirective($location, $rootScope, UserPreferencesService) {
4417
4418        var engineVariants = null;
4419        var modelRange = null;
4420        var buildAndPriceUrl = null;
4421        var gradeName = null;
4422        campaignModelRangeEngineVariantController.$inject = ['$scope', '$window', 'VehicleService', 'BuildPriceService', 'UserPreferencesService'];
4423        return {
4424            scope: true,
4425            restrict: 'A',
4426            controller: campaignModelRangeEngineVariantController,
4427            link: link
4428        };
4429
4430        function link(scope, element, attrs) {
4431            engineVariants = angular.fromJson(attrs.engineVariants);
4432            buildAndPriceUrl = attrs.buildAndPriceUrl;
4433            gradeName = attrs.gradeName;
4434
4435            for (var index = 0; index < engineVariants.length; index++) {
4436                engineVariants[index].selected = false;
4437            }
4438
4439            var dataVehicle = UserPreferencesService.getLexusVehicle($location.search());
4440            
4441            if (dataVehicle) {
4442                var variant = null;
4443                var foundVariant = null;
4444                var variantIndex = null;
4445
4446                for (var index = 0; index < engineVariants.length; index++) {
4447                    if (engineVariants[index].model === dataVehicle.model) {
4448                        variant = engineVariants[index];
4449                        foundVariant = variant.engineVariant;
4450                        break;
4451                    }
4452                }
4453
4454                if (foundVariant) {
4455                    variantIndex = scope.selectVariant(variant.engineVariant, variant.gradeId, variant.variantId, variant.enhancementPack);
4456                } else {
4457                    variantIndex = scope.selectVariant(engineVariants[0].engineVariant, engineVariants[0].gradeId, engineVariants[0].variantId, engineVariants[0].enhancementPack);
4458                }
4459            }
4460            else {
4461                variantIndex = scope.selectVariant(engineVariants[0].engineVariant, engineVariants[0].gradeId, engineVariants[0].variantId, engineVariants[0].enhancementPack);
4462            }
4463            
4464            if (variantIndex != null) {
4465                scope.setModelName(engineVariants[variantIndex].model, engineVariants[variantIndex].engineVariant);
4466            }    
4467        }
4468
4469        function campaignModelRangeEngineVariantController($scope, $window, VehicleService, BuildPriceService, UserPreferencesService) {
4470
4471            $scope.variantName = "";
4472            $scope.gradeId = "";
4473            $scope.variantId = "";
4474            $scope.enhancementPack = "";
4475
4476            $scope.selectVariant = function (variantName, gradeId, variantId, enhancementPack) {
4477                var selectedIndex = null;
4478                for (var index = 0; index < engineVariants.length; index++) {
4479                    if (engineVariants[index].engineVariant === variantName) {
4480                        engineVariants[index].selected = true;
4481
4482                        selectedIndex = index;
4483                        $rootScope.$broadcast('vehicle-preference-change', {
4484                            model: engineVariants[index].model,
4485                            engineVariant: engineVariants[index].engineVariant,
4486                            grade: engineVariants[index].grade,
4487                        });
4488                    } else {
4489                        engineVariants[index].selected = false;
4490                    }
4491                }
4492                $scope.variantName = variantName;
4493                $scope.gradeId = gradeId;
4494                $scope.variantId = variantId;
4495                $scope.enhancementPack = enhancementPack;
4496
4497                $rootScope.$broadcast('broadcast.lazyLoadRevalidate');
4498                return selectedIndex;
4499            }
4500
4501            $scope.isSelected = function (variantName) {
4502                for (var index = 0; index < engineVariants.length; index++) {
4503                    if (engineVariants[index].engineVariant === variantName) {
4504                        return engineVariants[index].selected;
4505                    }
4506                }
4507            }
4508
4509            $scope.isModelSelected = function (modelName) {
4510                return $scope.modelName === modelName;
4511            }
4512
4513            $scope.setModelName = function (modelName, variantName) {
4514                $scope.modelName = modelName;
4515                $scope.variantName = variantName;
4516
4517                if (variantName && !$scope.isSelected(variantName)) {
4518                    $scope.selectVariant(variantName, $scope.gradeId, $scope.variantId, $scope.enhancementPack);
4519                }
4520            }
4521
4522            $scope.displayTab = function (modelName) {
4523                return modelName == $scope.modelName;
4524            }
4525
4526            $scope.buildLink = function () {
4527                var trim = null;
4528                var paint = null;
4529
4530                var dataVisitor = UserPreferencesService.getLexusVisitor($location.search());
4531                var postCode = dataVisitor ? dataVisitor.postCode : "3000";
4532
4533                VehicleService.getTrims($scope.gradeId, $scope.variantId).then(function success(response) {
4534                    if (response.data.length == 0) {
4535                        console.info('getTrims received 0 results for ' + $scope.variantName + ' with grade ID ' + $scope.gradeId);
4536                    }
4537
4538                    var availableTrims = response.data;
4539                    if (availableTrims && availableTrims.length > 0)
4540                    {
4541                        trim = availableTrims[0];
4542
4543                        VehicleService.getPaints($scope.gradeId, $scope.variantId, trim.id).then(function success(response) {
4544                            if (response.data.length == 0) {
4545                                console.info('getPaints received 0 results for ' + $scope.variantName + ' with grade ID ' + $scope.gradeId + ' and trim Id ' + trim.id);
4546                            }
4547
4548                            var paints = response.data;
4549
4550                            if (paints && paints.length > 0) {
4551
4552                                paint = paints[0];
4553
4554                                var vehicle = {
4555                                    carmodel: $scope.modelName,
4556                                    engineVariant: $scope.variantName,
4557                                    grade: gradeName,
4558                                    postcode: postCode,
4559                                    enhancementPack: $scope.enhancementPack,
4560                                    colour: paint.uriName,
4561                                    trim: trim.uriName
4562                                };
4563
4564                                var url = buildAndPriceUrl ? buildAndPriceUrl + '/' + BuildPriceService.getBuildPriceLink(vehicle, 2) : '';
4565                                if (url) {
4566                                    $window.location.href = url;
4567                                }
4568                            }
4569
4570                        }, function error(response) {
4571                            console.error('getTrims returned an error');
4572                        });
4573
4574                    }
4575
4576                }, function error(response) {
4577                    console.error('getTrims returned an error');
4578                });
4579            }
4580        }
4581    }
4582}());
4583
4584;
4585"use strict";
4586/* Directives */
4587angular.module('Lexus.Directives')
4588.directive('lxContentGallery', [function () { // Lazy load images.
4589    return {
4590        scope: {
4591            'parentAccordionPanelId': '@'
4592        },
4593        restrict: 'A',
4594        link: function (scope, element, attrs) {
4595            var controls = attrs.controls || false,
4596                _window = angular.element(window),
4597                _allImages = element.find('.lx-content-gallery__slide'),
4598                breakpointSrc = 'src-lg',
4599                reassignSources = true,
4600                oldBreakpointSrc,
4601                breakpointHeightAdjust = Lexus.Breakpoint.Small,
4602                bxSlider;
4603
4604            function init() {
4605                imageSrc();
4606                initSlider();
4607
4608                scope.$on('broadcast.contentGalleryReload', function (event, args) {
4609                    if (args.targetId.toString() !== scope.parentAccordionPanelId) {
4610                        return;
4611                    }
4612                    bxSlider.reloadSlider();
4613                });
4614            }
4615
4616            function imageSrc() {
4617                if ((reassignSources || breakpointSrc == 'src') && _window.width() > breakpointHeightAdjust) {
4618                    breakpointSrc = 'src-lg';
4619                    reassignSrc();
4620                } else if ((reassignSources || breakpointSrc == 'src-lg') && _window.width() <= breakpointHeightAdjust) {
4621                    breakpointSrc = 'src';
4622                    reassignSrc();
4623                }
4624                reassignSources = false;
4625            }
4626
4627            function reassignSrc() {
4628                $(_allImages).each(function () {
4629                    var $img = $(this).find('img');
4630                    $img.attr('src', null).attr('src', $img.attr('data-' + breakpointSrc
4630));
4631                    var a = 1;
4632                });
4633            }
4634
4635            // Load bxslider.
4636            function initSlider() {
4637
4638                bxSlider = element.bxSlider({
4639                    mode: 'fade',
4640                    slideMargin: 0,
4641                    captions: true,
4642                    auto: false,
4643                    pause: 5000,
4644                    pager: false,
4645                    autoHover: true,
4646                    nextText: '',
4647                    prevText: '',
4648                    //controls: controls,
4649                    adaptiveHeight: true,
4650                    onSliderLoad: function (currentIndex) {
4651                    },
4652                    onSlideBefore: function () {
4653                    },
4654                    onSlideAfter: function ($slideElement, oldIndex, newIndex) {
4655
4656                    }
4657                });
4658            }
4659
4660
4661            _window.resize(function () {
4662                
4663            });
4664
4665            _window.ready(function () {
4666                init();
4667            });
4668        }
4669    };
4670}]);
4671;
4672"use strict";
4673/* Directives */
4674angular.module('Lexus.Directives')
4675    .directive('lxContentVideo', ['$rootScope', '$window', function ($rootScope, $window) {
4676        contentVideoController.$inject = ['$scope', '$rootScope'];
4677        return {
4678            scope: true,
4679            restrict: 'A',
4680            controller: contentVideoController,
4681            controllerAs: 'cv',
4682            bindToController: true,
4683            link: function (scope, element, attrs) {
4684
4685                if ((attrs.autoplay || '').toLowerCase() === 'true') {
4686                    embedVideo(scope, element, attrs, element[0]);
4687                }
4688                else {
4689                    element.on('click', function (event) {
4690                        embedVideo(scope, element, attrs, event.currentTarget);
4691                    });
4692                }
4693
4694                angular.element($window).bind('resize', function () {
4695                    scope.$broadcast('content-video-window-resize');
4696                });
4697
4698                function embedVideo(scope, element, attrs, targetElement) {
4699                    if (!attrs['data-playing']) {
4700                        var aspect = attrs.width / attrs.height;
4701                        var muted = ((attrs.muted || '').toLowerCase() === 'true') ? 'true' : 'false';
4702                        targetElement.innerHTML = '<iframe src="//www.youtube.com/embed/' + attrs.id + '?modestbranding=1&autoplay=1&mute=' + muted + '&autohide=2&border=0&wmode=opaque&enablejsapi=1&controls=1&rel=0" frameborder="0" class="lx-video__inner-iframe" width="' + attrs.width + '" widthOriginal="' + attrs.width + '" height="' + attrs.height + '" aspect="' + aspect + '" allowfullscreen="allowfullscreen"></iframe>';
4703                        attrs.$set('data-playing', true);
4704                        resize(element);
4705                        //Can only wire up the resize listener when the iframe is injected
4706                        scope.$on('content-video-window-resize', function (event, args) {
4707                            resize(element);
4708                        });
4709                    }
4710                }
4711
4712                function resize(myElement) {
4713                    var iframe = myElement.find('iframe');
4714                    if (iframe[0].attributes.widthOriginal.value >= myElement[0].clientWidth)
4715                    {
4716                        iframe[0].width = myElement[0].clientWidth;
4717                        iframe[0].height = myElement[0].clientWidth / iframe[0].attributes.aspect.value;
4718                    }
4719                }
4720        }
4721    };
4722    
4723    function contentVideoController($scope, $rootScope) {
4724        var cv = this;
4725        $scope.init = function () {
4726            
4727        }
4728    }
4729    
4730}]);
4731;
4732"use strict";
4733/* Directives */
4734angular.module("Lexus.Directives").directive("lxDealerLookup", [
4735    "$rootScope",
4736    "DealersService",
4737    "AddressSuggestService",
4738    "UserPreferencesService",
4739    function () {
4740        DealerLookupController.$inject = [
4741            "$scope",
4742            "$rootScope",
4743            "$http",
4744            "DealersService",
4745            "AddressSuggestService",
4746            "UserPreferencesService",
4747        ];
4748        return {
4749            scope: true,
4750            restrict: "A",
4751            controller: DealerLookupController,
4752            controllerAs: "dlc",
4753            bindToController: true,
4754            link: function (scope, element, attrs) {
4755                scope.$on("clear-dealer-search-input", function () {
4756                    element.find("tags-input input")[0].value = "";
4757                });
4758            },
4759        };
4760
4761        function DealerLookupController(
4762            $scope,
4763            $rootScope,
4764            $http,
4765            DealersService,
4766            AddressSuggestService,
4767            UserPreferencesService,
4768        ) {
4769            var dlc = this;
4770            var userPreferencesService = UserPreferencesService;
4771
4772            $scope.init = function (salesClosedDealers, serviceClosedDealers) {
4773                $scope.tags = [];
4774                $scope.tagsLoading = false;
4775                $scope.salesClosedDealers = salesClosedDealers;
4776                $scope.serviceClosedDealers = serviceClosedDealers;
4777
4778                $scope.ds = {
4779                    tags: [],
4780                    type: "Sales",
4781                    results: null,
4782                    loading: false,
4783                };
4784
4785                var visitorPreference = userPreferencesService.getLexusVisitor();
4786                var dealerPreference = userPreferencesService.getLexusDealer();
4787
4788                if (visitorPreference && visitorPreference.postCode && visitorPreference.suburb) {
4789                    $scope.ds.tags.push({
4790                        text: visitorPreference.suburb + " " + visitorPreference.postCode,
4791                        data: {
4792                            postCode: visitorPreference.postCode,
4793                            suburb: visitorPreference.suburb,
4794                        },
4795                    });
4796
4797                    $scope.searchNow();
4798                }
4799
4800                $scope.preferredDealer =
4801                    dealerPreference &&
4802                    dealerPreference.dealerID &&
4803                    dealerPreference.branchCode &&
4804                    dealerPreference.dealerName &&
4805                    dealerPreference.dealerUrl
4806                        ? {
4807                              id: dealerPreference.dealerID,
4808
4809                              branchCode: dealerPreference.branchCode,
4810                              name: dealerPreference.dealerName,
4811                              url: dealerPreference.dealerUrl,
4812                          }
4813                        : {};
4814            };
4815
4816            $scope.ChangeDealerType = function (type) {
4817                $scope.ds.type = type;
4818                if ($scope.ds.tags.length) {
4819                    $scope.searchNow();
4820                    $scope.updatePreferredDealer();
4821                }
4822            };
4823
4824            $scope.updatePreferredDealer = function () {
4825                $rootScope.$broadcast("preferred-dealer-change", {
4826                    dealerID: $scope.preferredDealer.id,
4827                    dealerCodeSimple: $scope.preferredDealer.dealerCodeSimple,
4828                    branchCode: $scope.preferredDealer.branchCode,
4829                    dealerName: $scope.preferredDealer.name,
4830                    dealerType: $scope.ds.type,
4831                    dealerUrl: $scope.preferredDealer.url,
4832                    dealerState: $scope.preferredDealer.state,
4833                });
4834            };
4835
4836            $scope.dealerSearch = function ($tag) {
4837                $scope.ds.tags = [];
4838                $scope.ds.tags[0] = $tag;
4839                $scope.searchNow();
4840            };
4841
4842            $scope.checkServiceType = function (result) {
4843                var serviceString = $scope.ds.type;
4844                if (typeof result == "string") {
4845                    serviceString = result;
4846                } else if (typeof result == "object" && result != null) {
4847                    serviceString = result.serviceType;
4848                }
4849
4850                return serviceString.toLowerCase().match(/^service$/) ? true : false;
4851            };
4852
4853            $scope.modifyResults = function (data) {
4854                if (data != null) {
4855                    var newData = data.filter(function (d) {
4856                        return (
4857                            ($scope.ds.type === "Sales" && !$scope.salesClosedDealers.includes(d.dealerCode)) ||
4858                            ($scope.ds.type !== "Sales" && !$scope.serviceClosedDealers.includes(d.dealerCode))
4859                        );
4860                    });
4861                    for (var r = 0, rLen = newData.length; r < rLen; r++) {
4862                        if (newData[r].serviceOnly != null) {
4863                            newData[r].serviceOnly = $scope.checkServiceType(data);
4864                        }
4865
4866                        if ($scope.ds.type === "Sales" && $scope.serviceClosedDealers.includes(newData[r].dealerCode)) {
4867                            newData[r].serviceType = "Sales";
4868                        }
4869
4870                        if ($scope.ds.type === "Service" && $scope.s
4870alesClosedDealers.includes(newData[r].dealerCode)) {
4871                            newData[r].serviceType = "Service";
4872                        }
4873                    }
4874
4875                    return newData;
4876                }
4877            };
4878
4879            $scope.searchNow = function () {
4880                $scope.ds.loading = true;
4881                $rootScope.$broadcast("visitor-details-change-autocomplete", $scope.ds.tags[0].data);
4882
4883                DealersService.setParam("postCode", $scope.ds.tags[0].data.postCode);
4884                DealersService.setParam("suburb", $scope.ds.tags[0].data.suburb);
4885                DealersService.setParam("type", $scope.ds.type);
4886
4887                DealersService.GetResults().then(
4888                    function (data) {
4889                        $scope.ds.results = $scope.modifyResults(data.Data);
4890                        $scope.ds.loading = false;
4891                        $scope.ds.showChangeDealer = false;
4892
4893                        if (typeof window !== "undefined") {
4894                            window.document.body.dispatchEvent(new CustomEvent("find-a-dealer-search"));
4895                        }
4896                    },
4897                    function (error) {
4898                        console.log(error);
4899                    },
4900                );
4901            };
4902
4903            $scope.clearSearch = function () {
4904                $scope.$broadcast("clear-dealer-search-input");
4905                $scope.ds.tags = [];
4906            };
4907
4908            $scope.setPreferredDealer = function (dealer, address, preferredDealer) {
4909                var allreadyChecked = dealer.dealerCode === preferredDealer.id;
4910                if (allreadyChecked) {
4911                    $scope.preferredDealer.id = null;
4912                    $scope.preferredDealer.branchCode = null;
4913                    $scope.preferredDealer.dealerCodeSimple = null;
4914                    $scope.preferredDealer.name = null;
4915                    $scope.preferredDealer.url = null;
4916                    $scope.preferredDealer.state = null;
4917                } else {
4918                    $scope.preferredDealer.id = dealer.dealerCode;
4919                    $scope.preferredDealer.branchCode = address.branchCode;
4920                    $scope.preferredDealer.dealerCodeSimple = dealer.dealerCodeSimple;
4921                    $scope.preferredDealer.name = dealer.dealerName;
4922                    $scope.preferredDealer.url = dealer.dealerUrl || address.webSite;
4923                    $scope.preferredDealer.state = address.state;
4924                }
4925                $scope.updatePreferredDealer();
4926            };
4927
4928            $scope.loadTags = function (query) {
4929                $scope.tagsLoading = true;
4930                AddressSuggestService.setParam("term", query);
4931                return AddressSuggestService.GetResults($scope.tagsLoaded);
4932            };
4933
4934            $scope.tagsLoaded = function () {
4935                $scope.tagsLoading = false;
4936            };
4937
4938            $scope.autocompleteLoading = function () {
4939                return $scope.tagsLoading;
4940            };
4941        }
4942    },
4943]);
4944;
4945'use strict';
4946
4947angular.module('Lexus.Directives')
4948.directive('lxFadeIn', ['$window', '$timeout', '$filter', '$http', '$analytics', function ($window, $timeout, $filter, $http, $analytics) {
4949    return {
4950        scope: true,
4951        restrict: 'A',
4952        bindToController: true,
4953        link: function (scope, element, attrs, filter, http) {
4954
4955            var helpers = {};
4956            helpers.now = Date.now || function () { return new Date().getTime(); };
4957            helpers.debounce = function (func, wait, immediate) {
4958                var timeout, args, context, timestamp, result;
4959
4960                var later = function () {
4961                    var last = helpers.now() - timestamp;
4962
4963                    if (last < wait && last >= 0) {
4964                        timeout = setTimeout(later, wait - last);
4965                    } else {
4966                        timeout = null;
4967                        if (!immediate) {
4968                            result = func.apply(context, args);
4969                            if (!timeout) context = args = null;
4970                        }
4971                    }
4972                };
4973
4974                return function () {
4975                    context = this;
4976                    args = arguments;
4977                    timestamp = helpers.now();
4978                    var callNow = immediate && !timeout;
4979                    if (!timeout) timeout = setTimeout(later, wait);
4980                    if (callNow) {
4981                        result = func.apply(context, args);
4982                        context = args = null;
4983                    }
4984
4985                    return result;
4986                };
4987            };
4988            helpers.throttle = function (func, wait) {
4989                var context, args, timeout, throttling, more, result;
4990                var whenDone = helpers.debounce(function () { more = throttling = false; }, wait);
4991                return function () {
4992                    context = this; args = arguments;
4993                    var later = function () {
4994                        timeout = null;
4995                        if (more) func.apply(context, args);
4996                        whenDone();
4997                    };
4998                    if (!timeout) timeout = setTimeout(later, wait);
4999                    if (throttling) {
5000                        more = true;
5001                    } else {
5002                        result = func.apply(context, args);
5003                    }
5004                    whenDone();
5005                    throttling = true;
5006                    return result;
5007                };
5008            };
5009
5010            if (window.Element && !Element.prototype.closest) {
5011                Element.prototype.closest =
5012                function (s) {
5013                    var matches = (this.document || this.ownerDocument).querySelectorAll(s),
5014                        i,
5015                        el = this;
5016                    do {
5017                        i = matches.length;
5018                        while (--i >= 0 && matches.item(i) !== el) { };
5019                    } while ((i < 0) && (el = el.parentElement));
5020                    return el;
5021                };
5022            }
5023
5024            var appearClass = 'lx-appear';
5025            var disappeaerClass = 'lx-disappeared';
5026            var includeEls = 'h1,h2,h3,h4,h5,h6,p,li,a[class*=cta],button[class*=cta],[class*=button],[class*=btn]';
5027            var excludeEls = '.lx-footer,.lx-footer-sticky,.lx-b-nav,.lx-c-nav,.lx-save-build-price__slideouttray,.lx-postcode-slideout';
5028
5029            var transitionEls = element.find(includeEls);
5030            transitionEls.each(function (index, el) {
5031                if (!el.closest(excludeEls)) {
5032                    angular.element(el).addClass(appearClass);
5033                    angular.element(el).addClass(disappeaerClass);
5034                }
5035            });
5036
5037            var appear = function () {
5038                var forceAppear = arguments ? arguments[0] : false;
5039                var topEdge = $window.pageYOffset,
5040                    clientHeight = $window.innerHeight,
5041                    fullHeight = topEdge + clientHeight;
5042                if (topEdge > 100 || forceAppear) {
5043                    transitionEls.each(function (index, el) {
5044                        if (angular.element(el).offset().top < fullHeight) {
5045                            angular.element(el).removeClass(disappeaerClass);
5046                            setTimeout(function () { angular.element(el).removeClass(appearClass) }, 500);
5047                        }
5048                    });
5049                }
5050            }
5051
5052            angular.element($window).bind("scroll", helpers.throttle(appear, 50));
5053
5054
5055            angular.element(document).ready(function () {
5056                setTimeout(function () { appear(true) } , 500);
5057            });
5058
5059        }
5060    }
5061
5062
5063}]);
5064
5065;
5066
5067"use strict";
5068/* Directives */
5069angular.module('Lexus.Directives')
5070  .directive('lxFeatureTiles', [function () {
5071      featureTilesController.$inject = ['$scope', '$compile', '$window', '$rootScope'];
5072      return {
5073          scope: true,
5074          restrict: 'A',
5075          controller: featureTilesController,
5076          controllerAs: 'ft',
5077          bindToController: true,
5078          link: function (scope, element, attrs) {
5079              scope.ft.panels = [];
5080              var panelCollection = element.find(scope.ft.section),
5081                imageCollection = element.find(scope.ft.section + ' img'),
5082                history = [],
5083                resizeTimer,
5084                imageLoaded = 0;
5085
5086              function loadSection(list, scopeList) {
5087                  if (scopeList.length === 0) {
5088                      angular.forEach(list, function (value, key) {
5089                          scopeList[key] = {
5090                              isLong: (value.offsetHeight > scope.ft.sectionHeight),
5091                              isOpen: false,
5092                              content: value.innerHTML
5093                          }
5094                      });
5095                  } else {
5096                      angular.forEach(list, function (value, key) {
5097                          if (!scopeList[key].isLong) {
5098                              scopeList[key].isLong =
5099                                (value.offsetHeight > scope.ft.sectionHeight);
5100                          }
5101                      });
5102                  }
5103              }
5104
5105              loadSection(panelCollection, scope.ft.panels);
5106
5107              for (var i = 0; i < imageCollection.length; i++) {
5108                  (function (i) {
5109                      imageCollection[i].addEventListener('load', function () {
5110                          imageLoaded++
5111                          if (imageLoaded === imageCollection.length) { // after all image loaded reload section class
5112                              loadSection(panelCollection, scope.ft.panels);
5113                              scope.$digest();
5114                          }
5115                      })
5116                  })(i);
5117              }
5118
5119              window.addEventListener('resize', function () {
5120                  clearTimeout(resizeTimer); // reduce flash
5121                  resizeTimer = setTimeout(function () {
5122                      loadSection(panelCollection, scope.ft.panels);
5123                      scope.$digest();
5124                  }, 50);
5125              });
5126          }
5127      };
5128
5129      function featureTilesController($scope, $compile, $window, $rootScope) {
5130          var ft = this;
5131
5132          $scope.init = function () {
5133              ft.modalWrapper = [
5134                '<div class="lx-feature-tiles__section is-in-modal">',
5135                '</div>'
5136              ];
5137              ft.section = '.lx-feature-tiles__section';
5138              ft.sectionHeight = '600';
5139              ft.breakpoint = Lexus.Breakpoint.ExtraSmall;
5140          }
5141
5142          $scope.featureBtn = function (i) {
5143              if (window.innerWidth >= ft.breakpoint) {
5144                  $scope.loadModal(ft.panels[i].content);
5145              } else {
5146                  $scope.ft.panels[i].isOpen = !$scope.ft.panels[i].isOpen;
5147              }
5148          }
5149
5150          $scope.loadModal = function (content) {
5151              $rootScope.$broadcast('open-modal-window', {
5152                  content: content,
5153                  wrapper: ft.modalWrapper
5154              });
5155          };
5156      }
5157  }]);
5158;
5159"use strict";
5160angular.module('Lexus.Directives')
5161  .directive("lxFixedFirstColumn", [function () {
5162    function init($scope, $element) {
5163      var interval = setInterval(function () {
5164        var tr = $element.find("tr");
5165        angular.forEach(tr, function (i) {
5166          var columns = angular.element(i).children();
5167
5168          if (columns.length < 1) {
5169            // Row with no columns? Ignore it.
5170            return;
5171          }
5172          if (columns.length === 2) {
5173            // Row with no columns? Ignore it.
5174            $element.find(".lx-responsive-table-viewport").css('overflow-x', 'hidden');
5175          }
5176          var column0 = angular.element(columns[0]).children()[0] || columns[0];
5177          var column1 = columns[1];
5178
5179          // reset height styles (if any) so we can measure the "natural" height of these <td>s
5180          columns[0].style.height = "";
5181          if (column1) {
5182            columns[1].style.height = "";
5183          }
5184
5185          // Calculate heights of each <td>.
5186          var height0 = (column0).offsetHeight;
5187          var height1 = column1 ? column1.offsetHeight : 0;
5188
5189          // Calculate final height.
5190          var height = Math.max(height0, height1);
5191
5192          // Set heights of <td> and <tr>.
5193          columns[0].style.height = height + "px";
5194          i.style.height = height + "px";
5195
5196          if (column1) {
5197            column1.style.height = height + "px";
5198          }
5199
5200          // If <td> heights have stabilized.
5201          if (height0 !== 0 && height0 === height1) {
5202            clearInterval(interval);
5203          }
5204        });
5205      }, 0);
5206    }
5207
5208    return {
5209      restrict: "A",
5210      template: "<div class='lx-responsive-table-viewport'><div ng-transclude></div></div>",
5211      transclude: true,
5212      link: function ($scope, $element) {
5213        init($scope, $element);
5214
5215        var resizeHandler = function () {
5216          init($scope, $element);
5217        };
5218
5219        $(window).on('resize', resizeHandler);
5220
5221        $scope.$on('$destroy', function () {
5222          $(window).off('resize', resizeHandler);
5223        });
5224      }
5225    };
5226
5227  }]);;
5228
5229"use strict";
5230/* Directives */
5231angular.module('Lexus.Directives')
5232.directive('lxFormFieldCarModelEngineVariantWithGrade', [function () {
5233
5234    formFieldController.$inject = ['$scope', '$rootScope', 'UserPreferencesService', 'VehicleService', '$filter', '$element', "$log"];
5235    return {
5236        scope: true,
5237        restrict: 'A',
5238        controller: formFieldController,
5239        controllerAs: 'form',
5240        bindToController: true,
5241        link: function (scope, element, attrs) {
5242            scope.vehicleIndex = attrs.vehicleIndex || "0";
5243        }
5244    };
5245
5246    function formFieldController($scope, $rootScope, UserPreferencesService, VehicleService, $filter, $element, $log) {
5247        var form = this;
5248
5249        $scope.init = function () {
5250            $scope.ffc = $scope.ffc || {};
5251            $scope.ffc.formfields = {
5252                data: {
5253                    enginevariants: [],
5254                    enhancementPacks: []
5255                },
5256                comingSoonModels: {}
5257            };
5258        }
5259
5260        function getGradeVariantLabel(engineVariant, grade) {
5261            return engineVariant.name + ' ' + $filter('replaceCaseInsensitive')(grade.name, engineVariant.name);
5262        }
5263
5264        //sorry, this is also hardcoded in forms-finance-calculator.js
5265        function getGradeVariantKey(engineVariant, grade) {
5266            return engineVariant.name + ';' + grade.name;
5267        }
5268
5269        function getVariantAndGradeFromKey(key) {
5270            if (key)
5271            {
5272                var values = key.split(';');
5273                if (values.length === 2) {
5274                    return {
5275                        engineVariant: values[0],
5276                        grade: values[1]
5277                    };
5278                }
5279            }
5280        }
5281
5282        function bindEnhancementPacks() {
5283            if ($scope.ffc.formfields.data.enginevariantsandgrades) {
5284                var engineVariantAndGrade = $scope.ffc.formfields.data.enginevariantsandgrades.find(function (item) {
5285                    return $scope.ffc.form.data.engineVariantAndGrade == item.key;
5286                });
5287
5288                if (engineVariantAndGrade) {
5289                    var selectedGradeId = engineVariantAndGrade.gradeId;
5290                    $scope.ffc.form.data.loadingEnhancementPacks = true;
5291                    VehicleService.getVariants(selectedGradeId)
5292                   .then(function (response) {
5293                       $scope.ffc.form.data.loadingEnhancementPacks = false;
5294                       if (response.status === "Success") {
5295                           $scope.ffc.formfields.data.enhancementPacks = response.data;
5296
5297                           if ($scope.ffc.form.data.engineVariantAndGrade == null) {
5298                               $scope.ffc.formfields.data.enhancementPacks = null;
5299                           }
5300
5301                           if ($scope.ffc.form.data.enhancementPack && $scope.ffc.form.data.enhancementPack !== "") {
5302                               var enhancementPackFound = $scope.ffc.formfields.data.enhancementPacks.find(function (item) {
5303                                   return $scope.ffc.form.data.enhancementPack == item.uriName;
5304                               });
5305
5306                               if (!enhancementPackFound) {
5307                                   $log.warn("resetting enhancement pack1");
5308                                   $scope.ffc.form.data.enhancementPack = $scope.ffc.formfields.data.enhancementPacks[0].uriName;
5309                               }
5310                           }
5311                           else {
5312                               $log.warn("resetting enhancement pack2");
5313                               $scope.ffc.form.data.enhancementPack = $scope.ffc.formfields.data.enhancementPacks[0].uriName;
5314                           }
5315                       }
5316                   });
5317                }
5318            }
5319        }
5320
5321        //At time of model change
5322        $scope.bindEngineVariants1 = function () {
5323            VehicleService.getVehiclesOverview().then(function success(response) {
5324                var myModel;
5325                response.data.find(function (bodyType) {
5326                    bodyType.models.find(function (model) {
5327                        if ($scope.ffc.form.data.carmodel == model.name)
5328                        {
5329                            myModel = model;
5330                            return;
5331                        }
5332                    });
5333                    return;
5334                });
5335
5336                VehicleService.getEngineVariantsWithGrades(myModel.id)
5337                .then(function success(response) {
5338                    if (response.status === "Success") {
5339                        $scope.ffc.formfields.data.enginevariantsandgrades = [];
5340                        var isPresent = false;
5341                        response.data.forEach(function (engineVariant) {
5342                            engineVariant.grades.forEach(function (grade) {
5343                                $scope.ffc.formfields.data.enginevariantsandgrades.push({
5344                                    engineVariant: engineVariant.name,
5345                                    grade: grade.name,
5346                                    gradeId: grade.id,
5347                                    mdmGradeId: grade.gradeID,
5348                                    label: getGradeVariantLabel(engineVariant, grade),
5349                                    key: getGradeVariantKey(engineVariant, grade)
5350                                });
5351                                if (getGradeVariantKey(engineVariant, grade) == $scope.ffc.form.data.engineVariantAndGrade) {
5352                                    isPresent = true;
5353                                }
5354                            });
5355                        });
5356
5357                        if (!isPresent) {
5358                            $scope.ffc.form.data.engineVariantAndGrade = null;
5359                            $scope.ffc.formfields.data.enhancementPacks = null;
5360                        }
5361
5362                        //Can only bind Enhancement packs after they have been returned on bootup
5363                        bindEnhancementPacks();
5364                    }
5365                })
5366                .then(function failure(response) { });
5367
5368            }).then(function failure(response) { });
5369
5370        };
5371
5372        $rootScope.$on('field-car-model-engine-variant-update-selected', function (event, data) {
5373            //Forms Have a UniqueVehicle Index , if not it defaults to one vehicle with index 0
5374            if (!data || (data && data.vehicleIndex && (data.vehicleIndex == $scope.vehicleIndex))) {
5375                $scope.updateModelSelection(true);
5376            }
5377        });
5378
5379        $rootScope.$on('field-car-model-engine-variant-update-without-reset', function () {
5380            $scope.updateModelSelection(false);
5381        });
5382
5383        $rootScope.$on('field-car-model-enhancement-pack-update-selected', function () {
5384            $scope.updateEngineVariantSelection();
5385        });
5386
5387        function resetChildControls() {
5388            $scope.ffc.formfields.data.enhancementPacks = null;
5389            $scope.ffc.formfields.data.enginevariantsandgrades = null;
5390            $scope.ffc.form.data.engineVariantAndGrade = null;
5391            $scope.ffc.form.data.engineVariant = null;
5392            $scope.ffc.form.data.grade = null;
5393        }
5394
5395        $scope.updateModelSelection = function (clearChildControls) {
5396
5397            if ($scope.ffc.formfields.comingSoonModels[$scope.ffc.form.data.carmodel]) {
5398                resetChildControls();
5399                return;
5400            }
5401
5402            if (clearChildControls) {
5403                resetChildControls();
5404            }
5405            $scope.bindEngineVariants1();
5406
5407            $rootScope.$broadcast('vehicle-preference-change', {
5408                model: $scope.ffc.form.data.carmodel,
5409                grade: (UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().grade : ""),
5410                engineVariant: (UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().engineVariant : ""),
5411                enhancementPack: (UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().enhancementPack : ""),
5412                vehicleIndex: $scope.vehicleIndex
5413            });
5414        };
5415
5416        //At time of engine variant change
5417        $scope.updateEngineVariantSelection = function (clearChildControls) {
5418            if (clearChildControls) {
5419                $scope.ffc.formfields.data.enhancementPacks = null;
5420                $scope.ffc.form.data.enhancementPack = null;
5421            }
5422
5423            bindEnhancementPacks();
5424
5425            var values = getVariantAndGradeFromKey($scope.ffc.form.data.engineVariantAndGrade);
5426            var engineVariant = (values ? values.engineVariant : null);
5427            var grade = (values ? values.grade : null);
5428            $scope.ffc.form.data.engineVariant = engineVariant;
5429            $scope.ffc.form.data.grade = grade;
5430
5431            console.log('updateEngineVariantSelection');
5432            console.log('clearChildControls=' + clearChildControls);
5433            $rootScope.$broadcast('vehicle-preference-change', {
5434                model: $scope.ffc.form.data.carmodel,
5435                engineVariant: engineVariant ? engineVariant : (UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().engineVariant : ""),
5436                grade: grade ? grade : (UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().grade : ""),
5437                enhancementPack: UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().enhancementPack : "",
5438                vehicleIndex: $scope.vehicleIndex
5439            });
5440
5441        };
5442
5443        $scope.updateEnhancementPackSelection = function () {
5444            var values = getVariantAndGradeFromKey($scope.ffc.form.data.engineVariantAndGrade);
5445            var engineVariant = values ? values.engineVariant : '';
5446            var grade = values ? values.grade : '';
5447            console.log('updateEnhancementPackSelection');
5448            $rootScope.$broadcast('vehicle-preference-change', {
5449                model: $scope.ffc.form.data.carmodel,
5450                grade: grade ? grade : UserPreferencesService.getLexusVehicle().grade,
5451                engineVariant: engineVariant ? engineVariant : UserPreferencesService.getLexusVehicle().engineVariant,
5452                enhancementPack: $scope.ffc.form.data.enhancementPack
5453            });
5454        };
5455
5456        $scope.modelDisplayName = function (name, fullName, isComingSoon, offerTitle) {
5457            $scope.ffc.formfields.comingSoonModels[name] = isComingSoon && isComingSoon.toLowerCase() == 'true';
5458            return (isComingSoon && isComingSoon.toLowerCase() == 'true') ? 'All-New ' + name + ' - ' + fullName + ' (' + offerTitle + ')' : name + '-' + fullName;
5459        };
5460    }
5461}])
5462
5463.directive('lxFormFieldCarModelEngineVariant', [function () {
5464
5465    formFieldController.$inject = ['$scope', '$rootScope', 'UserPreferencesService', 'VehicleService', '$filter', '$element'];
5466    return {
5467        scope: true,
5468        restrict: 'A',
5469        controller: formFieldController,
5470        controllerAs: 'form',
5471        bindToController: true,
5472        link: function (scope, element, attrs) {
5473
5474        }
5475    };
5476
5477    function formFieldController($scope, $rootScope, UserPreferencesService, VehicleService, $filter, $element) {
5478        var form = this;
5479        $scope.init = function () {
5480
5481            $scope.ffc.formfields = {
5482                data: {
5483                    enginevariants: []
5484                },
5485                comingSoonModels: {}
5486            };
5487        }
5488
5489        //At time of model change
5490        $scope.bindEngineVariants2 = function () {
5491            //Clear fields at lower level and do not bind variants for comingSoon models
5492            if ($scope.ffc.formfields.comingSoonModels[$scope.ffc.form.data.carmodel]) {
5493                $scope.ffc.form.data.carmodel = null;
5494                $scope.ffc.form.data.enginevariant = null;
5495                return;
5496            }
5497
5498            VehicleService.getVehiclesOverview().then(function success(response) {
5499                var myModel;
5500                response.data.find(function (bodyType) {
5501                    bodyType.models.find(function (model) {
5502                        if ($scope.ffc.form.data.carmodel == model.name) {
5503                            myModel = model;
5504                            return;
5505                        }
5506                    });
5507                    return;
5508                });
5509
5510                VehicleService.getEngineVariants(myModel.id)
5511                .then(function success(data) {
5512                    if (data.status === "Success") {
5513                        $scope.ffc.formfields.data.enginevariants = data.data;
5514                    }
5515                })
5516                .then(function failure(response) { });
5517
5518            }).then(function failure(response) { });
5519
5520        };
5521
5522        $rootScope.$on('field-car-model-engine-variant-update-selected', function () {
5523            $scope.bindEngineVariants2();
5524        });
5525
5526        $scope.updateModelSelection = function () {
5527            if ($scope.ffc.formfields.comingSoonModels[$scope.ffc.form.data.carmodel]) {
5528                $scope.ffc.form.data.enginevariant = null;
5529                return;
5530            }
5531            $scope.bindEngineVariants2();
5532            $rootScope.$broadcast('vehicle-preference-change', {
5533                model: $scope.ffc.form.data.carmodel
5534                //engineVariant: UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().engineVariant : ""
5535                //grade: UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().grade : ""
5536            });
5537        };
5538
5539        //At time of engine variant change
5540        $scope.updateEngineVariantSelection = function () {
5541
5542            console.log("updateEngineVariantSelection");
5543
5544            $rootScope.$broadcast('vehicle-preference-change', {
5545                model: $scope.ffc.form.data.carmodel,
5546                engineVariant: $scope.ffc.form.data.enginevariant,
5547                //grade: UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().grade : "",
5548            });
5549        };
5550
5551        $scope.modelDisplayName = function (name, fullName, isComingSoon, offerTitle) {
5552            $scope.ffc.formfields.comingSoonModels[name] = isComingSoon && isComingSoon.toLowerCase() == 'true';
5553            return (isComingSoon && isComingSoon.toLowerCase() == 'true') ? 'All-New ' + name + ' - ' + fullName + ' (' + offerTitle + ')' : name + '-' + fullName;
5554        };
5555
5556    }
5557}])
5558.directive('lxFormFieldCarModel', [function () {
5559    formFieldController.$inject = ['$scope', '$rootScope', 'UserPreferencesService', 'VehicleService', '$filter', '$element'];
5560    return {
5561        scope: true,
5562        restrict: 'A',
5563        controller: formFieldController,
5564        controllerAs: 'form',
5565        bindToController: true,
5566        link: function (scope, element, attrs) {
5567
5568        }
5569    };
5570
5571    function formFieldController($scope, $rootScope, UserPreferencesService, VehicleService, $filter, $element) {
5572        var form = this;
5573        $scope.init = function () {
5574
5575            $scope.ffc.formfields = {
5576                data: {
5577                    enginevariants: []
5578                },
5579                comingSoonModels: {}
5580            };
5581
5582        }
5583
5584        $rootScope.$on('field-car-model-update-selected', function () {
5585            $scope.updateModelSelection();
5586        });
5587
5588        $scope.updateModelSelection = function () {
5589            if ($scope.ffc.formfields.comingSoonModels[$scope.ffc.form.data.carmodel]) {
5590                $scope.ffc.form.data.carmodel = null;
5591                return;
5592            }
5593            $rootScope.$broadcast('vehicle-preference-change', {
5594                model: $scope.ffc.form.data.carmodel,
5595                //grade: UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().grade : "",
5596                //engineVariant: UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().engineVariant : ""
5597            });
5598        };
5599
5600        $scope.modelDisplayName = function (name, fullName, isComingSoon, offerTitle) {
5601            $scope.ffc.formfields.comingSoonModels[name] = isComingSoon && isComingSoon.toLowerCase() == 'true';
5602            return (isComingSoon && isComingSoon.toLowerCase() == 'true') ? 'All-New ' + name + ' - ' + fullName + ' (' + offerTitle + ')' : name + '-' + fullName;
5603        };
5604    }
5605}])
5606.directive('lxFormFieldDateWrap', ['$rootScope', function ($rootScope) {
5607    formFieldDateWrapController.$inject = ['$scope', '$rootScope', '$filter', '$element'];
5608    return {
5609        scope: true,
5610        restrict: 'E',
5611        controller: formFieldDateWrapController,
5612        controllerAs: 'form',
5613        bindToController: true,
5614        link: function (scope, element, attrs) {
5615            var formName = attrs.name;
5616            var dp = element.find("#datePicker");
5617            dp.bind('change', function (elem) {
5618                if (!moment(elem.target.value, 'DD-MM-YYYY').isValid()) {
5619                    $rootScope.$broadcast('date-picker-invalid');
5620                }
5621                else {
5622                    $rootScope.$broadcast('date-picker-valid');
5623                }
5624
5625            });
5626        }
5627    };
5628
5629    function formFieldDateWrapController($scope, $rootScope, $filter, $element) {
5630        var form = this;
5631        $scope.init = function () {
5632
5633        }
5634    }
5635}])
5636.directive('lxFormFieldRangeInput', ['$rootScope', function ($rootScope) {
5637    formFieldRangeInputController.$inject = ['$scope', '$rootScope', '$filter', '$element'];
5638    return {
5639        scope: true,
5640        restrict: 'A',
5641        controller: formFieldRangeInputController,
5642        controllerAs: 'form',
5643        bindToController: true,
5644        link: function (scope, element, attrs) {
5645            var control, controlMin, controlMax, controlVal, formattedValue, labelWidth, controlThumbWidth, position, positionOffset, output, range, format;
5646            control = element.find("input[type='range']");
5647            control.on('input change', function () {
5648                calculatePosition();
5649            });
5650
5651            if (attrs['format']) {
5652                format = attrs['format'];
5653            }
5654
5655            if (attrs['watch']) {
5656                var watchArgs = []
5657
5658                if (attrs.watch.split) {
5659                    attrs['watch'].split(',').forEach(function (str) {
5660                        watchArgs.push(str.trim());
5661                    });
5662                }
5663
5664                watchArgs.forEach(function (item) {
5665                    scope.$watch(item, function (oldValue, newValue) {
5666                        if (oldValue != newValue) {
5667                            calculatePosition();
5668                        }
5669                    });
5670                });
5671            }
5672
5673            $rootScope.$on('balloon-payment-options-received', function () {
5674                //This is for second slider change can be optimised further
5675                calculatePosition();
5676            });
5677
5678            function calculatePosition() {
5679                controlMin = parseInt(control.attr('min')) || 0;
5680                controlMax = parseInt(control.attr('max')) || 1;
5681                controlVal = control.val();
5682                if (format) {
5683                    formattedValue = scope.formatOutput(format, controlVal);
5684                } else {
5685                    formattedValue = controlVal;
5686                }
5687
5688                range = controlMax - controlMin;
5689                controlThumbWidth = 25;
5690                labelWidth = 38;
5691
5692                position = ((controlVal - controlMin) / range) * 100;
5693
5694                // Calculate negative offset:
5695                // half label width
5696                // - half thumb width            zero            + half thumb width
5697                // |--------------------------------------------------------------|
5698                positionOffset = Math.round(((controlThumbWidth) * position / 100) - (controlThumbWidth / 2)) + labelWidth / 2;
5699                output = control.next('.lx-form-slider__output');
5700
5701                output.css('left', 'calc(' + position + '% - ' + positionOffset + 'px)').html(formattedValue);
5702            }
5703
5704            calculatePosition();
5705        }
5706
5707    };
5708
5709    function formFieldRangeInputController($scope, $rootScope, $filter, $element) {
5710        var form = this;
5711        $scope.init = function () {
5712
5713        }
5714
5715        $scope.formatOutput = function(format, val){
5716            return $filter('formatArgs')(format, [val]);
5717        }
5718    }
5719}])
5720.directive('lxTooltip', [function () {
5721    disclaimerBubbleController.$inject = ['$scope', '$rootScope', '$filter', '$element'];
5722    return {
5723        scope : {
5724            'cssClass': '@'
5725        },
5726        restrict: 'AE',
5727        controller: disclaimerBubbleController,
5728        controllerAs: 'dbc',
5729        bindToController: true,
5730        transclude: true,
5731        //template: '<div class="lx-tooltip__info-overlay"><div class="lx-tooltip__info-icon" data-ng-init="init();" data-ng-mouseover="show()" data-ng-mouseleave="hide()"></div><div class="lx-tooltip__info-content" data-ng-transclude data-ng-show="showDisclaimer"></div></div>',
5732        template: '<div class="lx-tooltip__info-overlay is-{{alignment}} {{cssClass}}"' +
5733                        'ng-class="{\'is-open\': isMouse && isMouseInside || !isMouse && isToggled}" ' +
5734                        'mouse-detected="isMouse = true" ' +
5735                        'ng-click="toggleIt($event)" ' +
5736                        'ng-mouseenter="isMouseInside = true" ' +
5737                        'ng-mouseleave="isMouseInside = false">' +
5738                        '<div class="lx-tooltip__info-icon" ng-hide="showOffer"></div>' +
5739                        '<div class="lx-tooltip__info-on-offer" ng-show="showOffer"><span>{{onOfferTitle}}</span></div>' +
5740                        '<div class="lx-tooltip__info-content" data-ng-transclude ></div>' +
5741                  '</div>',
5742        link: function (scope, element, attrs) {
5743            scope.alignment = attrs.lxTooltip;
5744            scope.showOffer = (attrs.tooltipOffer != null) ? attrs.tooltipOffer : false;
5745            scope.onOfferTitle = (attrs.tooltipTitle != null) ? attrs.tooltipTitle : "ON OFFER*";
5746        }
5747    };
5748
5749    function disclaimerBubbleController($scope, $rootScope, $filter, $element) {
5750        var dbc = this;
5751        $scope.init = function () {
5752            $scope.showDisclaimer = false;
5753        }
5754
5755        $scope.show = function () {
5756            $scope.showDisclaimer = true;
5757        }
5758
5759        $scope.hide = function () {
5760            $scope.showDisclaimer = false;
5761        }
5762
5763        $scope.toggleIt = function (event) {
5764            $scope.isToggled = !$scope.isToggled;
5765            event.stopPropagation();
5766        }
5767
5768    }
5769}]);
5770;
5771
5772"use strict";
5773/* Directives */
5774angular.module('Lexus.Directives')
5775  .directive('lxForm', [function () {
5776      var formBroadcast = '';
5777      var formName = '';
5778      formController.$inject = ['$scope', '$rootScope', 'UserPreferencesService', '$filter', '$element', '$timeout', 'AddressSuggestService'];
5779      return {
5780          scope: true,
5781          restrict: 'A',
5782          controller: formController,
5783          controllerAs: 'form',
5784          bindToController: true,
5785          link: function (scope, element, attrs) {
5786              formBroadcast = attrs.lxForm;
5787              formName = attrs.name;
5788              scope.$broadcast("initialize");
5789          }
5790      };
5791      
5792      function formController($scope, $rootScope, UserPreferencesService, $filter, $element, $timeout, AddressSuggestService) {
5793          var form = this,
5794            i,
5795            monthList = [4, 6, 9, 11],
5796            yesterday = moment().subtract(1, 'days'),
5797            inThreeMonths = moment().add(3, 'months');
5798
5799          function addMonths(d, m) {
5800              var years = Math.floor(m / 12);
5801              var months = m - (years * 12);
5802              if (years) d.setFullYear(d.getFullYear() + years);
5803              if (months) d.setMonth(d.getMonth() + months);
5804              return d;
5805          }
5806
5807          function leadingZero(value) {
5808              if (value < 10) {
5809                  return '0' + value;
5810              }
5811
5812              return value;
5813          }
5814
5815          function formatDateString(date) {
5816              var d = new Date(date),
5817                  month = '' + (d.getMonth() + 1),
5818                  day = '' + d.getDate(),
5819                  year = d.getFullYear();
5820
5821              if (month.length < 2) month = '0' + month;
5822              if (day.length < 2) day = '0' + day;
5823              return [year, month, day].join('-');
5824          }
5825
5826          $scope.dateError = function (formName) {
5827              formName = formName || "lxForm";
5828
5829              var value = $scope[formName].datePicker.$viewValue ?
5830              $scope[formName].datePicker.$viewValue : '';
5831
5832              if (!value)
5833                  return $scope.form.dateError[2];
5834
5835              var dateParts = (value.indexOf('/') > 1) ?
5836                value.split('/') : false,
5837              date = (dateParts != false) ? (dateParts[2] + '-'
5838                + dateParts[1] + '-' + dateParts[0]) : value,
5839              date = Date.parse(date);
5840
5841              if (isNaN(date)) {
5842                  return $scope.form.dateError[0];
5843              }
5844              else {
5845                  return $scope.form.dateError[2];
5846              }
5847          }
5848
5849          //DO NOT NEST THIS OBJECT, Plugin doesnt like form.dateChange
5850          $scope.dateChange = function (modelName, newValue) {
5851              
5852          }
5853
5854          $scope.$on("initialize", function () {
5855              $scope.user = {};
5856              $scope.form.dateError = [
5857                'Invalid date',
5858                'Please choose a date that is in the valid range',
5859                'Please pick date from the calendar'
5860              ];
5861
5862              $scope.form.specificErrorMessages = {
5863                  'VINMsg': 'VIN must be 17 alphanumberic characters',
5864                  'VINLookupFailedMsg': 'VIN cannot be found, please check that what you have entered is correct',
5865                  'RegistrationInvalidMsg': 'Maximum seven characters. Numbers and letters only.'
5866              };
5867
5868
5869              $scope.form.errorMessages = [
5870                'This field is required',
5871                'The format is not valid',
5872                'The input is too short',
5873                'The captcha is required',
5874                'The value is not suitable'
5875              ];
5876
5877              $scope.form.namePattern = /^[a-zA-Z- ']*$/;
5878              $scope.form.nameMinLength = 2;
5879              $scope.form.messageMinLength = 16;
5880              $scope.form.postcodeMinLength = 4;
5881              $scope.form.dealerPostcodeMinLength = 4;
5882              $scope.form.yearMinLength = 4;
5883              $scope.form.phoneMinLength = 10;
5884              $scope.form.vinMinLength = 17;
5885              $scope.form.emailPattern = /^(([^<>()\[\]\\.,;:\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,})$/;
5886              $scope.form.numberPattern = /^\d+$/;
5887              $scope.form.phonePattern = /^(0[23478]|\+[1-9])\d{8,15}$/i;
5888              $scope.form.hiddenUnselected = false;
5889              $scope.form.hasHTML5DatePicker = Modernizr.inputtypes.date;
5890              $scope.form.dateFormat = 'date';
5891              $scope.form.minYear = "1990";
5892              //Do not put this in a nested object, You have been warned by Ettienne!
5893              $scope.dateMin = moment().add(1, 'd').hour(12).startOf('h');//today;
5894              $scope.dateMax = moment().add(3, 'months').hour(12).startOf('h');//inThreeMonths;
5895              $scope.dateDefault = new Date(moment().add(3, 'd').hour(12).startOf('h'));//inThreeDays
5896
5897              $scope.form.dateMin2 = formatDateString(yesterday);
5898              $scope.form.dateMax2 = formatDateString(inThreeMonths);
5899              $scope.form.datePattern = /^(0[1-9]|1[0-9]|2[0-9]|3[01])\/(0[1-9]|1[012])\/(201[6-9])/;
5900              $scope[formName].date = $scope.dateDefault;
5901              $scope.form.tagsLoading = false;
5902              $scope.form.alphanumeric = /^[a-zA-Z0-9]*$/;
5903          });
5904
5905          $scope.errorText = function (length, min, data, value, specificErrorKey) {
5906              if (value) {
5907                  //  console.log(value);
5908              }
5909              if(specificErrorKey) {
5910                   return form.specificErrorMessages[specificErrorKey];
5911              }
5912              else
5913              {
5914                  if (!length) {
5915                      return form.errorMessages[0];
5916                  } else if (min && length < min) {
5917                      return form.errorMessages[2];
5918                  } else if (data) {
5919                      return form.errorMessages[4];
5920                  } else {
5921                      return form.errorMessages[1];
5922                  }
5923              }
5924              
5925          }
5926
5927          $scope.showUnselected = function () {
5928              form.hiddenUnselected = false;
5929          }
5930
5931          $scope.hideUnselected = function () {
5932              if (!form.hiddenUnselected) {
5933                  form.hiddenUnselected = true;
5934              }
5935          }
5936
5937          $scope.VINchange = function() {
5938              $rootScope.$broadcast('vin-change');
5939          };
5940
5941          $scope.submit = function (formName) {
5942              formName = formName || "lxForm";
5943              //console.log(formBroadcast);
5944              if ($scope[formName].$invalid) {
5945                  angular.forEach($scope[formName].$error, function (field) {
5946                      angular.forEach(field, function (errorField) {
5947                          if (errorField && errorField.$setTouched) {
5948                              errorField.$setTouched();
5949                              errorField.$setDirty();
5950                          }
5951                      })
5952                  });
5953                  if ($element.find(".ng-invalid")[0].id === 'postcodes') {
5954                      $element.find(".ng-invalid input")[0].focus();
5955                  } else {
5956                      $element.find(".ng-invalid")[0].focus();
5957                  }
5958
5959              } else {
5960                  $rootScope.$broadcast('open-modal-window', {
5961                      content: '<div class="lx-loader">Loading...</div>',
5962                      closeOnEvent: 'close-modal-window',
5963                      variant: Lexus.ModalVariant.Spinner
5964                  });
5965                  $rootScope.$broadcast(formBroadcast, {
5966                      content: $scope.user
5967                  });
5968              }
5969          }
5970
5971          $rootScope.$on('form-result', function (events, arg) {
5972              $rootScope.$broadcast('close-modal-window');
5973          });
5974
5975          $rootScope.$on('date-picker-invalid', function (event, value) {
5976              $scope[formName].datePicker.$setValidity("validDate", false);
5977              $scope.$apply();
5978          });
5979
5980          $rootScope.$on('vin-lookup-success', function (event, value) {
5981              $scope[formName].vin.$setValidity("notfound", true);
5982          });
5983
5984          $rootScope.$on('vin-lookup-failed', function (event, value) {
5985              $scope[formName].vin.$setValidity("notfound", false);
5986          });
5987
5988          $scope.loadTags = function (query) {
5989              $scope.form.tagsLoading = true;
5990              AddressSuggestService.setParam('term', query);
5991              return AddressSuggestService.GetResults($scope.tagsLoaded);
5992          };
5993
5994          $scope.tagsLoaded = function () {
5995              $scope.form.tagsLoading = false;
5996          }
5997
5998          $scope.autocompleteLoading = function () {
5999              return $scope.form.tagsLoading;
6000          };
6001
6002          $rootScope.$on('date-picker-valid', function (event, value) {
6003              $scope[formName].datePicker.$setValidity("validDate", true);
6004              $scope.$apply();
6005          });
6006
6007
6008      }
6009  }]);
6010;
6011"use strict";
6012/* Directives */
6013angular.module('Lexus.Directives')
6014.directive('lxFormsFinanceCalculator', ['$http', '$filter', '$timeout', '$window', '$interval', '$location',
6015    '$rootScope', 'DealersService', 'AddressSuggestService', 'UserPreferencesService',
6016    'VehicleService', 'vcRecaptchaService', 'FormsService', 'PricingService', '$sce',
6017    'BuildPriceService', 'BuildPriceAPIService', 'EloquaService', '$log', FormsFinanceCalculatorDirective]);
6018
6019
6020function FormsFinanceCalculatorDirective($http, $filter, $timeout, $window, $interval, $location,
6021    $rootScope, DealersService, AddressSuggestService, UserPreferencesService,
6022    VehicleService, vcRecaptchaService, FormsService, PricingService, $sce,
6023    BuildPriceService, BuildPriceAPIService, eloquaService, $log) {
6024    var formName = '';
6025    var forceDealerId = '';
6026    var dealerState = '';
6027    FormsFormsFinanceCalculatorController.$inject = ['$scope'];
6028
6029    return {
6030        scope: true,
6031        restrict: 'A',
6032        controller: FormsFormsFinanceCalculatorController,
6033        controllerAs: 'ffc',
6034        bindToController: true,
6035        link: function (scope, element, attrs, filter, http) {
6036            scope.successContent = element.find('.lx-form-success');
6037            scope.failureContent = element.find('.lx-form-failure');
6038            formName = attrs.lxFormsName;
6039        }
6040    };
6041
6042    function FormsFormsFinanceCalculatorController($scope) {
6043        var ftd = this;
6044
6045        $log.info("Build and Price Data:");
6046        $log.info($scope.buildAndPriceData);
6047
6048        $scope.ffc.urlParameters = $location.search();
6049        $scope.BuildPriceService = BuildPriceService;
6050        $scope.testValue = 10;
6051
6052        function getModelFromAvailableModels(carModel) {
6053
6054            var availableModels = $scope.ffc.form.settings.availableModels;
6055
6056            if (!availableModels || (availableModels && availableModels.length == 0))
6057                return null;
6058
6059            if (availableModels.length === 1) {
6060                return availableModels[0];
6061            }
6062
6063            if (availableModels.length > 1) {
6064                var modelIndex = availableModels.findIndex(function (m) { return carModel != null ? m.toLowerCase() === carModel.toLowerCase() : false });
6065                return modelIndex > -1 ? availableModels[modelIndex] : null;
6066            }
6067
6068            return null;
6069        }
6070
6071        $rootScope.$on('change-active-vehicle', function () {
6072            var dataVehicle = UserPreferencesService.getLexusVehicle($scope.ffc.urlParameters);
6073            $scope.ffc.form.data.carmodel = getModelFromAvailableModels(dataVehicle.model);
6074            $scope.clearSomeCarData();
6075
6076            if ($scope.ffc.form.data.carmodel == null)
6077                return;
6078
6079            $rootScope.$broadcast('field-car-model-engine-variant-update-selected');
6080        });
6081
6082        $scope.init = function (settings) {
6083
6084            var dataVehicle = UserPreferencesService.getLexusVehicle($scope.ffc.urlParameters);
6085            var dataDealer = UserPreferencesService.getLexusDealer($scope.ffc.urlParameters);
6086            var settings = settings || {};
6087            settings.balloonPayment = settings.balloonPayment || {};
6088            settings.loanTerm = settings.loanTerm || {};
6089            settings.summary = settings.summary || {};
6090            settings.messages = settings.messages || {};
6091
6092            $scope.dealerState = settings.dealerState;
6093            $scope.forceDealerId = settings.forceDealerId;
6094            $scope.isDealerSite = settings.isDealerSite;
6095            $scope.isMultiBranchDealer = settings.isMultiBranchDealer;
6096            $scope.dealerCompositeID = settings.dealerCompositeID;
6097
6098            $scope.pricingSuccess = true;
6099
6100            $scope.currentPath = $location.path();
6101
6102            //defaults
6103            $scope.ffc.form = {
6104                submitting: false,
6105                completed: false,
6106                errors: false,
6107                settings: {
6108                    availableModels: settings.availableModels ? settings.availableModels : null,
6109                    availableGrades: settings.availableGrades ? settings.availableGrades : null,
6110                    balloonPayment: {
6111                        fixedValue: settings.balloonPayment.fixedValue ? settings.balloonPayment.fixedValue : null,
6112                        min: settings.balloonPayment.min ? settings.balloonPayment.min : 0,
6113                        max: settings.balloonPayment.max ? settings.balloonPayment.max : 50,
6114                        step: settings.balloonPayment.step ? settings.balloonPayment.step : 5,
6115                        disabled: settings.balloonPayment.disabled != null ? settings.balloonPayment.disabled : true
6116                    },
6117                    loanTerm: {
6118                        personalMax: settings.loanTerm.personalMax ? settings.loanTerm.personalMax : 5,
6119                        businessMax: settings.loanTerm.businessMax ? settings.loanTerm.businessMax : 5,
6120                        max: settings.loanTerm.max ? settings.loanTerm.max : 5,
6121                        min: 1
6122                        //disabled: settings.loanTerm.disabled ? settings.loanTerm.disabled : true
6123                    },
6124                    /*interestRate: {
6125                        min: settings.interestRate.min ? settings.interestRate.min : 0,
6126                        max: settings.interestRate.max ? settings.interestRate.max : 100,
6127                        step: settings.interestRate.step ? settings.interestRate.step : 0.25
6128                    },*/
6129                    summary: {
6130                        paymentInterval: settings.summary.paymentInterval ? settings.summary.paymentInterval : 'month'
6131                    },
6132                    messages: {
6133                        apiError: settings.messages.apiError ? settings.messages.apiError : 'Oops, something went wrong while we were doing the sums.  Please check the form values and try again.  If the problem persists, please contact your dealer directly.'
6134                    },
6135                    disclaimerPage: settings.disclaimerPage,
6136                    enablePricingByZone: settings.enablePricingByZone == 'true'
6137                },
6138                data: {
6139                    postcodes: null,
6140                    deposit: null,
6141                    interestrate: null,
6142                    interestRateCopy: "",
6143                    carmodel: dataVehicle.model,
6144                    grade: dataVehicle.grade,
6145                    engineVariant: dataVehicle.engineVariant,
6146                    engineVariantAndGrade: dataVehicle.engineVariant + ";" + dataVehicle.grade,
6147                    enhancementPack: dataVehicle.enhancementPack,
6148                    trim: dataVehicle.trim,
6149                    colour: dataVehicle.colour,
6150                    loanterm: null,
6151                    balloonPayment: 0,
6152                    loantype: 'Personal', //Default Selected Value
6153                    dealerId: dataDealer.dealerID,
6154                    dealerName: dataDealer.dealerName,
6155                    source: $scope.ffc.urlParameters.source || null
6156                },
6157                emailsettings: {
6158                    email: {
6159                        subject: 'Lexus Financial Services: Finance estimate',
6160                        body: '',
6161                        introText: '',
6162                        disclaimer: '',
6163                        fullBodyContents: '',
6164                        //cc: emailsettings.email.cc ? emailsettings.email.cc : '',
6165                        //bcc: emailsettings.email.bcc ? emailsettings.email.bcc : ''
6166                    }
6167                },
6168                results: {
6169                    paint: null,
6170                    trim: null,
6171                    driveAwayPrice: 0,
6172                    installmentPerMonth: 0,
6173                    installmentPerWeek: 0,
6174                    finalBalloonPayment: 0
6175                },
6176                pricingSuccess: true,
6177                dealerquoteform: {
6178                    Show: false
6179                }
6180            };
6181
6182            $log.info("$scope.ffc.form.data:");
6183            $log.info($scope.ffc.form.data);
6184
6185            if ($scope.ffc.form.settings.balloonPayment.fixedValue != null) {
6186                $scope.ffc.form.data.balloonPayment = $scope.ffc.form.settings.balloonPayment.fixedValue;
6187            }
6188
6189            // LEXWEB-450 / AC 16 - clear cookie if carmodel unavailable for this campaign
6190            var availableModels = $scope.ffc.form.settings.availableModels;
6191            if (availableModels) {
6192
6193                $scope.ffc.form.data.carmodel = getModelFromAvailableModels($scope.ffc.form.data.carmodel);
6194
6195                $scope.clearSomeCarData();
6196
6197                $rootScope.$broadcast('vehicle-preference-change', {
6198                    model: $scope.ffc.form.data.carmodel, //Ac6 reset all cookies on model change
6199                    grade: undefined,
6200                    engineVariant: undefined,
6201                    enhancementPack: undefined
6202                });
6203
6204                $rootScope.$broadcast('field-car-model-engine-variant-update-selected');
6205            }
6206
6207            UserPreferencesService.getLexusVisitorSession().then(function success(data) {
6208                var dataUser = UserPreferencesService.getLexusVisitor($scope.ffc.urlParameters);
6209
6210
6211                //initialise subform fields
6212                $scope.ffc.form.data.email = data.Data.email;
6213                $scope.ffc.form.data.firstname = data.Data.name;
6214                $scope.ffc.form.data.lastname = data.Data.surname;
6215                $scope.ffc.form.data.phone = data.Data.phone;
6216                $scope.ffc.form.data.message = null;
6217                $scope.ffc.form.data.prefer = data.Data.preferredContactMethod ? data.Data.preferredContactMethod : 'email',
6218                $scope.ffc.form.data.phonetype = data.Data.phoneType ? data.Data.phoneType : 'home';
6219                $scope.ffc.form.data.additionalcomments = null;
6220                $scope.ffc.form.data.subscribe = false;
6221                $scope.ffc.form.data.captcha = '';
6222                $scope.ffc.form.data.messagedynamic = "";
6223
6224                if (dataUser.suburb && dataUser.postCode && dataUser.state) {
6225                    if (!$scope.ffc.form.data.postcodes) {
6226                        $scope.ffc.form.data.postcodes = [];
6227                    }
6228                    $scope.ffc.form.data.postcodes.push(UserPreferencesService.getAutocompleteTagFromVisitor(dataUser));
6229                }
6230            }, function failure(data) {
6231                $scope.ffc.form.data.email = null;
6232                $scope.ffc.form.data.firstname = null;
6233                $scope.ffc.form.data.lastname = null;
6234                $scope.ffc.form.data.phone = null;
6235                $scope.ffc.form.data.message = null;
6236                $scope.ffc.form.data.prefer = 'email';
6237                $scope.ffc.form.data.phonetype = 'home';
6238                $scope.ffc.form.data.additionalcomments = null;
6239                $scope.ffc.form.data.subscribe = false;
6240                $scope.ffc.form.data.captcha = '';
6241                $scope.ffc.form.data.messagedynamic = "";
6242
6243
6244            }).finally(function () {
6245
6246                if ($scope.ffc.form.data.carmodel) {
6247                    $rootScope.$broadcast('field-car-model-engine-variant-update-without-reset');
6248
6249                    if ($scope.ffc.form.data.engineVariantAndGrade) {
6250                        $rootScope.$broadcast('field-car-model-enhancement-pack-update-selected');
6251                    }
6252
6253                }
6254
6255                //Main Site
6256                if ($scope.ffc.form.data.postcodes && $scope.ffc.form.data.postcodes.length > 0 && !$scope.isDealerSite) {
6257                    $scope.searchNow();
6258                }
6259
6260                //Dealer Site Multi Branch
6261                if ($scope.isDealerSite && $scope.isMultiBranchDealer) {
6262                    $scope.ffc.form.data.dealerId = null;
6263                    $scope.ffc.form.data.dealerName = null;
6264                    $scope.loadBranches($scope.forceDealerId);
6265                }
6266
6267                //Dealer Site NOT Multi Branch
6268                if ($scope.isDealerSite && !$scope.isMultiBranchDealer) {
6269                    $scope.ffc.form.data.dealerId = $scope.dealerCompositeID;
6270                    $scope.ffc.form.data.dealerName = null;
6271                }
6272                });
6273        }
6274
6275        $scope.clearSomeCarData = function () {
6276            $scope.ffc.form.data.engineVariant = undefined;
6277            $scope.ffc.form.data.grade = undefined;
6278            $scope.ffc.form.data.engineVariantAndGrade = undefined;
6279            $scope.ffc.form.data.enhancementPack = undefined;
6280        }
6281
6282        $scope.createGenericMessage = function (vehicleIndex) {
6283
6284            var carModel = $('#carmodel_'+ vehicleIndex + ' option:selected').text(); // should we split by name of full name is ok? carModel.split('-')[0] $scope.ffc.form.data.carmodel is populating id for time being jquery is used to get model value ,once refactored shd be in ng-variable
6285            var bodyContents = "I am interested in finding out more about vehicle finance on the "; // <br>";
6286            bodyContents += carModel + " " + $('#engineVariant_'+vehicleIndex+ ' option:selected').text() + ", ";
6287            bodyContents += ($('#enhancementPack_' + vehicleIndex + ' option:selected').val() == "") ? "" : $('#enhancementPack_' + vehicleIndex + ' option:selected').text() + ", ";
6288            bodyContents += ($scope.ffc.form.results.paint ? $scope.ffc.form.results.paint.uriName : "") + "<br><br>";
6289
6290            bodyContents += "<strong>Usage:</strong> " + $scope.ffc.form.data.loantype.replace(/\w\S*/g, function (txt) { return txt.charAt(0).toUpperCase() + txt.substr(1) }) + "<br>";
6291            bodyContents += "<strong>" + ($scope.ffc.form.data.loantype.toLowerCase() === "personal" ? "Comparison Rate" : "Interest Rate") + ":</strong> " + $scope.ffc.form.data.interestrate + " %" + "<br>";
6292            bodyContents += "<strong>Deposit:</strong> $" + $scope.ffc.form.data.deposit + "<br>";
6293            bodyContents += "<strong>Loan Term:</strong> " + $scope.ffc.form.data.loanterm + " years" + "<br>";
6294            bodyContents += "<strong>Balloon Payment<span class='lx-finance-summary__section-payment-info'>3</span> :</strong> " + ($scope.pricingSuccess ? "$" + $scope.ffc.form.results.finalBalloonPayment : "Price currently not available") + "<br>";
6295            bodyContents += "<strong>Drive Away<span class='lx-finance-summary__section-payment-info'>1</span> :</strong> " + ($scope.pricingSuccess ? "$" + $scope.ffc.form.results.driveAwayPrice : "Price currently not available") + "<br>";
6296
6297            bodyContents += "<strong>Estimated Repayment<span class='lx-finance-summary__section-payment-info'>2</span> :</strong> ";
6298
6299            if ($scope.ffc.form.settings.summary.paymentInterval == 'month') {
6300                bodyContents += ($scope.pricingSuccess) ? "$" + $scope.ffc.form.results.installmentPerMonth + " per month <br>" : "Price currently not available <br>";
6301            } else {
6302                bodyContents += ($scope.pricingSuccess) ? "$" + $scope.ffc.form.results.installmentPerWeek + " per week <br>" : "Price currently not available <br>";
6303            }
6304
6305            $scope.ffc.form.data.messagedynamic = bodyContents;
6306
6307            var t = $sce.trustAsHtml(bodyContents);
6308
6309            return t;
6310        }
6311
6312        $scope.setFocus = function ($event) {
6313            $('html, body').animate({
6314                scrollTop: ($('#postcodes').offset().top - 80)
6315            });
6316            $event.preventDefault();
6317            $("#postcodes :input").focus();
6318        };
6319
6320        $scope.setFocusWithScroll = function ($event) {
6321            $event.preventDefault();
6322            $('html, body').animate({
6323                scrollTop: ($('#postcodes').offset().top - 80)
6324            }, 200);
6325        }
6326
6327        $scope.getMailLink = function (vehicleIndex) {
6328            vehicleIndex = vehicleIndex || 0;
6329
6330            var carModel = $('#carmodel_'+ vehicleIndex + ' option:selected').text(); // should we split by name of full name is ok? carModel.split('-')[0] $scope.ffc.form.data.carmodel is populating id for time being jquery is used to get model value ,once refactored shd be in ng-variable
6331            var engineVariantandGrade = $('#engineVariant_' + vehicleIndex + ' option:selected').text();
6332            var enhancementPack = $('#enhancementPack_' + vehicleIndex + ' option:selected').text();
6333            var lineBreak = "\n"; //"%0D%0A";
6334            var bodyContents =  $scope.ffc.form.emailsettings.email.introText + lineBreak + lineBreak;
6335                bodyContents += "Vehicle Model : " + carModel + lineBreak;
6336                bodyContents += "Variant and Grade : " + engineVariantandGrade + lineBreak;
6337                bodyContents += "Enhancement Pack : " + enhancementPack + lineBreak;
6338                bodyContents += "Disclaimer Paint : " + "The price is based on " + ($scope.ffc.form.results.paint ? $scope.ffc.form.results.paint.uriName.toUpperCase() : "a default") + " exterior and ";
6339                bodyContents += ($scope.ffc.form.results.trim ? $scope.ffc.form.results.trim.uriName.toUpperCase() : "a default") + " trim" + lineBreak;
6340                bodyContents += "Interest Rate : " + $scope.ffc.form.data.interestrate + "%" + lineBreak;
6341                bodyContents += "Deposit : $" + $scope.ffc.form.data.deposit + lineBreak;
6342                bodyContents += "Loan Term : " + $scope.ffc.form.data.loanterm + " years" + lineBreak;
6343                bodyContents += "Usage : " + $scope.ffc.form.data.loantype + lineBreak;
6344                bodyContents += "Drive-away : ";
6345                bodyContents += ($scope.pricingSuccess) ? "$" + $scope.ffc.form.results.driveAwayPrice : "Price currently not available";
6346                bodyContents += lineBreak;
6347
6348            if ($scope.ffc.form.settings.summary.paymentInterval == 'month') {
6349                bodyContents += "Estimated Monthly Repayment^ : ";
6350                bodyContents += ($scope.pricingSuccess) ? "$" + $scope.ffc.form.results.installmentPerMonth : "Price currently not available";
6351            } else {
6352                bodyContents += "Estimated Weekly Repayment^ : ";
6353                bodyContents += ($scope.pricingSuccess) ? "$" + $scope.ffc.form.results.installmentPerWeek : "Price currently not available";
6354            }
6355
6356            bodyContents += lineBreak;
6357
6358            bodyContents += "Final Balloon Payment++ : $" + $scope.ffc.form.results.finalBalloonPayment + lineBreak + lineBreak;
6359            //Final Balloon Payment++ : $4,959.10 or 10% of Drive-away
6360            bodyContents += $scope.ffc.form.emailsettings.email.disclaimer + lineBreak;
6361            $scope.ffc.form.emailsettings.email.fullBodyContents = bodyContents;
6362
6363            var t = "mailto:?subject=" + encodeURIComponent($scope.ffc.form.emailsettings.email.subject) + "&body=" + encodeURIComponent($scope.ffc.form.emailsettings.email.fullBodyContents);
6364            return t;
6365
6366        }
6367
6368        $scope.getBuildPriceLink = function () {
6369
6370            var url = $scope.ffc.form.data.carmodel ? $scope.BuildPriceService.encodeUriFragment($scope.ffc.form.data.carmodel) : "";
6371            url = url + "/";
6372
6373            if ($scope.ffc.form.data.engineVariant) {
6374
6375                url = url + $scope.BuildPriceService.encodeUriFragment($scope.ffc.form.data.engineVariant) + "/";
6376                url = url + $scope.BuildPriceService.encodeUriFragment($scope.ffc.form.data.grade) + "/";
6377
6378                if ($scope.ffc.form.data.enhancementPack) {
6379                    url = url + $scope.BuildPriceService.encodeUriFragment($scope.ffc.form.data.enhancementPack) + "/";
6380                }
6381
6382                if ($scope.ffc.form.results.paint) {
6383                    url = url + $scope.BuildPriceService.encodeUriFragment(
6384                        $scope.ffc.form.results.paint.uriName) + "/";
6385                }
6386
6387                if ($scope.ffc.form.results.trim) {
6388                    url = url + $scope.BuildPriceService.encodeUriFragment($scope.ffc.form.results.trim.uriName);
6389                }
6390
6391                url = url + "/4#lx-bp-nav";
6392            }
6393
6394            var path = $scope.BuildPriceService.encodeUriPath(url);
6395
6396            return path;
6397        }
6398
6399        $scope.updateIntroText = function (introText) {
6400            $scope.ffc.form.emailsettings.email.introText = $sce.trustAsHtml(introText);
6401        }
6402
6403        $scope.updateDisclaimerText = function (disclaimerText) {
6404            $scope.ffc.form.emailsettings.email.disclaimer = $sce.trustAsHtml(disclaimerText);
6405        }
6406
6407        $scope.deselectDealer = function () {
6408            $scope.ffc.form.data.dealerId = $scope.ffc.form.data.dealerName = null;
6409        }
6410
6411        $scope.selectDealer = function (dealerInputId) {
6412            var scrollPos = $(document).scrollTop();
6413            var maxPos = $('#' + dealerInputId).parents('.lx-form__find-dealer-js').offset().top;
6414            if (scrollPos > maxPos) {
6415                TweenLite.to(window, 0.5, { scrollTo: { y: maxPos }, ease: Power2.easeOut });
6416            }
6417        }
6418
6419        $scope.searchNow = function () {
6420            $scope.ffc.form.dealersLoading = true;
6421            $rootScope.$broadcast('visitor-details-change-autocomplete', $scope.ffc.form.data.postcodes[0].data);
6422            DealersService.setParam('postCode', $scope.ffc.form.data.postcodes[0].data.postCode);
6423            DealersService.setParam('suburb', $scope.ffc.form.data.postcodes[0].data.suburb);
6424            DealersService.setParam('type', 'Sales');
6425            $scope.ffc.form.data.postcode = $scope.ffc.form.data.postcodes[0].data.postCode;
6426            DealersService.GetResults();
6427        }
6428
6429        $scope.clearPostcode = function () {
6430            $scope.ffc.form.data.postcodes = null;
6431            $scope.ffc.form.dealerquoteform.Show = false;
6432        }
6433
6434        $scope.updatePostcode = function (tag) {
6435            $scope.ffc.form.tagsLoading = false;
6436            $rootScope.$broadcast('visitor-details-cookie-change', {
6437                postCode: tag.data.postcode,
6438                suburb: tag.data.suburb,
6439                state: tag.data.state,
6440                pricingZone: tag.data.pricingZone
6441            });
6442
6443            $scope.ffc.form.data.postcodes = [];
6444            $scope.ffc.form.data.postcodes[0] = tag;
6445
6446            $scope.dealerSearch(tag);
6447        }
6448
6449        $scope.loadBranches = function (dealerID) {
6450            $scope.ffc.form.dealersLoading = true;
6451            DealersService.setParam('dealerID', dealerID);
6452            DealersService.setParam('type', 'Sales');
6453            DealersService.GetDealerBranches();
6454        }
6455
6456        $scope.dealerSearch = function ($tag) {
6457            $scope.searchDealersNow();
6458        }
6459
6460        $scope.searchDealersNow = function () {
6461            $scope.ffc.form.dealersLoading = true;
6462            $rootScope.$broadcast('visitor-details-change-autocomplete', $scope.ffc.form.data.postcodes[0].data);
6463
6464
6465            //Main Site
6466            if ($scope.ffc.form.data.postcodes && $scope.ffc.form.data.postcodes.length > 0 && !$scope.isDealerSite) {
6467                $scope.searchNow();
6468            }
6469
6470            //Dealer Site Multi Branch
6471            if ($scope.isDealerSite && $scope.isMultiBranchDealer) {
6472                $scope.ffc.form.data.dealerId = null;
6473                $scope.ffc.form.data.dealerName = null;
6474                $scope.loadBranches($scope.forceDealerId);
6475            }
6476
6477            //Dealer Site NOT Multi Branch
6478            if ($scope.isDealerSite && !$scope.isMultiBranchDealer && $scope.dealerState) {
6479                DealersService.setParam('dealerID', $scope.forceDealerId);
6480                DealersService.setParam('dealerState', $scope.dealerState);
6481                DealersService.setParam('type', 'Sales');
6482                DealersService.GetResult();
6483            }
6484        }
6485
6486        $scope.$on('dealer-search-results-received', function (event, data) {
6487            $scope.ffc.form.dealersLoading = false;
6488            $scope.ffc.form.dealers = data.Data;
6489
6490            $scope.checkDealersLoaded();
6491
6492            if ($scope.ffc.form.dealerquoteform.Show) {
6493                $('html, body').animate({
6494                    scrollTop: ($('#scrollFormTop').offset().top)
6495                }, 200);
6496            }
6497        });
6498
6499        //Do a sanity check to see if the bound dealers match the forceDealer if not clear the force dealer
6500        $scope.checkDealersLoaded = function () {
6501            var result = $scope.ffc.form.dealers.find(function (dealer) {
6502                return dealer.dealerCode == $scope.ffc.form.data.dealerId;
6503            });
6504            if (!result) {
6505                $scope.ffc.form.data.dealerId = null;
6506            }
6507
6508            if ($scope.ffc.form.dealers.length == 1)
6509                $scope.ffc.form.data.dealerId = $scope.ffc.form.dealers[0].dealerCode;
6510        }
6511
6512        $scope.$on('dealer-search-result-received', function (event, data) {
6513            $scope.ffc.form.dealersLoading = false;
6514            $scope.ffc.form.dealer = data.Data;
6515
6516            if ($scope.ffc.form.dealerquoteform.Show) {
6517                $('html, body').animate({
6518                    scrollTop: ($('#scrollFormTop').offset().top)
6519                }, 200);
6520            }
6521
6522            $scope.ffc.form.dealerlocations = data.Data.locations;
6523
6524            if ($scope.ffc.form.dealerlocations == null) {
6525                $scope.ffc.form.dealerlocations = [];
6526            }
6527            //Multiple dealers for same dealerId - A bug to be corrected latter -wont require looping in that case on parent
6528            if (data.Data) {
6529                angular.forEach(data.Data, function (val) {
6530                    if (val) {
6531                        angular.forEach(val.locations, function (innerval) {
6532                            this.push(innerval);
6533                        }, $scope.ffc.form.dealerlocations);
6534                    }
6535                });
6536            }
6537
6538            if ($scope.ffc.form.dealerlocations.length == 1)
6539                $scope.ffc.form.data.locationId = $scope.ffc.form.dealerlocations[0].locationId;
6540
6541            //$("#additionalcomments").focus();
6542
6543        });
6544
6545        $scope.getData = function () {
6546            return {
6547                DriveAwayPrice: $scope.ffc.form.results.driveAwayPrice,
6548                Deposit: $scope.ffc.form.data.deposit,
6549                InterestRate: $scope.ffc.form.data.interestrate,
6550                LoanTerm: $scope.ffc.form.data.loanterm,
6551                BalloonPayment: $scope.ffc.form.data.balloonpayment
6552            };
6553        }
6554
6555        $scope.isSectionDisabled = function (sectionName) {
6556            switch (sectionName) { // note: falls through to test multiple cases
6557                case 'summary':
6558                case 'loanDetails':
6559                    if (!($scope.ffc.form.data.carmodel && $scope.ffc.form.data.engineVariantAndGrade)) {
6560                        return true;
6561                    }
6562                case 'vehicleDetails':
6563                    if (!($scope.ffc.form.data.postcodes && $scope.ffc.form.data.postcodes.length > 0)) {
6564                        return true;
6565                    }
6566            }
6567            return false;
6568        }
6569
6570        $scope.updateLoanType = function () {
6571            $scope.bindLoanTermOptions();
6572            $scope.bindBalloonPaymentOptions();
6573            $scope.interestRateCopy();
6574        }
6575
6576        $scope.updateLoanTerm = function () {
6577            $scope.bindBalloonPaymentOptions();
6578        }
6579
6580        $scope.interestRateCopy = function () {
6581            if ($scope.ffc.form.data.loantype.toLowerCase() === 'business') {
6582                $scope.ffc.form.data.interestRateCopy = $scope.ffc.form.data.interestRateBusiness > 0 ? $scope.ffc.form.data.interestRateBusiness :
6583                $scope.ffc.form.data.interestrate;
6584            }
6585
6586            if ($scope.ffc.form.data.loantype.toLowerCase() === 'personal') {
6587                $scope.ffc.form.data.interestRateCopy = $scope.ffc.form.data.interestRatePersonal > 0 ? $scope.ffc.form.data.interestRatePersonal :
6588                    $scope.ffc.form.data.interestrate;
6589            }
6590        }
6591
6592        $scope.bindLoanTermOptions = function () {
6593            switch ($scope.ffc.form.data.loantype) {
6594                case 'personal':
6595                    $scope.ffc.form.settings.loanTerm.max = $scope.ffc.form.settings.loanTerm.personalMax
6596                    break;
6597                case 'business':
6598                    $scope.ffc.form.settings.loanTerm.max = $scope.ffc.form.settings.loanTerm.businessMax
6599                    break;
6600            }
6601            $scope.ffc.form.data.loanterm = Math.min($scope.ffc.form.settings.loanTerm.max, Math.max($scope.ffc.form.settings.loanTerm.min, $scope.ffc.form.data.loanterm));
6602        }
6603
6604        $scope.bindBalloonPaymentOptions = function () {
6605            if ($scope.ffc.form.settings.balloonPayment.fixedValue) {
6606                return;
6607            }
6608            if ($scope.ffc.form.data.loantype && $scope.ffc.form.data.loanterm) {
6609                $http({ method: 'GET', url: $filter('formatArgs')(Lexus.API.Lookup.FinanceBalloonPaymentOptions, [Lexus.ID.Site, $scope.ffc.form.data.loantype, $scope.ffc.form.data.loanterm, $scope.ffc.form.data.loantype]) })
6610                .then(function success(response) {
6611                    if (response.data.Data) {
6612                        $scope.ffc.form.settings.balloonPayment.min = response.data.Data.Min;
6613                        $scope.ffc.form.settings.balloonPayment.step = response.data.Data.Step;
6614                        $scope.ffc.form.data.balloonPayment = Math.min(response.data.Data.Max, Math.max(response.data.Data.Min, $scope.ffc.form.data.balloonPayment));
6615                        $scope.ffc.form.settings.balloonPayment.max = response.data.Data.Max;
6616                    }
6617                    $scope.ffc.form.settings.balloonPayment.disabled = response.data.Data.DisableBalloonPayments;
6618                    $rootScope.$broadcast('balloon-payment-options-received');
6619                }, function error(response) {
6620                    $rootScope.$broadcast('open-modal-window', {
6621                        content: $scope.ffc.form.settings.messages.apiError,
6622                        variant: Lexus.ModalVariant.Content,
6623                        closeOnEvent: 'close-modal-window',
6624                        wrapper: ["<p style='padding: 20px 65px 0px 20px; text-align: left'>", "</p>"]
6625                    });
6626                })
6627                .finally(function () {
6628
6629                });
6630            }
6631        }
6632
6633        //Watches: carmodel, enhancementPack, postcode, grade
6634        //Modifies: driveAwayPrice
6635        $scope.$watchGroup(['ffc.form.data.carmodel', 'ffc.form.data.enhancementPack', 'ffc.form.data.postcodes', 'ffc.form.data.engineVariantAndGrade'], function (newDataArray, oldDataArray, $scope) {
6636            var newData = {};
6637            newData.carmodel = newDataArray[0];
6638            newData.enhancementPack = newDataArray[1];
6639            newData.postcodes = newDataArray[2];
6640            newData.engineVariantAndGrade = newDataArray[3];
6641
6642            //Something has changed since entering with a B&P Deeplink
6643            if ($scope.ffc.form.data.engineVariantAndGrade != newData.engineVariantAndGrade || $scope.ffc.form.data.enhancementPack != newData.enhancementPack) {
6644                $scope.ffc.form.data.trim = $scope.ffc.form.results.trim = null;
6645                $scope.ffc.form.data.colour = $scope.ffc.form.results.paint = null;
6646            }
6647
6648            $log.info("carmodel: " + newData.carmodel);
6649            $log.info("enhancementPack: " + newData.enhancementPack);
6650            $log.info("engineVariantAndGrade: " + newData.engineVariantAndGrade);
6651            var pricingZone = "";
6652            if (newData.postcodes && newData.postcodes.length > 0) {
6653                var visitor = UserPreferencesService.splitAutoCompleteResult(newData.postcodes[0].text);
6654                pricingZone = newData.postcodes[0].data.pricingZone;
6655                if (!visitor.postCode) {
6656                    // $scope.ffc.form.results.driveAwayPrice = 0; ?
6657                    return;
6658                }
6659            } else {
6660                $scope.ffc.form.results.driveAwayPrice = 0;
6661                return;
6662            }
6663
6664            BuildPriceService.waitFor.call($scope, 'ffc.formfields.data.enhancementPacks', BuildPriceService.waitCondition.NOTEMPTY)
6665            .then(function (enhancementPacks) {
6666
6667                var selectedEnhancementPack = $scope.ffc.formfields.data.enhancementPacks.find(function (item) {
6668                    return $scope.ffc.form.data.enhancementPack == item.uriName;
6669                });
6670
6671                var selectedGrade = $scope.ffc.formfields.data.enginevariantsandgrades.find(function (item) {
6672                    return $scope.ffc.form.data.engineVariantAndGrade == item.key;
6673                });
6674
6675                if (selectedEnhancementPack && selectedEnhancementPack.mdmid) {
6676
6677                    var selectedEnhancementPackMdmId = selectedEnhancementPack.mdmid;
6678
6679                    if (!selectedGrade) {
6680                        $scope.ffc.form.data.engineVariantAndGrade = undefined;
6681                        return;
6682                    }
6683
6684                    var selectedGradeId = selectedGrade.gradeId;
6685                    var selectedEngineVariant = $scope.ffc.form.data.engineVariant;
6686                    PricingService.setParam("enablePricingByZone", $scope.ffc.form.settings.enablePricingByZone);
6687                    if ($scope.ffc.form.data.trim && $scope.ffc.form.data.colour) //Deep Linked from B&P with a trim and paint
6688                    {
6689                        BuildPriceAPIService.getModels(function () {
6690                            BuildPriceAPIService.getVariants(selectedEngineVariant, selectedGradeId, function () {
6691                                var selectedVariant = BuildPriceAPIService.getVariantByName(selectedEngineVariant, selectedGradeId, $scope.ffc.form.data.enhancementPack);
6692                                BuildPriceAPIService.getTrims(selectedEngineVariant, selectedGradeId, selectedVariant.mdmid, function () {
6693                                    var selectedTrim = BuildPriceAPIService.getTrimByName(selectedEngineVariant, selectedGradeId, selectedVariant.mdmid, $scope.ffc.form.data.trim);
6694                                    if (!selectedTrim) {
6695                                        selectedTrim = selectedVariant.trims.availableTrims[0];
6696                                        $scope.ffc.form.data.trim = selectedTrim.uriName;
6697                                    }
6698                                    BuildPriceAPIService.getPaints(selectedEngineVariant, selectedGradeId, selectedVariant.mdmid, selectedTrim.id, function (availableColours, unavailableColours) {
6699                                        var selectedPaint = BuildPriceAPIService.getPaintByName(selectedEngineVariant, selectedGradeId, selectedVariant.mdmid, selectedTrim.id, $scope.ffc.form.data.colour);
6700                                        if (!selectedPaint) {
6701                                            selectedPaint = selectedTrim.paints.availableColours[0];
6702                                            $scope.ffc.form.data.colour = selectedPaint.uriName;
6703                                        }
6704                                        PricingService.detailPriceByPaint(
6705                                            selectedGradeId,
6706                                            selectedEnhancementPackMdmId,
6707                                            selectedTrim.id,
6708                                            selectedPaint.materialCode,
6709                                            visitor.postCode,
6710                                            pricingZone
6711                                        ).then(function success(data) {
6712                                            if (data.status === "Success") {
6713                                                $scope.pricingResult(data.data.driveAway);
6714                                                $scope.ffc.form.results.driveAwayPrice = data.data.driveAway;
6715                                                $scope.ffc.form.results.paint = selectedPaint;
6716                                                $scope.ffc.form.results.trim = selectedTrim;
6717                                            }
6718                                        }, function failure(data) {
6719                                            $scope.pricingResult(false);
6720                                            $rootScope.$broadcast('open-modal-window', {
6721                                                content: $scope.ffc.form.settings.messages.apiError,
6722                                                variant: Lexus.ModalVariant.Content,
6723                                                closeOnEvent: 'close-modal-window',
6724                                                wrapper: ["<p style='padding: 20px 65px 0px 20px; text-align: left'>", "</p>"]
6725                                            });
6726                                        });
6727
6728                                    });
6729                                });
6730                            });
6731                        });
6732
6733                    }
6734                    else {
6735                        PricingService.driveAwayByEnhancementPack(selectedGradeId, selectedEnhancementPackMdmId, visitor.postCode, pricingZone).then(function success(data) {
6736                            if (data.status === "Success") {
6737                                if (data.data && data.data.driveAway) {
6738                                    $scope.pricingResult(data.data.driveAway);
6739
6740                                    $scope.ffc.form.results.driveAwayPrice = data.data.driveAway;
6741                                    $scope.ffc.form.results.paint = data.data.paint;
6742                                    $scope.ffc.form.results.trim = data.data.trim;
6743                                } else {
6744                                    $scope.pricingResult(0);
6745                                }
6746                            }
6747                        }, function failure(data) {
6748                            $scope.pricingResult(false);
6749                            $rootScope.$broadcast('open-modal-window', {
6750                                content: $scope.ffc.form.settings.messages.apiError,
6751                                variant: Lexus.ModalVariant.Content,
6752                                closeOnEvent: 'close-modal-window',
6753                                wrapper: ["<p style='padding: 20px 65px 0px 20px; text-align: left'>", "</p>"]
6754                            });
6755                        });
6756                    }
6757                }
6758                else if (newData.engineVariantAndGrade && $scope.ffc.formfields.data.enginevariantsandgrades) {
6759
6760
6761                    if (selectedGrade) {
6762                        var selectedGradeId = selectedGrade.gradeId;
6763
6764                        PricingService.driveAwayByGrade(selectedGradeId, visitor.postCode, pricingZone).then(function success(data) {
6765                            if (data.status === "Success") {
6766                                if(data.data && data.data.driveAway){
6767                                    $scope.pricingResult(data.data.driveAway);
6768
6769                                    $scope.ffc.form.results.driveAwayPrice = data.data.driveAway;
6770                                    $scope.ffc.form.results.paint = data.data.paint;
6771                                    $scope.ffc.form.results.trim = data.data.trim;
6772                                } else {
6773                                    $scope.pricingResult(0);
6774                                }
6775                            }
6776                        }, function failure(data) {
6777
6778                            $scope.pricingResult(false);
6779                            $rootScope.$broadcast('open-modal-window', {
6780                                content: $scope.ffc.form.settings.messages.apiError,
6781                                variant: Lexus.ModalVariant.Content,
6782                                closeOnEvent: 'close-modal-window',
6783                                wrapper: ["<p style='padding: 20px 65px 0px 20px; text-align: left'>", "</p>"]
6784                            });
6785                        });
6786                    }
6787                } else {
6788                    $scope.ffc.form.results.driveAwayPrice = 0;
6789                }
6790
6791            });
6792
6793        });
6794
6795        //Watches: driveAwayPrice, deposit, interestrate, loanterm, balloonpayment, loantype
6796        //Modifies: installmentPerWeek/PerMonth, finalBalloonPayment
6797        $scope.$watchGroup(['ffc.form.results.driveAwayPrice', 'ffc.form.data.deposit', 'ffc.form.data.interestrate', 'ffc.form.data.loanterm', 'ffc.form.data.balloonPayment', 'ffc.form.data.loantype'], function (newDataArray, oldDataArray, $scope) {
6798            var newData = {};
6799            newData.driveAwayPrice = newDataArray[0];
6800            newData.deposit = newDataArray[1];
6801            newData.interestrate = newDataArray[2];
6802            newData.loanterm = newDataArray[3];
6803            newData.balloonPayment = newDataArray[4];
6804            newData.loantype = newDataArray[5];
6805
6806            if ($scope.delayedRecalc) {
6807                $timeout.cancel($scope.delayedRecalc);
6808            }
6809            if (newData.driveAwayPrice === 0) {
6810                $scope.ffc.form.results.installmentPerMonth = 0;
6811                $scope.ffc.form.results.installmentPerWeek = 0;
6812                $scope.ffc.form.results.finalBalloonPayment = 0;
6813                return;
6814            }
6815            if (newData.driveAwayPrice > 0 && newData.deposit && newData.interestrate && newData.loanterm && (newData.balloonPayment != null) && newData.loantype) {
6816                $scope.delayedRecalc = $timeout($scope.recalc, 500, true, newD
6816ata);
6817            }
6818        });
6819
6820        $scope.pricingResult = function (value) {
6821            var status = (typeof value == "number") ? (value != 0) : (typeof value == "boolean") ? value : false;
6822            if (status) {
6823                angular.element('body').removeClass('pricing-error');
6824            } else {
6825                angular.element('body').addClass('pricing-error');
6826            }
6827
6828            $scope.pricingSuccess = status;
6829
6830        }
6831
6832        $scope.recalc = function (newData) {
6833            var interrestRate = $scope.ffc.form.data.interestrate;
6834            /// This is being added so we can Hack around the bad finance service.
6835            /// Personal loans for the L'Exhibition Campaign should show 1% but pass 0.00000000001 only for Personal
6836            /// Go check this ticket LDM-8
6837            if ($scope.ffc.form.data.interestRateOverride > 0 && $scope.ffc.form.data.loantype.toLowerCase() === 'personal')
6838            {
6839                interrestRate = $scope.ffc.form.data.interestRateOverride;
6840            }
6841            if ($scope.ffc.form.data.interestRateOverrideBusiness > 0 && $scope.ffc.form.data.loantype.toLowerCase() === 'business') {
6842                interrestRate = $scope.ffc.form.data.interestRateOverrideBusiness;
6843            }
6844
6845
6846            //temporary toString().replace() until deposit is fixed to always be an int
6847            $http({ method: 'GET', url: $filter('formatArgs')(Lexus.API.Lookup.FinanceCalculator, [Lexus.ID.Site, $scope.ffc.form.results.driveAwayPrice, $scope.ffc.form.data.deposit.toString().replace(/,/g, ''), interrestRate, $scope.ffc.form.data.loanterm, $scope.ffc.form.data.balloonPayment, $scope.ffc.form.data.loantype]) })
6848            .then(function successCallback(response) {
6849
6850                if (response.data.Status == "Success") {
6851                    $scope.ffc.form.results.installmentPerMonth = response.data.Data.InstallmentPerMonth;
6852                    $scope.ffc.form.results.installmentPerWeek = response.data.Data.InstallmentPerWeek;
6853                    $scope.ffc.form.results.finalBalloonPayment = response.data.Data.FinalBalloonPayment;
6854                    $scope.ffc.form.failed = false;
6855                }
6856                else {
6857                    $rootScope.$broadcast('open-modal-window', {
6858                        content: response.data.ErrorMessage,
6859                        variant: Lexus.ModalVariant.Content,
6860                        closeOnEvent: 'close-modal-window',
6861                        wrapper: ["<p style='padding: 20px 65px 0px 20px; text-align: left'>", "</p>"]
6862                    });
6863                }
6864
6865            }, function errorCallback(error) {
6866                $scope.ffc.form.failed = true;
6867
6868                $rootScope.$broadcast('open-modal-window', {
6869                    content: $scope.ffc.form.settings.messages.apiError,
6870                    variant: Lexus.ModalVariant.Content,
6871                    closeOnEvent: 'close-modal-window',
6872                    wrapper: ["<p style='padding: 20px 65px 0px 20px; text-align: left'>", "</p>"]
6873                });
6874
6875            });
6876
6877        }
6878
6879        $scope.openDealerQuoteForm = function (vehicleIndex) {
6880            $scope.ffc.form.completed = false;
6881            $scope.ffc.form.dealerquoteform.Show = true;
6882
6883            $scope.createGenericMessage(vehicleIndex);
6884            $scope.searchDealersNow();
6885
6886            //$("#additionalcomments").focus();
6887
6888        }
6889
6890        $scope.getDataRequestQuoteData = function () {
6891
6892            var deposit = $scope.ffc.form.data.deposit.toString().replace(",", "");
6893
6894            return {
6895                AdditionalComments: $scope.ffc.form.data.additionalcomments,
6896                Source: $scope.ffc.form.data.source,
6897                DealerId: $scope.ffc.form.data.dealerId,
6898                ContextDealerSiteId: $scope.forceDealerId,
6899                Message: $scope.ffc.form.data.messagedynamic,
6900                PostCode: $scope.ffc.form.data.postcode,
6901                Phone: $scope.ffc.form.data.phone,
6902                FirstName: $scope.ffc.form.data.firstname,
6903                LastName: $scope.ffc.form.data.lastname,
6904                Email: $scope.ffc.form.data.email,
6905                PreferredContactMethod: $scope.ffc.form.data.prefer,
6906                Subscribe: $scope.ffc.form.data.subscribe,
6907                PhoneType: $scope.ffc.form.data.phonetype,
6908                Captcha: $scope.ffc.form.data.captcha,
6909                Model: $scope.ffc.form.data.carmodel,
6910                EngineVariant: $scope.ffc.form.data.engineVariant,
6911                Grade: $scope.ffc.form.data.grade,
6912                EnhancementPack: $scope.ffc.form.data.enhancementPack,
6913                Paint: $scope.ffc.form.results ? $scope.ffc.form.results.paint.name : null,
6914                Trim: $scope.ffc.form.results ? $scope.ffc.form.results.trim.name : null,
6915                LoanType: $scope.ffc.form.data.loantype,
6916                InterestRate: $scope.ffc.form.data.interestrate,
6917                Deposit: deposit,
6918                LoanTerm: $scope.ffc.form.data.loanterm,
6919                BalloonPayment: $scope.ffc.form.data.balloonPayment,
6920                DriveAwayPrice: $scope.ffc.form.results.driveAwayPrice,
6921                MonthlyPayments: $scope.ffc.form.results.installmentPerMonth,
6922                FormUrl: ($scope.ffc.form.settings.disclaimerPage && $scope.ffc.form.settings.disclaimerPage != "" ? $scope.ffc.form.settings.disclaimerPage : $location.absUrl())
6923            };
6924        }
6925
6926        $rootScope.$on('submit-finance-calculator-form', function () {
6927            $scope.ffc.form.submitting = true;
6928
6929            var formData = $scope.getDataRequestQuoteData();
6930
6931            eloquaService.attachEloquaFields(formData);
6932
6933            //Code to be changed once actual client side validation is in place on finance form to refer to actual api
6934            $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.RequestDealerQuoteUrl, [Lexus.ID.Site]), data: formData }).
6935                then(function success(response) {
6936
6937                    if (response.data.Status == "Success") {
6938                        $scope.submissionPass(response.data);
6939                    }
6940                    else {
6941                        $scope.submissionFail(response.data);
6942                    }
6943                }, function error(response) {
6944                    $scope.submissionFail(response.data);
6945                })
6946                .finally(function () {
6947                    $rootScope.$broadcast('form-result');
6948                });
6949        });
6950
6951        $scope.submissionPass = function (data) {
6952            // this callback will be called asynchronously
6953            // when the response is available
6954            $scope.ffc.form.completed = true;
6955            $scope.ffc.form.failed = false;
6956            $scope.ffc.form.submitting = false;
6957            var historyState = "RequestDealerQuoteForm=Pass";
6958            $location.state(historyState);
6959            FormsService.ShowResult($scope.successContent[0]);
6960            $rootScope.$broadcast('gtm-form-success', 'Finance Calculator');
6961            $rootScope.$broadcast('visitor-details-session-change', $scope.getDataRequestQuoteData());
6962        };
6963
6964        $scope.submissionFail = function (data) {
6965            // called asynchronously if an error occurs
6966            // or server returns response with an error status.
6967            $scope.ffc.form.failed = true;
6968            $scope.ffc.form.errors = data.Data;
6969            $scope.ffc.form.submitting = false;
6970            $scope.ffc.form.completed = true;
6971            var historyState = "RequestDealerQuoteForm=Fail";
6972            $location.state(historyState);
6973            FormsService.ShowResult($scope.failureContent[0]);
6974            console.error(data.ErrorMessage);
6975        };
6976
6977
6978        $scope.email = function () {
6979
6980        }
6981
6982        $scope.calculateNext = function (interestrate, interestrateInc, highestinterestrate, lowestinterestrate, type, obj) {
6983            var currentRate = Number($scope.ffc.form.data.interestrate);
6984            var maxRate = Number(highestinterestrate);
6985            var minRate = Number(lowestinterestrate);
6986            var increment = Number(interestrateInc);
6987            var adjustedRate;
6988            if (type == 'Increase') {
6989                adjustedRate = $scope.roundToIncrement(currentRate + increment, increment);
6990                if (adjustedRate > maxRate) {
6991                    $scope.showInterestrateModal(lowestinterestrate, highestinterestrate)
6992                }
6993                else {
6994                    $scope.ffc.form.data.interestrate = adjustedRate.toFixed(2);
6995                }
6996            }
6997            else if (type == 'Decrease') {
6998                adjustedRate = $scope.roundToIncrement(currentRate - increment, increment);
6999                if (adjustedRate < minRate || adjustedRate <= 0) {
7000                    $scope.showInterestrateModal(lowestinterestrate, highestinterestrate)
7001                }
7002                else {
7003                    $scope.ffc.form.data.interestrate = adjustedRate.toFixed(2);
7004                }
7005            }
7006
7007            $scope.formatNumberToDecimal($scope.ffc.form.data.interestrate, obj);
7008
7009        }
7010
7011        $scope.showInterestrateModal = function (lowestinterestrate, highestinterestrate) {
7012            $scope.$emit('open-modal-window', {
7013                content: 'Interest Rate should be between ' + lowestinterestrate + ' and ' + highestinterestrate + '',
7014                wrapper: ['<p class="lx-modal__paragraph">', '</p>']
7015            });
7016        }
7017
7018        $scope.filterValue = function ($event, val, type) { //Allow comma's ,dot and backspace
7019            var asciiVal = $event.keyCode || $event.charCode;
7020            if (isNaN(String.fromCharCode(asciiVal))) {
7021                if (asciiVal != 44 && asciiVal != 46 && asciiVal != 8) {
7022                    $event.preventDefault();
7023                }
7024
7025                if (type == 'interestrate') {
7026                    if (asciiVal == 46) {
7027                        if ($("#interestRate").val().indexOf(".") != -1) {
7028                            $event.preventDefault();
7029                        }
7030                    }
7031
7032                    if (asciiVal == 44) {
7033                        $event.preventDefault();
7034                    }
7035                }
7036            }
7037
7038        }
7039
7040        $scope.initDeposit = function (val, obj) {
7041
7042            var formattedNum = Math.floor(Number(val.toString().replace(',', ''))).toLocaleStr
7042ing('en', { useGrouping: true });
7043
7044            $scope.ffc.form.data.deposit = formattedNum;
7045        }
7046
7047        $scope.validate = function (interestrate, interestrateInc, highestinterestrate, lowestinterestrate, obj) {
7048            var increment = Number(interestrateInc);
7049            var modifiedRate = $scope.roundToIncrement(Number($scope.ffc.form.data.interestrate), increment);
7050            var maxRate = Number(highestinterestrate);
7051            var minRate = Number(lowestinterestrate);
7052
7053
7054            if (modifiedRate > maxRate || modifiedRate < minRate || modifiedRate <= 0) {
7055                $scope.showInterestrateModal(lowestinterestrate, highestinterestrate)
7056            }
7057
7058            if (modifiedRate > maxRate) {
7059                $scope.ffc.form.data.interestrate = maxRate.toFixed(2);
7060            } else if (modifiedRate < minRate || modifiedRate <= 0 + increment) {
7061                $scope.ffc.form.data.interestrate = Math.max(minRate, 0 + increment).toFixed(2);
7062            } else {
7063                $scope.ffc.form.data.interestrate = modifiedRate.toFixed(2);
7064            }
7065            $scope.formatNumberToDecimal($scope.ffc.form.data.interestrate, obj);
7066        }
7067
7068        $scope.roundToIncrement = function (value, increment) {
7069
7070            if (typeof (value) != "number" || typeof (increment) != "number") {
7071                throw new Error("Value is not of type \"number\"");
7072            }
7073
7074            if (increment != 0) {
7075                var incRoundingFactor = 1 / increment;
7076                return (Math.round(value * incRoundingFactor) / incRoundingFactor);
7077            } else {
7078                return value;
7079            }
7080        }
7081
7082        $scope.formatNumber = function (val, obj) {
7083            if (obj != null) {
7084                var value;
7085                if (obj[formName] != null && !isNaN(value = Number(val.toString().replace(/,/g, '')))) {
7086                    $scope.ffc.form.data.deposit = Math.floor(value).toLocaleString('en', { useGrouping: true });
7087                }
7088                if (obj[formName] != null)
7089                    $scope.ffc.form.data.deposit = $scope.ffc.form.data.deposit.replace('-', '');
7090            }
7091        }
7092
7093        $scope.formatNumberToDecimal = function (val, obj) {
7094            if (obj != null) {
7095                if (obj[formName] != null && !isNaN(Number(val.replace(',', '')))) {
7096                    if (val.replace(',', '').indexOf('.') == -1)
7097                        $scope.ffc.form.data.interestrate = Number(val).toFixed(2);
7098                }
7099                if (obj[formName] != null)
7100                    $scope.ffc.form.data.interestrate = $scope.ffc.form.data.interestrate.replace('-', '');
7101            }
7102
7103        }
7104
7105        $scope.byAvailableGrades = function (engineVariantGrade) {
7106            return !$scope.ffc.form.settings.availableGrades || $scope.ffc.form.settings.availableGrades.includes(engineVariantGrade.mdmGradeId);
7107        };
7108    }
7109}
7110
7111;
7112"use strict";
7113/* Directives */
7114angular.module('Lexus.Directives')
7115.directive('lxFormsFinanceContact', ['$window', '$timeout', '$filter', '$http', '$location', function ($window, $timeout, $filter, $http, $location) {
7116    var typeofForm = '';
7117    formsFinanceContactController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location', '$rootScope', 'AddressSuggestService', 'UserPreferencesService', 'vcRecaptchaService', 'FormsService', 'API'];
7118    return {
7119        scope: true,
7120        restrict: 'A',
7121        controller: formsFinanceContactController,
7122        controllerAs: 'ffc',
7123        bindToController: true,
7124        link: function (scope, element, attrs, filter, http) {
7125            scope.successContent = element.find('.lx-form-success');
7126            scope.failureContent = element.find('.lx-form-failure');
7127            typeofForm = attrs.lxFormsType;
7128        }
7129    };
7130    
7131    function formsFinanceContactController($scope, $http, $filter, $timeout, $window, $interval, $location, $rootScope, AddressSuggestService, UserPreferencesService, vcRecaptchaService, FormsService, API) {
7132        var fcu = this;
7133
7134        $scope.init = function () {
7135            UserPreferencesService.getLexusVisitorSession().then(function (data) {
7136
7137                var visitorPreference = UserPreferencesService.getLexusVisitor();
7138                $scope.ffc.form = {
7139                    submitting: false,
7140                    completed: false,
7141                    errors: false,
7142                    data: {
7143                        subscribe: false,
7144                        postcodes: [],
7145                        postcode: "",
7146                        email: data.Data.email,
7147                        phonetype: data.Data.phoneType && data.Data.phoneType != '' ? data.Data.phoneType : 'home',
7148                        firstname: data.Data.name,
7149                        lastname: data.Data.surname,
7150                        phone: data.Data.phone,
7151                        prefer: data.Data.preferredContactMethod ? data.Data.preferredContactMethod : 'email',
7152                        captcha: '',
7153                        requesttype: null
7154                    }
7155                };
7156
7157                var visitorPreference = UserPreferencesService.getLexusVisitor();
7158                if (visitorPreference && visitorPreference.postCode && visitorPreference.suburb && visitorPreference.state) {
7159                    $scope.ffc.form.data.postcodes.push(UserPreferencesService.getAutocompleteTagFromVisitor(visitorPreference));
7160                }
7161
7162            }, function (reason) {
7163            });
7164        }
7165       
7166        $scope.getData = function () {
7167            var visitor = UserPreferencesService.splitAutoCompleteResult($scope.ffc.form.data.postcodes[0].text);
7168            return {
7169                Message: $scope.ffc.form.data.message,
7170                FirstName: $scope.ffc.form.data.firstname,
7171                LastName: $scope.ffc.form.data.lastname,
7172                Email: $scope.ffc.form.data.email,
7173                PostCode: visitor.postCode,
7174                Phone: $scope.ffc.form.data.phone,
7175                PhoneType: $scope.ffc.form.data.phonetype,
7176                PreferredContactMethod: $scope.ffc.form.data.prefer,
7177                Subscribe: $scope.ffc.form.data.subscribe.toString(),
7178                Captcha: $scope.ffc.form.data.captcha,
7179                RequestType: $scope.ffc.form.data.requesttype,
7180            };
7181        }
7182
7183
7184
7185        $rootScope.$on('submit-financial-form', function () {
7186
7187            $rootScope.$broadcast('visitor-details-cookie-change-autocomplete', $scope.ffc.form.data.postcodes[0].text);
7188            
7189            $scope.ffc.form.submitting = true;
7190            
7191
7192            //Code to be changed once actual client side validation is in place on finance form to refer to actual api
7193            $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.FinanceContact, [Lexus.ID.Site]), data: $scope.getData() }).
7194                then(function success(response) {
7195                    if (response.data.Status == "Success") {
7196                        $scope.submissionPass(response.data);
7197                    }
7198                    else {
7199                        $scope.submissionFail(response.data);
7200                    }
7201                }, function error(response) {
7202                    $scope.submissionFail(response.data);
7203                    
7204                })
7205                .finally(function () {
7206                    $rootScope.$broadcast('form-result');
7207                });
7208        });
7209
7210        $scope.submissionPass = function (data) {
7211            // this callback will be called asynchronously
7212            // when the response is available
7213            $scope.ffc.form.completed = true;
7214            $scope.ffc.form.failed = false;
7215            $scope.ffc.form.submitting = false;
7216            var historyState = "FinanceForm=Pass";
7217            $location.state(historyState);
7218            FormsService.ShowResult($scope.successContent[0]);
7219            $rootScope.$broadcast('gtm-form-success', 'Finance Contact');
7220            $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
7221        }
7222
7223        $scope.submissionFail = function (data) {
7224            // called asynchronously if an error occurs
7225            // or server returns response with an error status.
7226            $scope.ffc.form.failed = true;
7227            $scope.ffc.form.errors = data.Data;
7228            $scope.ffc.form.submitting = false;
7229            $scope.ffc.form.completed = true;
7230            var historyState = "FinanceForm=Fail";
7231            $location.state(historyState);
7232            FormsService.ShowResult($scope.failureContent[0]);
7233            console.error(data.ErrorMessage);
7234        }
7235
7236        //$scope.loadTags = function (query) {
7237        //    AddressSuggestService.setParam('term', query);
7238        //    return AddressSuggestService.GetResults();
7239        //};
7240
7241        $scope.updatePostcode = function ($tag) {
7242            $scope.ffc.form.data.postcodes = [];
7243            $scope.ffc.form.data.postcodes[0] = $tag;
7244        };
7245
7246    }
7247}]);
7248;
7249"use strict";
7250/* Directives */
7251angular.module('Lexus.Directives')
7252.directive('lxFormsInsuranceContact', ['$window', '$timeout', '$filter', '$http', '$location', function ($window, $timeout, $filter, $http, $location) {
7253    var typeofForm = '';
7254    formsInsuranceContactController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location', '$rootScope', 'AddressSuggestService', 'UserPreferencesService', 'vcRecaptchaService', 'FormsService', 'API'];
7255    return {
7256        scope: true,
7257        restrict: 'A',
7258        controller: formsInsuranceContactController,
7259        controllerAs: 'ffc',
7260        bindToController: true,
7261        link: function (scope, element, attrs, filter, http) {
7262            scope.successContent = element.find('.lx-form-success');
7263            scope.failureContent = element.find('.lx-form-failure');
7264            typeofForm = attrs.lxFormsType;
7265        }
7266    };
7267    
7268    function formsInsuranceContactController($scope, $http, $filter, $timeout, $window, $interval, $location, $rootScope, AddressSuggestService, UserPreferencesService, vcRecaptchaService, FormsService, API) {
7269        var fcu = this;
7270
7271        $scope.init = function () {
7272            UserPreferencesService.getLexusVisitorSession().then(function (data) {
7273
7274                var visitorPreference = UserPreferencesService.getLexusVisitor();
7275                $scope.ffc.form = {
7276                    submitting: false,
7277                    completed: false,
7278                    errors: false,
7279                    data: {
7280                        subscribe: false,
7281                        postcodes: [],
7282                        postcode: "",
7283                        email: data.Data.email,
7284                        phonetype: data.Data.phoneType && data.Data.phoneType != '' ? data.Data.phoneType : 'home',
7285                        firstname: data.Data.name,
7286                        lastname: data.Data.surname,
7287                        phone: data.Data.phone,
7288                        prefer: data.Data.preferredContactMethod ? data.Data.preferredContactMethod : 'email',
7289                        captcha: '',
7290                        requesttype: null
7291                    }
7292                };
7293
7294
7295
7296                var visitorPreference = UserPreferencesService.getLexusVisitor();
7297                if (visitorPreference && visitorPreference.postCode && visitorPreference.suburb) {
7298                    $scope.ffc.form.data.postcodes.push({ text: visitorPreference.suburb + " " + visitorPreference.postCode });
7299                }
7300
7301            }, function (reason) {
7302            });
7303        }
7304       
7305        $scope.getData = function () {
7306            var visitor = UserPreferencesService.splitAutoCompleteResult($scope.ffc.form.data.postcodes[0].text);
7307            return {
7308                Message: $scope.ffc.form.data.message,
7309                FirstName: $scope.ffc.form.data.firstname,
7310                LastName: $scope.ffc.form.data.lastname,
7311                Email: $scope.ffc.form.data.email,
7312                PostCode: visitor.postCode,
7313                Phone: $scope.ffc.form.data.phone,
7314                PhoneType: $scope.ffc.form.data.phonetype,
7315                PreferredContactMethod: $scope.ffc.form.data.prefer,
7316                Subscribe: $scope.ffc.form.data.subscribe.toString(),
7317                Captcha: $scope.ffc.form.data.captcha,
7318                RequestType: $scope.ffc.form.data.requesttype,
7319            };
7320        }
7321
7322
7323
7324        $rootScope.$on('submit-insurance-form', function () {
7325
7326            $rootScope.$broadcast('visitor-details-cookie-change-autocomplete', $scope.ffc.form.data.postcodes[0].text);
7327            
7328            $scope.ffc.form.submitting = true;
7329            
7330
7331            //Code to be changed once actual client side validation is in place on finance form to refer to actual api
7332            $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.InsuranceContact, [Lexus.ID.Site]), data: $scope.getData() }).
7333                then(function success(response) {
7334                    if (response.data.Status == "Success") {
7335                        $scope.submissionPass(response.data);
7336                    }
7337                    else {
7338                        $scope.submissionFail(response.data);
7339                    }
7340                }, function error(response) {
7341                    $scope.submissionFail(response.data);
7342                })
7343                .finally(function () {
7344                    $rootScope.$broadcast('form-result');
7345                });
7346        });
7347
7348        $scope.submissionPass = function (data) {
7349            // this callback will be called asynchronously
7350            // when the response is available
7351            $scope.ffc.form.completed = true;
7352            $scope.ffc.form.failed = false;
7353            $scope.ffc.form.submitting = false;
7354            var historyState = "InsuranceForm=Pass";
7355            $location.state(historyState);
7356            FormsService.ShowResult($scope.successContent[0]);
7357            $rootScope.$broadcast('gtm-form-success', 'Insurance Contact');
7358            $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
7359        };
7360        $scope.submissionFail = function (data) {
7361            // called asynchronously if an error occurs
7362            // or server returns response with an error status.
7363            $scope.ffc.form.failed = true;
7364            $scope.ffc.form.errors = data.Data;
7365            $scope.ffc.form.submitting = false;
7366            $scope.ffc.form.completed = true;
7367            var historyState = "InsuranceForm=Fail";
7368            $location.state(historyState);
7369            FormsService.ShowResult($scope.failureContent[0]);
7370            console.error(data.ErrorMessage);
7371        };
7372
7373        $scope.updatePostcode = function ($tag) {
7374            $scope.ffc.form.data.postcodes = [];
7375            $scope.ffc.form.data.postcodes[0] = $tag;
7376        };
7377
7378
7379
7380    }
7381}]);
7382;
7383"use strict";
7384/* Directives */
7385angular.module('Lexus.Directives')
7386.directive('lxFormsRegisterInterest', ['$window', '$timeout', '$filter', '$http', '$location', function ($window, $timeout, $filter, $http, $location) {
7387    var formModel = '';
7388    var formDealerId = '';
7389    formsRegisterInterestController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location', '$rootScope', 'AddressSuggestService', 'UserPreferencesService', 'vcRecaptchaService', 'FormsService', 'EloquaService'];
7390    return {
7391        scope: true,
7392        restrict: 'A',
7393        controller: formsRegisterInterestController,
7394        controllerAs: 'ffc',
7395        bindToController: true,
7396        link: function (scope, element, attrs, filter, http) {
7397            scope.successContent = element.find('.lx-form-success');
7398            scope.failureContent = element.find('.lx-form-failure');
7399            formModel = attrs.lxFormsRegister;
7400            formDealerId = attrs.lxFormsDealerId;
7401        }
7402    };
7403    
7404    function formsRegisterInterestController($scope, $http, $filter, $timeout, $window, $interval, $location, $rootScope, AddressSuggestService, UserPreferencesService, vcRecaptchaService, FormsService, EloquaService) {
7405        var ffc = this;
7406
7407        $scope.init = function () {
7408            UserPreferencesService.getLexusVisitorSession().then(function (data) {
7409                data.Data = data.Data || {};
7410                var visitorPreference = UserPreferencesService.getLexusVisitor();
7411                $scope.ffc.form = {
7412                    submitting: false,
7413                    completed: false,
7414                    errors: false,
7415                    data: {
7416                        subscribe: false,
7417                        email: data.Data.email,
7418                        firstname: data.Data.name,
7419                        lastname: data.Data.surname,
7420                        phone: data.Data.phone,
7421                        daterange: '',
7422                        model: formModel,
7423                        vehicle: '',
7424                        dealerId: formDealerId,
7425                        captcha: '',
7426                        purchasedelay: null,
7427                        currentmake: null
7428                    }
7429                };
7430
7431            }, function (reason) {
7432            });
7433        }
7434
7435       
7436        $scope.getData = function () {
7437            var t= {
7438                Model: $scope.ffc.form.data.model,
7439                FirstName: $scope.ffc.form.data.firstname,
7440                LastName: $scope.ffc.form.data.lastname,
7441                Email: $scope.ffc.form.data.email,
7442                PlanToPurchase: $scope.ffc.form.data.purchasedelay,
7443                Phone: $scope.ffc.form.data.phone,
7444                VehicleTypeOwned: $scope.ffc.form.data.currentmake,
7445                Subscribe: $scope.ffc.form.data.subscribe.toString(),
7446                Captcha: $scope.ffc.form.data.captcha,
7447                DealerId: $scope.ffc.form.data.dealerId,
7448                //Leave alone this its to clear validation on the base type!
7449                //Its on the base type as we remember the value in session
7450                PhoneType: 'Not Specified'
7451            };
7452            return t;
7453        }
7454
7455
7456        $rootScope.$on('submit-register-interest-form', function () {
7457
7458            $scope.ffc.form.submitting = true;
7459            //Code to be changed once actual client side validation is in place on finance form to refer to actual api
7460            var formData = $scope.getData();
7461            EloquaService.attachEloquaFields(formData);
7462
7463            $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.FutureVehicleInterest, [Lexus.ID.Site]), data: formData }).
7464                then(function success(response) {
7465                    if (response.data.Status == "Success") {
7466                        $scope.submissionPass(response.data);
7467                    }
7468                    else {
7469                        $scope.submissionFail(response.data);
7470                    }
7471                }, function error(response) {
7472                    $scope.submissionFail(response.data);
7473                })
7474                .finally(function () {
7475                    $rootScope.$broadcast('form-result');
7476                });
7477        });
7478
7479
7480        $scope.submissionPass = function (data) {
7481            // this callback will be called asynchronously
7482            // when the response is available
7483            $scope.ffc.form.completed = true;
7484            $scope.ffc.form.failed = false;
7485            $scope.ffc.form.submitting = false;
7486            var historyState = "RegisterInterestForm=Pass";
7487            $location.state(historyState);
7488            FormsService.ShowResult($scope.successContent[0]);
7489            $rootScope.$broadcast('gtm-form-success', 'Register Interest Form');
7490            $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
7491        };
7492        $scope.submissionFail = function (data) {
7493            // called asynchronously if an error occurs
7494            // or server returns response with an error status.
7495            $scope.ffc.form.failed = true;
7496            $scope.ffc.form.errors = data.Data;
7497            $scope.ffc.form.submitting = false;
7498            $scope.ffc.form.completed = true;
7499            var historyState = "RegisterInterestForm=Fail";
7500            $location.state(historyState);
7501            FormsService.ShowResult($scope.failureContent[0]);
7502            if (data.ErrorMessage)
7503            console.error(data.ErrorMessage);
7504        };
7505
7506    }
7507}]);
7508;
7509(function () {
7510
7511    "use strict";
7512    /* Directives */
7513    angular.module('Lexus.Directives')
7514        .directive('lxFormsRequestBrochure', FormsRequestBrochureDirective);
7515
7516    FormsRequestBrochureDirective.$inject = [
7517        "$filter", "$timeout", "$http", "$location", "API", "$rootScope",
7518        "UserPreferencesService", "FormsService", "EloquaService",
7519        "DealersService", "AddressSuggestService"];
7520
7521    function FormsRequestBrochureDirective($filter, $timeout, $http, $location, API, $rootScope,
7522        userPreferencesService, formsService, eloquaService,
7523        dealersService, addressSuggestService) {
7524
7525        var brochureData;
7526        FormsRequestBrochureController.$inject = ['$scope'];
7527        return {
7528            scope: true,
7529            restrict: 'A',
7530            controller: FormsRequestBrochureController,
7531            controllerAs: 'ffc',
7532            bindToController: true,
7533            link: function (scope, element, attrs, filter, http) {
7534                scope.successContent = element.find('.lx-form-success');
7535                scope.failureContent = element.find('.lx-form-failure');
7536
7537                brochureData = JSON.parse(attrs.brochureData);
7538            }
7539        };
7540
7541        function FormsRequestBrochureController($scope) {
7542
7543            function initialize() {
7544
7545                var urlParameters = $location.search();
7546
7547                userPreferencesService.getLexusVisitorSession().then(function success(data) {
7548                    var dataVehicle = userPreferencesService.getLexusVehicle(urlParameters);
7549                    var dataDealer = userPreferencesService.getLexusDealer(urlParameters);
7550                    var dataUser = userPreferencesService.getLexusVisitor(urlParameters);
7551
7552                    $scope.ffc.form = {
7553                        submitting: false,
7554                        completed: false,
7555                        errors: false,
7556                        brochureUrl: null,
7557                        disableAddressValidation: true, //address fields hidden by default, so validation is disabled by default
7558                        dealerTagsLoading: false,
7559                        data: {
7560                            carmodel: dataVehicle.model,
7561                            brochureType: 'digital-brochure',
7562                            firstname: data.Data.name,
7563                            lastname: data.Data.surname,
7564                            address: data.Data.address,
7565                            postcodes: [],
7566                            dealerpostcodes: [],
7567                            postcode: dataUser.postCode,
7568                            pricingZone: dataUser.pricingZone,
7569                            suburb: dataUser.suburb,
7570                            state: dataUser.state,
7571                            email: data.Data.email,
7572                            phone: data.Data.phone,
7573                            purchasedelay: null,
7574                            currentmake: null,
7575                            subscribe: false,
7576                            captcha: '',
7577                            dealerId: dataDealer.dealerID,
7578                            dealerName: dataDealer.dealerName
7579                        }
7580                    };
7581                    if (dataUser.suburb && dataUser.postCode) {
7582                        $scope.ffc.form.data.dealerpostcodes.push({ text: dataUser.suburb + " " + dataUser.postCode });
7583                        $scope.ffc.form.data.postcodes.push({ text: dataUser.suburb + " " + dataUser.postCode });
7584                    }
7585                }, function failure(data) {
7586                    $scope.ffc.form = {
7587                        submitting: false,
7588                        completed: false,
7589                        errors: false,
7590                        brochureUrl: null,
7591                        disableAddressValidation: true,
7592                        data: {
7593                            carmodel: null,
7594                            brochureType: 'digital-brochure',
7595                            firstname: null,
7596                            lastname: null,
7597                            address: null,
7598                            postcodes: [],
7599                            postcode: null,
7600                            suburb: null,
7601                            state: null,
7602                            email: null,
7603                            phone: null,
7604                            purchasedelay: null,
7605                            currentmake: null,
7606                            subscribe: false,
7607                            captcha: ''
7608                        }
7609                    };
7610                }).finally(function () {
7611                    //Main Site
7612                    if ($scope.ffc.form.data.dealerpostcodes && $scope.ffc.form.data.dealerpostcodes.length > 0) {
7613                        searchNow();
7614                    }
7615
7616                    //Dealer Site Multi Branch
7617                    if (brochureData.isDealerSite && brochureData.isMultiBranchDealership) {
7618                        $scope.ffc.form.data.dealerId = null;
7619                        $scope.ffc.form.data.dealerName = null;
7620                        loadBranches(brochureData.forceDealerId);
7621                    }
7622
7623                    //Dealer Site NOT Multi Branch
7624                    if (brochureData.isDealerSite && !brochureData.isMultiBranchDealership) {
7625                        $scope.ffc.form.data.dealerId = brochureData.dealerCompositeID;
7626                        $scope.ffc.form.data.dealerName = null;
7627                    }
7628
7629                    if ($scope.ffc.form.data.carmodel) {
7630                        $rootScope.$broadcast('field-car-model-update-selected');
7631                    }
7632                });
7633
7634                $scope.updateBrochureType();
7635
7636            }
7637
7638            function loadBranches(dealerID) {
7639                $scope.ffc.form.dealersLoading = true;
7640                dealersService.setParam('dealerID', dealerID);
7641                dealersService.setParam('type', 'Sales');
7642                dealersService.GetDealerBranches();
7643            }
7644
7645            $scope.$on('dealer-search-results-received', function (event, data) {
7646                $scope.ffc.form.dealersLoading = false;
7647                $scope.ffc.form.dealers = data.Data;
7648
7649                var result = findDealer($scope.ffc.form.data.dealerId);
7650
7651                if (!result) {
7652                    $scope.ffc.form.data.dealerId = null;
7653                }
7654
7655                if ($scope.ffc.form.dealers.length == 1)
7656                    $scope.ffc.form.data.dealerId = $scope.ffc.form.dealers[0].dealerCode;
7657            });
7658
7659            function findDealer(dealerCode) {
7660                return $scope.ffc.form.dealers.find(function (dealer) {
7661                    return dealer.dealerCode == dealerCode;
7662                    });
7663            }
7664
7665            $scope.deselectDealer = function () {
7666                $scope.ffc.form.data.dealerId = $scope.ffc.form.data.dealerName = null;
7667            }
7668
7669            $scope.selectDealer = function (dealerInputId) {
7670                var scrollPos = $(document).scrollTop();
7671                var maxPos = $('#' + dealerInputId).parents('.lx-form__find-dealer-js').offset().top;
7672                if (scrollPos > maxPos) {
7673                    TweenLite.to(window, 0.5, { scrollTo: { y: maxPos }, ease: Power2.easeOut });
7674                }
7675            }
7676
7677            $scope.dealerSearch = function ($tag) {
7678                $scope.ffc.form.dealersLoading = true;
7679
7680                //Main Site
7681                if ($scope.ffc.form.data.dealerpostcodes && $scope.ffc.form.data.dealerpostcodes.length > 0 && !$scope.isDealerSite) {
7682                    searchNow();
7683                }
7684
7685                //Dealer Site Multi Branch
7686                if (brochureData.isDealerSite && brochureData.isMultiBranchDealership) {
7687                    $scope.ffc.form.data.dealerId = null;
7688                    $scope.ffc.form.data.dealerName = null;
7689                    loadBranches(brochureData.forceDealerId);
7690                }
7691
7692                //Dealer Site NOT Multi Branch
7693                if (brochureData.isDealerSite && !brochureData.isMultiBranchDealership && brochureData.dealerState) {
7694                    dealersService.setParam('dealerID', brochureData.forceDealerId);
7695                    dealersService.setParam('dealerState', brochureData.dealerState);
7696                    dealersService.setParam('type', 'Sales');
7697                    dealersService.GetResult();
7698                }
7699            }
7700
7701            $scope.$on('dealer-search-result-received', function (event, data) {
7702                $scope.ffc.form.dealersLoading = false;
7703                $scope.ffc.form.dealer = data.Data;
7704                $scope.ffc.form.dealerlocations = data.Data.locations;
7705
7706                if ($scope.ffc.form.dealerlocations == null) {
7707                    $scope.ffc.form.dealerlocations = [];
7708                }
7709                //Multiple dealers for same dealerId - A bug to be corrected latter -wont require looping in that case on parent
7710                if (data.Data) {
7711                    angular.forEach(data.Data, function (val) {
7712                        //console.log(val.locations);
7713                        if (val) {
7714                            angular.forEach(val.locations, function (innerval) {
7715                                console.log(innerval);
7716                                this.push(innerval);
7717                            }, $scope.ffc.form.dealerlocations);
7718                        }
7719                    });
7720                }
7721
7722                if ($scope.ffc.form.dealerlocations.length == 1)
7723                    $scope.ffc.form.data.locationId = $scope.ffc.form.dealerlocations[0].locationId;
7724            });
7725
7726            $scope.updateBrochureType = function () {
7727                if ($scope.ffc && $scope.ffc.form && $scope.ffc.form.data && $scope.ffc.form.data.brochureType) {
7728                    $scope.ffc.form.disableAddressValidation = ($scope.ffc.form.data.brochureType == "digital-brochure");
7729                }
7730            }
7731
7732            $scope.minTags = function () {
7733                if ($scope.ffc && $scope.ffc.form && $scope.ffc.form.disableAddressValidation) {
7734                    return $scope.ffc.form.disableAddressValidation ? '0' : '1';
7735                }
7736                return '1';
7737            }
7738
7739            $scope.clearDealerPostcode = function () {
7740                $scope.ffc.form.data.dealerpostcodes = null;
7741            }
7742
7743            $scope.clearPostcode = function () {
7744                $scope.ffc.form.data.postcodes = null;
7745            }
7746
7747            $scope.updateDealerPostcode = function ($tag) {
7748                $scope.ffc.form.data.dealerpostcodes = [];
7749                $scope.ffc.form.data.dealerpostcodes[0] = $tag;
7750
7751                $scope.dealerSearch($tag);
7752
7753                if (!$scope.ffc.form.data.postcode) {
7754                    $scope.ffc.form.data.postcodes = [];
7755                    $scope.ffc.form.data.postcodes.push({ text: $tag.data.suburb + " " + $tag.data.postcode });
7756                    $scope.ffc.form.data.postcode = $tag.data.postcode;
7757                    $scope.ffc.form.data.suburb = $tag.data.suburb;
7758                    $scope.ffc.form.data.state = $tag.data.state;
7759                    $scope.ffc.form.data.pricingZone = $tag.data.pricingZone;
7760
7761                    $rootScope.$broadcast('visitor-details-cookie-change', {
7762                        postCode: $tag.data.postcode,
7763                        suburb: $tag.data.suburb,
7764                        state: $tag.data.state,
7765                        pricingZone: $tag.data.pricingZone
7766                    });
7767                }
7768            }
7769
7770            $scope.updatePostcode = function ($tag) {
7771                $scope.ffc.form.data.postcodes = [];
7772                $scope.ffc.form.data.postcodes[0] = $tag;
7773
7774                $scope.ffc.form.data.postcode = $tag.data.postcode;
7775                $scope.ffc.form.data.suburb = $tag.data.suburb;
7776                $scope.ffc.form.data.state = $tag.data.state;
7777                $scope.ffc.form.data.pricingZone = $tag.data.pricingZone;
7778
7779                $rootScope.$broadcast('visitor-details-cookie-change', {
7780                        postCode: $tag.data.postcode,
7781                        suburb: $tag.data.suburb,
7782                        state: $tag.data.state,
7783                        pricingZone: $tag.data.pricingZone
7784                });
7785            }
7786
7787            function searchNow() {
7788                $scope.ffc.form.dealersLoading = true;
7789
7790                var visitor = userPreferencesService.splitAutoCompleteResult($scope.ffc.form.data.dealerpostcodes[0].text);
7791
7792                dealersService.setParam('postCode', visitor.postCode);
7793                dealersService.setParam('suburb', visitor.suburb);
7794                dealersService.setParam('type', 'Sales');
7795
7796                dealersService.GetResults();
7797            }
7798
7799            function getFormData() {
7800
7801                var result = {
7802                    Model: $scope.ffc.form.data.carmodel,
7803                    DealerId: $scope.ffc.form.data.dealerId,
7804                    ContextDealerSiteId: brochureData.forceDealerId,
7805                    FirstName: $scope.ffc.form.data.firstname,
7806                    LastName: $scope.ffc.form.data.lastname,
7807                    BrochureType: $scope.ffc.form.data.brochureType,
7808                    Address: $scope.ffc.form.data.address,
7809                    Postcode: $scope.ffc.form.data.postcode,
7810                    State: $scope.ffc.form.data.state,
7811                    Suburb: $scope.ffc.form.data.suburb,
7812                    Email: $scope.ffc.form.data.email,
7813                    Phone: $scope.ffc.form.data.phone,
7814                    PlanToPurchase: $scope.ffc.form.data.purchasedelay,
7815                    VehicleTypeOwned: $scope.ffc.form.data.currentmake,
7816                    Captcha: $scope.ffc.form.data.captcha,
7817                    Subscribe: $scope.ffc.form.data.subscribe
7818                }
7819                return result;
7820            };
7821
7822            $rootScope.$on('vehicle-preference-change', function (event, data) {
7823                $scope.ffc.form.data.carmodel = data.model;
7824            });
7825
7826            $rootScope.$on('submit-request-brochure-form', function (event, data) {
7827                var formData = getFormData();
7828
7829                eloquaService.attachEloquaFields(formData);
7830
7831                $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.RequestBrochure, [Lexus.ID.Site]), data: formData })
7832                    .then(function success(response) {
7833                        if (response.data.Status == "Success") {
7834                            submissionPass(response.data);
7835                            $rootScope.$broadcast('gtm-form-success', 'Request Brochure');
7836                        }
7837                        else {
7838                            submissionFail(response.data);
7839                        }
7840                    }, function error(response) {
7841                        submissionFail(response.data);
7842                    })
7843                    .finally(function () {
7844                        $rootScope.$broadcast('form-result');
7845                    });
7846            });
7847
7848
7849            function submissionPass(data) {
7850                // this callback will be called asynchronously
7851                // when the response is available
7852                $scope.ffc.form.brochureUrl = data.Data.BrochureUrl;
7853                $scope.ffc.form.completed = true;
7854                $scope.ffc.form.failed = false;
7855                $scope.ffc.form.submitting = false;
7856                var historyState = "RequestBrochureForm=Pass";
7857                $location.state(historyState);
7858                formsService.ShowResult($scope.successContent[0]);
7859                $rootScope.$broadcast('visitor-details-session-change', getFormData());
7860            };
7861
7862            function submissionFail(data) {
7863                // called asynchronously if an error occurs
7864                // or server returns response with an error status.
7865                $scope.ffc.form.failed = true;
7866                $scope.ffc.form.errors = data.Data;
7867                $scope.ffc.form.submitting = false;
7868                $scope.ffc.form.completed = true;
7869                var historyState = "RequestBrochureForm=Fail";
7870                $location.state(historyState);
7871                formsService.ShowResult($scope.failureContent[0]);
7872                console.error(data.ErrorMessage);
7873            };
7874
7875            $scope.loadDealerTags = function (query) {
7876                $scope.ffc.form.dealerTagsLoading = true;
7877                addressSuggestService.setParam('term', query);
7878
7879                var results = addressSuggestService.GetResults(function () {
7880                    $scope.ffc.form.dealerTagsLoading = false;
7881                });
7882
7883                return results;
7884            };
7885
7886            initialize();
7887        }
7888    }
7889
7890}());
7891
7892;
7893"use strict";
7894/* Directives */
7895angular.module('Lexus.Directives')
7896.directive('lxFormsSubscribeNewsLetter', ['$window', '$timeout', '$filter', '$http', '$location', function ($window, $timeout, $filter, $http, $location) {
7897    var formDealerId = '';
7898    formsSubscribeNewsLetterController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location', '$rootScope', 'AddressSuggestService', 'UserPreferencesService', 'vcRecaptchaService', 'FormsService'];
7899    return {
7900        scope: true,
7901        restrict: 'A',
7902        controller: formsSubscribeNewsLetterController,
7903        controllerAs: 'ffc',
7904        bindToController: true,
7905        link: function (scope, element, attrs, filter, http) {
7906            scope.successContent = element.find('.lx-form-success');
7907            scope.failureContent = element.find('.lx-form-failure');
7908        }
7909    };
7910    
7911    function formsSubscribeNewsLetterController($scope, $http, $filter, $timeout, $window, $interval, $location, $rootScope, AddressSuggestService, UserPreferencesService, vcRecaptchaService, FormsService) {
7912        var ffc = this;
7913
7914        $scope.init = function () {
7915
7916            UserPreferencesService.getLexusVisitorSession().then(function (data) {
7917
7918                var dataUser = UserPreferencesService.getLexusVisitor();
7919                $scope.ffc.form = {
7920                    submitting: false,
7921                    completed: false,
7922                    errors: false,
7923                    data: {
7924                        subscribe: false,
7925                        email: data.Data.email,
7926                        firstname: data.Data.name,
7927                        lastname: data.Data.surname,
7928                        phone: data.Data.phone,
7929                        captcha: '' 
7930                    }
7931                };
7932                
7933
7934            }, function failure(data) {
7935                $scope.ffc.form = {
7936                    submitting: false,
7937                    completed: false,
7938                    errors: false,
7939                    data: {
7940                        subscribe: false,
7941                        email: null,
7942                        firstname: null,
7943                        lastname: null,
7944                        phone: null,
7945                        captcha: '' 
7946                    }
7947                };
7948            }).finally(function () {
7949                 
7950           });
7951        }
7952
7953       
7954        $scope.getData = function () {
7955            var t= {
7956                FirstName: $scope.ffc.form.data.firstname,
7957                LastName: $scope.ffc.form.data.lastname,
7958                Email: $scope.ffc.form.data.email,
7959                Phone: $scope.ffc.form.data.phone,
7960                Subscribe: $scope.ffc.form.data.subscribe.toString(),
7961                Captcha: $scope.ffc.form.data.captcha,
7962                //Leave alone this its to clear validation on the base type!
7963                //Its on the base type as we remember the value in session
7964                PhoneType: 'Not Specified' 
7965            };
7966            return t;
7967        }
7968
7969        
7970        $rootScope.$on('submit-subscribe-newsletter-form', function () {
7971            $scope.ffc.form.submitting = true;
7972
7973            $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.SubscribeNewsletterUrl, [Lexus.ID.Site]), data: $scope.getData() }).
7974                then(function success(response) {
7975                    if (response.data.Status == "Success") {
7976                        $scope.submissionPass(response.data);
7977                    }
7978                    else {
7979                        $scope.submissionFail(response.data);
7980                    }
7981                }, function error(response) {
7982                    $scope.submissionFail(response.data);
7983                })
7984                .finally(function () {
7985                    $rootScope.$broadcast('form-result');
7986                });
7987        });
7988
7989        $scope.submissionPass = function (data) {
7990            // this callback will be called asynchronously
7991            // when the response is available
7992            $scope.ffc.form.completed = true;
7993            $scope.ffc.form.failed = false;
7994            $scope.ffc.form.submitting = false;
7995            var historyState = "SubscribeNewsLetterForm=Pass";
7996            $location.state(historyState);
7997            FormsService.ShowResult($scope.successContent[0]);
7998            $rootScope.$broadcast('gtm-form-success', 'Subscribe Newsletter Form');
7999            $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
8000        };
8001
8002        $scope.submissionFail = function (data) {
8003            // called asynchronously if an error occurs
8004            // or server returns response with an error status.
8005            $scope.ffc.form.failed = true;
8006            $scope.ffc.form.errors = data.Data;
8007            $scope.ffc.form.submitting = false;
8008            $scope.ffc.form.completed = true;
8009            var historyState = "SubscribeNewsLetterForm=Fail";
8010            $location.state(historyState);
8011            FormsService.ShowResult($scope.failureContent[0]);
8012            console.error(data.ErrorMessage);
8013        };
8014
8015    }
8016}]);
8017;
8018(function () {
8019    "use strict";
8020
8021    /* Directives */
8022    angular.module('Lexus.Directives')
8023        .directive("lxFormsTestDriveConcierge", TestDriveConciergeDirective);
8024
8025    TestDriveConciergeDirective.$inject = [
8026        "$filter", "$http", "$location", "API", "$rootScope", "DealersService", 
8027        "AddressSuggestService", "UserPreferencesService", "FormsService", "EloquaService"];
8028
8029    function TestDriveConciergeDirective($filter, $http, $location, API, $rootScope, DealersService,
8030        AddressSuggestService, UserPreferencesService, FormsService, EloquaService) {
8031        FormsTestDriveConciergeController.$inject = ['$scope'];
8032        return {
8033            scope: true,
8034            restrict: 'A',
8035            controller: FormsTestDriveConciergeController,
8036            controllerAs: 'ffc',
8037            bindToController: true,
8038            link: function (scope, element, attrs, filter, http) {
8039                scope.successContent = element.find('.lx-form-success');
8040                scope.failureContent = element.find('.lx-form-failure');
8041            }
8042        };
8043
8044        function FormsTestDriveConciergeController($scope) {
8045            var ftd = this;
8046            $scope.ffc.urlParameters = $location.search();
8047
8048            $scope.init = function (forceDealerId, dealerCompositeID, isDealerSite, isMultiBranchDealer) {
8049                UserPreferencesService.getLexusVisitorSession().then(function success(data) {
8050                    var dataVehicle = UserPreferencesService.getLexusVehicle($scope.ffc.urlParameters);
8051                    var dataDealer = UserPreferencesService.getLexusDealer($scope.ffc.urlParameters);
8052                    var dataUser = UserPreferencesService.getLexusVisitor($scope.ffc.urlParameters);
8053                    $scope.ffc.form = {
8054                        submitting: false,
8055                        completed: false,
8056                        errors: false,
8057                        dealers: [],
8058                        data: {
8059                            postcodes: [],
8060                            carmodel: dataVehicle.model,
8061                            grade: dataVehicle.grade,
8062                            enginevariant: dataVehicle.engineVariant,
8063                            contextDealerSiteId: forceDealerId, 
8064                            subscribe: false,
8065                            postcode: "",
8066                            email: data.Data.email,
8067                            phonetype: data.Data.phoneType ? data.Data.phoneType : 'home',
8068                            firstname: data.Data.name,
8069                            lastname: data.Data.surname,
8070                            phone: data.Data.phone,
8071                            prefer: data.Data.preferredContactMethod ? data.Data.preferredContactMethod : 'email',
8072                            captcha: '',
8073                            dealerId: dataDealer.dealerID,
8074                            dealerName: dataDealer.dealerName,
8075                            preferredlocation: 'home',
8076                            purchasedelay: null,
8077                            currentmake: null
8078                        }
8079                    };
8080                    if (dataUser.suburb && dataUser.postCode) {
8081                        $scope.ffc.form.data.postcodes.push(
8082                            {
8083                                text: dataUser.suburb + " " + dataUser.postCode,
8084                                data: {
8085                                    postCode: dataUser.postCode,
8086                                    suburb: dataUser.suburb
8087                                }
8088                            });
8089                    }
8090                }, function failure(data) {
8091                    $scope.ffc.form = {
8092                        submitting: false,
8093                        completed: false,
8094                        errors: false,
8095                        dealers: [],
8096                        data: {
8097                            postcodes: [],
8098                            carmodel: null,
8099                            grade: null,
8100                            subscribe: false,
8101                            postcode: "",
8102                            email: null,
8103                            phonetype: 'home',
8104                            firstname: null,
8105                            lastname: null,
8106                            phone: null,
8107                            prefer: 'email',
8108                            captcha: '',
8109                            dealerId: null,
8110                            dealerName: null,
8111                            preferredlocation: 'home',
8112                            purchasedelay: null,
8113                            currentmake: null
8114                        }
8115                    };
8116                }).finally(function () {
8117                    if ($scope.ffc.form.data.carmodel) {
8118                        $rootScope.$broadcast('field-car-model-engine-variant-update-selected');
8119                    }
8120
8121                    //Main Site
8122                    if ($scope.ffc.form.data.postcodes && $scope.ffc.form.data.postcodes.length > 0 && !isDealerSite) {
8123                        $scope.searchNow();
8124                    }
8125
8126                    //Dealer Site Multi Branch
8127                    if (isDealerSite && isMultiBranchDealer) {
8128                        $scope.ffc.form.data.dealerId = null;
8129                        $scope.ffc.form.data.dealerName = null;
8130                        $scope.loadBranches(forceDealerId);
8131                    }
8132
8133                    //Dealer Site NOT Multi Branch
8134                    if (isDealerSite && !isMultiBranchDealer) {
8135                        $scope.ffc.form.data.dealerId = dealerCompositeID;
8136                        $scope.ffc.form.data.dealerName = null;
8137                    }
8138
8139                });
8140
8141
8142            }
8143
8144
8145            //$scope.loadTags = function (query) {
8146            //    AddressSuggestService.setParam('term', query);
8147            //    return AddressSuggestService.GetResults();
8148            //};
8149
8150            $scope.deselectDealer = function () {
8151                $scope.ffc.form.data.dealerName = null;
8152            }
8153
8154            $scope.selectDealer = function (dealerInputId) {
8155                var scrollPos = $(document).scrollTop();
8156                var maxPos = $('#' + dealerInputId).parents('.lx-form__find-dealer-js').offset().top;
8157                if (scrollPos > maxPos) {
8158                    TweenLite.to(window, 0.5, { scrollTo: { y: maxPos }, ease: Power2.easeOut });
8159                }
8160                try {
8161                    var code = dealerInputId.replace('option-', '');
8162                    $scope.ffc.form.data.dealerName = $scope.ffc.form.dealers.find(function (dealer) { return dealer.dealerCode == code; }).dealerName;
8163                } catch (ex) {
8164                    (console.error || console.log)(ex);
8165                }
8166            }
8167
8168            $scope.loadBranches = function (dealerID) {
8169                DealersService.setParam('dealerID', dealerID);
8170                DealersService.setParam('type', 'Sales');
8171                DealersService.GetDealerBranches();
8172            }
8173
8174            $scope.dealerSearch = function ($tag) {
8175                $scope.ffc.form.data.postcodes = [];
8176                $scope.ffc.form.data.postcodes[0] = $tag;
8177                $scope.searchNow();
8178            }
8179
8180            $scope.searchNow = function () {
8181                $scope.ffc.form.dealersLoading = true;
8182                $rootScope.$broadcast('visitor-details-change-autocomplete', $scope.ffc.form.data.postcodes[0].data);
8183                DealersService.setParam('postCode', $scope.ffc.form.data.postcodes[0].data.postCode); //Dummy Implementation needs to be changed
8184                DealersService.setParam('suburb', $scope.ffc.form.data.postcodes[0].data.suburb); //Dummy Implementation needs to be changed
8185                DealersService.setParam('type', 'Sales');
8186                DealersService.GetResults();
8187
8188            }
8189
8190            $rootScope.$on('vehicle-preference-change', function (event, data) {
8191                $scope.ffc.form.data.carmodel = data.model;
8192                $scope.ffc.form.data.enginevariant = data.engineVariant;
8193            });
8194
8195            $scope.$on('dealer-search-results-received', function (event, data) {
8196                $scope.ffc.form.dealersLoading = false;
8197                $scope.ffc.form.dealers = data.Data;
8198                $scope.checkDealersLoaded();
8199            });
8200
8201            //Do a sanity check to see if the bound dealers match the forceDealer if not clear the force dealer
8202            $scope.checkDealersLoaded = function () {
8203                var result = $scope.ffc.form.dealers.find(function (dealer) {
8204                    return dealer.dealerCode == $scope.ffc.form.data.dealerId;
8205                });
8206                if (!result) {
8207                    $scope.ffc.form.data.dealerId = null;
8208                }
8209
8210                if ($scope.ffc.form.dealers.length == 1)
8211                    $scope.ffc.form.data.dealerId = $scope.ffc.form.dealers[0].dealerCode; 
8212            }
8213
8214
8215            $scope.getData = function () {
8216                var utmParameters = UserPreferencesService.getUtmParameters();
8217                var postcode = null;
8218                if ($scope.ffc.form.data.postcodes.length > 0 && $scope.ffc.form.data.postcodes[0].text) {
8219                    postcode = parseInt($scope.ffc.form.data.postcodes[0].text.split(' ').reverse()[0]);
8220                }
8221                return {
8222                    FirstName: $scope.ffc.form.data.firstname,
8223                    LastName: $scope.ffc.form.data.lastname,
8224                    Email: $scope.ffc.form.data.email,
8225                    Phone: $scope.ffc.form.data.phone,
8226                    PreferredContactMethod: $scope.ffc.form.data.prefer,
8227                    Model: $scope.ffc.form.data.carmodel,
8228                    Grade: $scope.ffc.form.data.grade,
8229                    EngineVariant: $scope.ffc.form.data.enginevariant,
8230                    ContextDealerSiteId: $scope.ffc.form.data.contextDealerSiteId,
8231                    DealerId: $scope.ffc.form.data.dealerId,
8232                    Subscribe: $scope.ffc.form.data.subscribe.toString(),
8233                    Captcha: $scope.ffc.form.data.captcha,
8234                    PreferredLocation: $scope.ffc.form.data.preferredlocation,
8235                    PlanToPurchase: $scope.ffc.form.data.purchasedelay,
8236                    VehicleTypeOwned: $scope.ffc.form.data.currentmake,
8237                    //Leave alone this its to clear validation on the base type!
8238                    //Its on the base type as we remember the value in session
8239                    PhoneType: 'Not Specified',
8240                    UtmSource: utmParameters.source,
8241                    UtmMedium: utmParameters.medium,
8242                    UtmCampaign: utmParameters.campaign,
8243                    UtmContent: utmParameters.content,
8244                    Postcode: postcode,
8245                    DealerName: $scope.ffc.form.data.dealerName
8246                };
8247            }
8248
8249            $rootScope.$on('submit-test-drive-concierage-form', function () {
8250                var formData = $scope.getData();
8251
8252                EloquaService.attachEloquaFields(formData);
8253
8254                $scope.ffc.form.submitting = true;
8255                //Code to be changed once actual client side validation is in place on finance form to refer to actual api
8256                $http({
8257                    method: 'POST',
8258                    url: $filter('formatArgs')(Lexus.API.FormSubmission.TestDriveConcierge, [Lexus.ID.Site]),
8259                    data: formData
8260                    })
8261                    .then(function success(response) {
8262                        if (response.data.Status == "Success") {
8263                            $scope.submissionPass(response.data);
8264                        }
8265                        else {
8266                            $scope.submissionFail(response.data);
8267                        }
8268                    }, function error(response) {
8269                        $scope.submissionFail(response.data);
8270                    })
8271                    .finally(function () {
8272                        $rootScope.$broadcast('form-result');
8273                    });
8274            });
8275
8276            $scope.submissionPass = function (data) {
8277                // this callback will be called asynchronously
8278                // when the response is available
8279                $scope.ffc.form.completed = true;
8280                $scope.ffc.form.failed = false;
8281                $scope.ffc.form.submitting = false;
8282                var historyState = "ConcierageTestDriveForm=Pass";
8283                $location.state(historyState);
8284                FormsService.ShowResult($scope.successContent[0]);
8285                $rootScope.$broadcast('gtm-form-success', 'Test Drive Concierge');
8286                $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
8287            };
8288            $scope.submissionFail = function (data) {
8289                // called asynchronously if an error occurs
8290                // or server returns response with an error status.
8291                $scope.ffc.form.failed = true;
8292                $scope.ffc.form.errors = data.Data;
8293                $scope.ffc.form.submitting = false;
8294                $scope.ffc.form.completed = true;
8295                var historyState = "ConcierageTestDriveForm=Fail";
8296                $location.state(historyState);
8297                FormsService.ShowResult($scope.failureContent[0]);
8298                console.error(data.ErrorMessage);
8299            };
8300        }
8301     }
8302
8303
8304}());
8305
8306;
8307(function () {
8308
8309    "use strict";
8310
8311    angular.module('Lexus.Directives')
8312    .directive('lxFormsTestDrivePreLaunch', FormsTestDrivePreLaunchDirective); 
8313
8314    FormsTestDrivePreLaunchDirective.$inject = [
8315        "$filter", "$http", "$location", "API", "$rootScope", "DealersService",
8316        "AddressSuggestService", "UserPreferencesService", "FormsService", "EloquaService"];
8317
8318    function FormsTestDrivePreLaunchDirective($filter, $http, $location, API, $rootScope, DealersService,
8319        AddressSuggestService, UserPreferencesService, FormsService, EloquaService) {
8320
8321        var formFutureModel = '';
8322        FormsTestDrivePreLaunchController.$inject = ['$scope'];
8323        return {
8324            scope: true,
8325            restrict: 'A',
8326            controller: FormsTestDrivePreLaunchController,
8327            controllerAs: 'ffc',
8328            bindToController: true,
8329            link: function (scope, element, attrs, filter, http) {
8330                scope.successContent = element.find('.lx-form-success');
8331                scope.failureContent = element.find('.lx-form-failure');
8332                formFutureModel = attrs.lxFormsFutureModel;
8333            }
8334        };
8335
8336        function FormsTestDrivePreLaunchController($scope) {
8337            var ftd = this;
8338            $scope.ffc.urlParameters = $location.search();
8339
8340            $scope.init = function (forceDealerId, dealerCompositeID, isDealerSite, isMultiBranchDealer) {
8341                UserPreferencesService.getLexusVisitorSession().then(function success(data) {
8342                    //var dataVehicle = UserPreferencesService.getLexusVehicle($scope.ffc.urlParameters);  Wont be passed from URL or cookies for future vehicles
8343                    var dataDealer = UserPreferencesService.getLexusDealer($scope.ffc.urlParameters);
8344                    var dataUser = UserPreferencesService.getLexusVisitor($scope.ffc.urlParameters);
8345                    $scope.ffc.form = {
8346                        submitting: false,
8347                        completed: false,
8348                        errors: false,
8349                        dealers: [],
8350                        data: {
8351                            postcodes: [],
8352                            carmodel: formFutureModel,
8353                            subscribe: false,
8354                            postcode: "",
8355                            email: data.Data.email,
8356                            phonetype: data.Data.phoneType ? data.Data.phoneType : 'home',
8357                            firstname: data.Data.name,
8358                            lastname: data.Data.surname,
8359                            phone: data.Data.phone,
8360                            prefer: data.Data.preferredContactMethod ? data.Data.preferredContactMethod : 'email',
8361                            captcha: '',
8362                            contextDealerSiteId: forceDealerId,
8363                            dealerId: dataDealer.dealerID,
8364                            dealerName: dataDealer.dealerName,
8365                            preferredlocation: 'home',
8366                            purchasedelay: null,
8367                            currentmake: null
8368                        }
8369                    };
8370                    if (dataUser.suburb && dataUser.postCode) {
8371                        $scope.ffc.form.data.postcodes.push(
8372                            {
8373                                text: dataUser.suburb + " " + dataUser.postCode,
8374                                data: {
8375                                    postCode: dataUser.postCode,
8376                                    suburb: dataUser.suburb
8377                                }
8378                            });
8379                    }
8380                }, function failure(data) {
8381                    $scope.ffc.form = {
8382                        submitting: false,
8383                        completed: false,
8384                        errors: false,
8385                        dealers: [],
8386                        data: {
8387                            postcodes: [],
8388                            carmodel: formFutureModel,
8389                            subscribe: false,
8390                            postcode: "",
8391                            email: null,
8392                            phonetype: 'home',
8393                            firstname: null,
8394                            lastname: null,
8395                            phone: null,
8396                            prefer: 'email',
8397                            captcha: '',
8398                            dealerId: null,
8399                            dealerName: null,
8400                            preferredlocation: 'home',
8401                            purchasedelay: null,
8402                            currentmake: null
8403                        }
8404                    };
8405                }).finally(function () {
8406                    //Main Site
8407                    if ($scope.ffc.form.data.postcodes && $scope.ffc.form.data.postcodes.length > 0 && !isDealerSite) {
8408                        $scope.searchNow();
8409                    }
8410
8411                    //Dealer Site Multi Branch
8412                    if (isDealerSite && isMultiBranchDealer) {
8413                        $scope.ffc.form.data.dealerId = null;
8414                        $scope.ffc.form.data.dealerName = null;
8415                        $scope.loadBranches(forceDealerId);
8416                    }
8417
8418                    //Dealer Site NOT Multi Branch
8419                    if (isDealerSite && !isMultiBranchDealer) {
8420                        $scope.ffc.form.data.dealerId = dealerCompositeID;
8421                        $scope.ffc.form.data.dealerName = null;
8422                    }
8423
8424                });
8425            }
8426
8427            //$scope.loadTags = function (query) {
8428            //    AddressSuggestService.setParam('term', query);
8429            //    return AddressSuggestService.GetResults();
8430            //};
8431
8432            $scope.deselectDealer = function () {
8433                $scope.ffc.form.data.dealerName = null;
8434            }
8435
8436            $scope.selectDealer = function (dealerInputId) {
8437                var scrollPos = $(document).scrollTop();
8438                var maxPos = $('#' + dealerInputId).parents('.lx-form__find-dealer-js').offset().top;
8439                if (scrollPos > maxPos) {
8440                    TweenLite.to(window, 0.5, { scrollTo: { y: maxPos }, ease: Power2.easeOut });
8441                }
8442                try {
8443                    var code = dealerInputId.replace('option-', '');
8444                    $scope.ffc.form.data.dealerName = $scope.ffc.form.dealers.find(function (dealer) { return dealer.dealerCode == code; }).dealerName;
8445                } catch(ex) {
8446                    (console.error || console.log)(ex);
8447                }
8448            }
8449
8450            $scope.loadBranches = function (dealerID) {
8451                DealersService.setParam('dealerID', dealerID);
8452                DealersService.setParam('type', 'Sales');
8453                DealersService.GetDealerBranches();
8454            }
8455
8456            $scope.dealerSearch = function ($tag) {
8457                $scope.ffc.form.data.postcodes = [];
8458                $scope.ffc.form.data.postcodes[0] = $tag;
8459                $scope.searchNow();
8460            }
8461
8462            $scope.searchNow = function () {
8463                $scope.ffc.form.dealersLoading = true;
8464                $rootScope.$broadcast('visitor-details-change-autocomplete', $scope.ffc.form.data.postcodes[0].data);
8465                DealersService.setParam('postCode', $scope.ffc.form.data.postcodes[0].data.postCode); //Dummy Implementation needs to be changed
8466                DealersService.setParam('suburb', $scope.ffc.form.data.postcodes[0].data.suburb); //Dummy Implementation needs to be changed
8467                DealersService.setParam('type', 'Sales');
8468                DealersService.GetResults();
8469
8470            }
8471
8472            $scope.$on('dealer-search-results-received', function (event, data) {
8473                $scope.ffc.form.dealersLoading = false;
8474                $scope.ffc.form.dealers = data.Data;
8475                $scope.checkDealersLoaded();
8476            });
8477
8478            //Do a sanity check to see if the bound dealers match the forceDealer if not clear the force dealer
8479            $scope.checkDealersLoaded = function () {
8480                var result = $scope.ffc.form.dealers.find(function (dealer) {
8481                    return dealer.dealerCode == $scope.ffc.form.data.dealerId;
8482                });
8483                if (!result) {
8484                    $scope.ffc.form.data.dealerId = null;
8485                }
8486                 
8487                if ($scope.ffc.form.dealers.length == 1)
8488                    $scope.ffc.form.data.dealerId = $scope.ffc.form.dealers[0].dealerCode;
8489            }
8490
8491
8492            $scope.getData = function () {
8493                var utmParameters = UserPreferencesService.getUtmParameters();
8494                var postcode = null;
8495                if ($scope.ffc.form.data.postcodes.length > 0 && $scope.ffc.form.data.postcodes[0].text) {
8496                    postcode = parseInt($scope.ffc.form.data.postcodes[0].text.split(' ').reverse()[0]);
8497                }
8498                return {
8499                    FirstName: $scope.ffc.form.data.firstname,
8500                    LastName: $scope.ffc.form.data.lastname,
8501                    Email: $scope.ffc.form.data.email,
8502                    Phone: $scope.ffc.form.data.phone,
8503                    PreferredContactMethod: $scope.ffc.form.data.prefer,
8504                    Model: formFutureModel,
8505                    DealerId: $scope.ffc.form.data.dealerId,
8506                    ContextDealerSiteId: $scope.ffc.form.data.contextDealerSiteId,
8507                    Subscribe: $scope.ffc.form.data.subscribe.toString(),
8508                    Captcha: $scope.ffc.form.data.captcha,
8509                    PlanToPurchase: $scope.ffc.form.data.purchasedelay,
8510                    VehicleTypeOwned: $scope.ffc.form.data.currentmake,
8511                    PreferredLocation: $scope.ffc.form.data.preferredlocation,
8512                    //Leave alone this its to clear validation on the base type!
8513                    //Its on the base type as we remember the value in session
8514                    PhoneType: 'Not Specified',
8515                    UtmSource: utmParameters.source,
8516                    UtmMedium: utmParameters.medium,
8517                    UtmCampaign: utmParameters.campaign,
8518                    UtmContent: utmParameters.content,
8519                    Postcode: postcode,
8520                    DealerName: $scope.ffc.form.data.dealerName
8521                };
8522            }
8523
8524            $rootScope.$on('submit-test-drive-prelaunch-form', function () {
8525                var formData = $scope.getData();
8526
8527                EloquaService.attachEloquaFields(formData);
8528
8529                $scope.ffc.form.submitting = true;
8530                //Code to be changed once actual client side validation is in place on finance form to refer to actual api
8531                $http({
8532                    method: 'POST',
8533                    url: $filter('formatArgs')(Lexus.API.FormSubmission.PreLaunchTestDriveUrl, [Lexus.ID.Site]),
8534                    data: formData
8535                })
8536                    .then(function success(response) {
8537                        if (response.data.Status == "Success") {
8538                            $scope.submissionPass(response.data);
8539                        }
8540                        else {
8541                            $scope.submissionFail(response.data);
8542                        }
8543
8544                    }, function error(response) {
8545                        $scope.submissionFail(response.data);
8546                    })
8547                    .finally(function () {
8548                        $rootScope.$broadcast('form-result');
8549                    });
8550            });
8551
8552            $scope.submissionPass = function (data) {
8553                // this callback will be called asynchronously
8554                // when the response is available
8555                $scope.ffc.form.completed = true;
8556                $scope.ffc.form.failed = false;
8557                $scope.ffc.form.submitting = false;
8558                var historyState = "PreBookTestDriveForm=Pass";
8559                $location.state(historyState);
8560                FormsService.ShowResult($scope.successContent[0]);
8561                $rootScope.$broadcast('gtm-form-success', 'Test Drive Prelaunch');
8562                $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
8563
8564            };
8565
8566            $scope.submissionFail = function (data) {
8567                // called asynchronously if an error occurs
8568                // or server returns response with an error status.
8569                $scope.ffc.form.failed = true;
8570                $scope.ffc.form.errors = data.Data;
8571                $scope.ffc.form.submitting = false;
8572                $scope.ffc.form.completed = true;
8573                var historyState = "PreBookTestDriveForm=Fail";
8574                $location.state(historyState);
8575                FormsService.ShowResult($scope.failureContent[0]);
8576                console.error(data.ErrorMessage);
8577            };
8578        }
8579    }
8580
8581}());
8582
8583;
8584"use strict";
8585/* Directives */
8586angular.module('Lexus.Directives')
8587    .directive('lxFormsTestDrive', 
8588    [
8589        "$rootScope", "$filter", "$http", "$location", "API", "DealersService", "AddressSuggestService", "UserPreferencesService", "FormsService", "EloquaService", 
8590        function ($rootScope, $filter, $http, $location, API, DealersService, AddressSuggestService, UserPreferencesService, FormsService, EloquaService)
8591        {
8592            FormsTestDriveController.$inject = ['$scope'];
8593            return {
8594                scope: true,
8595                restrict: 'A',
8596                controller: FormsTestDriveController,
8597                controllerAs: 'ffc',
8598                bindToController: true,
8599                link: function (scope, element, attrs, filter, http) {
8600                    scope.successContent = element.find('.lx-form-success');
8601                    scope.failureContent = element.find('.lx-form-failure');
8602                }
8603            };
8604
8605            function FormsTestDriveController($scope) {
8606                var ftd = this;
8607                $scope.ffc.urlParameters = $location.search();
8608
8609                $scope.init = function (forceDealerId, dealerCompositeID, isDealerSite, isMultiBranchDealer) {
8610                    UserPreferencesService.getLexusVisitorSession().then(function success(data) {
8611                        var dataVehicle = UserPreferencesService.getLexusVehicle($scope.ffc.urlParameters);
8612                        var dataDealer = UserPreferencesService.getLexusDealer($scope.ffc.urlParameters);
8613                        var dataUser = UserPreferencesService.getLexusVisitor($scope.ffc.urlParameters);
8614                        $scope.ffc.form = {
8615                            submitting: false,
8616                            completed: false,
8617                            errors: false,
8618                            dealers: null,
8619                            data: {
8620                                postcodes: [],
8621                                carmodel: dataVehicle.model,
8622                                grade: dataVehicle.grade,
8623                                enginevariant: dataVehicle.engineVariant,
8624                                subscribe: false,
8625                                postcode: "",
8626                                email: data.Data.email,
8627                                phonetype: data.Data.phoneType ? data.Data.phoneType : 'home',
8628                                firstname: data.Data.name,
8629                                lastname: data.Data.surname,
8630                                phone: data.Data.phone,
8631                                prefer: data.Data.preferredContactMethod ? data.Data.preferredContactMethod : 'email',
8632                                captcha: '',
8633                                dealerId: dataDealer.dealerID,
8634                                dealerName: dataDealer.dealerName,
8635                                purchasedelay: null,
8636                                currentmake: null,
8637                                contextDealerSiteId: forceDealerId
8638                            }
8639                        };
8640                        if (dataUser.suburb && dataUser.postCode) {
8641                            $scope.ffc.form.data.postcodes.push(
8642                                {
8643                                    text: dataUser.suburb + " " + dataUser.postCode,
8644                                    data: {
8645                                        postCode: dataUser.postCode,
8646                                        suburb: dataUser.suburb
8647                                    }
8648                                });
8649                        }
8650                    }, function failure(data) {
8651                        $scope.ffc.form = {
8652                            submitting: false,
8653                            completed: false,
8654                            errors: false,
8655                            dealers: null,
8656                            data: {
8657                                postcodes: [],
8658                                carmodel: null,
8659                                grade: null,
8660                                subscribe: false,
8661                                postcode: "",
8662                                email: null,
8663                                phonetype: 'home',
8664                                firstname: null,
8665                                lastname: null,
8666                                phone: null,
8667                                prefer: 'email',
8668                                captcha: '',
8669                                dealerId: null,
8670                                dealerName: null,
8671                                purchasedelay: null,
8672                                currentmake: null
8673                            }
8674                        };
8675                    }).finally(function () {
8676                        if ($scope.ffc.form.data.carmodel) {
8677                            $rootScope.$broadcast('field-car-model-engine-variant-update-selected');
8678                        }
8679
8680                        //Main Site
8681                        if ($scope.ffc.form.data.postcodes && $scope.ffc.form.data.postcodes.length > 0 && !isDealerSite) {
8682                            //Do nothing, Pass empty dealer ID
8683                            $scope.searchNow();
8684                        }
8685
8686                        //Dealer Site Multi Branch
8687                        if (isDealerSite && isMultiBranchDealer) {
8688                            $scope.ffc.form.data.dealerId = null;
8689                            $scope.ffc.form.data.dealerName = null;
8690                            $scope.loadBranches(forceDealerId);
8691                        }
8692
8693                        //Dealer Site NOT Multi Branch
8694                        if (isDealerSite && !isMultiBranchDealer) {
8695                            $scope.ffc.form.data.dealerId = dealerCompositeID;
8696                            $scope.ffc.form.data.dealerName = null;
8697                        }
8698
8699                    });
8700                }
8701
8702                $scope.deselectDealer = function () {
8703                    $scope.ffc.form.data.dealerName = null;
8704                }
8705
8706                $scope.selectDealer = function (dealerInputId) {
8707                    var scrollPos = $(document).scrollTop();
8708                    var maxPos = $('#' + dealerInputId).parents('.lx-form__find-dealer-js').offset().top;
8709                    if (scrollPos > maxPos) {
8710                        TweenLite.to(window, 0.5, { scrollTo: { y: maxPos }, ease: Power2.easeOut });
8711                    }
8712                    try {
8713                        var code = dealerInputId.replace('option-', '');
8714                        $scope.ffc.form.data.dealerName = $scope.ffc.form.dealers.find(function (dealer) { return dealer.dealerCode == code; }).dealerName;
8715                    } catch (ex) {
8716                        (console.error || console.log)(ex);
8717                    }
8718                }
8719
8720                $scope.loadBranches = function (dealerID) {
8721                    $scope.ffc.form.dealersLoading = true;
8722                    DealersService.setParam('dealerID', dealerID);
8723                    DealersService.setParam('type', 'Sales');
8724                    DealersService.GetDealerBranches();
8725                }
8726
8727                $scope.dealerSearch = function ($tag) {
8728                    $scope.ffc.form.data.postcodes = [];
8729                    $scope.ffc.form.data.postcodes[0] = $tag;
8730                    $scope.searchNow();
8731                }
8732
8733                $scope.$on('dealer-search-results-received', function (event, data) {
8734                    $scope.ffc.form.dealersLoading = false;
8735                    $scope.ffc.form.dealers = data.Data;
8736                    $scope.checkDealersLoaded();
8737                });
8738
8739                //Do a sanity check to see if the bound dealers match the forceDealer if not clear the force dealer
8740                $scope.checkDealersLoaded = function () {
8741                    var result = $scope.ffc.form.dealers.find(function (dealer) {
8742                        return dealer.dealerCode == $scope.ffc.form.data.dealerId;
8743                    });
8744                    if (!result) {
8745                        $scope.ffc.form.data.dealerId = null;
8746                    }
8747
8748                    if ($scope.ffc.form.dealers.length == 1)
8749                        $scope.ffc.form.data.dealerId = $scope.ffc.form.dealers[0].dealerCode;
8750                }
8751
8752
8753                $scope.searchNow = function () {
8754                    $scope.ffc.form.dealersLoading = true;
8755                    $rootScope.$broadcast('visitor-details-change-autocomplete', $scope.ffc.form.data.postcodes[0].data);
8756                    DealersService.setParam('postCode', $scope.ffc.form.data.postcodes[0].data.postCode); //Dummy Implementation needs to be changed
8757                    DealersService.setParam('suburb', $scope.ffc.form.data.postcodes[0].data.suburb); //Dummy Implementation needs to be changed
8758                    DealersService.setParam('type', 'Sales');
8759                    DealersService.GetResults();
8760
8761                }
8762
8763                $scope.getData = function () {
8764                    var utmParameters = UserPreferencesService.getUtmParameters();
8765                    var postcode = null;
8766                    if ($scope.ffc.form.data.postcodes.length > 0 && $scope.ffc.form.data.postcodes[0].text) {
8767                        postcode = parseInt($scope.ffc.form.data.postcodes[0].text.split(' ').reverse()[0]);
8768                    }
8769
8770                    var result = {
8771                        Model: $scope.ffc.form.data.carmodel,
8772                        EngineVariant: $scope.ffc.form.data.enginevariant,
8773                        DealerId: $scope.ffc.form.data.dealerId,
8774                        ContextDealerSiteId: $scope.ffc.form.data.contextDealerSiteId,
8775                        FirstName: $scope.ffc.form.data.firstname,
8776                        LastName: $scope.ffc.form.data.lastname,
8777                        Email: $scope.ffc.form.data.email,
8778                        Phone: $scope.ffc.form.data.phone,
8779                        PreferredContactMethod: $scope.ffc.form.data.prefer,
8780                        PlanToPurchase: $scope.ffc.form.data.purchasedelay,
8781                        VehicleTypeOwned: $scope.ffc.form.data.currentmake,
8782                        Captcha: $scope.ffc.form.data.captcha,
8783                        Subscribe: $scope.ffc.form.data.subscribe,
8784                        UtmSource: utmParameters.source,
8785                        UtmMedium: utmParameters.medium,
8786                        UtmCampaign: utmParameters.campaign,
8787                        UtmContent: utmParameters.content,
8788                        Postcode: postcode,
8789                        DealerName: $scope.ffc.form.data.dealerName
8790                    };
8791
8792                    return result;
8793                };
8794
8795                $rootScope.$on('vehicle-preference-change', function (event, data) {
8796                    $scope.ffc.form.data.carmodel = data.model;
8797                    $scope.ffc.form.data.enginevariant = data.engineVariant;
8798                });
8799
8800                $rootScope.$on('submit-test-drive-form', function (event, data) {
8801                    var formData = $scope.getData();
8802
8803                    EloquaService.attachEloquaFields(formData);
8804
8805                    $scope.ffc.form.submitting = true;
8806                    $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.TestDrive, [Lexus.ID.Site]), data: formData })
8807                        .then(function success(response) {
8808                            if (response.data.Status == "Success") {
8809                                $scope.submissionPass(response.data);
8810                            }
8811                            else {
8812                                $scope.submissionFail(response.data);
8813                            }
8814                        }, function error(response) {
8815                            $scope.submissionFail(response.data);
8816                        })
8817                        .finally(function () {
8818                            $rootScope.$broadcast('form-result');
8819                        });
8820                });
8821
8822
8823                $scope.submissionPass = function (data) {
8824                    // this callback will be called asynchronously
8825                    // when the response is available
8826                    $scope.ffc.form.completed = true;
8827                    $scope.ffc.form.failed = false;
8828                    $scope.ffc.form.submitting = false;
8829                    var historyState = "TestDriveForm=Pass";
8830                    $location.state(historyState);
8831                    FormsService.ShowResult($scope.successContent[0]);
8832                    $rootScope.$broadcast('gtm-form-success', 'Test Drive');
8833                    $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
8834                };
8835                $scope.submissionFail = function (data) {
8836                    // called asynchronously if an error occurs
8837                    // or server returns response with an error status.
8838                    $scope.ffc.form.failed = true;
8839                    $scope.ffc.form.errors = data.Data;
8840                    $scope.ffc.form.submitting = false;
8841                    $scope.ffc.form.completed = true;
8842                    var historyState = "TestDriveForm=Fail";
8843                    $location.state(historyState);
8844                    FormsService.ShowResult($scope.failureContent[0]);
8845                    console.error(data.ErrorMessage);
8846                };
8847
8848            }
8849        }
8850]);
8851
8852    
8853
8854;
8855"use strict"
8856/* Directives */
8857
8858angular.module('Lexus.Directives')
8859.directive('lxFormsUpdateDetail', ['$window', '$timeout', '$filter', '$http', '$location', function ($window, $timeout, $filter, $http, $location) {
8860    formUpdateDetailController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location', '$rootScope', 'vcRecaptchaService', 'API', 'FormsService', 'UserPreferencesService'];
8861    return {
8862        scope: true,
8863        restrict: 'A',
8864        controller: formUpdateDetailController,
8865        controllerAs: 'ffc',
8866        bindToController: true,
8867        link: function (scope, element, attrs, filter, http) {
8868            scope.successContent = element.find('.lx-form-success');
8869            scope.failureContent = element.find('.lx-form-failure');
8870        }
8871    };
8872
8873    function formUpdateDetailController($scope, $http, $filter, $timeout, $window, $interval, $location, $rootScope, vcRecaptchaService, API, FormsService, UserPreferencesService) {
8874        var fcu = this;
8875
8876        $scope.init = function (optionValue) {
8877            $scope.ffc.form = {
8878                submitting: false,
8879                completed: false,
8880                errors: false,
8881                data: {
8882                    subscribe: false,
8883                    firstname: '',
8884                    lastname: '',
8885                    address: '',
8886                    postcodes: [],
8887                    email: '',
8888                    phonetype: '',
8889                    updatetype: '',
8890                    phone: '',
8891                    prefer: 'email',
8892                    captcha: '',
8893                    vin: '',
8894                    registration: '',
8895                    requesttype: ''
8896                }
8897            };
8898            $scope.ffc.form.data.updatetype = optionValue;
8899        }
8900
8901        $rootScope.$on('submit-update-detail-form', function () {
8902            var formData = $scope.getData();
8903
8904            $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.UpdateDetails, [Lexus.ID.Site]), data: formData })
8905                .then(function success(response) {
8906                    if (response.data.Status == "Success") {
8907                        $scope.submissionPass(response.data);
8908                    }
8909                    else {
8910                        $scope.submissionFail(response.data);
8911                    }
8912                }, function error(response) {
8913                    $scope.submissionFail(response.data);
8914                })
8915                .finally(function () {
8916                    $rootScope.$broadcast('form-result');
8917                });
8918        });
8919
8920        $rootScope.$on('vin-change', function (event, data) {
8921            if (!$scope.ffc.form.data.vin) {
8922                $rootScope.$broadcast('vin-lookup-success');
8923                return;
8924            }
8925            var formData = $scope.getData();
8926
8927            $scope.ffc.form.submitting = true;
8928            API.post($filter('formatArgs')(Lexus.API.Lookup.VINLookupTakata, [Lexus.ID.Site]), $scope.getVinData())
8929                .then(function success(response) {
8930                    if (response.Status == "Success") {
8931                        $rootScope.$broadcast('vin-lookup-success');
8932                    }
8933                    else {
8934                        $rootScope.$broadcast('vin-lookup-failed');
8935                    }
8936                }, function error(response) {
8937                    $rootScope.$broadcast('vin-lookup-failed');
8938                })
8939        });
8940
8941        $scope.getData = function () {
8942            var visitor = UserPreferencesService.splitAutoCompleteResult($scope.ffc.form.data.postcodes[0].text);
8943            var result = {
8944                UpdateType: $scope.ffc.form.data.updatetype,
8945                FirstName: $scope.ffc.form.data.firstname,
8946                LastName: $scope.ffc.form.data.lastname,
8947                Email: $scope.ffc.form.data.email,
8948                Phone: $scope.ffc.form.data.phone,
8949                StreetAddress: $scope.ffc.form.data.address,
8950                Postcode: visitor.postCode,
8951                PhoneType: $scope.ffc.form.data.phonetype,
8952                PreferredContactMethod: $scope.ffc.form.data.prefer,
8953                Captcha: $scope.ffc.form.data.captcha,
8954                Vin: $scope.ffc.form.data.vin,
8955                Registration: $scope.ffc.form.data.registration,
8956                StateRegistered: $scope.ffc.form.data.requesttype,
8957                Subscribe: $scope.ffc.form.data.subscribe
8958            };
8959            return result;
8960
8961        };
8962
8963        $scope.getVinData = function () {
8964            var result = {
8965                Vin: $scope.ffc.form.data.vin
8966            };
8967            return result;
8968
8969        };
8970
8971        $scope.submissionPass = function (data) {
8972            $scope.ffc.form.completed = true;
8973            $scope.ffc.form.failed = false;
8974            $scope.ffc.form.submitting = false;
8975            var historyState = "UpdateDetails=Pass";
8976            $location.state(historyState);
8977            FormsService.ShowResult($scope.successContent[0]);
8978            $rootScope.$broadcast('gtm-form-success', 'Update Details');
8979            $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
8980        };
8981
8982        $scope.submissionFail = function (data) {
8983            $scope.ffc.form.failed = true;
8984            $scope.ffc.form.errors = data.Data;
8985            $scope.ffc.form.submitting = false;
8986            $scope.ffc.form.completed = true;
8987            var historyState = "UpdateDetails=Fail";
8988            $location.state(historyState);
8989            FormsService.ShowResult($scope.failureContent[0]);
8990            console.error(data.ErrorMessage);
8991        };
8992    }
8993
8994}]);
8995;
8996"use strict";
8997angular.module('Lexus.Directives')
8998.directive('lxGoogleMap', ['$window', '$timeout', '$filter', '$http', function ($window, $timeout, $filter, $http) {
8999    googleMapController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval'];
9000    return {
9001        scope: true,
9002        restrict: 'A',
9003        controller: googleMapController,
9004        controllerAs: 'gm',
9005        bindToController: true,
9006        link: function (scope, element, attrs, filter, http) {
9007
9008            var mapStyles = [{"featureType":"all","elementType":"geometry.fill","stylers":[{"color":"#d7d3c6"},{"saturation":"0"}]},{"featureType":"administrative","elementType":"all","stylers":[{"color":"#f0eee7"}]},{"featureType":"administrative","elementType":"geometry","stylers":[{"color":"#d7d3c6"}]},{"featureType":"administrative","elementType":"labels.text.fill","stylers":[{"color":"#232635"}]},{"featureType":"administrative","elementType":"labels.icon","stylers":[{"color":"#ca4b19"}]},{"featureType":"administrative.country","elementType":"all","stylers":[{"color":"#d7d3c6"}]},{"featureType":"landscape","elementType":"all","stylers":[{"color":"#d7d3c6"}]},{"featureType":"landscape.man_made","elementType":"geometry.fill","stylers":[{"color":"#d7d3c6"}]},{"featureType":"landscape.man_made","elementType":"labels.text.fill","stylers":[{"color":"#313649"}]},{"featureType":"landscape.natural.landcover","elementType":"geometry.fill","stylers":[{"color":"#d7d3c6"}]},{"featureType":"landscape.natural.landcover","elementType":"labels.text.fill","stylers":[{"color":"#313649"}]},{"featureType":"landscape.natural.terrain","elementType":"geometry.fill","stylers":[{"color":"#d7d3c6"}]},{"featureType":"poi","elementType":"all","stylers":[{"visibility":"off"},{"color":"#ca4b19"}]},{"featureType":"road","elementType":"all","stylers":[{"saturation":-100},{"lightness":45}]},{"featureType":"road","elementType":"labels.text.fill","stylers":[{"color":"#ffffff"}]},{"featureType":"road","elementType":"labels.text.stroke","stylers":[{"color":"#f0eee7"}]},{"featureType":"road.highway","elementType":"all","stylers":[{"visibility":"simplified"}]},{"featureType":"road.highway","elementType":"geometry.fill","stylers":[{"color":"#f0eee7"}]},{"featureType":"road.highway","elementType":"labels.text.fill","stylers":[{"color":"#313649"}]},{"featureType":"road.highway","elementType":"labels.text.stroke","stylers":[{"color":"#f0eee7"}]},{"featureType":"road.arterial","elementType":"geometry.fill","stylers":[{"color":"#f0eee7"}]},{"featureType":"road.arterial","elementType":"labels.text.fill","stylers":[{"color":"#313649"}]},{"featureType":"road.arterial","elementType":"labels.text.stroke","stylers":[{"color":"#f0eee7"}]},{"featureType":"road.arterial","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"road.local","elementType":"geometry.fill","stylers":[{"color":"#e4e1d7"}]},{"featureType":"road.local","elementType":"geometry.stroke","stylers":[{"visibility":"simplified"},{"color":"#f0eee7"}]},{"featureType":"road.local","elementType":"labels.text.fill","stylers":[{"color":"#313649"}]},{"featureType":"road.local","elementType":"labels.text.stroke","stylers":[{"color":"#f0eee7"}]},{"featureType":"transit","elementType":"all","stylers":[{"visibility":"off"},{"color":"#beb4b2"}]},{"featureType":"transit","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"transit.station.rail","elementType":"all","stylers":[{"visibility":"off"}]},{"featureType":"water","elementType":"all","stylers":[{"color":"#313649"},{"visibility":"on"}]}];
9009
9010            if (inViewport(element[0])) {
9011                loadMap(element[0], attrs);
9012                scope.loaded = true;
9013            }
9014            else {
9015                scope.loaded = false;
9016                var runEvent = true;
9017                angular.element($window).bind("scroll", function () {
9018                    if (runEvent) {
9019                        runEvent = false;
9020                        $timeout(function () {
9021                            if (!scope.loaded && inViewport(element[0])) {
9022                                loadMap(element[0], attrs);
9023                                scope.loaded = true;
9024                            }
9025                            runEvent = true;
9026                        }, 2000);
9027                    }
9028                });
9029            }
9030
9031            // dealerStyles
9032            function loadMap(el, att) {
9033                var myLatLng = { lat: Number(att.lat), lng: Number(att.lng) };
9034                var map = new google.maps.Map(el, {
9035                    center: myLatLng,
9036                    zoom: 13
9037                });
9038                map.setOptions({ styles: mapStyles,
9039                    zoomControl: true,
9040                    mapTypeControl: false,
9041                    scaleControl: false,
9042                    streetViewControl: true,
9043                    rotateControl: false,
9044                    fullscreenControl: false
9045                });
9046                google.maps.event.trigger(map, 'resize');
9047                var icon;
9048                if (att.iconAnchorX && att.icon) {
9049                    icon = {
9050                        url: att.icon,
9051                        size: new google.maps.Size(att.iconWidth, att.iconHeight),
9052                        origin: new google.maps.Point(att.iconOriginX, att.iconOriginY),
9053                        anchor: new google.maps.Point(att.iconAnchorX, att.iconAnchorY)
9054                    };
9055                } else if (att.icon) {
9056                    icon = att.icon;
9057                } else {
9058                    icon = '';
9059                }
9060                var marker = new google.maps.Marker({
9061                    position: myLatLng,
9062                    map: map,
9063                    title: att.dealername,
9064                    icon: icon
9065                });
9066            }
9067
9068            function inViewport(el) {
9069                var r, html;
9070                if (!el || 1 !== el.nodeType) { return false; }
9071                html = document.documentElement;
9072                r = el.getBoundingClientRect();
9073
9074                return (!!r
9075                  && r.bottom >= 0
9076                  && r.right >= 0
9077                  && r.top <= html.clientHeight
9078                  && r.left <= html.clientWidth
9079                );
9080            }
9081        }
9082    }
9083
9084    function googleMapController($scope, $http, $filter, $timeout, $window, $interval) {
9085        var gm = this;
9086    }
9087}]);
9088
9089;
9090"use strict";
9091
9092angular.module('Lexus.Directives')
9093.directive('buttonClick', [function () {
9094    return function (scope, element, attrs) {
9095        $(element).click(function (event) {
9096            event.preventDefault();
9097        });
9098    };
9099}])
9100.directive('runOnBlur', [function () {
9101    return function (scope, element, attrs) {
9102        element.blur(function () {
9103            if (angular.isDefined(attrs.runOnBlur)) {
9104                scope.$apply(function () {
9105                    scope.$eval(attrs.runOnBlur);
9106                });
9107            }
9108        });
9109    };
9110}])
9111.directive('onFinishRender', ['$timeout', function ($timeout) {
9112    return {
9113        restrict: 'A',
9114        link: function (scope, element, attr) {
9115            if (scope.$last === true) {
9116                $timeout(function () {
9117                    scope.$emit('ngRepeatFinished');
9118                });
9119            }
9120        }
9121    }
9122}])
9123.directive('enterPress', [function () {
9124    return function (scope, element, attrs) {
9125        element.bind('keydown keypress', function (event) {
9126            if (event.which === 13) {
9127                scope.$apply(function () {
9128                    scope.$eval(attrs.enterPress);
9129                });
9130
9131                event.preventDefault();
9132            }
9133        });
9134    };
9135}])
9136.directive('backImg', function(){
9137    return function(scope, element, attrs){
9138        var url = attrs.backImg;
9139        element.css('background-image', 'url(' + url +')');
9140    };
9141})
9142.directive('forceFocusOnClick', [function () {
9143    // This directive forces focus on devices that don't support focus on non input/anchor elements (ipads + other ios devices)
9144    return function (scope, element, attrs) {
9145        element.bind('click', function (event) {
9146            if (this != document.activeElement){
9147                event.stopPropagation();
9148                this.focus();
9149            }
9150        });
9151    };
9152}])
9153.directive('lxSmallLoader', [function () {
9154
9155    return {
9156        scope: {
9157            showLoader: '=',
9158            transition: '@'
9159        },
9160        restrict: 'AE',
9161        transclude: true,
9162        template:
9163            '<div class="lx-small-loader" ' +
9164                'data-ng-class="{\'is-loading\': showLoader, \'lx-small-loader--with-transition\': transition}">' +
9165                '<div class="lx-small-loader__container" data-ng-class="{\'lx-small-loader__transitionable\': transition}" data-ng-show="showLoader">' +
9166                    new Array(12 + 1).join('<i class="lx-small-loader__circle">•</i>') +
9167                '</div>' +
9168                '<div data-ng-class="{\'lx-small-loader__transitionable\': transition}" data-ng-transclude data-ng-hide="showLoader"></div>' +
9169            '</div>'
9170    };
9171
9172}])
9173.directive('mouseDetected', ['$rootScope', 'MouseDetectionService', function ($rootScope, MouseDetectionService) {
9174    return {
9175        restrict: 'A',
9176        scope: {
9177            onMouseDetected: '&mouseDetected'
9178        },
9179        link: function(scope, elem, attrs) {
9180            $rootScope.$on('did-detect-mouse', function() {
9181                //console.log('about to call', scope.onMouseDetected);
9182                scope.onMouseDetected();
9183                scope.$apply();
9184            });
9185        }
9186    }
9187}])
9188.directive('scrollTopOnFocus', ['$window', function ($window) {
9189    return function (scope, element, attrs) {
9190        element.bind('click', function (event) {
9191            var scrollYPos = $(element[0]).offset().top;
9192            event.preventDefault();
9193            if ($window.innerWidth <= Lexus.Breakpoint.ExtraSmall) {
9194                TweenLite.to(window, 1, { scrollTo: { y: scrollYPos, x: 0 }, ease: Power4.easeOut });
9195            }
9196        });
9197    };
9198}])
9199.directive('scrollToIdOnClick', [function() {
9200    return function (scope, element, attrs) {
9201        element.bind('click', function () {
9202            var duration = attrs.scrollDuration != null ? attrs.scrollDuration : 2;
9203            var $element = $('#' + attrs.scrollTarget);
9204
9205            if (!$element.length) {
9206                return;
9207            }
9208
9209            TweenMax.to(window, duration, { scrollTo: { y: '#' + attrs.scrollTarget, offsetY: attrs.scrollOffset, ease: Power4.easeOut, autoKill: false } });
9210        });
9211    };
9212}]);
9213;
9214(function () {
9215    "use strict";
9216
9217    angular.module('Lexus.Directives')
9218        .directive('lxImageDisclaimer', ImageDisclaimerDirective);
9219
9220    function ImageDisclaimerDirective() {
9221        return {
9222            scope: {
9223                'cssClass': '@'
9224            },
9225            restrict: 'A',
9226            transclude: true,
9227            template: '<div class="{{cssClass}}" ' +
9228                        'ng-class="{\'is-open\': isMouse && isMouseInside || !isMouse && isToggled}" ' +
9229                        'mouse-detected="isMouse = true" ' +
9230                        'ng-click="isToggled = !isToggled" ' +
9231                        'ng-mouseenter="isMouseInside = true" ' +
9232                        'ng-mouseleave="isMouseInside = false"><ng-transclude></ng-transclude></div>'
9233        };
9234    }
9235}());;
9236"use strict";
9237/* Directives */
9238angular.module('Lexus.Directives')
9239.directive('lazyLoadImages', [function () { // Lazy load images.
9240    return {
9241        link: function (scope, element, attrs) {
9242            var _window = angular.element(window),
9243                bLazy;
9244
9245            function initImages() {
9246                var _images = element.find('.b-lazy');
9247
9248                angular.forEach(_images, function (value, key) {
9249                    var _image = angular.element(value),
9250                        src = _image.data('src'),
9251                        mw = _image.data('src-mw'),
9252                        q = _image.data('src-q'),
9253                        mwXxs = Math.round(Lexus.Breakpoint.ExtraExtraSmall * 1.5),
9254                        mwXs = Math.round(Lexus.Breakpoint.ExtraSmall * 1.5),
9255                        mwSm = Math.round(Lexus.Breakpoint.Small * 1.5),
9256                        mwMd = Math.round(Lexus.Breakpoint.Medium),
9257                        imgQuery = '';
9258
9259                    // Check for extra image settings.
9260                    if (angular.isDefined(mw)) {
9261                        imgQuery += 'mw=' + mw;
9262                        mwXs = mwSm = mwMd = mw;
9263                    }
9264
9265                    if (angular.isDefined(q)) {
9266                        if (angular.isDefined(mw)) {
9267                            imgQuery += '&';
9268                        }
9269
9270                        imgQuery += 'q=' + q;
9271                    }
9272
9273                    if (imgQuery) {
9274                        _image.attr('data-src', src + '?' + imgQuery);
9275                    }
9276
9277                    _image
9278                        .attr('data-src-xxs', src + '?mw=' + mwXxs + '&q=95')
9279                        .attr('data-src-xs', src + '?mw=' + mwXs + '&q=95')
9280                        .attr('data-src-sm', src + '?mw=' + mwSm + '&q=95')
9281                        .attr('data-src-md', src + '?mw=' + mwMd + '&q=95');
9282                });
9283            }
9284
9285            function initBLazy() {
9286                bLazy = new Blazy({
9287                    offset: 600,
9288                    breakpoints: [{
9289                        width: Lexus.Breakpoint.ExtraExtraSmall, // mobile
9290                        src: 'data-src-xxs'
9291                    }, {
9292                        width: Lexus.Breakpoint.ExtraSmall, // small tabet
9293                        src: 'data-src-xs'
9294                    }, {
9295                        width: Lexus.Breakpoint.Small, // tablet
9296                        src: 'data-src-sm'
9297                    }, {
9298                        width: Lexus.Breakpoint.Medium, // medium destkop
9299                        src: 'data-src-md'
9300                    }]
9301                });
9302            }
9303
9304            initImages();
9305
9306            initBLazy();
9307
9308            // Watch app for lazy load rescan.
9309            scope.$on('broadcast.lazyLoad', function (event, args) {
9310                initImages();
9311
9312                bLazy.revalidate();
9313            });
9314
9315            scope.$on('broadcast.lazyLoadRevalidate', function (event, args){
9316                bLazy.revalidate();
9317            });
9318
9319            // On resize.
9320            _window.on('resize', function () {
9321                bLazy.revalidate();
9322            });
9323        }
9324    };
9325}])
9326.directive('initLazyLoadImage', ['$rootScope', '$timeout', function ($rootScope, $timeout) { // Lazy load new images.
9327    return {
9328        link: function (scope, element, attrs) {
9329            element.on('click', function () {
9330
9331                // Lazy load new images.
9332                $timeout(function () {
9333                    $rootScope.$broadcast('broadcast.lazyLoad');
9334                }, 300);
9335            });
9336        }
9337    };
9338}]);
9339;
9340"use strict";
9341/* Directives */
9342angular.module('Lexus.Directives')
9343.directive('lxModal', [function () {
9344    modalController.$inject = ['$scope', '$timeout', '$sce', '$rootScope'];
9345    return {
9346        scope: true,
9347        restrict: 'A',
9348        controller: modalController,
9349        controllerAs: 'modal',
9350        bindToController: true,
9351        link: function (scope, element, attrs) {
9352        }
9353    };
9354    
9355    function modalController($scope,$timeout, $sce, $rootScope) {
9356        var modal = this;
9357
9358        modal = modalStateUpdate(modal, false);
9359        $scope.modalContent = '';
9360
9361        function modalStateUpdate(modal,set) {
9362          modal.modalIsOpen = set;
9363          return modal;
9364        }
9365
9366        $scope.modalClose = function () {
9367          modal.isClosing = 'is-closing';
9368          modal = modalStateUpdate(modal,false);
9369          $timeout(function() { $scope.modalContent = '' }, 250);
9370        }
9371
9372        $scope.modalOpen = function(callback) {
9373          modal = modalStateUpdate(modal,true);
9374          if (callback) {
9375            callback(event);
9376          }
9377        }
9378
9379        /* Event Args:
9380         * arg : {
9381         *   content : 'html-content'
9382         *   [variant : Lexus.ModalVariant.[Content|Spinner]],
9383         *   [closeOnEvent : 'event-name-to-listen-for'],
9384         *   [wrapper : ['html-before','html-after']],
9385         * }
9386         */
9387        $rootScope.$on('open-modal-window', function (events, arg) {
9388
9389            modal.variantClass = arg.variant;
9390            modal.closeOnEvent = (arg.variant == Lexus.ModalVariant.Spinner);
9391
9392            if (arg.wrapper) {
9393                arg.content = addModalWrapper(arg.wrapper, arg.content);
9394            }
9395            
9396            //caution: using trusted html, so be careful putting user generated content in the modal
9397            $scope.modalContent = $sce.trustAsHtml(arg.content);
9398            $scope.modalOpen();
9399        });
9400
9401        $rootScope.$on('close-modal-window', function (events, arg) {
9402            $scope.modalClose();
9403        });
9404
9405        function addModalWrapper(wrapper, content) {
9406            return content = wrapper[0]
9407              + content + wrapper[1];
9408        }
9409
9410        function getScrollbarWidth() {
9411            var outer = document.createElement("div"); outer.style.visibility = "hidden"; outer.style.width = "100px"; outer.style.msOverflowStyle = "scrollbar"; // needed for WinJS apps
9412
9413            document.body.appendChild(outer);
9414
9415            var widthNoScroll = outer.offsetWidth;
9416            // force scrollbars
9417            outer.style.overflow = "scroll";
9418
9419            // add innerdiv
9420            var inner = document.createElement("div");
9421            inner.style.width = "100%";
9422            outer.appendChild(inner);
9423
9424            var widthWithScroll = inner.offsetWidth;
9425
9426            // remove divs
9427            outer.parentNode.removeChild(outer);
9428
9429            var scrollBarWidth = widthNoScroll - widthWithScroll;
9430            document.styleSheets[0].insertRule(".is-locked { padding-right:" + scrollBarWidth + "px;}", document.styleSheets[0].cssRules.length);
9431        }
9432
9433        if (window.innerWidth > 480) {
9434            getScrollbarWidth();
9435        }
9436
9437    }
9438
9439}]);
9440;
9441"use strict";
9442
9443const attributes = {
9444    stepSpeed: "@animStepSpeed",
9445    minX: "@animMinX",
9446    maxX: "@animMaxX",
9447    minY: "@animMinY",
9448    maxY: "@animMaxY",
9449    case: "@animCase",
9450    enabledMobile: "@animEnabledMobile",
9451    triggerOffsetY: "@animTriggerOffsetY",
9452};
9453
9454const cases = {
9455    x: "<div data-du-parallax='true' x='controller.animatorFunc'><ng-transclude/></div>",
9456    y: "<div data-du-parallax='true' y='controller.animatorFunc'><ng-transclude/></div>",
9457    fade: "<div data-du-parallax='true' custom='controller.scrollFade'><ng-transclude/></div>",
9458};
9459
9460const checkIsAnimated = (window) => (attributes) => {
9461    const isMobile = window.innerWidth < Lexus.Breakpoint.Small;
9462    return !isMobile || (isMobile && attributes.animEnabledMobile === "1");
9463};
9464
9465const animations = {
9466    x: (controller) =>
9467        controller.initXY(+controller.stepSpeed, +controller.maxX, +controller.minX, +controller.triggerOffsetY),
9468    y: (controller) =>
9469        controller.initXY(+controller.stepSpeed, +controller.maxY, +controller.minY, +controller.triggerOffsetY),
9470    fade: (controller) => controller.initScrollFade(+controller.stepSpeed, +controller.triggerOffsetY),
9471};
9472
9473/* Directives */
9474angular.module("Lexus.Directives").directive("lxParallax", [
9475    "$window",
9476    "parallaxHelper",
9477    function ($window, parallaxHelper) {
9478        const checkIsAnimatedWithWindow = checkIsAnimated($window);
9479        const windowHeight = $window.innerHeight;
9480
9481        return {
9482            scope: attributes,
9483            restrict: "A",
9484            controller: parallaxController,
9485            controllerAs: "controller",
9486            bindToController: true,
9487            transclude: true,
9488            template: (_, attrs) => (checkIsAnimatedWithWindow(attrs) ? cases[attrs.animCase] : "<ng-transclude/>"),
9489            link: function ({ controller }, _, attrs) {
9490                if (checkIsAnimatedWithWindow(attrs)) {
9491                    animations[controller.case]?.(controller);
9492                }
9493            },
9494        };
9495
9496        function parallaxController() {
9497            this.initXY = (stepSpeed, max, min, triggerOffsetY) => {
9498                this.animatorFunc = parallaxHelper.createAnimator(stepSpeed, max, min, triggerOffsetY);
9499            };
9500
9501            this.initScrollFade = (stepSpeed, triggerOffsetY) => {
9502                const factor = windowHeight * stepSpeed;
9503                const offset = windowHeight * triggerOffsetY;
9504
9505                this.scrollFade = ({ elemY }) => {
9506                    const opacity = (elemY + offset) / factor;
9507                    const correctedOpacity = Math.max(0, Math.min(1, opacity));
9508                    return {
9509                        opacity: correctedOpacity,
9510                    };
9511                };
9512            };
9513        }
9514    },
9515]);
9516;
9517(function () {
9518    "use strict";
9519    /* Directives */
9520    angular.module('Lexus.Directives')
9521    .directive('lxSearchResults', ['$window', '$timeout', '$filter', '$http', '$location', function ($window, $timeout, $filter, $http, $location) {
9522        searchResultsController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location'];
9523        return {
9524            scope: true,
9525            restrict: 'A',
9526            controller: searchResultsController,
9527            controllerAs: 'src',
9528            bindToController: true,
9529            link: function (scope, element, attrs, filter, http) {
9530            }
9531        };
9532
9533        function searchResultsController($scope, $http, $filter, $timeout, $window, $interval, $location) {
9534            var src = this;
9535            $scope.init = function () {
9536                $scope.src = {
9537                    searchTerm: $location.search().q
9538                };
9539            }
9540
9541            $scope.runSearch = function () {
9542                $window.location.href = Lexus.URI.SearchResultsPage + "?q=" + $scope.src.searchTerm;
9543            }
9544        }
9545    }]);
9546}());
9547;
9548
9549"use strict";
9550/* Directives */
9551angular.module('Lexus.Directives')
9552.directive('lxMobileCarousel', [function () {
9553    mobileCarouselController.$inject = ['$scope'];
9554    return {
9555        scope: true,
9556        restrict: 'A',
9557        controller: mobileCarouselController,
9558        controllerAs: 'mc',
9559        bindToController: true,
9560        link: function (scope, element, attrs ) {
9561        }
9562    };
9563    
9564    function mobileCarouselController($scope) {
9565        var mc = this;
9566
9567        $scope.init= function(slide){
9568          mc.isShowing = 2;
9569          if(slide) {
9570            mc.isShowing = slide;
9571          }
9572
9573          mc.showingClass = ['is-showing-right','is-showing-left','is-showing-'];
9574          mc = classUpdate(mc);
9575        }
9576
9577
9578        function classUpdate(scope){
9579          scope.mainClass = mc.showingClass[2] + scope.isShowing;
9580          scope.firstSecClass = firstClassUpdate(scope.isShowing, scope.showingClass[0]);
9581          scope.secondSecClass = secondClassUpdate(scope.isShowing, scope.showingClass);
9582          scope.thirdSecClass = thirdClassUpdate(scope.isShowing, scope.showingClass[1]);
9583
9584          return scope;
9585        }
9586
9587        function firstClassUpdate(showing,classSet){
9588          var className = '';
9589          if(showing > 1) {
9590            className = classSet;
9591          }
9592          return className;
9593        };
9594
9595        function thirdClassUpdate(showing,classSet){
9596          var className = '';
9597          if(showing < 3) {
9598            className = classSet;
9599          }
9600          return className;
9601        };
9602
9603        function secondClassUpdate(showing,classList){
9604          var className = '';
9605          if(showing === 3) {
9606            className = classList[0];
9607          } else if(mc.isShowing === 1) {
9608            className = classList[1];
9609          }
9610          return className;
9611        };
9612
9613        $scope.prevSec= function(){
9614          if (mc.isShowing > 1) {
9615            mc.isShowing--;
9616          }
9617
9618          mc = classUpdate(mc);
9619        }
9620
9621        $scope.nextSec= function(){
9622          if (mc.isShowing < 3) {
9623            mc.isShowing++;
9624          }
9625
9626          mc = classUpdate(mc);
9627        }
9628    }
9629
9630}]);
9631;
9632"use strict";
9633/* Directives */
9634angular.module('Lexus.Directives')
9635.directive('lxStickToTop', ['$window', function ($window) {
9636    return {
9637        link: function ($scope, $element, $attrs) {
9638            var repaintState;
9639            var repaintStateOverrides;
9640            var restoreOverflowAnchorTimeout = null;
9641            var BUFFER_DIST = 50
9642
9643            function getElementHeightInclMargins($el) {
9644                var boundingRect = $el.get(0).getBoundingClientRect();
9645                var rawHeight = boundingRect.bottom - boundingRect.top;
9646                var marginTop = parseInt($el.css('margin-top').match(/([0-9]+(\.[0-9])?)/g)[0], 10);
9647                var marginBottom = parseInt($el.css('margin-bottom').match(/([0-9]+(\.[0-9])?)/g)[0], 10);
9648                return rawHeight + marginTop + marginBottom;
9649            }
9650
9651            function setPlaceholderVisibility(visible) {
9652                $placeholder.css({
9653                    'height': visible ? (getElementHeightInclMargins($element) + 'px') : '0',
9654                    'visibility': visible ? '' : 'hidden'
9655                });
9656                if ($pusher) {
9657                    $pusher.css({
9658                        'height': visible ? (repaintState.topBeforeFixed + 'px') : '0',
9659                        'visibility': visible ? '' : 'hidden'
9660                    });
9661                }
9662            }
9663
9664            function repaint(repaintState) {
9665                var $html = $('html');
9666                $html.css('overflow-anchor', 'none');
9667                $window.clearTimeout(restoreOverflowAnchorTimeout);
9668                if (repaintState.isFixed) {
9669                    // fix the element to the viewport, the same offset from the top. (this should stop it from actually moving anywhere)
9670                    // transform translate3d forces ipad to load position fixed on scroll
9671
9672                    $element.css({
9673                        position: 'fixed',
9674                        top: repaintState.topWhenFixed,
9675                        transform: 'translate3d(0,0,0)'
9676                    });
9677
9678                    $element.addClass($attrs.lxStickyHeaderClass || 'lx-sticky-header');
9679
9680                } else {
9681                    // put the nav back where it was
9682                    $element.css({
9683                        position: '',
9684                        top: 0,
9685                        transform: 'translate3d(0,0,0)'
9686                    });
9687                    $element.removeClass($attrs.lxStickyHeaderClass || 'lx-sticky-header');
9688                }
9689
9690                setPlaceholderVisibility(repaintState.isPlaceholderTopOutsideViewport || repaintState.isFixed, repaintState);
9691
9692                restoreOverflowAnchorTimeout = $window.setTimeout(function () {
9693                    $html.css('overflow-anchor', '');
9694                }, 100)
9695            }
9696
9697            function recalc() {
9698                var allPlaceholders = $('.lx-stick-to-top-placeholder').toArray();
9699                var previousPlaceholders = allPlaceholders.slice(0, allPlaceholders.indexOf($placeholder.get(0)));
9700                var sumOfPreviousPlaceholderHeights = 0;
9701                angular.forEach(previousPlaceholders, function (el, index) {
9702                    sumOfPreviousPlaceholderHeights += getElementHeightInclMargins($(el));
9703                });
9704
9705                //repaintState.isPlaceholderTopOutsideViewport = $placeholder.get(0).getBoundingClientRect().top < sumOfPreviousPlaceholderHeights;
9706                var placeHolderTop = $placeholder.get(0).getBoundingClientRect().top;
9707                var eleHeight = getElementHeightInclMargins($element);
9708                var distanceTravelled = eleHeight - BUFFER_DIST;
9709                
9710                if (!$element.hasClass($attrs.lxStickyHeaderClass)) {
9711                    repaintState.topBeforeFixed = distanceTravelled;
9712                }
9713
9714                if ($element.hasClass($attrs.lxStickyHeaderClass)) {
9715                    distanceTravelled = repaintState.topBeforeFixed;
9716                }
9717
9718                repaintState.isPlaceholderTopOutsideViewport = -placeHolderTop > distanceTravelled;
9719                repaintState.isFixed = repaintState.isPlaceholderTopOutsideViewport;
9720                
9721                repaintState.topWhenFixed = sumOfPreviousPlaceholderHeights;
9722
9723                if (repaintStateOverrides) {
9724                    $.extend(repaintState, repaintStateOverrides);
9725                }
9726
9727                repaint(repaintState);
9728            }
9729
9730            function setRepaintStateOverrides(newRepaintStateOverrides) {
9731                if (newRepaintStateOverrides === null || typeof newRepaintStateOverrides === 'undefined') {
9732                    repaintStateOverrides = undefined;
9733                } else {
9734                    repaintStateOverrides = newRepaintStateOverrides;
9735                }
9736                recalc();
9737            }
9738
9739            var exports = {
9740                setRepaintOverrides: setRepaintStateOverrides,
9741                clearRepaintOverrides: function () { setRepaintStateOverrides(null); }
9742            };
9743
9744            var $placeholder;
9745            var $pusher;
9746
9747            function init() {
9748                repaintState = {};
9749
9750                // publish API for availability to other components, e.g. secondary-nav
9751                $element.data('lxStickToTop', exports);
9752
9753                // put in a placeholder element to take up the space the initial element took up
9754                $placeholder = $('<div class="' + ($attrs.lxStickToTopPlaceholderClass || '') + ' lx-stick-to-top-placeholder"></div>');
9755                $element.before($placeholder);
9756                if (($attrs.lxUsePusher || true) == true) {
9757                    $pusher = $('<div class="lx-stick-to-top-pusher"></div>');
9758                    $element.after($pusher);
9759                }
9760
9761                var $window = $(window);
9762                $window.on('scroll', recalc);
9763                $window.on('resize', recalc);
9764                recalc();
9765            }
9766
9767            init();
9768
9769            $scope.$on('$destroy', function () {
9770                $placeholder.remove();
9771
9772                var $window = $(window);
9773                $window.off('scroll', recalc);
9774                $window.off('resize', recalc);
9775            });
9776        }
9777    }
9778}]);
9779;
9780
9781"use strict";
9782angular.module('Lexus.Directives')
9783  .directive('lxNumberAnimator', [function () {
9784      return {
9785          scope: {
9786              source: "<ngModel",
9787              roundingModel: "@round"
9788          },
9789          restrict: 'E',
9790          template: '{{animatedValue}}',
9791          link: function (scope, element, attrs) {
9792              scope.source = 0;
9793              scope.validRoundingModels = ["floor", "round", "ceiling"];
9794              init(scope, element, attrs);
9795          },
9796      };
9797                
9798      function init(scope, elem, attrs) {          
9799
9800          //set/validate rounding - default to floor
9801          scope.roundingModel = scope.roundingModel || "floor";
9802          if (scope.validRoundingModels.indexOf(scope.roundingModel) == -1) {
9803              var msg = "Invalid rounding model '" + scope.roundingModel + "' - value should be 'floor', 'round' or 'ceiling'";
9804              console.error ? console.error(msg) : console.log("ERROR: " + msg);
9805              scope.roundingModel = "floor";
9806          }
9807
9808          scope.animatedValue = 0;
9809          scope.interimValue = { value: 0 };
9810          scope.$watch('source', function (newVal) {
9811              if (typeof newVal === 'number') {
9812                  //scope.localValue = newVal;
9813                  TweenLite.to(scope.interimValue, 1.0, {
9814                      ease: Expo.easeOut,
9815                      value: newVal, onUpdate: function () {
9816                          var roundedValue;
9817                          if (scope.roundingModel === "floor") {
9818                              roundedValue = Math.floor(scope.interimValue.value);
9819                          } else if (scope.roundingModel === "ceiling") {
9820                              roundedValue = Math.ceil(scope.interimValue.value);
9821                          } else {
9822                              roundedValue = Math.round(scope.interimValue.value);
9823                          }
9824                         
9825                          scope.animatedValue = roundedValue.toLocaleString('en', { /*style: 'currency', currency: 'AUD',*/ useGrouping: true });
9826                          scope.$digest();
9827                      }
9828                  });
9829              }
9830        });
9831      }
9832
9833
9834  }]);
9835;
9836(function () {
9837
9838    "use strict";
9839    /* Directives */
9840    angular.module('Lexus.Directives')
9841    .directive('lxRangeDealerLookup', RangeDealerLookupDirective);
9842
9843    RangeDealerLookupDirective.$inject = ['UserPreferencesService', 'DealersService'];
9844
9845    function RangeDealerLookupDirective(userPreferencesService, dealersService){
9846        rangeDealerLookupController.$inject = ['$scope'];
9847        return {
9848            scope: true,
9849            restrict: 'A',
9850            controller: rangeDealerLookupController,
9851            controllerAs: 'rdl',
9852            bindToController: true,
9853            link: function (scope, element, attrs) {
9854                scope.rdl = {};
9855                scope.rdl.data = {};
9856
9857                scope.rdl.data.isDealerSite = (attrs.isDealerSite === 'true');
9858                scope.rdl.data.dealerID = attrs.dealerId;
9859                scope.rdl.data.dealerState = attrs.dealerState;
9860                scope.rdl.data.dealerToShow = false;
9861                scope.rdl.data.isMultiBranch = (attrs.dealerIsMultiBranch === 'true');
9862                scope.rdl.data.branchCode = attrs.dealerBranchCode;
9863                scope.rdl.data.serviceTypeToShow = attrs.serviceTypeToShow;
9864
9865                scope.init();
9866            }
9867        };
9868
9869        
9870
9871        function rangeDealerLookupController($scope) {
9872            var rdl = this;
9873
9874            $scope.init = function () {
9875                $scope.ds = {
9876                    loading: true
9877                };
9878
9879                var dealerPreference = userPreferencesService.getLexusDealer();
9880                $scope.rdl.data.hasPreferredDealer = (dealerPreference && dealerPreference.dealerID);
9881
9882                //Mainsite
9883                if (!$scope.rdl.data.isDealerSite && $scope.rdl.data.hasPreferredDealer) {
9884                    $scope.rdl.data.dealerState = dealerPreference.dealerState;
9885
9886                    var dealerDetails = dealersService.DeconstructCompositeID(dealerPreference.dealerID);
9887
9888                    $scope.rdl.data.dealerID = dealerDetails.dealerID;
9889                    $scope.rdl.data.dealerToShow = true;
9890                    dealersService.setParam('dealerID', $scope.rdl.data.dealerID);
9891                    dealersService.setParam('branchCode', $scope.rdl.data.branchCode || dealerDetails.branchCode);
9892                    dealersService.GetDealerBranch();
9893                }
9894                    //Dealer Site, ex Brisbane
9895                else if ($scope.rdl.data.isDealerSite && !$scope.rdl.data.isMultiBranch) {
9896                    dealersService.setParam('dealerID', $scope.rdl.data.dealerID);
9897                    dealersService.setParam('dealerState', $scope.rdl.data.dealerState);
9898                    dealersService.setParam('type', $scope.rdl.data.serviceTypeToShow);
9899                    $scope.rdl.data.dealerToShow = true;
9900
9901                    dealersService.GetDealerBranches();
9902                }
9903                    //All other Dealer Sites
9904                else if ($scope.rdl.data.isDealerSite && $scope.rdl.data.isMultiBranch) {
9905                    dealersService.setParam('dealerID', $scope.rdl.data.dealerID);
9906                    dealersService.setParam('type', $scope.rdl.data.serviceTypeToShow);
9907                    $scope.rdl.data.dealerToShow = true;
9908
9909                    dealersService.GetDealerBranches();
9910                }
9911            }
9912
9913            $scope.checkServiceType = function (result) {
9914                var serviceString = '';
9915                if (typeof result == 'string') {
9916                    serviceString = result;
9917                }
9918                else if (typeof result == 'object' && result != null) {
9919                    serviceString = result.serviceType;
9920                }
9921
9922                console.log('$scope.checkServiceType: ', serviceString);
9923
9924                return serviceString.toLowerCase().match(/^service$/) ? true : false;
9925            }
9926
9927            //Plural
9928            $scope.$on('dealer-search-results-received', function (event, data) {
9929                var dealers = data.Data;
9930                $scope.ds.results = dealers;
9931                $scope.ds.showChangeDealer = !$scope.rdl.data.isDealerSite;
9932                $scope.ds.loading = false;
9933
9934            });
9935
9936            //Singular
9937            $scope.$on('dealer-search-result-received', function (event, data) {
9938                var dealers = [];
9939                dealers.push(data.Data);
9940                $scope.ds.results = dealers;
9941                $scope.ds.showChangeDealer = !$scope.rdl.data.isDealerSite;
9942                $scope.ds.loading = false;
9943
9944            });
9945        }
9946    }
9947
9948}());
9949
9950
9951;
9952(function (undefined) {
9953
9954    "use strict";
9955    /* Directives */
9956    angular.module('Lexus.Directives')
9957        .directive('lxRangeEngineVariant', RangeEngineVariantDirective);
9958
9959    RangeEngineVariantDirective.$inject = ['$location', '$rootScope', 'UserPreferencesService'];
9960
9961    function RangeEngineVariantDirective($location, $rootScope, UserPreferencesService) {
9962
9963        var engineVariants = null;
9964        var showSeats = false;
9965
9966        rangeEngineVariantController.$inject = ['$scope'];
9967        return {
9968            scope: true,
9969            restrict: 'A',
9970            controller: rangeEngineVariantController,
9971            controllerAs: 'revModal',
9972            link: link
9973        };
9974
9975        function selectVariant(variantName) {
9976            var selectedIndex = null;
9977            for (var index = 0; index < engineVariants.length; index++) {
9978                if (engineVariants[index].engineVariant === variantName) {
9979                    engineVariants[index].selected = true;
9980                    selectedIndex = index;
9981                    $rootScope.$broadcast('vehicle-preference-change', {
9982                        model: engineVariants[index].model,
9983                        engineVariant: engineVariants[index].engineVariant,
9984                        grade: engineVariants[index].grade,
9985                    });
9986                } else {
9987                    engineVariants[index].selected = false;
9988                }
9989            }
9990
9991            $rootScope.$broadcast('broadcast.lazyLoadRevalidate');
9992            return selectedIndex;
9993        }
9994
9995        function link(scope, element, attrs) {
9996            engineVariants = angular.fromJson(attrs.engineVariants);
9997            showSeats = attrs.showSeats == 'true';
9998
9999            for (var index = 0; index < engineVariants.length; index++) {
10000                engineVariants[index].selected = false;
10001            }
10002
10003            var dataVehicle = UserPreferencesService.getLexusVehicle($location.search());
10004
10005            if (dataVehicle) {
10006                var foundVariant = null;
10007                var variantIndex = null;
10008
10009                for (var index = 0; index < engineVariants.length; index++) {
10010                    if (engineVariants[index].engineVariant === dataVehicle.engineVariant) {
10011                        foundVariant = dataVehicle.engineVariant;
10012                        break;
10013                    }
10014                }
10015
10016                if (foundVariant) {
10017                    variantIndex = selectVariant(dataVehicle.engineVariant);
10018                } else {
10019                    variantIndex = selectVariant(engineVariants[0].engineVariant);
10020                }
10021            }
10022            else {
10023                variantIndex = selectVariant(engineVariants[0].engineVariant);
10024            }
10025
10026            if (variantIndex != null) {
10027                scope.setFilterKey(engineVariants[variantIndex].seats, engineVariants[variantIndex].engineVariant);
10028            }            
10029        }
10030
10031        function rangeEngineVariantController($scope) {
10032            var revModal = this;
10033            
10034            $scope.select = selectVariant;
10035
10036            $scope.isSelected = function(variantName) {
10037                for (var index = 0; index < engineVariants.length; index++) {
10038                    if (engineVariants[index].engineVariant === variantName) {
10039                        return engineVariants[index].selected;
10040                    }
10041                }
10042            }
10043
10044            $scope.setFilterKey = function (seats, variantName) {
10045                $scope.filterKey = seats;
10046                if (!$scope.isSelected(variantName)) {
10047                    $scope.select(variantName);
10048                }                
10049            }
10050
10051            $scope.displayTab = function (seats) {
10052                return seats == $scope.filterKey;
10053            }
10054        }
10055    }
10056}());
10057
10058;
10059"use strict";
10060angular.module('Lexus.Directives')
10061    .directive('lxLifestyleEngineVariant', function () {
10062        lxLifestyleEngineVariantController.$inject = ['$scope', '$attrs', '$filter', '$rootScope'];
10063        return {
10064            scope: true,
10065            restrict: 'A',
10066            controller: lxLifestyleEngineVariantController,
10067            controllerAs: 'lev',
10068            bindToController: true,
10069            link: function (scope, element, attrs, controller) {
10070                scope.lifestyleSlider = element.find('.lx-lifestyle-engine-variant__lifestyles');
10071                scope.itemWidth = scope.lifestyleSlider.find('.lx-lifestyle-engine-variant__lifestyle-image').first().width();
10072
10073                scope.lifestyleTabs = element.find('.lx-lifestyle-engine-variant__lifestyle-selector__inner');
10074                scope.tabContainerWidth = scope.lifestyleTabs.first().width();
10075            }
10076        }
10077
10078        function lxLifestyleEngineVariantController($scope, $attrs, $filter, $rootScope) {
10079            $scope.lifestyles = [];
10080            $scope.selectedLifestyleIndex = 0;
10081
10082            $scope.variants = [];
10083            $scope.selectedVariant = null;
10084            $scope.selectedVariantImage = null;
10085            $scope.selectedVariantDescription = null;
10086
10087            this.addLifeStyle = function (lifestyle) {
10088                $scope.lifestyles.push(lifestyle);
10089                if ($scope.selectedLifestyleIndex == 0) {
10090                    broadcastLifestyle($scope.lifestyles[$scope.selectedLifestyleIndex]);
10091                }
10092            }
10093
10094            this.setSelectedLifestyle = function (lifestyleIndex) {
10095                if (lifestyleIndex >= 0 && lifestyleIndex < $scope.lifestyles.length) {
10096                    scrollTabsToIndex(lifestyleIndex);
10097                    scrollToIndex(lifestyleIndex);
10098                    $scope.selectedLifestyleIndex = lifestyleIndex;
10099                    var lifestyle = $scope.lifestyles[$scope.selectedLifestyleIndex];
10100                    broadcastLifestyle(lifestyle);
10101                }
10102            }
10103
10104            this.addVariant = function (variant) {
10105                $scope.variants.push(variant);
10106                if ($scope.selectedVariant == null) {
10107                    setSelectedVariant(variant);
10108                }
10109            }
10110
10111            this.setSelectedVariant = function (variant) {
10112                var filtered = $filter("filter")($scope.variants, { name: variant });
10113                var item = filtered != null && filtered.length > 0 ? filtered[0] : null;
10114                if (item != null) {
10115                    setSelectedVariant(item);
10116                }
10117            }
10118
10119            this.prevLifestyle = function () {
10120                this.setSelectedLifestyle($scope.selectedLifestyleIndex - 1);
10121            }
10122
10123            this.nextLifestyle = function () {
10124                this.setSelectedLifestyle($scope.selectedLifestyleIndex + 1);
10125            }
10126
10127            $scope.$on("variant-selected", function (event, args) {
10128                var item = $scope.variants[args.selectedVariantIndex];
10129                if (item != null) {
10130                    setSelectedVariant(item);
10131                }
10132            });
10133
10134            var _window = angular.element(window);
10135
10136            _window.resize(function () {
10137                $scope.itemWidth = $scope.lifestyleSlider.find('.lx-lifestyle-engine-variant__lifestyle-image').first().width();
10138                $scope.tabContainerWidth = $scope.lifestyleTabs.first().width();
10139            });
10140
10141            function setSelectedVariant(variant) {
10142                $scope.selectedVariant = variant.name;
10143                $scope.selectedVariantDescription = variant.description;
10144                broadcastVehicle();
10145
10146                var _size = 1200;
10147                if (_window.width() <= Lexus.Breakpoint.ExtraExtraSmall) { _size = Lexus.Breakpoint.ExtraExtraSmall; }
10148                else if (_window.width() <= Lexus.Breakpoint.ExtraSmall) { _size = Lexus.Breakpoint.ExtraSmall; }
10149                else if (_window.width() <= Lexus.Breakpoint.Small) { _size = Lexus.Breakpoint.Small; }
10150
10151                var resolvedImage = variant.image + '?q=95&w=' + _size;
10152                $scope.selectedVariantImage = resolvedImage;
10153            }
10154
10155            function scrollToIndex(lifestyleIndex) {
10156                var left = $scope.itemWidth * lifestyleIndex;
10157                TweenLite.to($scope.lifestyleSlider, 1, { scrollTo: { x: (left) }, ease: $scope.easingFunction });
10158            }
10159
10160            function scrollTabsToIndex(lifestyleIndex) {
10161                var tabItems = $scope.lifestyleTabs.find('.lx-tabs__item');
10162                var left = 0;
10163                if (lifestyleIndex > 0) {
10164                    left = tabItems[lifestyleIndex].offsetLeft - ($scope.tabContainerWidth * Lexus.Tabs.CenterFactor);
10165                }
10166                TweenLite.to($scope.lifestyleTabs, 1, { scrollTo: { x: (left) }, ease: $scope.easingFunction });
10167            }
10168
10169            function broadcastVehicle() {
10170                $rootScope.$broadcast('vehicle-preference-change', {
10171                    model: 'UX',//get model
10172                    engineVariant: $scope.selectedVariant,
10173                });
10174            }
10175
10176            function broadcastLifestyle(lifestyle) {
10177                if (!lifestyle)
10178                    return;
10179
10180                $rootScope.$broadcast('personalization-change', {
10181                    lifestyle: {
10182                        name: lifestyle.name,
10183                        image: lifestyle.image,
10184                    },
10185                });
10186            }
10187        }
10188    })
10189    .directive('lxLifestyle', ['$window', '$timeout', '$filter', '$http', '$location', '$rootScope', function ($window, $timeout, $filter, $http, $location, $rootScope) {
10190        return {
10191            scope: true,
10192            restrict: 'A',
10193            require: '^lxLifestyleEngineVariant',
10194            link: function (scope, element, attrs, lxLifestyleEngineVariantController) {
10195                lxLifestyleEngineVariantController.addLifeStyle(attrs);
10196            }
10197        }
10198    }])
10199    .directive('lxVariant', ['$window', '$timeout', '$filter', '$http', '$location', '$rootScope', function ($window, $timeout, $filter, $http, $location, $rootScope) {
10200        return {
10201            scope: true,
10202            restrict: 'A',
10203            require: '^lxLifestyleEngineVariant',
10204            link: function (scope, element, attrs, lxLifestyleEngineVariantController) {
10205                lxLifestyleEngineVariantController.addVariant(attrs);
10206            }
10207        }
10208    }]);
10209;
10210"use strict";
10211/* Directives */
10212angular.module('Lexus.Directives')
10213.directive('lxRangeExploreBodyTypeSelector', function () {
10214    exploreRangeBodyTypeSelectorController.$inject = ['$scope', '$window', '$rootScope'];
10215    return {
10216        scope: true,
10217        restrict: 'A',
10218        controller: exploreRangeBodyTypeSelectorController,
10219        controllerAs: 'erbts',
10220        bindToController: true,
10221        link: function (scope, element, attrs) {
10222        }
10223    };
10224
10225    function exploreRangeBodyTypeSelectorController($scope, $window, $rootScope) {
10226        var erbts = this;
10227
10228        $scope.init = function () {
10229            $scope.erbts.bodyTypeSelectors = [];
10230            $scope.highlightBox(angular.element('.lx-explore-range__body-type-item:eq(0)'));
10231        }
10232
10233        $scope.addBodyType = function (index, bodyType) {
10234            $scope.erbts.bodyTypeSelectors[index] = {};
10235            $scope.erbts.bodyTypeSelectors[index].bodyType = bodyType
10236            $scope.erbts.bodyTypeSelectors[index].active = (index == 0 ? true : false);
10237        }
10238
10239        $scope.switchActiveBodyType = function ($event, index) {
10240            $scope.highlightBox(angular.element($event.currentTarget));
10241
10242            angular.forEach($scope.erbts.bodyTypeSelectors, function (value, key) {
10243                if (key == index) {
10244                    value.active = true;
10245                    $rootScope.$broadcast('filter-body-type', value.bodyType);
10246                }
10247                else {
10248                    value.active = false;
10249                }
10250            });
10251        }
10252
10253        $scope.highlightBox = function (target) {
10254            var myTarget = target;
10255            var left = myTarget.offset().left - myTarget.parent().offset().left;
10256
10257            angular.element('.lx-explore-range__body-type-box').css({ 'width': myTarget[0].clientWidth, 'transform': 'translate3d(' + left + 'px,0,0)' });
10258            angular.element('.lx-explore-range__body-type-box').css({ 'opacity': 1 });
10259        }
10260
10261        angular.element($window).bind('resize', function () {
10262            $scope.highlightBox(angular.element('.lx-explore-range__body-type-item.is-active'));
10263        });
10264
10265        $rootScope.$on('set-body-type', function (event, args) {
10266            angular.forEach($scope.erbts.bodyTypeSelectors, function (value, key) {
10267                if (value.bodyType == args) {
10268                    $scope.switchActiveBodyType(key);
10269                }
10270            });
10271
10272        });
10273
10274    }
10275
10276})
10277.directive('lxRangeExploreHero', ['$window', '$timeout', '$filter', '$http', '$location', '$rootScope', function ($window, $timeout, $filter, $http, $location, $rootScope) {
10278    exploreRangeController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location'];
10279    return {
10280        scope: true,
10281        restrict: 'A',
10282        controller: exploreRangeController,
10283        controllerAs: 'er',
10284        bindToController: true,
10285        link: function (scope, element, attrs, filter, http) {
10286        }
10287    };
10288
10289
10290    function exploreRangeController($scope, $http, $filter, $timeout, $window, $interval, $location) {
10291        var er = this,
10292            _window = angular.element(window),
10293            _currentSize = 0,
10294            _newSize = 0,
10295            _sizeFilter = '',
10296            _panelBkg = angular.element('.lx-explore-range__image-panel').attr('data-background');
10297
10298        $scope.resolvedImage = null;
10299
10300        function init() {
10301            updateBackgroundImage();
10302        }
10303
10304        $rootScope.$on('change-active-vehicle', function (event, args) {
10305            $scope.er.activeVehicle = args;
10306            updateBackgroundImage(args.imageSrc);
10307        });
10308
10309        function updateBackgroundImage(imageSrc) {
10310
10311            if (_window.width() <= Lexus.Breakpoint.ExtraExtraSmall) { _newSize = Lexus.Breakpoint.ExtraExtraSmall; }
10312            else if (_window.width() <= Lexus.Breakpoint.ExtraSmall) { _newSize = Lexus.Breakpoint.ExtraSmall; }
10313            else if (_window.width() <= Lexus.Breakpoint.Small) { _newSize = Lexus.Breakpoint.Small; }
10314            else { _newSize = Lexus.Breakpoint.Medium; }
10315
10316            if (_currentSize != _newSize) {
10317                _sizeFilter = '?q=95&w=' + _newSize;
10318                _currentSize = _newSize;
10319                if (_newSize <= Lexus.Breakpoint.Small) {
10320                    _sizeFilter = '';
10321                }
10322            }
10323
10324            if ($scope.resolvedImage != (imageSrc + _sizeFilter)) {
10325                if (_sizeFilter == '') {
10326                    $scope.resolvedimage = null;
10327                } else {
10328                    $scope.resolvedImage = imageSrc + _sizeFilter;
10329                }
10330                //console.log(' resolvedBackground: ', _sizeFilter, '\n imageSrc: ', imageSrc, '\n imageSrc + _sizeFilter: ',imageSrc + _sizeFilter, '\n $scope.resolvedImage: ', $scope.resolvedImage);
10331            }
10332
10333        }
10334
10335        _window.resize(function () {
10336            updateBackgroundImage($scope.er.activeVehicle.imageSrc);
10337        });
10338
10339
10340        $scope.init = function () {
10341            $scope.er = {
10342                activeVehicle: {
10343                }
10344            };
10345        }
10346
10347    }
10348}])
10349.directive('lxRangeExploreSlider', ['$window', '$timeout', '$filter', '$http', '$location', '$rootScope', function ($window, $timeout, $filter, $http, $location, $rootScope) {
10350    exploreRangeSliderController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location', '$rootScope'];
10351    return {
10352        scope: true,
10353        restrict: 'A',
10354        controller: exploreRangeSliderController,
10355        controllerAs: 'ers',
10356        bindToController: true,
10357        link: function (scope, element, attrs, filter, http) {
10358
10359            $rootScope.$on('calculate-widths', function (event, args) {
10360                scope.carouselSlider = element;
10361                scope.activeMarker = element.find('.lx-explore-range__marker-js');
10362                scope.$innerContainer = scope.carouselSlider.children().first('.lx-explore-range__scroll-container');
10363                scope.itemWidth = scope.$innerContainer.find('.lx-explore-range__subpanel').first().width();
10364
10365                scope.outerContainerWidth = scope.carouselSlider.width();
10366                scope.maxScrollX = scope.$innerContainer.width() - scope.outerContainerWidth;
10367
10368
10369                //use native swipe scrolling for touch devices, If its a laptop with touch and wide screen assume they are using K&M
10370                if (!Modernizr.touchevents || angular.element($window).width() >= Lexus.Breakpoint.Medium) {
10371                    scope.carouselSlider.mousemove(function (event) { scope.smoothScrollTo(event); });
10372                    scope.carouselSlider.mouseleave(function (event) { scope.smoothSnapToNearest(event); });
10373                }
10374
10375
10376                angular.forEach(scope.$innerContainer.children('.lx-explore-range__subpanel'), function (value, key) {
10377                    value.className = value.className;
10378                });
10379                //scope.smoothSnapToNearest(event);
10380            });
10381
10382            angular.element($window).bind('resize', function () {
10383                $rootScope.$broadcast('calculate-widths');
10384            });
10385        }
10386    }
10387
10388
10389    function exploreRangeSliderController($scope, $http, $filter, $timeout, $window, $interval, $location, $rootScope) {
10390        var ers = this;
10391
10392        $scope.init = function (overscrollPadding, markerRadius) {
10393            $scope.overscrollPadding = overscrollPadding;
10394            $scope.markerRadius = markerRadius;
10395            $scope.carouselTweenTime = 1;
10396            $scope.markerTweenTime = 1;
10397            $scope.easingFunction = Power2.easeOut;
10398            $scope.sliderItemCollection = [];
10399        }
10400
10401        $scope.smoothScrollToIndex = function (index) {
10402            var left = $scope.itemWidth * index;
10403            left = left + $scope.overscrollPadding;
10404            TweenLite.to($scope.carouselSlider, $scope.carouselTweenTime, { scrollTo: { x: (left) }, ease: $scope.easingFunction });
10405            $rootScope.$broadcast('pause-smooth-scroll-to', '');
10406        }
10407
10408        $rootScope.$on('pause-smooth-scroll-to', function (event, args) {
10409            $rootScope.pauseSmoothScrollTo = true;
10410            setTimeout(function () {
10411                $rootScope.pauseSmoothScrollTo = false;
10412            }, 1500);
10413        });
10414
10415        $scope.smoothScrollTo = function (event) {
10416            if($rootScope.pauseSmoothScrollTo === true){
10417                return;
10418            }
10419            var offsetX = $scope.carouselSlider.position().left;
10420            var posInPc = (event.clientX - offsetX) / $scope.outerContainerWidth;
10421            $scope.carouselPos = $scope.maxScrollX * posInPc;
10422            TweenLite.to($scope.carouselSlider, $scope.carouselTweenTime, { scrollTo: { x: ($scope.carouselPos) }, ease: $scope.easingFunction });
10423        }
10424
10425        $scope.smoothSnapToNearest = function (event) {
10426            var offset = $scope.overscrollPadding;
10427            var snapWidth = $scope.itemWidth;
10428
10429            var misalignment = ($scope.carouselPos % snapWidth) - offset;
10430            var previousSnapPoint = $scope.carouselPos - misalignment;
10431            var nextSnapPoint = $scope.carouselPos - misalignment + snapWidth;
10432
10433            if (misalignment === 0) {
10434                return;
10435            } else if (misalignment < (snapWidth / 2)) {
10436                TweenLite.to($scope.carouselSlider, $scope.carouselTweenTime, { scrollTo: { x: previousSnapPoint }, ease: $scope.easingFunction });
10437            } else {
10438                TweenLite.to($scope.carouselSlider, $scope.carouselTweenTime, { scrollTo: { x: nextSnapPoint }, ease: $scope.easingFunction });
10439            }
10440        }
10441
10442        $scope.setMarkerPosition = function (failCount) {
10443            var i = failCount || 0;
10444            if (!$scope.activeMarker && i < 10) {
10445                setTimeout(function () {
10446                    $scope.setMarkerPosition(i + 1);
10447                }, 100);
10448                return;
10449            }
10450            var newPosition = Math.round($scope.overscrollPadding + ($scope.carouselPos * $scope.itemWidth) + ($scope.itemWidth * 0.5) - $scope.markerRadius);
10451            $scope.activeMarker.css('left', newPosition + 'px');
10452            //why? $scope.smoothScrollToIndex($scope.carouselPos);
10453        }
10454
10455        $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
10456            $scope.sliderItemCollection[0].style = { "margin-left": $scope.overscrollPadding + "px" };
10457            $scope.sliderItemCollection[$scope.sliderItemCollection.length - 1].style = { "margin-right": $scope.overscrollPadding + "px" };
10458            $timeout(function () { $rootScope.$broadcast('calculate-widths') }, 100);
10459            $scope.setMarkerPosition();
10460        });
10461
10462        this.addSliderItem = function (data) {
10463            $scope.sliderItemCollection[data.index] = data;
10464            $scope.sliderItemCollection[data.index].disabled = false;
10465            $scope.sliderItemCollection[data.index].style = {};
10466            if (data.init) {
10467                $scope.carouselPos = parseInt(data.index);
10468                $scope.sliderItemCollection[data.index].active = true;
10469                $timeout(function () { $rootScope.$broadcast('change-active-vehicle', data) }, 100);
10470            }
10471            else {
10472                $scope.sliderItemCollection[data.index].active = false;
10473            }
10474        }
10475
10476        $scope.changeActiveVehicleClick = function (index) {
10477
10478            if ($scope.sliderItemCollection[index].disabled) {
10479                $rootScope.$broadcast('set-body-type', 'all');
10480            }
10481
10482            $scope.carouselPos = index;
10483            angular.forEach($scope.sliderItemCollection, function (value, key) {
10484                value.active = (key == index);
10485            });
10486            $scope.setMarkerPosition();
10487            $timeout(function () { $rootScope.$broadcast('change-active-vehicle', $scope.sliderItemCollection[index]) }, 100);
10488
10489
10490
10491        }
10492
10493
10494
10495        $rootScope.$on('filter-body-type', function (event, args) {
10496            var newActiveIndex = 0;
10497            var needtoApplyNewIndex = false;
10498            angular.forEach($scope.sliderItemCollection, function (value, key) {
10499                var isDisabled = !(args == 'all' || args == value.bodyType);
10500                value.disabled = isDisabled;
10501                if (isDisabled && value.active) {
10502                    value.active = false;
10503                    needtoApplyNewIndex = true;
10504                }
10505            });
10506            if (needtoApplyNewIndex) {
10507                var keepGoing = true;
10508                angular.forEach($scope.sliderItemCollection, function (value, key) {
10509                    if (keepGoing) {
10510                        if (!value.disabled) {
10511                            value.active = true;
10512                            $scope.smoothScrollToIndex(key);
10513                            keepGoing = false;
10514                            $timeout(function () {
10515                                $rootScope.$broadcast('change-active-vehicle', value);
10516                                $scope.changeActiveVehicleClick(value.index);
10517                            }, 100);
10518                        }
10519                    }
10520                });
10521            }
10522        });
10523
10524
10525    }
10526}])
10527.directive('lxRangeExploreSliderItem', ['$window', '$timeout', '$filter', '$http', '$location', '$rootScope', function ($window, $timeout, $filter, $http, $location, $rootScope) {
10528    return {
10529        scope: true,
10530        restrict: 'AE',
10531        require: '^lxRangeExploreSlider',
10532        link: function (scope, element, attrs, exploreRangeSliderController) {
10533            exploreRangeSliderController.addSliderItem(attrs);
10534        }
10535    }
10536
10537}]);
10538;
10539(function (undefined) {
10540    "use strict";
10541    /* Directives */
10542    angular.module('Lexus.Directives')
10543        .directive('lxRangeTechData', RangeTechDataDirective);
10544
10545    RangeTechDataDirective.$inject = ['$http', '$filter', '$timeout', '$interval',
10546        '$location', '$rootScope', 'UserPreferencesService', 'API', '$sce', '$log'];
10547
10548    function RangeTechDataDirective($http, $filter, $timeout, $interval, $location, $rootScope, UserPreferencesService, API, $sce, $log) {
10549        rangeTechDataController.$inject = ['$scope'];
10550        return {
10551            scope: true,
10552            restrict: 'A',
10553            controller: rangeTechDataController,
10554            controllerAs: 'rtd',
10555            bindToController: true
10556        };
10557
10558        function rangeTechDataController($scope) {
10559            var rtd = this;
10560            $scope.rtd.urlParameters = $location.search();
10561
10562            rtd.apiService = API;
10563
10564            $scope.init = function () {
10565                $scope.rtd = $scope.rtd || {};
10566
10567                var dataVehicle = UserPreferencesService.getLexusVehicle($scope.rtd.urlParameters);
10568                $scope.rtd.data = {
10569                    model: UserPreferencesService.getLexusVehicle().model ? UserPreferencesService.getLexusVehicle().model : null,
10570                    grade: UserPreferencesService.getLexusVehicle() ? UserPreferencesService.getLexusVehicle().grade : null,
10571                    engineVariant: null,
10572                    featuresData: null,
10573                    featuresByCategory: []
10574                };
10575
10576                $scope.rtd.data.engineVariants = [];
10577
10578                $scope.rtd.featuresByCategoryRendering = false;
10579
10580                if (dataVehicle) {
10581                    $scope.rtd.data.engineVariant = dataVehicle.engineVariant;
10582                }
10583
10584                if ($scope.rtd.data.engineVariant) {
10585                    updateVehicleSelection($scope.rtd.data.model, $scope.rtd.data.engineVariant, $scope.rtd.data.grade);
10586                }
10587            };
10588
10589            $scope.loadLazyImages = function () {
10590                $rootScope.$broadcast('broadcast.lazyLoadRevalidate');
10591            };
10592
10593            $scope.getActiveClass = function (counter, variant, totalVariants) {
10594
10595                if (counter === 0) {
10596                    $scope.defaultTab = variant;
10597                }
10598
10599                if ($scope.rtd.data.engineVariant === variant ||  !$scope.rtd.data.engineVariant && counter === 0) {
10600                    $scope.defaultTab = variant;
10601                    if ($scope.selectedTab === undefined) {
10602                        $scope.updateVehicleSelectionIndex(counter, true);
10603                    }
10604                    $scope.selectedTab = counter;
10605                    return 'active';
10606                }
10607
10608                if (counter === totalVariants - 1 && $scope.selectedTab === undefined) {
10609                    $("#" + $scope.defaultTab.replace(" ", "")).addClass("active");
10610
10611                    $scope.rtd.data.engineVariant = $scope.defaultTab;
10612
10613                    $scope.selectedTab = 0;
10614                    //update the cookie to default tab
10615                    $scope.updateVehicleSelectionIndex(0, true);
10616                }
10617
10618                return '';
10619            };
10620
10621            function updateVehicleSelection(model, engineVariant, grade) {
10622
10623                $scope.rtd.data.engineVariant = engineVariant;
10624
10625                $rootScope.$broadcast('vehicle-preference-change', {
10626                    model: model,
10627                    grade: grade,
10628                    engineVariant: engineVariant
10629                });
10630
10631                fetchTechDataDetails();
10632            }
10633
10634            $scope.updateVehicleSelectionIndex = function (index) {
10635
10636                updateVehicleSelection($scope.rtd.data.engineVariants[index].model, $scope.rtd.data.engineVariants[index].engineVariant, $scope.rtd.data.engineVariants[index].grade);
10637            };
10638
10639            $scope.addEngineVariant = function (index, model, engineVariant, grade) {
10640                $scope.rtd.data.engineVariants[index] = { 'model': model, 'engineVariant': engineVariant, 'grade': grade };
10641            };
10642
10643            $scope.findValueByKey = function (grade, feature) {
10644                if (feature.specs) {
10645                    return feature.specs[grade];
10646                }
10647            };
10648
10649            function fetchTechDataDetails() {
10650                $scope.rtd.featuresByCategoryLoading = true;
10651
10652                API.get($filter('formatArgs')(Lexus.API.Lookup.RangeTechDataFeatures, [
10653                    Lexus.ID.Site,
10654                    $scope.rtd.data.engineVariant
10655                ]))
10656                    .then(function (data) {
10657                        if (!data) {
10658                            return;
10659                        }
10660
10661                        $scope.rtd.featuresByCategoryRendering = true;
10662                        $timeout(function () {
10663
10664                            $scope.rtd.data.featuresByCategory = data.data;
10665                            $scope.rtd.featuresByCategoryRendering = false;
10666                        }, 1);
10667
10668                        $rootScope.$broadcast('refresh-gtm', 'Tech Data');
10669
10670                        $scope.rtd.featuresByCategoryLoading = false;
10671                    }, function (error) {
10672                        $log.error(error);
10673                        $scope.rtd.featuresByCategoryLoading = false;
10674                    });
10675            };
10676        }
10677    }
10678
10679}());
10680
10681
10682
10683;
10684"use strict";
10685/* Directives */
10686angular.module('Lexus.Directives')
10687.directive('lxRepairerLookup', ['$rootScope', 'RepairersService', function () {
10688    RepairerLookupController.$inject = ['$scope', '$rootScope', '$http', 'RepairersService', 'AddressSuggestService', 'UserPreferencesService'];
10689    return {
10690        scope: true,
10691        restrict: 'A',
10692        controller: RepairerLookupController,
10693        controllerAs: 'dlc',
10694        bindToController: true,
10695        link: function (scope, element, attrs) {
10696            
10697        }
10698    };
10699    
10700    function RepairerLookupController($scope, $rootScope, $http, RepairersService, AddressSuggestService) {
10701        var dlc = this;
10702
10703        $scope.init = function () {
10704            $scope.ds = {
10705                state: 'nsw',
10706                results: [],
10707                loading: false
10708            };
10709            $scope.repairerSearch();
10710        }
10711
10712        $scope.repairerInit = function () {
10713
10714        }
10715
10716        $scope.repairerSearch = function () {
10717            $scope.ds.loading = true;
10718            RepairersService.setParam('state', $scope.ds.state); //Dummy Implementation needs to be changed
10719            RepairersService.GetResults();
10720        }
10721
10722        $scope.$on('repairers-search-results-received', function (event, data) {
10723            $scope.ds.results = data.Data;
10724            $scope.ds.loading = false;
10725        });
10726
10727        
10728
10729    }
10730    
10731}]);
10732;
10733"use strict";
10734/* Directives */
10735angular.module('Lexus.Directives')
10736.directive('lxResponsiveSet', [function () {
10737    responsiveSetController.$inject = ['$scope', '$window'];
10738    return {
10739        scope: true,
10740        restrict: 'A',
10741        controller: responsiveSetController,
10742        controllerAs: 'rs',
10743        bindToController: true,
10744        link: function (scope, element, attrs) {
10745        }
10746    };
10747
10748
10749    function responsiveSetController($scope, $window) {
10750        var rs = this;
10751        rs.breakpoint = Lexus.Breakpoint.Small;
10752
10753        function responsiveSet(type, breakPoint, set) {
10754            function isMobile() {
10755                return (breakPoint >= window.innerWidth);
10756            };
10757
10758            if (isMobile() && type === 'mobile') {
10759                return set;
10760            }
10761            if (!isMobile() && type === 'desktop') {
10762                return set;
10763            }
10764
10765            return '';
10766        }
10767
10768        $scope.desktopSet = function (set) {
10769            return responsiveSet('desktop', rs.breakpoint, set);
10770        };
10771
10772        window.addEventListener('resize', function () {
10773
10774            setTimeout(function () {
10775                $scope.$digest();
10776            }, 50);
10777        });
10778    }
10779}]);
10780;
10781(function () {
10782
10783    angular.module('Lexus.Directives')
10784        .directive("lxUnsubscribeQuestion", UnsubscribeQuestionDirective);
10785
10786    function UnsubscribeQuestionDirective() {
10787
10788        return {
10789            replace: true,
10790            scope: {
10791                name: "@",
10792                label: "@",
10793                description: "@",
10794                ngModel: "="
10795            },
10796            template: '<div class="lx-form-text-label lx-form-unsubscribe" for="{{name}}">' +
10797                            '<input class="lx-form-checkbox" type="checkbox" id="{{name}}" name="{{name}}" ng-model="ngModel"/>' +
10798                            '<label class="lx-form-checkbox-label lx-form-unsubscribe__label" for="{{name}}" data-gtm-label="{{label}}"> {{label}}</label>' +
10799                            '<p class="lx-form-unsubscribe__explanation">{{description}}</p>' +
10800                        '</div>'
10801        };
10802    }
10803
10804}());;
10805(function () {
10806
10807    angular.module('Lexus.Directives')
10808    .directive('lxFormsUnsubscribeMarketingEmails', formsUnsubscribeMarketingEmailsDirective);
10809
10810    formsUnsubscribeMarketingEmailsDirective.$inject = ["EloquaService", "$http", "$filter", "FormsService", "$location", "$rootScope"];
10811
10812    function formsUnsubscribeMarketingEmailsDirective(EloquaService, $http, $filter, formsService, $location, $rootScope) {
10813        formsUnsubscribeMarketingEmailsController.$inject = ['$scope'];
10814        return {
10815            scope: true,
10816            restrict: 'A',
10817            controllerAs: 'ffc',
10818            bindToController: true,
10819            controller: formsUnsubscribeMarketingEmailsController,
10820            link: link
10821        };
10822
10823        function link(scope, element, attrs, filter, http) {
10824            scope.successContent = element.find('.lx-form-success');
10825            scope.failureContent = element.find('.lx-form-failure');
10826
10827            scope.ffc.form = {
10828                submitting: false,
10829                completed: false,
10830                failed: false
10831            };
10832
10833            scope.ffc.formData = JSON.parse(attrs['formData']);
10834            scope.ffc.fullUnsubscribe = (attrs["fullUnsubscribe"] === "true");
10835        }
10836
10837        function formsUnsubscribeMarketingEmailsController($scope) {
10838
10839            var ffc = this;
10840
10841            $scope.$on('submit-unsubscribe-marketing-emails-form', function () {
10842
10843                EloquaService.attachEloquaFields(ffc.formData);
10844
10845                var params = $location.search();
10846
10847                ffc.formData.ename = params.ename;
10848                ffc.formData.emailAddress = params.email;
10849
10850                ffc.form.submitting = true;
10851
10852                var formSubmission = '';
10853
10854                if (ffc.fullUnsubscribe) {
10855                    formSubmission = Lexus.API.FormSubmission.UnsubscribeMarketingEmailsOwner;
10856                }
10857                else {
10858                    formSubmission = Lexus.API.FormSubmission.UnsubscribeMarketingEmailProspect;
10859                }
10860
10861                var url = $filter('formatArgs')(formSubmission, [Lexus.ID.Site]);
10862
10863                $http({
10864                        method: 'POST',
10865                        url: url,
10866                        data: ffc.formData
10867                    })
10868                    .then(function success(response) {
10869                        if (response.data.Status == "Success") {
10870                            ffc.form.completed = true;
10871                            ffc.form.failed = false;
10872                            ffc.form.submitting = false;
10873                            $rootScope.$broadcast('gtm-form-success', 'Unsubscribe Marketing Emails');
10874                            formsService.ShowResult($scope.successContent[0]);
10875                        }
10876                        else {
10877                            submissionFail(response.data);
10878                        }
10879                    }, function error(response) {
10880                        submissionFail(response.data);
10881                    })
10882                    .finally(function () {
10883                        $rootScope.$broadcast('form-result');
10884                    });
10885            });
10886
10887            function submissionFail(data) {
10888                ffc.form.failed = true;
10889                ffc.form.submitting = false;
10890                ffc.form.completed = true;
10891
10892                formsService.ShowResult($scope.failureContent[0]);
10893            }
10894        }
10895
10896    }
10897    
10898
10899}());;
10900//Only allowed to watch rangeBuildPrice.collections.enhancementpacks[]
10901//Only allowed to emit the enhancement pack selected
10902(function (undefined) {
10903
10904    "use strict";
10905    /* Directives */
10906    angular.module('Lexus.Directives')
10907    .directive('lxBuildPriceEnhancementPack', [function () {
10908        buildPriceEnhancementPackController.$inject = ['$scope', '$q', 'VehicleService', 'BuildPriceService', 'BuildPriceAPIService', 'PricingService', '$log', '$timeout'];
10909        return {
10910            scope: {
10911                engineVariant: '<variant',
10912                grade: '<grade',
10913                enhancementPack: '<enhancement',
10914                trim: '<trim',
10915                colour: '<colour',
10916                paintId: '<paintId',
10917                specifics: '<specifics',
10918                postCode: '<postcode',
10919                pricingZone: '<pricingZone',
10920                vehicleImage: '<vehicleImage',
10921                disableDiffPricing: '<disableDiffPricing',
10922                enablePricingByZone: '<enablePricingZone',
10923            },
10924            restrict: 'A',
10925            controller: buildPriceEnhancementPackController,
10926            controllerAs: 'bpEnPack',
10927            bindToController: true,
10928            transclude: true,
10929            link: function (scope, element, attrs, ctrl, transclude) {
10930                transclude(scope, function (clone) {
10931                    element.append(clone);
10932                });
10933                scope.init();
10934            }
10935        }
10936
10937        
10938        function buildPriceEnhancementPackController($scope, $q, VehicleService, BuildPriceService, BuildPriceAPIService, PricingService, $log, $timeout) {
10939            var bpEnPack = this;
10940            bpEnPack.variants = [];
10941
10942            bpEnPack.setSelection = setSelection;
10943            bpEnPack.isSelected = isSelected;
10944            bpEnPack.isNotSelected = isNotSelected;
10945            bpEnPack.previousPage = previousPage;
10946            bpEnPack.nextPage = nextPage;
10947            //bpEnPack.image = '';
10948            bpEnPack.gradeObj = null;
10949            bpEnPack.pricingSuccess = true;
10950            bpEnPack.holdPricingCalls = true;
10951
10952            $scope.BuildPriceService = BuildPriceService;
10953
10954            $scope.$on(BuildPriceService.events.postcodeChanged, function (event, data) {
10955                getDifferentialPrices();
10956            });
10957
10958            $scope.$on(BuildPriceService.events.holdPricingCallsRelay, function (event, data) {
10959                //console.info("bpEnPack received holdPricingCalls " + data)
10960                bpEnPack.holdPricingCalls = data;
10961            });
10962
10963            $scope.$watch('bpEnPack.specifics', $scope.BuildPriceService.watchDebounce(function (newValue, oldValue) {
10964                if (newValue != null) {
10965                    calculateDifferentialPrices();
10966                }
10967            }, 50), true);
10968
10969            $scope.$watch('bpEnPack.grade', $scope.BuildPriceService.watchDebounce(function (newValue, oldValue) {
10970                if (newValue != null) {
10971                    console.info("%c 2. buildPriceEnhancementPackController received GRADE: ", "color: mediumvioletred;", newValue + " (with VARIANT: " + bpEnPack.engineVariant + ")", bpEnPack);
10972                    getSelectedGrade(bpEnPack.engineVariant, newValue);
10973                }
10974            }, 50), true);
10975
10976            function getSelectedGrade(engineVariantName, gradeName, clearDownstream) {
10977                var engineVariant = BuildPriceAPIService.findEngineVariantInVehicleData(engineVariantName);
10978                if (engineVariant) {
10979
10980                    var grade = engineVariant.grades.find(function (grade) {
10981                        return grade.uriName == gradeName;
10982                    });
10983
10984                    if (grade && grade.id) {
10985                        bpEnPack.gradeObj = grade; //Set it so it can be accessed by the promise response
10986
10987                        bpEnPack.variants = [];
10988
10989                        BuildPriceAPIService.getVariants(bpEnPack.engineVariant, grade.id, enhancementPacksReceived);
10990                    } else {
10991                        console.error("buildPriceEnhancementPackController cannot load enhancement packs for EngineVariant: " + bpEnPack.engineVariant + " + Grade: " + bpEnPack.grade);
10992                    }
10993                }
10994            }
10995
10996            function enhancementPacksReceived(response) {
10997                bpEnPack.variants = response;
10998
10999                getDifferentialPrices();
11000
11001                $scope.$emit(BuildPriceService.events.enhancementPacksReceived, bpEnPack.enhancementPack);
11002                getPackSelectionInGrade();
11003            }
11004
11005            function getDifferentialPrices() {
11006                if (bpEnPack.postCode) {
11007                    for (var index = 0; index < bpEnPack.variants.length; index++) {
11008                        bpEnPack.variants[index].showSpinner = true;
11009                    }
11010                    if (!bpEnPack.disableDiffPricing) {
11011                        //console.info("bpEnPack waiting for holdPricingCalls to clear");
11012                        BuildPriceService.waitFor.call($scope, 'bpEnPack.holdPricingCalls', BuildPriceService.waitCondition.ISSTRICTLYFALSE).
11013                        then(function success(value) {
11014                            //console.info("holdPricingCalls cleared in bpEnPack");
11015                            getPrices().then(calculateDifferentialPrices, ajaxFailure);
11016                        });
11017                    }
11018                }
11019            }
11020
11021            function getPrices() {
11022                var promises = [];
11023
11024                for (var index = 0; index < bpEnPack.variants.length; index++) {
11025                    promises.push(getPromise(bpEnPack.variants[index]));
11026                }
11027
11028                return $q.all(promises).then(function (responses) {
11029                    bpEnPack.pricingSuccess = true;
11030
11031                    for (var returnIndex = 0; returnIndex < responses.length; returnIndex++) {
11032                        //console.log("bpEnPack -> responses[returnIndex].data.driveAway: ", responses[returnIndex].data.driveAway);
11033                        var resp = responses[returnIndex];
11034                        if (resp.status != "Success") {
11035                            bpEnPack.pricingSuccess = false;
11036                        } else if (resp.data && resp.data.driveAway) {
11037                            bpEnPack.variants[returnIndex].driveAwayPrice = parseFloat(resp.data.driveAway);
11038                        } else {
11039                            //success but no price
11040                        }
11041                    }
11042                    pricingReport();
11043
11044                }, ajaxPriceFailure);
11045
11046                function getPromise(variant) {
11047                    PricingService.setParam("enablePricingByZone", bpEnPack.enablePricingByZone);
11048                    return PricingService.driveAwayByEnhancementPack(bpEnPack.gradeObj.id, variant.mdmid, bpEnPack.postCode, bpEnPack.pricingZone);
11049                }
11050            }
11051
11052            function calculateDifferentialPrices() {
11053
11054                var selectedVariant = BuildPriceAPIService.getVariantByName(bpEnPack.engineVariant, bpEnPack.gradeObj.id, bpEnPack.enhancementPack);
11055                if (!bpEnPack.variants) {
11056                    return;
11057                }
11058
11059                for (var index = 0; index < bpEnPack.variants.length; index++) {
11060                    if (!isSelected(bpEnPack.variants[index]) && selectedVariant) {
11061                        bpEnPack.variants[index].differentialPrice = bpEnPack.variants[index].driveAwayPrice - selectedVariant.driveAwayPrice;
11062                    }
11063                    else {
11064                        bpEnPack.variants[index].differentialPrice = null;
11065                    }
11066
11067                    bpEnPack.variants[index].showSpinner = false;
11068                }
11069            }
11070
11071            function ajaxFailure(data) {
11072                console.error('BuildPrice AjaxFailure -> EnPack: ', data);
11073
11074                $scope.$emit('open-modal-window', {
11075                    content: 'Oops, something went wrong while obtaining the details. If the problem persists, please contact your dealer directly.',
11076                    wrapper: ['<p class="lx-modal__paragraph">', '</p>']
11077                });
11078            }
11079
11080            function ajaxPriceFailure(data) {
11081                console.error('BuildPrice AjaxPriceFailure -> EnPack: ', data);
11082
11083                //$scope.$emit('open-modal-window', {
11084                //    content: 'Oops, something went wrong while obtaining the details. If the problem persists, please contact your dealer directly.',
11085                //    wrapper: ['<p class="lx-modal__paragraph">', '</p>']
11086                //});
11087            }
11088
11089            function pricingReport() {
11090                if (bpEnPack.pricingSuccess) {
11091                    angular.element('.lx-build-price-enhancement-pack').removeClass('pricing-error');
11092                } else {
11093                    console.log("bpGrade pricingSuccess: ", bpEnPack.pricingSuccess);
11094                    angular.element('.lx-build-price-enhancement-pack').addClass('pricing-error');
11095                }
11096            }
11097
11098            function getPackSelectionInGrade() {
11099                //check if existing selection is in valid set, if it is, don't change the selection
11100
11101                var selectionValid = false;
11102
11103                if (bpEnPack.enhancementPack) {
11104                    if (bpEnPack.variants.find(function (ep) {
11105                        return ep.uriName === bpEnPack.enhancementPack;
11106                    }) || bpEnPack.variants.find(function (ep) {
11107                        return ep.enhancementPackName === bpEnPack.enhancementPack;
11108                    })) {
11109                        selectionValid = true;
11110                    }
11111                }
11112
11113                var selectionNotNull = bpEnPack.enhancementPack != null;
11114
11115                if (bpEnPack.variants.length > 0 && !selectionValid) {
11116                    var defaultSelection = getDefaultEnhancementPack();
11117                    if (selectionNotNull) {
11118                        //trigger invalid selection error
11119                        var invalidSelectionData = {
11120                            controllerName: $scope.BuildPriceService.stepIds.ENHANCEMENT_PACK,
11121                            $scope: $scope,
11122                            defaultValue: defaultSelection.uriName,
11123                            invalidValue: bpEnPack.enhancementPack,
11124                            /*
11125                             * Once we have a user-initiated selection process implement these variables
11126                             userInitiated: true/false,
11127                             clearDownstreamFrom: userInitiated ? 3 : null
11128                             */
11129                        };
11130                        $scope.BuildPriceService.emitInvalidSelection(invalidSelectionData)
11131                    }
11132
11133                    bpEnPack.enhancementPack = defaultSelection.uriName;
11134                }
11135
11136                $scope.$emit(BuildPriceService.events.enhancementPackSelected, bpEnPack.enhancementPack);
11137
11138                bpEnPack.currentPage = setCurrentPage(indexOfPack(bpEnPack.enhancementPack));
11139            }
11140
11141            function getDefaultEnhancementPack() {
11142                return bpEnPack.variants.find(function (variant) {
11143                    return variant.isBaseVariant == true;
11144                });
11145            }
11146
11147            function getSelectedEnhancementPack() {
11148                return (bpEnPack.variants || []).find(function (item) {
11149                    return item.uriName == bpEnPack.enhancementPack;
11150                });
11151            }
11152
11153            function setSelection(pack) {
11154
11155                var packName = (bpEnPack.enhancementPack === pack.uriName) ? getDefaultEnhancementPack().uriName : pack.uriName;
11156
11157                bpEnPack.packIndex = (indexOfPack(packName)) ? indexOfPack(packName) : 0;
11158                bpEnPack.currentPage = setCurrentPage(bpEnPack.packIndex, true);
11159                if (bpEnPack.enhancementPack != packName) {
11160
11161                    $scope.$emit(BuildPriceService.events.userSelection, 'Enhancement Pack selected');
11162
11163                    emitPackSelection(packName, true, true, bpEnPack.trim, bpEnPack.colour);
11164                }
11165                return false;
11166            }
11167
11168            function isSelected(pack) {
11169                return bpEnPack.enhancementPack === pack.uriName;
11170            }
11171
11172            function isNotSelected(pack) {
11173                var notSelected = false;
11174
11175                if (getDefaultEnhancementPack()) {
11176                    if (bpEnPack.enhancementPack != getDefaultEnhancementPack().uriName) {
11177                        if (bpEnPack.enhancementPack != pack.uriName) {
11178                            notSelected = true;
11179                        }
11180                    }
11181                }
11182
11183                return notSelected;
11184            }
11185
11186            function isCurrentStep(current) {
11187                var current = current != null ? current : false;
11188                bpEnPack.isCurrentStep = current;
11189            }
11190
11191            function indexOfPack(packName) {
11192                var packs = bpEnPack.variants;
11193                for (var i = 0; i < packs.length; i++) {
11194                    if (packs[i].uriName === packName) {
11195                        return i;
11196                    }
11197                }
11198                $log.error('Enhancement Pack ' + packName + ' not found');
11199            }
11200
11201            function setCurrentPage(packIndex, user) {
11202                var user = user != null ? user : false;
11203                var page = (user) ? bpEnPack.currentPage : 0;
11204                if (packIndex != 0) {
11205                    page = packIndex - 2;
11206                }
11207                return page;
11208            }
11209
11210            function previousPage() {
11211                if (bpEnPack.currentPage > 0) bpEnPack.currentPage--;
11212            }
11213
11214            function nextPage() {
11215                if (bpEnPack.currentPage <= ((bpEnPack.variants.length - 2) - bpEnPack.currentPage)) bpEnPack.currentPage++;
11216            }
11217
11218            function emitPackSelection(packName, clearDownstream, userInitiated, revertTrim, revertColour) {
11219                var data = {
11220                    clearDownstream: clearDownstream,
11221                    selection: packName,
11222                    userInitiated: userInitiated,
11223                    revertTrim: revertTrim,
11224                    revertColour: revertColour
11225
11226                };
11227                $scope.$emit(BuildPriceService.events.enhancementPackSelected, data);
11228            }
11229
11230            //#region EVENTS
11231            $scope.$on(BuildPriceService.events.pageNavigationBroadcast, function (e, target) {
11232                var isCurrent = target.id == 'enhancement';
11233                bpEnPack.isCurrentStep = isCurrent;
11234
11235                // If on phone scroll to currently selected enhancement pack. 
11236                if (isCurrent && indexOfPack(bpEnPack.enhancementPack) != 0 && window.matchMedia("(max-width: 767px)").matches) {
11237                    setTimeout(function () {
11238                        window.scrollTo(0, angular.element('.' + bpEnPack.enhancementPack.replace(/ /g, ''))[0].offsetTop + 250);
11239                    });
11240                }
11241            });
11242
11243            $scope.$on(BuildPriceService.events.threeSixtyLoad, function (event, data) {
11244                console.info("%c buildPriceEnhancementPackController received threeSixtyLoad: ", "color: mediumvioletred;", data);
11245            });
11246
11247            $scope.init = function () {
11248            }
11249        }
11250    }]);
11251
11252}());;
11253(function(undefined) {
11254    'use strict';
11255
11256    /* Directives */
11257    angular.module('Lexus.Directives').directive('lxBuildPriceExterior', [
11258        function() {
11259            buildPriceExteriorController.$inject = [
11260                '$scope',
11261                '$timeout',
11262                '$element',
11263                'BuildPriceService',
11264                'BuildPriceAPIService',
11265                'ViewportService'
11266            ];
11267
11268            return {
11269                scope: {
11270                    engineVariant: '<variant',
11271                    trim: '<trim',
11272                    colour: '<colour'
11273                },
11274                restrict: 'A',
11275                controller: buildPriceExteriorController,
11276                controllerAs: 'bpExterior',
11277                template: '<div data-ng-transclude></div>',
11278                bindToController: true,
11279                transclude: true,
11280                require: '^lxBuildPricePaintTrim',
11281                link: function(scope, element, attrs, ctrl, transclude) {
11282                    scope.loadingExterior = true;
11283                    ctrl.registerExterior(scope.bpExterior);
11284                }
11285            };
11286
11287            function buildPriceExteriorController(
11288                $scope,
11289                $timeout,
11290                $element,
11291                BuildPriceService,
11292                BuildPriceAPIService,
11293                ViewportService
11294            ) {
11295                var bpExterior = this;
11296
11297                bpExterior.loadExterior = function(data) {
11298                    $scope.loadingExterior = true;
11299                    $scope.$emit(BuildPriceService.events.threeSixtyLoad, data);
11300                    loadImages(data.gradeId, data.variantId, data.trimId, data.materialCode);
11301                };
11302
11303                $scope.$on('bp-user-selection', function(event) {
11304                    console.info("%c 3. buildPriceExteriorController 'bp-user-selection': ", 'color: blue; ', event);
11305                });
11306
11307                function loadImages(gradeId, variantId, trimId, materialCode) {
11308                    BuildPriceAPIService.getExteriorImages(
11309                        bpExterior.engineVariant,
11310                        gradeId,
11311                        variantId,
11312                        trimId,
11313                        materialCode,
11314                        function(images) {
11315                            var reelElement = $($element)
11316                                .find('img')
11317                                .first();
11318                            initReel(reelElement, images);
11319                            $scope.$emit(BuildPriceService.events.threeSixtyLoaded);
11320                        }
11321                    );
11322                    $scope.$emit(BuildPriceService.events.holdPricingCalls, false);
11323                };
11324
11325                function initReel($element, images) {
11326                    console.log('is loading reel');
11327                    var bestFrame = 18;
11328
11329                    images = ViewportService.isDesktop()
11330                        ? images.threeSixtyImagesDesktop
11331                        : images.threeSixtyImagesMobile;
11332
11333                    // Prevent image flickering back to old one as Reel performs teardown
11334                    if ($element.reel('backup')) {
11335                        $element.reel('backup').attr.src = $element.src;
11336                    }
11337
11338                    // Preserve angle if reel already exists
11339                    if ($element.reel('frame')) {
11340                        bestFrame = $element.reel('frame');
11341                    }
11342
11343                    $element.unreel();
11344                    $element
11345                        .reel({
11346                            opening: 0,
11347                            speed: 0,
11348                            images: images,
11349                            frame: bestFrame,
11350                            frames: images.length,
11351                            laziness: 1, // don't "underclock" mobile framerate
11352                            scrollable: true,
11353                            wheelable: false
11354                        })
11355                        .on('loaded', function(ev) {
11356                            if ($scope.loadingExterior) {
11357                                $scope.$apply(function() {
11358                                    $scope.loadingExterior = false;
11359                                });
11360                            }
11361                        })
11362                        .on('pan', function() {
11363                            if (bpExterior.reelPanTimeout) {
11364                                $timeout.cancel(bpExterior.reelPanTimeout);
11365                            }
11366
11367                            bpExterior.reelPanTimeout = $timeout(function() {
11368                                $scope.$emit(BuildPriceService.events.gtmEvent, { source: '360' });
11369                            }, 50); // Don't bother showing "loading" for <100ms
11370                        });
11371                };
11372            }
11373        }
11374    ]);
11375})();
11376;
11377(function (undefined) {
11378
11379    "use strict";
11380
11381    angular.module("Lexus.Directives")
11382        .directive("buildPriceGradeFeature", BuildPriceGradeFeatureDirective);
11383
11384    function BuildPriceGradeFeatureDirective() {
11385        return {
11386            scope: {
11387                expression: "="
11388            },
11389            restrict: "E",
11390            replace: false,
11391            template: '<span class="variant-feature" ng-bind-html="expression"></span><br />'
11392        };
11393    }
11394
11395})();;
11396//Only Allowed to watch 
11397//BuildPriceAPIService.data[ct200h].grades
11398//Only allowed to emit the grade selected
11399(function (undefined) {
11400
11401    "use strict";
11402    /* Directives */
11403    angular.module('Lexus.Directives')
11404        .directive('lxBuildPriceGrade', [function () {
11405            BuildPriceGradeController.$inject = ['$scope', 'PricingService', '$q', 'VehicleService', 'BuildPriceService', 'BuildPriceAPIService'];
11406            return {
11407                scope: {},
11408                restrict: 'A',
11409                controller: BuildPriceGradeController,
11410                controllerAs: 'bpGrade',
11411                bindToController: {
11412                    engineVariant: '<variant',
11413                    grade: '<grade',
11414                    enhancementPack: '<enhancement',
11415                    trim: '<trim',
11416                    colour: '<colour',
11417                    postCode: '<postcode',
11418                    pricingZone: '<pricingZone',
11419                    specifics: '<specifics',
11420                    disableDiffPricing: '<disableDiffPricing',
11421                    enablePricingByZone: '<enablePricingZone',
11422                },
11423                transclude: true,
11424                link: function (scope, element, attrs, ctrl, transclude) {
11425                    transclude(scope, function (clone) {
11426                        element.append(clone);
11427                    });
11428                    scope.init();
11429                }
11430            }
11431
11432
11433            
11434
11435            function BuildPriceGradeController($scope, PricingService, $q, VehicleService, BuildPriceService, BuildPriceAPIService) {
11436                var bpGrade = this;
11437
11438                //bpGrade.setSelection = setSelection;
11439                bpGrade.getPreviousGrade = getPreviousGrade;
11440                bpGrade.getNextGrade = getNextGrade;
11441                //bpGrade.BuildPriceService = BuildPriceService; 
11442                //bpGrade.BuildPriceAPIService = BuildPriceAPIService;
11443                bpGrade.isSelected = isSelected;
11444                bpGrade.pricingSuccess = true;
11445                bpGrade.holdPricingCalls = true;
11446
11447                $scope.bpGrade.setSelection = function (grade) {
11448                    bpGrade.gradeIndex = indexOfGrade(grade.name);
11449
11450                    $scope.$emit(BuildPriceService.events.onOfferText, grade.onOfferText);
11451
11452                    if (bpGrade.grade != grade.uriName) {
11453
11454                        $scope.$emit(BuildPriceService.events.userSelection, 'Grade selected');
11455
11456                        emitGradeSelection(grade.uriName, true, true, bpGrade.enhancementPack, bpGrade.trim, bpGrade.colour);
11457                    }
11458                    return false;
11459                }
11460
11461                $scope.$on(BuildPriceService.events.holdPricingCallsRelay, function (event, data) {
11462                    //console.info("bpGrade received holdPricingCalls: ", data)
11463                    bpGrade.holdPricingCalls = data;
11464                });
11465
11466                
11467                $scope.$on(BuildPriceService.events.postcodeChanged, function (event, data) {
11468                    getDifferentialPrices();
11469                });
11470
11471                $scope.$watch('bpGrade.specifics', BuildPriceService.watchDebounce(function (newValue, oldValue) {
11472                    if (newValue && oldValue && newValue != oldValue) {
11473                        // if (newValue != null) {
11474                        console.info("%c buildPriceGradeController received SPECIFICS: ", "color: gold;", newValue);
11475                        VehicleService.getVehicleImages(newValue.gradeId, newValue.variantId, newValue.trimId, newValue.materialCode).then(function (response) {
11476                            bpEnPack.image = response.data.threeSixtyImagesDesktop[2];
11477                        });
11478                    }
11479                }, 50), true);
11480
11481                $scope.$watch('bpGrade.grade', BuildPriceService.watchDebounce(function (newValue, oldValue) {
11482                    if (oldValue && newValue != oldValue) {
11483                        calculateDifferentialPrices();
11484                    }
11485                }, 50), true);
11486
11487                $scope.$watch('bpGrade.engineVariant', BuildPriceService.watchDebounce(function (newValue, oldValue) {
11488                    if (newValue != null) {
11489
11490                        console.info("%c 1. buildPriceGradeController $watch ENGINE VARIANT: ", "color: gold; ", newValue);
11491
11492                        var previousSelectionNull = (oldValue == null);
11493                        var selectionChanged = (oldValue !== newValue);
11494
11495                        $scope.getGradeSelectionInVariant(newValue, (selectionChanged && !previousSelectionNull), true);
11496                    }
11497                }, 50), true);
11498
11499
11500                $scope.getGradeSelectionInVariant = function (engineVariant, clearDownstream, userInitiated) {
11501
11502                    var variantData = BuildPriceAPIService.findEngineVariantInVehicleData(engineVariant);
11503
11504                    bpGrade.grades = variantData.grades;
11505
11506                    for (var index = 0; index < bpGrade.grades.length; index++) {
11507                        bpGrade.grades[index].showSpinner = false;
11508                        bpGrade.grades[index].driveAwayPrice = 0;
11509                    }
11510
11511                    //check if existing selection is in valid set, if it is, don't change the selection
11512                    var selectionValid = bpGrade.grade ? bpGrade.grades.find(function (grade) { return grade.uriName === bpGrade.grade; }) != null : false;
11513                    var selectionNotNull = bpGrade.grade != null;
11514
11515                    if (bpGrade.grades.length > 0 && !selectionValid) {
11516                        if (selectionNotNull) {
11517                            //trigger invalid selection error
11518                            var invalidSelectionData = {
11519                                controllerName: BuildPriceService.stepIds.GRADE,
11520                                $scope: $scope,
11521                                defaultValue: bpGrade.grades[0].uriName,
11522                                invalidValue: bpGrade.grade,
11523                                userInitiated: userInitiated,
11524                                clearDownstreamFrom: userInitiated ? 2 : null
11525                            };
11526                            BuildPriceService.emitInvalidSelection(invalidSelectionData);
11527                        }
11528                        emitGradeSelection(getDefaultGrade().uriName, (clearDownstream || !selectionValid), false);
11529                        bpGrade.gradeIndex = indexOfGrade(getDefaultGrade().name);
11530
11531                        $scope.$emit(BuildPriceService.events.onOfferText, getDefaultGrade().onOfferText);
11532
11533                    } else {
11534                        var selectedGrade = BuildPriceAPIService.getGradeByName(engineVariant, bpGrade.grade);
11535                        if (selectedGrade) {
11536                            emitGradeSelection(selectedGrade.uriName, (clearDownstream || !selectionValid), false);
11537                            bpGrade.gradeIndex = indexOfGrade(selectedGrade.name);
11538
11539                            $scope.$emit(BuildPriceService.events.onOfferText, selectedGrade.onOfferText);
11540
11541                        }
11542                    }
11543
11544                    getFeatures();
11545
11546                    getDifferentialPrices();
11547
11548                }
11549
11550                $scope.isOnOffer = function (obj) {
11551                    var bool = obj.onOffer;
11552                    return bool
11553                }
11554
11555                function getDifferentialPrices() {
11556                    //console.info("%c 1B. buildPriceGradeController getGradesDetails: -> bpGrade.postCode", "color: gold; ", bpGrade.postCode);
11557                    if (bpGrade.postCode) {
11558                        //console.info("bpGrade waiting for holdPricingCalls to clear");
11559                        BuildPriceService.waitFor.call($scope, 'bpGrade.holdPricingCalls', BuildPriceService.waitCondition.ISSTRICTLYFALSE).
11560                        then(function success(value) {
11561                            console.info("holdPricingCalls cleared in bpGrade");
11562                            getPrices()
11563                            //.then(getFeatures, ajaxFailure)
11564                            .then(calculateDifferentialPrices, ajaxFailure);
11565                        });
11566                    }
11567                }
11568
11569                function getDefaultGrade() {
11570                    return bpGrade.grades ? bpGrade.grades[0] : undefined;
11571                }
11572
11573                function getPrices() {
11574                    var promises = [];
11575
11576                    for (var index = 0; index < bpGrade.grades.length; index++) {
11577                        promises.push(getPromise(bpGrade.grades[index]));
11578                        if (!isSelected(bpGrade.grades[index])) {
11579                            bpGrade.grades[index].showSpinner = true;
11580                        }
11581                    }
11582
11583                    return $q.all(promises).then(function (responses) {
11584
11585                        bpGrade.pricingSuccess = true;
11586
11587                        for (var returnIndex = 0; returnIndex < responses.length; returnIndex++) {
11588                            var resp = responses[returnIndex];
11589                            if (resp.status != "Success") {
11590                                bpGrade.pricingSuccess = false;
11591                            } else if (resp.data && resp.data.driveAway) {
11592                                bpGrade.grades[returnIndex].driveAwayPrice = parseFloat(resp.data.driveAway);
11593                            } else {
11594                                //success but no price
11595                                bpGrade.pricingSuccess = false;
11596                            }
11597                        }
11598
11599                        pricingReport();
11600                    }, ajaxPriceFailure);
11601
11602                    function getPromise(grade) {
11603                        PricingService.setParam("enablePricingByZone", bpGrade.enablePricingByZone);
11604                        return PricingService.driveAwayByGrade(grade.id, bpGrade.postCode, bpGrade.pricingZone);
11605                    }
11606                }
11607
11608                function getFeatures() {
11609
11610
11611                    var promises = [];
11612
11613                    for (var index = 0; index < bpGrade.grades.length; index++) {
11614                        promises.push(getPromise(bpGrade.grades[index]));
11615                    }
11616
11617                    return $q.all(promises).then(function (responses) {
11618                        for (var index = 0; index < responses.length; index++) {
11619                            bpGrade.grades[index].features = angular.copy(responses[index].data[0].features);
11620
11621                            Object.defineProperty(bpGrade.grades[index], "featureComparisonTitle", {
11622                                enumerable: true,
11623                                configurable: false,
11624                                writable: false,
11625                                value: responses[index].data[0].featureComparisonTitle
11626                            });
11627                        }
11628                    }, ajaxFailure);
11629
11630                    function getPromise(grade) {
11631                        return VehicleService.getVariants(grade.id);
11632                    }
11633                }
11634
11635                function ajaxFailure(data) {
11636                    console.error('BuildPrice AjaxFailure -> Grade: ', data);
11637
11638                    $scope.$emit('open-modal-window', {
11639                        content: 'Oops, something went wrong while obtaining the details. If the problem persists, please contact your dealer directly.',
11640                        wrapper: ['<p class="lx-modal__paragraph">', '</p>']
11641                    });
11642                }
11643
11644                function ajaxPriceFailure(data) {
11645                    console.error('BuildPrice AjaxPriceFailure -> Grade: ', data);
11646
11647                    //$scope.$emit('open-modal-window', {
11648                    //    content: 'Oops, something went wrong while obtaining the details. If the problem persists, please contact your dealer directly.',
11649                    //    wrapper: ['<p class="lx-modal__paragraph">', '</p>']
11650                    //});
11651                }
11652
11653                function pricingReport() {
11654                    if (bpGrade.pricingSuccess) {
11655                        angular.element('.lx-build-price-grade').removeClass('pricing-error');
11656                    } else {
11657                        //console.log("bpGrade pricingSuccess: ", bpGrade.pricingSuccess);
11658                        angular.element('.lx-build-price-grade').addClass('pricing-error');
11659                    }
11660                }
11661
11662                function calculateDifferentialPrices() {
11663
11664                    var selectedGrade = BuildPriceAPIService.getGradeByName(bpGrade.engineVariant, bpGrade.grade) || getDefaultGrade();
11665
11666                    if (!bpGrade.grades) { return; }
11667
11668                    for (var index = 0; index < bpGrade.grades.length; index++) {
11669                        if (!isSelected(bpGrade.grades[index]) && selectedGrade) {
11670                            bpGrade.grades[index].differentialPrice =
11671                                bpGrade.grades[index].driveAwayPrice - selectedGrade.driveAwayPrice;
11672                        }
11673                        else {
11674                            bpGrade.grades[index].differentialPrice = 0;
11675                        }
11676
11677                        bpGrade.grades[index].showSpinner = false;
11678                    }
11679                }
11680
11681                function isSelected(grade) {
11682                    return bpGrade.grade === grade.uriName;
11683                }
11684
11685                function indexOfGrade(gradeName) {
11686                    var grades = bpGrade.grades;
11687                    for (var i = 0; i < grades.length; i++) {
11688                        if (grades[i].name === gradeName) {
11689                            return i;
11690                        }
11691                    }
11692                    throw new Error('Grade ' + gradeName + ' not found');
11693                }
11694
11695                function getPreviousGrade(grade, wrap) {
11696                    var previousGradeIndex = indexOfGrade(grade) - 1;
11697                    if (wrap) {
11698                        previousGradeIndex = (previousGradeIndex + bpGrade.grades.length) % bpGrade.grades.length;
11699                    } else {
11700                        previousGradeIndex = Math.max(previousGradeIndex, 0);
11701                    }
11702                    return bpGrade.grades[previousGradeIndex];
11703                }
11704
11705                function getNextGrade(grade, wrap) {
11706                    var nextGradeIndex = indexOfGrade(grade) + 1;
11707                    if (wrap) {
11708                        nextGradeIndex = nextGradeIndex % bpGrade.grades.length;
11709                    } else {
11710                        nextGradeIndex = Math.min(nextGradeIndex, bpGrade.grades.length - 1);
11711                    }
11712                    return bpGrade.grades[nextGradeIndex];
11713                }
11714
11715                function emitGradeSelection(gradeName, clearDownstream, userInitiated, revertEnhancementPack, revertTrim, revertColour) {
11716                    var data = {
11717                        selection: gradeName,
11718                        clearDownstream: clearDownstream,
11719                        userInitiated: userInitiated,
11720                        revertEnhancementPack: revertEnhancementPack,
11721                        revertTrim: revertTrim,
11722                        revertColour: revertColour
11723                    };
11724                    $scope.$emit(BuildPriceService.events.gradeSelected, d
11724ata);
11725                }
11726
11727                $scope.init = function () {
11728                }
11729
11730            }
11731        }]);
11732
11733}());
11734;
11735(function (undefined) {
11736
11737    "use strict";
11738    /* Directives */
11739    angular.module('Lexus.Directives')
11740    .directive('lxBuildPriceInterior', [function () {
11741        buildPriceInteriorController.$inject = ['$scope', '$filter', 'BuildPriceService', 'VehicleService'];
11742        return {
11743            scope: {
11744                engineVariant: '<variant',
11745                trim: '<trim',
11746                colour: '<colour',
11747                paintId: '<paintId',
11748                specifics: '<specifics',
11749                tab: '<tab'
11750            },
11751            restrict: 'A',
11752            controller: buildPriceInteriorController,
11753            controllerAs: 'bpInterior',
11754            template: '<div data-ng-transclude></div>',
11755            bindToController: true,
11756            transclude: true,
11757            require: '^lxBuildPricePaintTrim',
11758            link: function (scope, element, attrs, ctrl, transclude) {
11759                scope.loadingInterior = true;
11760
11761                ctrl.registerInterior(scope.bpInterior);
11762            }
11763        }
11764
11765        
11766        function buildPriceInteriorController($scope, $filter, BuildPriceService, VehicleService) {
11767            var bpInterior = this;
11768            
11769            bpInterior.vlookat = null;
11770            bpInterior.hlookat = null;
11771            bpInterior.pano = {
11772                    id: null,
11773                    panoControl: null
11774                };
11775            
11776            bpInterior.ensurePanoRemoved = ensurePanoRemoved;
11777
11778            //When the trim is changed the available paints need to be found 
11779            //which gives us new paints and Material Codes
11780            $scope.$watch('bpInterior.trim', BuildPriceService.watchDebounce(function (newValue, oldValue) {
11781                if (newValue != null) {
11782                    console.info("%c 5. buildPriceInteriorController received Trim: ", "color: purple; ", newValue + " (with VARIANT: " + bpInterior.engineVariant + ")");
11783                }
11784            },50));
11785
11786            $scope.$watch('bpInterior.specifics', BuildPriceService.watchDebounce(function (newValue, oldValue) {
11787                if (!bpInterior.tabExterior) {
11788                    loadPano(newValue, true);
11789                }
11790                else {
11791                    //Warms the API call without actually loading krpano
11792                    loadPano(newValue, false);
11793                }
11794                
11795            }, 50), true);
11796
11797
11798            function ensurePanoRemoved() {
11799                if (bpInterior.pano) {
11800                    removepano(bpInterior.pano.id);
11801                    bpInterior.pano.panoControl = null;
11802                }
11803                bpInterior.pano = null;
11804            }
11805
11806            this.loadInterior = loadInterior;
11807
11808            function loadInterior(data) {
11809                loadPano(data, true);
11810            }
11811
11812            $scope.$on(BuildPriceService.events.pageNavigationBroadcast, function (e, target) {
11813                //console.info("%c buildPriceInteriorController pageNaigation: ", "color: purple; ", target);
11814                var isCurrent = target.id == 'colour';
11815                bpInterior.isCurrentStep = isCurrent;
11816                if (bpInterior.specifics) loadPano(bpInterior.specifics, true);
11817            });
11818
11819            function loadPano(data, runEmbed) {
11820                //Added this to allow us to test for web WebGL issues on mobile browsers. 
11821                //To skip loading interior Pano add ?loadInterior=false
11822                var loadInterior = (window.location.search.match(/loadInterior\=false/)) ? false : true;
11823
11824                if (data && data.gradeId && data.variantId && data.trimId && data.materialCode && loadInterior && bpInterior.isCurrentStep) {
11825
11826                    $scope.loadingInterior = true;
11827                    
11828                    var xml = VehicleService.getVehiclePanoXMLUrl(data.gradeId, data.variantId, data.trimId, data.materialCode);
11829                    if (runEmbed) {
11830                        // Data source didn't even change? No work to do.
11831                        if (bpInterior.pano && bpInterior.pano.xml === xml)
11832                            return;
11833
11834                        ensurePanoRemoved();
11835
11836                        console.info("%c buildPriceInteriorController loadPano: ", "color: purple; ", xml);
11837
11838                        bpInterior.pano = bpInterior.pano || {};
11839                        bpInterior.pano.id = 'krpano_' + $scope.$id;
11840                        bpInterior.pano.xml = xml;
11841
11842                        var settings = {};
11843                        settings["events.onviewchange"] = $scope.viewChange;
11844                        if (bpInterior.hlookat && bpInterior.vlookat) {
11845                            settings["view.hlookat"] = bpInterior.hlookat;
11846                            settings["view.vlookat"] = bpInterior.vlookat;
11847                        }
11848
11849                        embedpano({
11850                            id: bpInterior.pano.id,
11851                            xml: xml,
11852                            target: "interiorPano",
11853                            html5: "only",
11854                            mobilescale: 1.0,
11855                            onready: function (krPano) {
11856                                $scope.loadingInterior = false;
11857                            },
11858                            vars: settings
11859                        });
11860                    }
11861                }
11862            }
11863
11864            $scope.viewChange = function () {
11865                //krpano.get("view.hlookat");
11866                if (!bpInterior.pano.panoControl) {
11867                    bpInterior.pano.panoControl = document.getElementById(bpInterior.pano.id)
11868                }
11869
11870                if (bpInterior.pano.panoControl) {
11871                    //console.info('Change View ' + krpano.get("view.hlookat") + ' ' + krpano.get("view.vlookat"));
11872                    bpInterior.hlookat = bpInterior.pano.panoControl.get("view.hlookat");
11873                    bpInterior.vlookat = bpInterior.pano.panoControl.get("view.vlookat");
11874                }
11875            }
11876        }
11877    }]);
11878
11879}());;
11880//Only Allowed to watch 
11881//BuildPriceAPIService.data
11882
11883//Only Allowed to emit 
11884//model selected
11885//engine variant selected
11886
11887(function (undefined) {
11888
11889    "use strict";
11890    /* Directives */
11891    angular.module('Lexus.Directives')
11892    .directive('lxBuildPriceModel', [function () {
11893      buildPriceModelController.$inject = ['$scope', '$timeout', 'VehicleService', 'BuildPriceService', 'BuildPriceAPIService'];
11894      return {
11895    
11896        scope: {
11897            selectedModel: '<carmodel',
11898            selectedEngineVariant: '<variant'
11899        },
11900        restrict: 'A',
11901        controller: buildPriceModelController,
11902        controllerAs: 'bpModel',
11903        bindToController: true,
11904        transclude: true,
11905        link: function (scope, element, attrs, ctrl, transclude) {
11906            transclude(scope, function (clone) {
11907                element.append(clone);
11908            });
11909            scope.bpModel.currentPage = 0;
11910        }
11911      };
11912
11913      
11914
11915      function buildPriceModelController($scope, $timeout, VehicleService, BuildPriceService, BuildPriceAPIService) {
11916        var bpModel = this;
11917        bpModel.vehicleData = null;
11918        bpModel.modelSelected = false;
11919        bpModel.previousPage = previousPage;
11920        bpModel.nextPage = nextPage;
11921
11922        function setCurrentPage(packIndex, user) {
11923            var user = user != null ? user : false;
11924            var page = (user) ? bpModel.currentPage : 0;
11925            if (packIndex != 0) {
11926                page = packIndex - 2;
11927            }
11928            return page;
11929        }
11930
11931        function previousPage() {
11932            if (bpModel.currentPage > 0) bpModel.currentPage--;
11933        }
11934
11935        function nextPage(variantsLength) {
11936            if (bpModel.currentPage <= ((variantsLength - 2) - bpModel.currentPage)) bpModel.currentPage++;
11937        }
11938
11939        $scope.setModelHeights = function () {
11940            $timeout(function () {
11941                if (window.innerWidth > 767) {
11942                    var choosers = angular.element('.lx-build-price-model-chooser__models');
11943
11944                    for (var c = 0, clen = choosers.length; c < clen; c++) {
11945                        var models = choosers[c].querySelectorAll('.lx-build-price-model-chooser-model');
11946                        var modelHeights = [], lastHeight = 0;
11947                        for (var m = 0, mlen = models.length; m < mlen; m++) { modelHeights.push(models[m].clientHeight); }
11948                        modelHeights.sort();
11949                        lastHeight = modelHeights[modelHeights.length - 1];
11950                        if (modelHeights[0] != lastHeight) {
11951                            for (var m = 0, mlen = models.length; m < mlen;
11951 m++) { models[m].setAttribute('style', 'height:' + lastHeight + 'px;'); }
11952                        }
11953                    }
11954                }
11955            }, 0, false);
11956        }
11957
11958        $scope.emitEngineVariantSelected = function (engineVariant, currentSelection, userInitiated) {
11959            var data = {
11960                selection: currentSelection === engineVariant.uriName ? null : engineVariant.uriName,
11961                clearDownstream: (engineVariant != currentSelection && currentSelection != null),
11962                userInitiated: userInitiated
11963            };
11964            $scope.$emit(BuildPriceService.events.userSelection, 'Model + Variant selected');
11965
11966            $scope.$emit(BuildPriceService.events.engineVariantSelected, data);
11967        };
11968
11969        $scope.variantHasOffer = function (model) {
11970            var hasOffer = false;
11971            model.modelEngineVariants.forEach(function (variant) {
11972                if ($scope.isOnOffer(variant)) {
11973                    hasOffer = true;
11974                }
11975            });
11976
11977            return hasOffer;
11978        };
11979
11980        $scope.isOnOffer = function (obj) {
11981            var bool = obj.onOffer;
11982            return bool;
11983        }
11984
11985        $scope.emitModelSelected = function (model, currentSelection, userInitiated, modelElement) {
11986            //reset
11987            bpModel.currentPage = 0;
11988
11989            if (userInitiated) {
11990                var shortModel = modelElement.replace(/\s+/g, '');
11991                setTimeout(function () {
11992                    $('html, body').animate({ scrollTop: $(shortModel).offset().top - 10 }, 250)
11993                }, 500);
11994            }
11995
11996            $scope.$emit(BuildPriceService.events.onOfferText, null);
11997
11998            var data = {
11999                selection: currentSelection === model.uriName ? null : model.uriName,
12000                clearDownstream: (model != currentSelection && currentSelection != null),
12001                userInitiated: userInitiated
12002            };
12003            $scope.$emit(BuildPriceService.events.modelSelected, data);
12004        };
12005
12006        $scope.ReceiveModels = function () {
12007            bpModel.currentPage = setCurrentPage(0);
12008            bpModel.vehicleData = BuildPriceAPIService.data;            
12009            console.info("%c 0. buildPriceModelController -> ReceiveModels: ", "color: darkblue; ", BuildPriceAPIService.data);
12010            $scope.$emit(BuildPriceService.events.overviewDataReceived, BuildPriceAPIService.data);
12011            $scope.setModelHeights();
12012        };
12013
12014        $scope.$on(BuildPriceService.events.pageNavigationBroadcast, function (e, target) {
12015            var isCurrent = target.id == 'model'; 
12016            bpModel.isCurrentStep = isCurrent;
12017        });
12018
12019        $scope.$watch('bpModel.selectedModel', function (newValue, oldValue) {
12020            if (newValue != null && bpModel.isCurrentStep && !bpModel.modelSelected) {
12021                var shortModel = '.contains-model--' + newValue.replace(/\s+/g, '');
12022
12023                setTimeout(function () {
12024                    $('html, body').animate({ scrollTop: $(shortModel).offset().top - 10 }, 250)
12025                }, 500);
12026
12027                bpModel.modelSelected = true;
12028            }
12029        });
12030
12031        function init() {
12032            //console.log('bpModel init: ', BuildPriceAPIService.getModels($scope.ReceiveModels));
12033            BuildPriceAPIService.getModels().then($scope.ReceiveModels);
12034        }
12035
12036        init();
12037      }
12038    }]);
12039
12040}());;
12041"use strict";
12042/* Directives */
12043angular.module("Lexus.Directives").directive("lxBuildPriceNavigation", [
12044    function () {
12045        buildPriceNavigationController.$inject = ["$scope", "$window", "$interpolate", "$element", "BuildPriceService"];
12046        return {
12047            scope: {
12048                model: "<carmodel",
12049                engineVariant: "<variant",
12050                enhancementPack: "<enhancement",
12051                grade: "<grade",
12052                colour: "<colour",
12053                trim: "<trim",
12054                driveAwayPrice: "<price",
12055                driveAwayPriceLoading: "<priceLoading",
12056                vehicleLoading: "<vehicleLoading",
12057                steps: "<steps",
12058                activeStepIdx: "<activeIndex",
12059                paintResolved: "<paintresolved",
12060                paintId: "<paintId",
12061                onOfferText: "<onOfferText",
12062                toggleSbp: "<toggleSbp",
12063                hideSaveBuild: "<hideSaveBuild",
12064                postcode: "<postcode",
12065            },
12066            restrict: "A",
12067            controller: buildPriceNavigationController,
12068            controllerAs: "bpNav",
12069            bindToController: true,
12070            transclude: true,
12071            link: function (scope, element, attrs, ctrl, transclude) {
12072                transclude(scope, function (clone) {
12073                    element.append(clone);
12074                });
12075                scope.init();
12076            },
12077        };
12078
12079        function buildPriceNavigationController($scope, $window, $interpolate, $element, BuildPriceService) {
12080            var bpNav = this;
12081            bpNav.activeStep = null;
12082            bpNav.toggleSbp = false;
12083            bpNav.lastEngineVariant = null;
12084
12085            $scope.isLastStep = function () {
12086                return $scope.bpNav.activeStepIdx + 1 < $scope.bpNav.steps.length;
12087            };
12088
12089            $scope.shiftMenu = function (targetIndex) {
12090                var overflow = angular.element(".lx-c-nav__menu-group");
12091
12092                overflow.scrollLeft(0);
12093
12094                var windowwidth = $window.innerWidth;
12095                var p = angular.element(".lx-bp-nav__item-" + targetIndex + "");
12096
12097                var scrollPositionCenter = p.offset().left - p.width() / 2;
12098                var pRight = p.offset().left + p.width();
12099
12100                if (overflow[0].scrollWidth > windowwidth && pRight > windowwidth / 3) {
12101                    if (pRight < windowwidth / 2) {
12102                        overflow.scrollLeft(0);
12103                    } else {
12104                        overflow.scrollLeft(scrollPositionCenter + 10);
12105                    }
12106                }
12107            };
12108
12109            //#region EVENTS
12110
12111            //allows navigation by index or id
12112            $scope.emitNavigation = function (data, $event) {
12113                console.log("emitNavigation: ", bpNav);
12114
12115                if (bpNav.vehicleLoading) {
12116                    return;
12117                }
12118                if (typeof data == "number") {
12119                    if (bpNav.steps[data].disabled) {
12120                        return;
12121                    }
12122                } else {
12123                    if (
12124                        bpNav.steps.find(function (step) {
12125                            return step.id === data;
12126                        }).disabled
12127                    ) {
12128                        return;
12129                    }
12130                }
12131
12132                $scope.$emit("bp-page-navigation", data);
12133            };
12134            //#endregion
12135
12136            $scope.$watch(
12137                function (scope) {
12138                    return scope.bpNav.activeStepIdx;
12139                },
12140                function (newVal, oldVal) {
12141                    if (typeof newVal == "number") {
12142                        bpNav.activeStep = bpNav.steps[newVal];
12143
12144                        $scope.shiftMenu(bpNav.activeStep.id);
12145                    }
12146                },
12147            );
12148
12149            $scope.$watch(
12150                function (scope) {
12151                    return scope.bpNav.engineVariant;
12152                },
12153                function (newVal, oldVal) {
12154                    if (typeof oldVal == "string") {
12155                        bpNav.lastEngineVariant = oldVal;
12156                    }
12157                    if (newVal != null && newVal != oldVal) {
12158                        setTimeout(function () {
12159                            var stickyHeight = angular.element(".lx-footer-sticky")[0].clientHeight + 40 + "px";
12160                            angular.element(".lx-footer").css({ "padding-bottom": stickyHeight });
12161                        }, 100);
12162                    }
12163                },
12164            );
12165
12166            // SF Personaliation - Build & Price tool
12167            $scope.$watch(
12168                "bpNav.activeStep.id",
12169                BuildPriceService.watchDebounce(function (newVal, oldVal) {
12170                    let navTitle = newVal.toLowerCase();
12171                    if (navTitle === "colour") {
12172                        navTitle = "colour & trim";
12173                    }
12174                    if (typeof window !== "undefined") {
12175                        const sfCustomEvent = new CustomEvent("build-and-price-nav-change", {
12176                            detail: { navTitle },
12177                        });
12178                        window.document.dispatchEvent(sfCustomEvent);
12179                    }
12180                }, 600),
12181                true,
12182            );
12183
12184            $scope.encodeUriFragment = function (fragment) {
12185                return BuildPriceService.encodeUriFragment(fragment);
12186            };
12187
12188            $scope.getUrl = function (prefix, route) {
12189                return prefix + route;
12190            };
12191
12192            $scope.init = function () {};
12193
12194            $scope.openNav = function () {
12195                bpNav.toggleSbp
12196                    ? $scope.$emit(BuildPriceService.events.triggerCloseSaveBuildPriceDialog, {})
12197                    : $scope.$emit(BuildPriceService.events.triggerSaveBuildPriceDialog, {});
12198            };
12199
12200            $scope.openPostcodeModal = function () {
12201                $scope.$emit(BuildPriceService.events.openPostcodeDialog);
12202            };
12203
12204            $scope.isStickyFooterVisible = function () {
12205                var result = (bpNav.model && bpNav.engineVariant) || !bpNav.hideSaveBuild;
12206                $scope.$emit("sticky-footer-visible", result);
12207                return result;
12208            };
12209
12210            $scope.openPostcodeSlideout = function () {
12211                $scope.$emit(BuildPriceService.events.openPostcodeSlideout);
12212            };
12213        }
12214    },
12215]);
12216;
12217//Only Allowed to watch 
12218//rangeBuildPrice.collections.trims[] 
12219//rangeBuildPrice.collections.colours[]
12220//rangeBuildPrice.collections.exteriorImages[]
12221//rangeBuildPrice.collections.interiorImages[]
12222
12223//Only allowed to emit 
12224//trim selected 
12225//colour selected
12226
12227(function (undefined) {
12228
12229    "use strict";
12230    /* Directives */
12231    angular.module('Lexus.Directives')
12232    .directive('lxBuildPricePaintTrim', [function () {
12233
12234        var bpInteriorCtrl = null;
12235        var bpExteriorCtrl = null;
12236        buildPricePaintTrimController.$inject = ['$scope', 'BuildPriceService', 'BuildPriceAPIService', 'VehicleService', '$timeout', '$log'];
12237        return {
12238            scope: {
12239                engineVariant: '<variant',
12240                grade: '<grade',
12241                trim: '<trim',
12242                enhancementPack: '<enhancement',
12243                colour: '<colour',
12244                paintId: '<paintId',
12245                specifics: '<specifics'
12246            },
12247            restrict: 'A',
12248            controller: buildPricePaintTrimController,
12249            controllerAs: 'bpPaintTrim',
12250            bindToController: true,
12251            transclude: true,
12252            link: function (scope, element, attrs, ctrl, transclude) {
12253                transclude(scope, function (clone) {
12254                    element.append(clone);
12255                });
12256            }
12257        };
12258
12259        
12260        function buildPricePaintTrimController($scope, BuildPriceService, BuildPriceAPIService, VehicleService, $timeout, $log) {
12261            var bpPaintTrim = this;
12262
12263            bpPaintTrim.selectedPaintName = '';
12264            bpPaintTrim.selectedPaintCode = '';
12265            bpPaintTrim.selectedTrimName = '';
12266
12267            bpPaintTrim.setSelectedPaint = setSelectedPaint;
12268            bpPaintTrim.setSelectedTrim = setSelectedTrim;
12269            bpPaintTrim.isTrimSelected = isTrimSelected;           
12270
12271            bpPaintTrim.registerInterior = function (controller) {
12272                bpInteriorCtrl = controller;
12273            };
12274
12275            bpPaintTrim.registerExterior = function (controller) {
12276                bpExteriorCtrl = controller;
12277            };
12278
12279            function setSelectedPaint(paint) {
12280
12281                bpPaintTrim.selectedPaintCode = paint.code;
12282                bpPaintTrim.selectedPaintName = paint.shortName;
12283                setColour(bpPaintTrim.specifics.gradeId, bpPaintTrim.specifics.variantId, bpPaintTrim.specifics.trimId);
12284            }
12285
12286            function setSelectedTrim(trim) {
12287
12288                bpPaintTrim.selectedTrimName = trim.name;
12289                if (bpPaintTrim.trim !== trim.uriName) {
12290                    emitTrimSelection(trim.uriName, true, true, bpPaintTrim.colour);
12291                }
12292            }
12293
12294            function isTrimSelected(trim) {
12295                return bpPaintTrim.trim === trim.uriName;
12296            }
12297
12298            BuildPriceService = BuildPriceService;
12299
12300            var unloadExteriorPromise = null;
12301
12302            $scope.exterior = function () {
12303                //Set scope to exterior.
12304                $scope.tabExterior = true;
12305                if (bpInteriorCtrl)
12306                {
12307                    bpInteriorCtrl.ensurePanoRemoved();
12308                }
12309            };
12310
12311            $scope.interior = function () {
12312                //Set scope to interior.
12313                $scope.tabExterior = false;
12314
12315                if ($scope.bpPaintTrim.specifics) {
12316                    bpInteriorCtrl.loadInterior($scope.bpPaintTrim.specifics);
12317                }
12318            };
12319
12320            $scope.$watch('bpPaintTrim.specifics', BuildPriceService.watchDebounce(function (newValue, oldValue) {
12321                if (newValue) {
12322                    //setColours(newValue.gradeId, newValue.variantId, newValue.trimId);
12323                    console.info("%c 6. BuildPricePaintTrimController received SPECIFICS: ", "color: teal; ", [newValue]);
12324                }
12325            }, 50), true);
12326
12327            //Need to rebind trims when colour changes to show which trims are available and unavailable in that colour
12328            //$scope.$watch('bpPaintTrim.colour', BuildPriceService.watchDebounce(function trimReceived(newValue, oldValue) {
12329            //    if (newValue && newValue != oldValue) {
12330            //        setAvailableTrimsBasedOnPaint(bpPaintTrim.engineVariant, bpPaintTrim.grade, bpPaintTrim.enhancementPack);
12331            //    }
12332            //}, 50));
12333
12334            $scope.$watch('bpPaintTrim.trim', BuildPriceService.watchDebounce(function trimReceived(newValue, oldValue) {
12335                if (newValue) {
12336                    console.info("%c 3. buildPricePaintTrimController received TRIM: ", "color: teal; ", newValue);
12337
12338                    var previousSelectionNull = (oldValue == null);
12339                    var selectionChanged = (oldValue !== newValue);
12340
12341                    getSelectedEnhancementPack(bpPaintTrim.engineVariant, bpPaintTrim.grade, bpPaintTrim.enhancementPack);
12342
12343                    BuildPriceService.waitFor.call($scope, 'bpPaintTrim.availableTrims', BuildPriceService.waitCondition.NOTNULL)
12344                    .then(function (value) {
12345                        validateTrimSelection(newValue, value, (selectionChanged && !previousSelectionNull), true);
12346                    });
12347                }
12348            }, 50), true);
12349
12350            $scope.$watch('bpPaintTrim.grade', BuildPriceService.watchDebounce(function (newValue, oldValue) {
12351                if (bpPaintTrim.allTrims) delete bpPaintTrim.allTrims;
12352            }, 50), true);
12353
12354            $scope.$watch('bpPaintTrim.enhancementPack', BuildPriceService.watchDebounce(function (newValue, oldValue) {
12355                if (newValue && newValue != oldValue) {
12356                    console.info("%c 4. buildPricePaintTrimController received Enhancement Pack: ", "color: teal; ", newValue);
12357
12358                    getSelectedEnhancementPack(bpPaintTrim.engineVariant, bpPaintTrim.grade, newValue);
12359                }
12360            }, 50), true);
12361
12362            function setAvailableTrimsBasedOnPaint() { } 
12363
12364            //This method still gets called twice on load with a deeplink, wouldnt mind fixing that
12365            function getSelectedEnhancementPack(engineVariantName, gradeName, enhancementPack, callback) {
12366                var engineVariant = BuildPriceAPIService.findEngineVariantInVehicleData(engineVariantName);
12367                var grade = BuildPriceAPIService.getGradeByName(engineVariantName, gradeName);
12368
12369                BuildPriceAPIService.getVariants(engineVariantName, grade.id, function (variants) {
12370
12371                    var variant = BuildPriceAPIService.getVariantByName(engineVariantName, grade.id, enhancementPack);
12372                    
12373                    BuildPriceAPIService.getTrims(engineVariantName, grade.id, variant.mdmid, function (availableTrims, unavailableTrims, allTrims) {
12374                        //This is setting trims based on the MV response
12375                        setTrims(availableTrims, unavailableTrims, allTrims, false);
12376
12377                        bpPaintTrim.allTrims = allTrims;
12378
12379                        setTrim(engineVariantName, grade.id, variant.mdmid);
12380
12381
12382                        if (bpPaintTrim.trim) {
12383                            var trim = BuildPriceAPIService.getTr
12383imByName(engineVariantName, grade.id, variant.mdmid, bpPaintTrim.trim);
12384
12385                            BuildPriceAPIService.getPaints(engineVariantName, grade.id, variant.mdmid, trim.id, function (availableColours, unavailableColours) {
12386
12387                                setColours(availableColours, unavailableColours);
12388                                setColour(grade.id, variant.mdmid, trim.id);
12389
12390                            });
12391                        }
12392
12393
12394                    });                    
12395                });
12396            }
12397
12398            function validateTrimSelection(trim, trims, clearDownstream, userInitiated) {
12399                var trimObj = {};
12400
12401                for (var index = 0; index < trims.length; index++) {
12402                    if (trims[index].uriName == trim) {
12403                        trimObj = trims[index];
12404                        break;
12405                    }
12406                }
12407
12408                if (!trimObj) {
12409                    var defaultSelection = getDefaultTrim();
12410
12411                    var invalidSelectionData = {
12412                        controllerName: BuildPriceService.stepIds.PAINT_TRIM,
12413                        $scope: $scope,
12414                        defaultValue: defaultSelection.uriName,
12415                        invalidValue: bpPaintTrim.trim,
12416                        userInitiated: userInitiated,
12417                        clearDownstreamFrom: userInitiated ? 4 : null
12418                    };
12419
12420                    BuildPriceService.emitInvalidSelection(invalidSelectionData);
12421
12422                    emitTrimSelection(defaultSelection.uriName, clearDownstream, userInitiated);
12423                } else {
12424                    bpPaintTrim.selectedTrimName = trimObj.name;
12425                }
12426            }
12427
12428            function validatePaintSelection(paint) {
12429                var selectedPaint = {};
12430
12431                for (var index = 0; index < bpPaintTrim.availableColours.length; index++) {
12432                    if (bpPaintTrim.availableColours[index].uriName == paint) {
12433                        selectedPaint = bpPaintTrim.availableColours[index];
12434                        break;
12435                    }
12436                }
12437
12438                if (!selectedPaint) {
12439                    selectedPaint = getDefaultPaint();
12440
12441                    var invalidSelectionData = {
12442                        controllerName: 'buildPricePaintTrimController (expected - incomplete)',
12443                        $scope: $scope,
12444                        defaultValue: selectedPaint.uriName,
12445                        invalidValue: bpPaintTrim.colour
12446                        /*
12447                        * Once we have a user-initiated selection process implement these variables
12448                        userInitiated: true/false,
12449                        clearDownstreamFrom: userInitiated ? 5 : null
12450                        */
12451                    };
12452                    BuildPriceService.emitInvalidSelection(invalidSelectionData);
12453                }
12454
12455
12456                bpPaintTrim.selectedPaintName = selectedPaint.shortName;
12457                bpPaintTrim.selectedPaintCode = selectedPaint.code;
12458                emitPaintSelection(selectedPaint);
12459            }
12460
12461            function getDefaultPaint() {
12462                return bpPaintTrim.availableColours[0];
12463            }
12464
12465            function getDefaultTrim() {
12466                return bpPaintTrim.availableTrims[0];
12467            }
12468
12469            //#region Events 
12470
12471            function emitPaintSelection(paintObj) {
12472                $scope.$emit(BuildPriceService.events.paintSelected, paintObj);
12473            };
12474
12475            function emitPaintsReceived(paints) {
12476                $scope.$emit(BuildPriceService.events.paintsReceived, paints);
12477            };
12478
12479            function emitTrimSelection(trimName, clearDownstream, userInitiated, revertColour) {
12480                var data = {
12481                    clearDownstream: clearDownstream,
12482                    selection: trimName,
12483                    userInitiated: userInitiated,
12484                    revertColour: revertColour
12485                };
12486
12487                $scope.$emit(BuildPriceService.events.trimSelected, data)
12488            };
12489
12490            function emitTrimsReceived(trims) {
12491                $scope.$emit(BuildPriceService.events.trimsReceived, trims);
12492            };
12493
12494            //#endregion
12495
12496            function setTrims(availableTrims, unAvailableTrims, allTrims, showTrims) {
12497                //console.info("%c 3b. buildPricePaintTrimController setTrims 
12497-> allTrims: ", "color: teal; ", allTrims.map(function (v, i) { return (i + 1) + ". " + v.code + "" }).join(', '));
12498                //console.info("%c 3c. buildPricePaintTrimController setTrims -> available: ", "color: teal; ", availableTrims.map(function (v, i) { return (i + 1) + ". " + v.code + "" }).join(', '));
12499                //console.info("%c 3d. buildPricePaintTrimController setTrims -> unavailable: ", "color: teal; ", unAvailableTrims.map(function (v, i) { return (i + 1) + ". " + v.code + "" }).join(', '));
12500                bpPaintTrim.availableTrims = availableTrims;
12501                bpPaintTrim.unavailableTrims = unAvailableTrims;
12502                emitTrimsReceived(bpPaintTrim.availableTrims);
12503                bpPaintTrim.showTrims = showTrims;
12504            }
12505
12506            function setTrim(engineVariant, gradeId, variantId) {
12507                if (!bpPaintTrim.trim && bpPaintTrim.availableTrims.length > 0) {
12508                    var defaultTrim = getDefaultTrim();
12509                    bpPaintTrim.selectedTrimName = defaultTrim.name;
12510                    emitTrimSelection(defaultTrim.uriName, true, false);
12511                }
12512                else {
12513                    var trimobj = BuildPriceAPIService.getTrimByName(engineVariant, gradeId, variantId, bpPaintTrim.trim);
12514
12515                    if (trimobj) {
12516                        bpPaintTrim.selectedTrimName = trimobj.name;
12517                        emitTrimSelection(trimobj.uriName, true, false);
12518                    }
12519                    else { //Matching Trim couldnt be found revert to the default
12520                        bpPaintTrim.selectedTrimName = bpPaintTrim.availableTrims[0].name;
12521                        emitTrimSelection(bpPaintTrim.availableTrims[0].uriName, true, false);
12522                    }
12523                } 
12524            }
12525
12526            function setColours(availableColours, unavailableColours) {
12527                var maxColoursCanBeShown = 12;
12528                if (availableColours && unavailableColours && ((availableColours.length + unavailableColours.length) > maxColoursCanBeShown))
12529                {
12530                    unavailableColours = null;
12531                }
12532
12533                bpPaintTrim.availableColours = availableColours;
12534                bpPaintTrim.unavailableColours = unavailableColours;
12535                emitPaintsReceived(bpPaintTrim.availableColours);
12536
12537                if (!bpPaintTrim.colour && bpPaintTrim.availableColours.length > 0) {
12538                    var defaultPaint = getDefaultPaint();
12539                    bpPaintTrim.selectedPaintName = defaultPaint.shortName;
12540                }
12541                if(!bpPaintTrim.selectedPaintName){
12542                    bpPaintTrim.selectedPaintName = bpPaintTrim.colour;
12543                }
12544
12545                bpPaintTrim.showColours = (availableColours.length > 0) ? true : false;
12546            }
12547
12548            function setColour(gradeId, variantMDMId, trimId) {
12549                //Need to find by URI name if its being reversed from the path, 
12550                //need to use shortname if its triggered by a swatch click
12551                var paintobj = BuildPriceAPIService.getPaintByName(bpPaintTrim.engineVariant, gradeId, variantMDMId, trimId, bpPaintTrim.selectedPaintName);
12552                
12553                if (!paintobj) {
12554                    paintobj = bpPaintTrim.availableColours[0];
12555                }
12556
12557                //$log.info("paintobj");
12558                //$log.info(paintobj);
12559
12560                bpPaintTrim.selectedPaintName = paintobj.shortName;
12561                bpPaintTrim.selectedPaintCode = paintobj.code;
12562
12563                var myGrade = BuildPriceAPIService.getGradeByID(bpPaintTrim.engineVariant, gradeId);
12564
12565                emitPaintSelection(paintobj, true, false);
12566
12567                bpExteriorCtrl.loadExterior(
12568                    {
12569                        gradeId: gradeId,
12570                        gradeMDMId: myGrade.gradeID,
12571                        variantId: variantMDMId,
12572                        trimId: trimId,
12573                        materialCode: paintobj.materialCode,
12574                        paintId: paintobj.id
12575                    });
12576
12577
12578                BuildPriceAPIService.getTrims(bpPaintTrim.engineVariant, gradeId, variantMDMId, function (availableTrims, unavailableTrims, allTrims) {
12579
12580                    var currentTrims = BuildPriceAPIService.getUnavailableTrimsBasedOnPaint(bpPaintTrim.engineVariant, gradeId, variantMDMId, trimId, bpPaintTrim.selectedPaintName);
12581
12582                    //This needs to be filtered so we get the nice name from Grade it isn't pretty but data is lacking.
12583                    var currentUnavailable = BuildPriceAPIService.GetUnAvailableCollection(allTrims, currentTrims.availableTrims);
12584                    var currentAvailable = BuildPriceAPIService.FilterAvailableCollection(availableTrims, currentTrims.availableTrims);
12585                    //This is setting trims based on the Paint Response
12586                    setTrims(currentAvailable, currentUnavailable, allTrims, true);
12587                });
12588
12589            }
12590        }
12591    }]);
12592
12593}());
12594
12595;
12596(function (undefined) {
12597    "use strict";
12598    /* Directives */
12599    angular.module('Lexus.Directives')
12600    .directive('lxBuildPriceSummary', [function () {
12601        BuildPriceSummaryController.$inject = ['$rootScope', '$scope', '$timeout', '$httpParamSerializer', 'PricingService', 'BuildPriceService', 'BuildPriceAPIService', 'VehicleService'];
12602        return {
12603            scope: {},
12604            restrict: 'A',
12605            controller: BuildPriceSummaryController,
12606            controllerAs: 'bpSummary',
12607            bindToController: {
12608                emailDeeplinkUrlParameters: '<emailDeeplinkUrlParameters',
12609                model: '<model',
12610                engineVariant: '<variant',
12611                grade: '<grade',
12612                enhancementPack: '<enhancement',
12613                trim: '<trim',   //INTERIOR value
12614                colour: '<colour',  //EXTERIOR value
12615                postcode: '<postcode',
12616                pricingZone: '<pricingZone',
12617                paintId: '<paintId',
12618                specifics: '<specifics',
12619                vehicleImage: '<vehicleImage',
12620                enablePricingByZone: '<enablePricingZone',
12621                hostOrigin: '<hostOrigin',
12622            },
12623            transclude: true,
12624            link: function (scope, element, attrs, ctrl, transclude) {
12625                transclude(scope, function (clone) {
12626                    element.append(clone);
12627                });
12628
12629                scope.showContactDealer = false;
12630            }
12631        };
12632
12633        function BuildPriceSummaryController($rootScope, $scope, $timeout, $httpParamSerializer, PricingService, BuildPriceService, BuildPriceAPIService, VehicleService) {
12634            var bpSummary = this;
12635            bpSummary.loadingData = false;
12636            bpSummary.pricingSuccess = true;
12637            bpSummary.holdPricingCalls = true;
12638
12639            $scope.$on(BuildPriceService.events.holdPricingCallsRelay, function (event, data) {
12640                bpSummary.holdPricingCalls = data;
12641            });
12642
12643            $scope.$watch("bpSummary.paintId", BuildPriceService.watchDebounce(function (newValue, oldValue) {
12644                if (newValue !== null) {
12645                    getDetailPriceByPaint();
12646                }
12647            }, 50), true);
12648
12649            $scope.$watch('bpSummary.specifics', BuildPriceService.watchDebounce(function (newValue, oldValue) {
12650                if (newValue !== null) {
12651                    console.info("%c 7. BuildPriceSummaryController received SPECIFICS: ", "color: green; ", newValue);
12652                    $rootScope.$broadcast('refresh-gtm', 'Build and Price');
12653                }
12654            }, 50), true);
12655
12656            $scope.$watch("bpSummary.postcode", BuildPriceService.watchDebounce(function (newValue, oldValue) {
12657                if (newValue !== null) {
12658                    console.info("%c BuildPriceSummaryController received POSTCODE: ", "color: green; ", newValue);
12659                    getDetailPriceByPaint();
12660                }
12661            }, 50), true);
12662
12663            function getDetailPriceByPaint() {
12664                PricingService.setParam("enablePricingByZone", bpSummary.enablePricingByZone);
12665                if (bpSummary.paintId && bpSummary.postcode && $scope.bpSummary.specifics) {
12666
12667                    BuildPriceService.waitFor.call($scope, 'bpSummary.holdPricingCalls', BuildPriceService.waitCondition.ISSTRICTLYFALSE)
12668                    .then(function success(val) {
12669                        PricingService.detailPriceByPaint(
12670                        $scope.bpSummary.specifics.gradeId,
12671                        $scope.bpSummary.specifics.variantId,
12672                        $scope.bpSummary.specifics.trimId,
12673                        bpSummary.paintId,
12674                        bpSummary.postcode,
12675                        bpSummary.pricingZone)
12676                        .then(function (data) {
12677                            if (data.status === "Success") {
12678                                if (data.data && data.data.listPriceIncGST && data.data.driveAway) {
12679                                    if (bpSummary.driveAwayPriceLoadingTimeout) {
12680                                        $timeout.cancel(bpSummary.driveAwayPriceLoadingTimeout);
12681                                    }
12682
12683                                    bpSummary.pricingSuccess = true;
12684                                    bpSummary.details = data.data;
12685                                    emitDriveAwayPriceReceived(data.data);
12686                                } else {
12687                                    //success but no price
12688                                    bpSummary.pricingSuccess = false;
12689                                }
12690
12691                            } else {
12692                                bpSummary.pricingSuccess = false;
12693                            }
12694                        }, function (reason) { ajaxPriceFailure(reason, "PricingService.detailPriceByPaint");  })
12695                        .finally(function (data) {
12696
12697                            emitDriveAwayPriceDidExitLoading(data);
12698                            pricingReport();
12699                            bpSummary.loadingData = false;
12700                        });
12701                        emitDriveAwayPriceDidEnterLoading();
12702                        bpSummary.loadingData = true;
12703                        var variant = BuildPriceAPIService.getVariantByName(bpSummary.engineVariant, bpSummary.specifics.gradeId, bpSummary.enhancementPack);
12704                        var trim = BuildPriceAPIService.getTrimByName(bpSummary.engineVariant, bpSummary.specifics.gradeId, variant.mdmid, bpSummary.trim);
12705                        var paint = BuildPriceAPIService.getPaintByName(bpSummary.engineVariant, bpSummary.specifics.gradeId, variant.mdmid, trim.id, bpSummary.colour);
12706                        bpSummary.enhancementPackName = variant.enhancementPackName;
12707                        bpSummary.trimName = trim.name;
12708                        bpSummary.colourName = paint.shortName;
12709                    });
12710                }
12711            }
12712
12713            bpSummary.navigate = navigate;
12714
12715            //#region Events
12716            function emitDriveAwayPriceDidEnterLoading(data) {
12717                $scope.$emit(BuildPriceService.events.priceLoad, data);
12718                console.log('%c $$$$$$$$   priceLoad   $$$$$$$$', 'color: limegreen; font-weight: bold;');
12719            }
12720
12721            function emitDriveAwayPriceDidExitLoading(data) {
12722                $scope.$emit(BuildPriceService.events.priceDone, data);
12723                console.log('%c $$$$$$$$   priceDone   $$$$$$$$', 'color: limegreen; font-weight: bold;');
12724            }
12725
12726            function emitDriveAwayPriceReceived(data) {
12727                $scope.$emit(BuildPriceService.events.priceReceived, data);
12728                console.log('%c $$$$$$$$ priceReceived $$$$$$$$', 'color: limegreen; font-weight: bold;');
12729            }
12730
12731            function ajaxPriceFailure(data, source) {
12732
12733                bpSummary.pricingSuccess = false;
12734                console.log('%c  ajaxPriceFailure  ', 'color: red; font-weight: bold;', source);
12735
12736                pricingReport();
12737
12738                $scope.$emit(BuildPriceService.events.priceError, { source: "bpSummary" });
12739
12740            }
12741
12742
12743            function pricingReport() {
12744                console.log("bpSummary -> pricingReport: ", bpSummary.pricingSuccess);
12745
12746                var delay = 500;  // milliseconds to delay the loading.
12747
12748                bpSummary.driveAwayPriceLoadingTimeout = $timeout(function () {
12749
12750                    console.log("bpSummary -> pricingReport: timeout finished", bpSummary.pricingSuccess);
12751
12752                    if (bpSummary.pricingSuccess) {
12753                        angular.element('.lx-build-price-summary').removeClass('pricing-error');
12754                        angular.element('.lx-footer-sticky').removeClass('pricing-error');
12755                    } else {
12756                        angular.element('.lx-build-price-summary').addClass('pricing-error');
12757                        angular.element('.lx-footer-sticky').addClass('pricing-error');
12758                    }
12759
12760                }, delay); // Don't bother showing "loading" for time in delay
12761            }
12762
12763            $scope.$on(BuildPriceService.events.vehicleDataRelay, function (event, data) {
12764                bpSummary.vehicleData = data;
12765            });
12766
12767            $scope.$on(BuildPriceService.events.threeSixtyLoad, function (event, data) {
12768                console.info("%c BuildPriceSummaryController received threeSixtyLoad: ", "color: green; ", data);
12769            });
12770
12771            //#endregion
12772
12773            $scope.$on('hide-build-price-contact-dealer-form', function (event, data) {
12774                $scope.toggleContactDealerForm();
12775            });
12776
12777            $scope.toggleContactDealerForm = function () {
12778                $scope.$broadcast('toggle-build-price-contact-dealer-form', getVehicleData());
12779                bpSummary.showContactDealer = !(bpSummary.showContactDealer || false);
12780            };
12781
12782            $scope.getGradeNameFromId = BuildPriceService.getGradeNameFromId;
12783
12784            function navigate(page) {
12785                $scope.$emit('bp-page-navigation', page);
12786            }
12787
12788            function getVehicleData() {
12789                return {
12790                    model: bpSummary.model,
12791                    engineVariant: bpSummary.engineVariant,
12792                    grade: bpSummary.grade,
12793                    enhancementPack: bpSummary.enhancementPack,
12794                    trim: bpSummary.trim,
12795                    colour: bpSummary.colour
12796                };
12797            }
12798
12799            function updateModelFromVehicle(vehicleData) {
12800                bpSummary.bookTestDriveUrlParameters = $httpParamSerializer(vehicleData);
12801            }
12802
12803            $scope.openPostcodeModal = function () {
12804                $scope.$emit(BuildPriceService.events.openPostcodeDialog);
12805            };
12806        }
12807    }]);
12808}());
12809
12810;
12811"use strict";
12812/* Directives */
12813angular.module('Lexus.Directives')
12814.directive('lxFormsBuildPriceContactDealer', [function () {
12815    FormsBuildPriceContactDealerController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$location', '$rootScope', 'DealersService', 'AddressSuggestService', 'UserPreferencesService', 'VehicleService', 'vcRecaptchaService', 'FormsService', 'ngDialog', 'EloquaService'];
12816    return {
12817        scope: true,
12818        restrict: 'A',
12819        controller: FormsBuildPriceContactDealerController,
12820        controllerAs: 'ffc',
12821        bindToController: true,
12822        link: function (scope, element, attrs, filter, http) {
12823            scope.successContent = element.find('.lx-form-success');
12824            scope.failureContent = element.find('.lx-form-failure');
12825            scope.initVars = {
12826                showForm: attrs.showForm,
12827                forceDealerId: attrs.forceDealerId,
12828                dealerCompositeId: attrs.dealerCompositeId,
12829                isDealerSite: attrs.isDealerSite,
12830                isMultiBranchDealership: attrs.isMultiBranchDealership,
12831
12832            }
12833        }
12834    };
12835
12836    function FormsBuildPriceContactDealerController($scope, $http, $filter, $timeout, $window, $interval, $location, $rootScope, DealersService, AddressSuggestService, UserPreferencesService, VehicleService, vcRecaptchaService, FormsService, ngDialog, EloquaService) {
12837        var ffc = this;
12838        $scope.ffc.urlParameters = $location.search();
12839        $scope.ffc.formInitialized = false;
12840        $scope.$watch('bpSummary.showContactDealer', function (newValue, oldValue) {
12841            if (newValue) {
12842                $scope.ffc.formInitialized = true;
12843                $scope.init($scope.initVars.forceDealerId, $scope.initVars.dealerCompositeId, $scope.initVars.isDealerSite === 'true', $scope.initVars.isMultiBranchDealership === 'true');
12844            }
12845        });
12846
12847        $scope.init = function (forceDealerId, dealerCompositeID, isDealerSite, isMultiBranchDealer) {
12848            UserPreferencesService.getLexusVisitorSession().then(function success(data) {
12849                var dataVehicle = UserPreferencesService.getLexusVehicle($scope.ffc.urlParameters);
12850                var dataDealer = UserPreferencesService.getLexusDealer($scope.ffc.urlParameters);
12851                var dataUser = UserPreferencesService.getLexusVisitor($scope.ffc.urlParameters);
12852                $scope.ffc.form = {
12853                    submitting: false,
12854                    completed: false,
12855                    errors: false,
12856                    dealers: null,
12857                    data: {
12858                        postcodes: [],
12859                        carmodel: dataVehicle.model,
12860                        grade: dataVehicle.grade,
12861                        enginevariant: dataVehicle.engineVariant,
12862                        subscribe: false,
12863                        postcode: "",
12864                        suburb: "",
12865                        pricingZone: "",
12866                        additionalcomments: "",
12867                        email: data.Data.email,
12868                        firstname: data.Data.name,
12869                        lastname: data.Data.surname,
12870                        phone: data.Data.phone,
12871                        captcha: "",
12872                        dealerId: dataDealer.dealerID,
12873                        dealerName: dataDealer.dealerName,
12874                        contextDealerSiteId: forceDealerId,
12875                        mcpUserId: window.SalesforceInteractions.getAnonymousId()
12876                    },
12877                };
12878                if (dataUser.suburb && dataUser.postCode && dataUser.state && dataUser.pricingZone) {
12879                    $scope.ffc.form.data.postcodes.push(UserPreferencesService.getAutocompleteTagFromVisitor(dataUser));
12880                    $scope.ffc.form.data.postcode = dataUser.postCode;
12881                    $scope.ffc.form.data.suburb = dataUser.suburb;
12882                    $scope.ffc.form.data.state = dataUser.state;
12883                    $scope.ffc.form.data.pricingZone = dataUser.pricingZone;
12884                }
12885            }, function failure(data) {
12886                $scope.ffc.form = {
12887                    submitting: false,
12888                    completed: false,
12889                    errors: false,
12890                    dealers: null,
12891                    data: {
12892                        postcodes: [],
12893                        carmodel: null,
12894                        grade: null,
12895                        subscribe: false,
12896                        postcode: '',
12897                        additionalcomments: '',
12898                        email: null,
12899                        firstname: null,
12900                        lastname: null,
12901                        phone: null,
12902                        captcha: '',
12903                        dealerId: null,
12904                        dealerName: null
12905                    }
12906                };
12907            }).finally(function () {
12908                if ($scope.ffc.form.data.carmodel) {
12909                    $rootScope.$broadcast('field-car-model-engine-variant-update-selected');
12910                }
12911
12912                //Main Site
12913                if ($scope.ffc.form.data.postcodes && $scope.ffc.form.data.postcodes.length > 0 && !isDealerSite) {
12914                    $scope.searchNow();
12915                }
12916
12917                //Dealer Site Multi Branch
12918                if (isDealerSite && isMultiBranchDealer) {
12919                    $scope.ffc.form.data.dealerId = null;
12920                    $scope.ffc.form.data.dealerName = null;
12921                    $scope.loadBranches(forceDealerId);
12922                }
12923
12924                //Dealer Site NOT Multi Branch
12925                if (isDealerSite && !isMultiBranchDealer) {
12926                    $scope.ffc.form.data.dealerId = dealerCompositeID;
12927                    $scope.ffc.form.data.dealerName = null;
12928                }
12929            });
12930        }
12931
12932        $scope.loadTags = function (query) {
12933            AddressSuggestService.setParam('term', query);
12934            return AddressSuggestService.GetResults();
12935        };
12936
12937        $scope.deselectDealer = function () {
12938            $scope.ffc.form.data.dealerId = $scope.ffc.form.data.dealerName = null;
12939        }
12940
12941        $scope.selectDealer = function (dealerInputId) {
12942            var scrollPos = $(document).scrollTop();
12943            var maxPos = $('#' + dealerInputId).parents('.lx-form__find-dealer-js').offset().top;
12944            if (scrollPos > maxPos) {
12945                TweenLite.to(window, 0.5, { scrollTo: { y: maxPos }, ease: Power2.easeOut });
12946            }
12947        }
12948
12949        $scope.loadBranches = function (dealerID) {
12950            $scope.ffc.form.dealersLoading = true;
12951            DealersService.setParam('dealerID', dealerID);
12952            DealersService.setParam('type', 'Sales');
12953            DealersService.GetDealerBranches();
12954        }
12955
12956        $scope.searchNow = function () {
12957            $scope.ffc.form.dealersLoading = true;
12958            $rootScope.$broadcast("visitor-details-change-autocomplete", {
12959                postCode: $scope.ffc.form.data.postcode,
12960                suburb: $scope.ffc.form.data.suburb,
12961                state: $scope.ffc.form.data.state,
12962                pricingZone: $scope.ffc.form.data.pricingZone,
12963            });
12964            DealersService.setParam('postCode', $scope.ffc.form.data.postcode); //Dummy Implementation needs to be changed
12965            DealersService.setParam('suburb', $scope.ffc.form.data.suburb); //Dummy Implementation needs to be changed
12966            DealersService.setParam('type', 'Sales');
12967            DealersService.GetResults();
12968
12969        }
12970
12971        $scope.getData = function () {
12972            var visitor;
12973            if($scope.ffc.form.data.postcodes.length>0 && $scope.ffc.form.data.postcodes[0].text){
12974                visitor=UserPreferencesService.splitAutoCompleteResult($scope.ffc.form.data.postcodes[0].text);
12975            }
12976            var result = {
12977                vehicle: {
12978                    url: window.location.toString(),
12979                    model: $scope.bpSummary.model,
12980                    engineVariant: $scope.bpSummary.engineVariant,
12981                    grade: $scope.bpSummary.grade,
12982                    enhancementPack: $scope.bpSummary.enhancementPack,
12983                    trim: $scope.bpSummary.trim,
12984                    paint: $scope.bpSummary.colour,
12985                    materialCode: $scope.bpSummary.paintId
12986                },
12987                pricingEstimate: {
12988                    driveAway: $scope.bpSummary?.details?.driveAway,
12989                    ctpInsuranceAmount: $scope.bpSummary?.details?.ctpInsuranceAmount,
12990                    dealerDelivery: $scope.bpSummary?.details?.dealerDelivery,
12991                    enhancementPackValue: $scope.bpSummary?.details?.enhancementPackValue,
12992                    listPriceIncGST: $scope.bpSummary?.details?.listPriceIncGST,
12993                    luxuryCarTax: $scope.bpSummary?.details?.luxuryCarTax,
12994                    onRoadCosts: $scope.bpSummary?.details?.onRoadCosts,
12995                    paintPrice: $scope.bpSummary?.details?.paintPrice,
12996                    registrationAmount: $scope.bpSummary?.details?.registrationAmount,
12997                    stampDuty: $scope.bpSummary?.details?.stampDuty,
12998                    finance: {
12999                        deposit: $scope.bpSummary.deposit,
13000                        interestRate: $scope.bpSummary.interestRate,
13001                        loanTerm: $scope.bpSummary.loanTerm,
13002                        loanType: $scope.bpSummary.loanType,
13003                        installmentPerMonth: $scope.bpSummary?.details?.installmentPerMonth,
13004                        balloonPayment: $scope.bpSummary.balloonPayment
13005                    }
13006                },
13007                dealerId: $scope.ffc.form.data.dealerId,
13008                firstName: $scope.ffc.form.data.firstname,
13009                lastName: $scope.ffc.form.data.lastname,
13010                email: $scope.ffc.form.data.email,
13011                additionalComments: $scope.ffc.form.data.additionalcomments,
13012                phone: $scope.ffc.form.data.phone,
13013                suburb: $scope.ffc.form.data.suburb,
13014                state: $scope.ffc.form.data.state,
13015                postcode: $scope.ffc.form.data.postcode,
13016                captcha: $scope.ffc.form.data.captcha,
13017                subscribe: $scope.ffc.form.data.subscribe,
13018                contextDealerSiteId: $scope.ffc.form.data.contextDealerSiteId,
13019                mcpUserId: $scope.ffc.form.data.mcpUserId
13020            }
13021            EloquaService.attachEloquaFields(result);
13022            return result;
13023        };
13024        $scope.resetForm = function () {
13025          $scope.init();
13026          $scope.hideContactDealerForm();
13027        }
13028        $rootScope.$on('submit-build-price-contact-dealer-form', function (event, data) {
13029            var formData = $scope.getData();
13030            $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.BuildPriceContactDealer, [Lexus.ID.Site]), data: formData })
13031                .then(function success(response) {
13032                    // this callback will be called asynchronously
13033                    // when the response is available
13034                    $scope.ffc.form.completed = true;
13035                    $scope.ffc.form.failed = false;
13036                    $scope.ffc.form.submitting = false;
13037                    var historyState = "BuildPriceContactDealerForm=Pass";
13038                    $location.state(historyState);
13039                    FormsService.ShowResult($scope.successContent[0]);
13040                    $rootScope.$broadcast('visitor-details-session-change', $scope.getData());
13041                    $rootScope.$broadcast('gtm-form-success', 'Build Price Contact Dealer');
13042                }, function error(response) {
13043                    // called asynchronously if an error occurs
13044                    // or server returns response with an error status.
13045                    $scope.ffc.form.failed = true;
13046                    $scope.ffc.form.errors = data.Data;
13047                    $scope.ffc.form.submitting = false;
13048                    $scope.ffc.form.completed = true;
13049                    var historyState = "BuildPriceContactDealerForm=Fail";
13050                    $location.state(historyState);
13051                    FormsService.ShowResult($scope.failureContent[0]);
13052
13053                    $window.LexusComponents.Foundation.Shared.renderToast(true);
13054                })
13055                .finally(function () {
13056                    $rootScope.$broadcast('form-result');
13057                });
13058        });
13059
13060        $scope.$on('dealer-search-results-received', function (event, data) {
13061            $scope.ffc.form.dealersLoading = false;
13062            $scope.ffc.form.dealers = data.Data;
13063            $scope.checkDealersLoaded();
13064
13065        });
13066
13067        //Do a sanity check to see if the bound dealers match the forceDealer if not clear the force dealer
13068        $scope.checkDealersLoaded = function () {
13069            var result = $scope.ffc.form.dealers.find(function (dealer) {
13070                return dealer.dealerCode == $scope.ffc.form.data.dealerId;
13071            });
13072            if (!result) {
13073                $scope.ffc.form.data.dealerId = null;
13074            }
13075        }
13076
13077
13078        $scope.$on('toggle-build-price-contact-dealer-form', function (event, data) {
13079          var formYPos = $("#contact-dealer-btn").offset().top;
13080          TweenLite.to(window, 1, { scrollTo: { y: formYPos, x: 0 }, ease: Power4.easeOut });
13081          $scope.updatePreferenceDetails(data);
13082        });
13083
13084        $scope.hideContactDealerForm = function () {
13085          $scope.$emit('hide-build-price-contact-dealer-form');
13086          var navYPos = $("[lx-build-price-summary]").offset().top;
13087          TweenLite.to(window, 1, { scrollTo: { y: navYPos - 20, x: 0 }, ease: Power4.easeOut });
13088        }
13089
13090        $scope.$on('postcode-changed', function () {
13091            if (!($scope.initVars.isDealerSite === 'true'))
13092            {
13093                var dataUser = UserPreferencesService.getLexusVisitor();
13094                if (dataUser && dataUser.postCode && dataUser.suburb && $scope.ffc.form) {
13095                    $scope.ffc.form.data.postcodes = [];
13096                    $scope.ffc.form.data.postcodes.push({ text: dataUser.suburb + " " + dataUser.postCode });
13097                    $scope.ffc.form.data.postcode = dataUser.postCode;
13098                    $scope.ffc.form.data.suburb = dataUser.suburb;
13099                    $scope.ffc.form.data.state = dataUser.state;
13100                    $scope.ffc.form.data.pricingZone = dataUser.pricingZone;
13101                    $scope.deselectDealer();
13102                    $scope.searchNow();
13103                }
13104            }
13105
13106        });
13107
13108        // to do: Let's get the vehicle details directly from the model instead of the cookie change event for the sake of consistency
13109        /*$rootScope.$on('vehicle-preference-change', function (event, data) {
13110          $scope.updatePreferenceDetails(data);
13111          $scope.resetForm();
13112
13113        });
13114        */
13115
13116        $scope.updatePreferenceDetails = function (data) {
13117            if ($scope.ffc.form && $scope.ffc.form.data) {
13118                $scope.ffc.form.data.vehicledetails = $filter('formatArgs')('Model: {0} \nGrade: {1} \nEngine Variant: {2} \nEnhancement Pack: {3}\nURL: {4}', [data.model, data.grade, data.enginevariant, data.enhancementPack, $location.$$absUrl]);
13119            }
13120        }
13121
13122        $scope.openUpdatePostcodeDialog = function () {
13123            $scope.$emit('open-change-postcode');
13124        }
13125
13126        $scope.closeUpdatePostcodeDialog = function () {
13127            $scope.$emit('close-change-postcode');
13128        }
13129
13130
13131
13132    }
13133}]);
13134;
13135//How the directive communication works with an example
13136//Changing a enhancement Pack
13137//#1 Fires an event in the EP directive
13138//#2 EP directive EMITs the change back to the parent directive
13139//#3 Parent Directive then updates its model
13140//#4 All the other directives Watch that Model Property and do stuff
13141
13142(function (undefined) {
13143    "use strict";
13144    /* Directives */
13145    angular.module('Lexus.Directives')
13146    .config(["$rootScopeProvider", function ($rootScopeProvider) {
13147        $rootScopeProvider.digestTtl(1000);
13148    }])
13149    .directive('lxRangeBuildPrice', [function () {
13150        rangeBuildPriceController.$inject = ['$scope', '$rootScope', '$timeout', '$location', '$http', '$filter', 'UserPreferencesService', 'DealersService', 'PricingService', 'AddressSuggestService', 'BuildPriceService', 'ngDialog', '$anchorScroll', 'BuildPriceAPIService', 'EloquaService', 'HttpPendingRequestsService', 'Geodecode'];
13151        return {
13152            scope: true,
13153            restrict: 'A',
13154            controller: rangeBuildPriceController,
13155            controllerAs: 'bp',
13156            bindToController: true,
13157            link: function (scope, element, attrs) {
13158                scope.bp = scope.bp || {};
13159                try {
13160                    scope.bp.settings = JSON.parse(attrs.bpSettings);
13161                } catch (ex) {
13162                    console.error(ex.message);
13163                }
13164                scope.bp.settings = scope.bp.settings || {};
13165                scope.bp.settings.debug = scope.bp.settings.debug === 'true' ? true : false;
13166                scope.bp.settings.disableDiffPricing = scope.bp.settings.disableDiffPricing === 'true' ? true : false;
13167                scope.bp.settings.hideSaveBuild = scope.bp.settings.hideSaveBuild === 'true' ? true : false;
13168                scope.bp.settings.enablePricingByZone = scope.bp.settings.enablePricingByZone === 'true' ? true : false;
13169
13170                scope.bp.roadblockData = {};
13171                scope.bp.data = {
13172                    steps: $.makeArray(element.find('.lx-range-build-price__section-js').map(function (idx, item) {
13173                        var $item = $(item);
13174                        return {
13175                            id: $item.attr('data-id'),
13176                            label: $item.attr('data-label'),
13177                            disabled: (($item.attr('data-disabled') || '').toLowerCase() == 'true'),
13178                            route: $item.attr('data-route'),
13179                            nextLabel: $item.attr('data-label-next') || 'Next',
13180                            element: $item
13181                        }
13182                    })),
13183                    userInteraction: false,
13184                    activeIndex: 0,
13185                    paintResolved: false,
13186
13187                    vehicleLoading: false,
13188                    overviewDataReceived: false,
13189                    driveAwayPriceLoading: false,
13190                    pricingErrors: [],
13191                    openSaveBuildPriceDialog: false,
13192                    onOfferText: null,
13193
13194                    vehicle: {
13195                        carmodel: (scope.bp && scope.bp.data && scope.bp.data.vehicle && scope.bp.data.vehicle.carmodel) ? scope.bp.data.vehicle.carmodel : null,
13196                        engineVariant: (scope.bp && scope.bp.data && scope.bp.data.vehicle && scope.bp.data.vehicle.engineVariant) ? scope.bp.data.vehicle.engineVariant : null,
13197                        grade: (scope.bp && scope.bp.data && scope.bp.data.vehicle && scope.bp.data.vehicle.grade) ? scope.bp.data.vehicle.grade : null,
13198                        postcode: '',
13199                        enhancementPack: (scope.bp && scope.bp.data && scope.bp.data.vehicle && scope.bp.data.vehicle.enhancementPack) ? scope.bp.data.vehicle.enhancementPack : null,
13200                        colour: (scope.bp && scope.bp.data && scope.bp.data.vehicle && scope.bp.data.vehicle.colour) ? scope.bp.data.vehicle.colour : null,
13201                        trim: (scope.bp && scope.bp.data && scope.bp.data.vehicle && scope.bp.data.vehicle.trim) ? scope.bp.data.vehicle.trim : null,
13202                        colourId: (scope.bp && scope.bp.data && scope.bp.data.vehicle && scope.bp.data.vehicle.colourId) ? scope.bp.data.vehicle.colourId : null,
13203                        pricingZone: '',
13204                    },
13205                    vehicleImage: '',
13206                    apiResults: {
13207                        vehicleData: {},
13208                        enhancementPacks: {},
13209                        colours: {},
13210                        trims: {}
13211                    },
13212                    postcodes: [],
13213                    pricing: {
13214                        driveAway: '',
13215                        driveAwayPerMonth: '',
13216                        itemised: {
13217                            rrp: '',
13218                            premiumPaint: '',
13219                            enhancementPack: '',
13220                            dealerDelivery: ''
13221                        },
13222                        onroads: {
13223                            total: '',
13224                            registration: '',
13225                            ctp: '',
13226                            stampDuty: '',
13227                            luxuryCarTax: ''
13228                        }
13229                    },
13230                    hostOrigin: ''
13231                };
13232                scope.init();
13233                scope.bp.showSlideOutTray = false;
13234            }
13235        };
13236
13237        function rangeBuildPriceController($scope, $rootScope, $timeout, $location, $http, $filter, UserPreferencesService, DealersService, PricingService, AddressSuggestService, BuildPriceService, ngDialog, $anchorScroll, BuildPriceAPIService, EloquaService, HttpPendingRequestsService, Geodecode) {
13238            var bp = this;
13239
13240
13241            $scope.UserPreferencesService = UserPreferencesService;
13242            $scope.BuildPriceService = BuildPriceService;
13243
13244            $scope.init = function () {
13245                $scope.navigateToPage(0);
13246                $scope.tagsLoading = false;
13247                var visitor = $scope.UserPreferencesService.getLexusVisitor();
13248                if (visitor && visitor.postCode && visitor.suburb && visitor.state) {
13249                    var postcodeSelection = UserPreferencesService.getAutocompleteTagFromVisitor(visitor);
13250                    $scope.bp.data.postcodes.push(postcodeSelection);
13251                    var postcodeData = postcodeSelection.data;
13252                    updateVehicleWithPostcodeData(postcodeData);
13253
13254                    //$scope.bp.test.push(postcodeSelection);
13255                }
13256
13257                if ($scope.bp.settings.firstVisit = !UserPreferencesService.isPageVisited($scope.bp.settings.pageGuid)) {
13258                    $rootScope.$broadcast('record-page-visit', $scope.bp.settings.pageGuid);
13259                }
13260
13261                if ($scope.bp.data.postcodes.length != 0 && !$scope.bp.settings.firstVisit) {
13262                    $scope.$broadcast(BuildPriceService.events.gtmEvent, {
13263                        source: 'Postcode Roadblock', data: {
13264                            dialogDisplayed: false,
13265                            firstVisit: $scope.bp.settings.firstVisit,
13266                            postcode: $scope.bp.data.postcodes
13267                        }
13268                    });
13269                }
13270
13271                $scope.bp.data.hostOrigin = $location.protocol() + '://' + $location.host();
13272            };
13273
13274            $scope.loadUserPreferences = function () {
13275                var vCookie = $scope.UserPreferencesService.getLexusVehicle();
13276                var sbdv = $scope.bp.data.vehicle;//For Brevity
13277                //Clear mismatched downstream values between current cookie and deeplinks
13278                if (sbdv.carmodel != vCookie.model)
13279                {
13280                    vCookie.engineVariant = vCookie.grade = vCookie.enhancementPack = vCookie.trim  = vCookie.colour = null;
13281                }
13282
13283                if (sbdv.engineVariant != vCookie.engineVariant) {
13284                    vCookie.grade = vCookie.enhancementPack = vCookie.trim = vCookie.colour = null;
13285                }
13286
13287                if (sbdv.grade != vCookie.grade) {
13288                    vCookie.enhancementPack = vCookie.trim = vCookie.colour = null;
13289                }
13290
13291                if (sbdv.enhancementPack != vCookie.enhancementPack) {
13292                    vCookie.trim = vCookie.colour = null;
13293                }
13294
13295                if (sbdv.trim != vCookie.trim) {
13296                    vCookie.colour = null;
13297                }
13298
13299                if (vCookie) {
13300                    // current value => cookie value if parent model value not null (eg. if model exists, we can use the enginevariant cookie value) => null
13301                    sbdv.carmodel = sbdv.carmodel ? sbdv.carmodel : vCookie.model;
13302                    sbdv.engineVariant = sbdv.engineVariant ? sbdv.engineVariant : (sbdv.carmodel ? vCookie.engineVariant : null);
13303                    sbdv.grade = sbdv.grade ? sbdv.grade : (sbdv.engineVariant ? vCookie.grade : null);
13304                    sbdv.enhancementPack = sbdv.enhancementPack ? sbdv.enhancementPack : (sbdv.grade ? vCookie.enhancementPack : null);
13305                    sbdv.trim = sbdv.trim ? sbdv.trim : (sbdv.colour ? vCookie.trim : null);
13306                    sbdv.colour = sbdv.colour ? sbdv.colour : (sbdv.enhancementPack ? vCookie.colour : null);
13307
13308                }
13309            }
13310
13311            $scope.loadTags = function (query) {
13312                $scope.tagsLoading = true;
13313                AddressSuggestService.setParam('term', query);
13314                return AddressSuggestService.GetResults($scope.tagsLoaded);
13315            };
13316
13317            $scope.tagsLoaded = function () {
13318                $scope.tagsLoading = false;
13319            }
13320
13321            $scope.autocompleteLoading = function () {
13322                return $scope.tagsLoading;
13323            };
13324
13325            $scope.updatePostcode = function ($tag, focusElement) {
13326                $scope.bp.data.postcodes = [$tag];
13327                focusElement = focusElement != null && focusElement.length > 0 ? focusElement : '.lx-postcode-dialog__choose';
13328                var button = angular.element(focusElement)[0];
13329                $timeout(function () { button.focus(); }, 10, false);
13330            };
13331
13332            $scope.clearPostcode = function () {
13333                $scope.bp.data.postcodes = [];
13334            };
13335
13336            $scope.navigateToPage = function (pageIdOrIdx) {
13337                var userInitiated = (arguments.length > 1) ? arguments[1] : true;
13338                for (var i = 0; i < $scope.bp.data.steps.length; i++) {
13339                    if (pageIdOrIdx === i || pageIdOrIdx === $scope.bp.data.steps[i].id) {
13340                        $scope.bp.data.steps[i].visible = true;
13341                        $scope.bp.data.activeIndex = i;
13342                        window.scrollTo(0, angular.element('.lx-range-build-price')[0].offsetTop);
13343                        $scope.$broadcast(BuildPriceService.events.pageNavigationBroadcast, $scope.bp.data.steps[i]);
13344                    } else {
13345                        $scope.bp.data.steps[i].visible = false;
13346                    }
13347                }
13348
13349                $rootScope.$broadcast('refresh-gtm', 'Build and Price');
13350
13351                if (userInitiated && pageIdOrIdx + 1 == $scope.bp.data.steps.length || pageIdOrIdx == 'summary') {
13352                    $scope.$broadcast(BuildPriceService.events.gtmEvent, { source: 'Summary', data: { ...$scope.bp.data.vehicle, dealerName: $scope.bp.settings.dealerName } });
13353                }
13354            };
13355
13356            $scope.clearDownstream = function (level, data) {
13357
13358                if(data.userInitiated)
13359                    HttpPendingRequestsService.cancelAll();
13360
13361                //Model = 5
13362                if (level >= 5) $scope.bp.data.vehicle.engineVariant = null;
13363                //Variant = 4
13364                if (level >= 4) $scope.bp.data.vehicle.grade = null;
13365                //Grade = 3
13366                if (level >= 3) $scope.bp.data.vehicle.enhancementPack = null;
13367                if (level >= 3) $scope.bp.data.vehicleImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAsYAAAGRCAQAAADMAr3sAAAEOUlEQVR42u3UIQEAAAzDsM+/6SsYHkkkFDQHwFwkADBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjCQDMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGoHq/RgGSYZc9owAAAABJRU5ErkJggg==';
13368
13369                //Enhancement pack = 2
13370                if (level >= 2) $scope.bp.data.vehicle.colour = null;
13371                if (level >= 2) $scope.bp.data.vehicle.trim = null;
13372                if (level >= 2) $scope.bp.data.vehicleImageLoading = true;
13373
13374                //Trim = 1
13375                if (level >= 1) $scope.bp.data.vehicle.colourId = null;
13376
13377                $scope.broadcastHoldPricingCalls(true);
13378            };
13379
13380            $scope.broadcastHoldPricingCalls = function(val){
13381                $scope.$broadcast(BuildPriceService.events.holdPricingCallsRelay, val);
13382            }
13383
13384            $scope.$on("sticky-footer-visible", function (evt, data) {
13385                $rootScope.$broadcast("shift-promo-tiles-up", data);
13386            });
13387
13388            $scope.closePostcodeSlideout = function () {
13389                closePostcodeSlideout();
13390            }
13391
13392            $scope.findLocation = function () {
13393                Geodecode().then(
13394                    //success
13395                    function (response) {
13396                        if (response.isSuccessfull) {
13397                            console.log("Postcode-" + response.data.postcode);
13398                            console.log("Suburb-" + response.data.suburb);
13399                            response["text"] = response.data.suburb + " " + response.data.postcode;
13400                            $scope.bp.data.postcodes = [response];
13401                            savePostcodeAndBroadcastEvent();
13402                            closePostcodeSlideout();
13403                        }
13404                    },
13405                    //failed
13406                    function (response) {
13407                        console.log("Geo Location Failed");
13408                    }
13409                ).finally(function () {
13410                });
13411            }
13412            //#region EVENTS
13413            $scope.$on(BuildPriceService.events.holdPricingCalls, function (event, data) {
13414                //console.info("Relaying holdPricingCalls: " + data);
13415                $scope.broadcastHoldPricingCalls(data);
13416            });
13417
13418            $scope.$on(BuildPriceService.events.pageNavigation, function (event, data) {
13419                $scope.navigateToPage(data);
13420            });
13421
13422            $scope.$on(BuildPriceService.events.userInteraction, function () {
13423                $scope.bp.data.userInteraction = true;
13424            });
13425
13426            $scope.$on(BuildPriceService.events.overviewDataReceived, function () {
13427                $scope.bp.data.overviewDataReceived = true;
13428                $scope.loadUserPreferences();
13429            });
13430
13431            $scope.$on(BuildPriceService.events.openPostcodeDialog, function () {
13432                $scope.$broadcast(BuildPriceService.events.gtmEvent, {
13433                    source: 'Postcode Roadblock', data: {
13434                        dialogDisplayed: true,
13435                        firstVisit: $scope.bp.settings.firstVisit,
13436                        postcode: $scope.bp.data.postcodes
13437                    }
13438                });
13439
13440                $scope.ngDialog = ngDialog;
13441                ngDialog.open({
13442                    template: 'updatePostcodeDialogTemplate',
13443                    controller: '',
13444                    className: 'ngdialog-theme-default',
13445                    scope: $scope, // this line wasn't here before
13446                    plain: false,
13447                    showClose: false,
13448                    closeByDocument: true,
13449                    disableAnimation: true,
13450                    closeByEscape: true,
13451                    appendTo: false,
13452                    preCloseCallback: function (value) {
13453                        //setPageVisited needs to be run here to catch the click-outside / pressing ESC case
13454
13455
13456                        return (value === '$document' || value === '$closeButton' || value === '$escape') ? true : ($scope.bp.data.postcodes.length > 0) && $scope.setPageVisited();
13457                    }
13458                });
13459            });
13460
13461            $scope.$on(BuildPriceService.events.closePostcodeDialog, function () {
13462                console.log('BuildPriceService.events.closePostcodeDialog', arguments);
13463                ngDialog.close();
13464                $scope.$broadcast(BuildPriceService.events.postcodeChanged);
13465                savePostcodeAndBroadcastEvent();
13466            });
13467
13468            $scope.$on(BuildPriceService.events.openPostcodeSlideout, function () {
13469                $scope.$broadcast(BuildPriceService.events.gtmEvent, {
13470                    source: 'Postcode Roadblock', data: {
13471                        dialogDisplayed: true,
13472                        firstVisit: $scope.bp.settings.firstVisit,
13473                        postcode: $scope.bp.data.postcodes
13474                    }
13475                });
13476
13477                $scope.bp.showSlideOutTray = true;
13478                $('body').addClass("modal-open");
13479            });
13480
13481            $scope.$on(BuildPriceService.events.confirmPostcodeFromSlideout, function () {
13482                closePostcodeSlideout();
13483                console.log('BuildPriceService.events.confirmPostcodeFromSlideout', arguments);
13484                savePostcodeAndBroadcastEvent();
13485            });
13486
13487            $scope.$on(BuildPriceService.events.onOfferText, function (event, data) {
13488                $scope.bp.data.onOfferText = data;
13489            });
13490
13491            $scope.$on(BuildPriceService.events.gtmEvent, function (event, data) {
13492                $rootScope.$broadcast('gtm-buildPrice', data);
13493            });
13494
13495            $scope.errorText = function (length, min, data, value) {
13496                return "Postcode required"
13497            }
13498
13499            $scope.openUpdatePostcodeDialog = function (openDialog) {
13500                if (openDialog) {
13501                    $scope.$broadcast(BuildPriceService.events.openPostcodeDialog);
13502                    return;
13503                }
13504
13505                Geodecode().then(
13506                    //success
13507                    function (response) {
13508                        if (response.isSuccessfull) {
13509                            console.log("Postcode-" + response.data.postcode);
13510                            console.log("Suburb-" + response.data.suburb);
13511                            response["text"] = response.data.suburb + " " + response.data.postcode;
13512                            $scope.updatePostcode(response);
13513                        }
13514                    },
13515                    //failed
13516                    function (response) {
13517                        console.log("Geo Location Failed");
13518                        $scope.$broadcast(BuildPriceService.events.openPostcodeDialog);
13519                    }
13520                ).finally(function () {
13521                });
13522            }
13523
13524            $scope.openPostcodeSlideout = function () {
13525                $scope.$emit(BuildPriceService.events.openPostcodeSlideout);
13526            }
13527
13528            $scope.setPageVisited = function () {
13529                $scope.bp.settings.firstVisit = false;
13530                return true;
13531            }
13532
13533            $scope.closeUpdatePostcodeDialog = function () {
13534                console.log('BuildPriceService.events.closePostcodeDialog', arguments);
13535                angular.element('tags-input').addClass('is-submitted');
13536                $scope.$broadcast(BuildPriceService.events.closePostcodeDialog);
13537                $scope.sendRoadblockForm();
13538            }
13539
13540            $scope.closeUpdatePostcodeSlideout = function () {
13541                console.log('BuildPriceService.events.confirmPostcodeFromSlideout', arguments);
13542                angular.element('tags-input').addClass('is-submitted');
13543                $scope.$broadcast(BuildPriceService.events.confirmPostcodeFromSlideout);
13544            }
13545
13546            function closePostcodeSlideout () {
13547                $scope.bp.showSlideOutTray = false;
13548                $('body').removeClass("modal-open");
13549            }
13550
13551            function savePostcodeAndBroadcastEvent() {
13552                var postcodeData = $scope.bp.data.postcodes[0].data;
13553                updateVehicleWithPostcodeData(postcodeData);
13554
13555                $scope.$emit('visitor-details-cookie-change', {
13556                    postCode: postcodeData.postcode,
13557                    suburb: postcodeData.suburb,
13558                    state: postcodeData.state,
13559                    pricingZone: postcodeData.pricingZone,
13560                });
13561                $scope.$broadcast(BuildPriceService.events.postcodeChanged);
13562            }
13563
13564            $scope.getRoadblockData = function(){
13565                var dataVehicle = UserPreferencesService.getLexusVehicle();
13566
13567                var postcode;
13568                if($scope.bp.data.postcodes && $scope.bp.data.postcodes.length > 0 && $scope.bp.data.postcodes[0].data){
13569                    postcode = $scope.bp.data.postcodes[0].data.postcode;
13570                }
13571                var data = EloquaService.attachEloquaFields({
13572                    Email: $scope.bp.roadblockData.email,
13573                    FirstName: $scope.bp.roadblockData.firstname,
13574                    EngineVariant: $scope.bp.data.vehicle.engineVariant
13575                        ? $scope.bp.data.vehicle.engineVariant
13576                        : dataVehicle.engineVariant,
13577                    Postcode: postcode,
13578                    Model: $scope.bp.data.vehicle.carmodel,
13579                });
13580                return data;
13581            }
13582
13583            $scope.setUserInteraction = function (data) {
13584                if (data.userInitiated) {
13585                    $scope.bp.data.userInteraction = true;
13586                }
13587            }
13588
13589            $scope.sendRoadblockForm = function(){
13590                if ($scope.bp.roadblockData.email) {
13591                    $http({ method: 'POST', url: $filter('formatArgs')(Lexus.API.FormSubmission.Roadblock, [Lexus.ID.Site]), data: $scope.getRoadblockData() })
13592                    .then(function success(response) {
13593                        var msg = "Roadblock form sent.";
13594                        console.info ? console.info(msg) : console.log(msg);
13595                    }, function failure(response) {
13596                        var msg = "Roadblock form could not be sent.";
13597                        console.error ? console.error(response) 
13597: console.log(msg);
13598                    });
13599                }
13600            }
13601
13602            $scope.hasLocation = function() {
13603                return bp.data.vehicle.postcode && bp.data.vehicle.postcode.length > 0;
13604            }
13605
13606            $scope.$on(BuildPriceService.events.triggerSaveBuildPriceDialog, function () {
13607                $scope.bp.data.openSaveBuildPriceDialog = true;
13608                $scope.$broadcast(BuildPriceService.events.openSaveBuildPriceDialog, $scope.bp.data.vehicle);
13609            });
13610
13611            $scope.$on(BuildPriceService.events.triggerCloseSaveBuildPriceDialog, function () {
13612                $scope.bp.data.openSaveBuildPriceDialog = false;
13613            });
13614
13615            //#region vehicle selection listeners
13616            //each listener sets the selected value, then sets null on all downstream vehicle selections
13617            $scope.$on(BuildPriceService.events.modelSelected, function (event, data) {
13618                if (data.clearDownstream) $scope.clearDownstream(5, data);
13619
13620                $scope.setUserInteraction(data);
13621                $scope.bp.data.vehicle.carmodel = data.selection;
13622
13623
13624                $scope.saveCurrentState();
13625            });
13626
13627            $scope.$on(BuildPriceService.events.engineVariantSelected, function (event, data) {
13628                if (data.clearDownstream) $scope.clearDownstream(4, data);
13629                $scope.setUserInteraction(data);
13630                $scope.bp.data.vehicle.engineVariant = data.selection;
13631
13632
13633                $scope.saveCurrentState();
13634            });
13635
13636            $scope.$on(BuildPriceService.events.gradeSelected, function (event, data) {
13637                if (data.clearDownstream) $scope.clearDownstream(3, data);
13638
13639                $scope.setUserInteraction(data);
13640                $scope.bp.data.vehicle.grade = data.selection;
13641
13642                $scope.saveCurrentState();
13643            });
13644
13645            $scope.$on(BuildPriceService.events.enhancementPackSelected, function (event, data) {
13646                if (data.clearDownstream) $scope.clearDownstream(2, data);
13647
13648                $scope.setUserInteraction(data);
13649                $scope.bp.data.vehicle.enhancementPack = (data.selection) ? data.selection : data;
13650
13651
13652                $scope.saveCurrentState();
13653                $scope.setUserInteraction(data);
13654            });
13655
13656            $scope.$on(BuildPriceService.events.trimSelected, function (event, data) {
13657                if (data.clearDownstream) $scope.clearDownstream(1, data);
13658
13659                $scope.setUserInteraction(data);
13660                $scope.bp.data.vehicle.trim = data.selection;
13661
13662            });
13663
13664            $scope.$on(BuildPriceService.events.paintSelected, function (event, paintObj) {
13665                $scope.clearDownstream(0, { 'userInitiated': false });
13666
13667                $scope.setUserInteraction(paintObj);
13668                $scope.bp.data.vehicle.colour = paintObj.uriName;
13669                $scope.bp.data.vehicle.colourId = paintObj.materialCode;
13670                $scope.saveCurrentState();
13671            });
13672
13673            $scope.$on(BuildPriceService.events.threeSixtyLoad, function (event, data) {
13674                $scope.bp.data.vehicle.specifics = data;
13675
13676                BuildPriceAPIService.getExteriorImages($scope.bp.data.vehicle.engineVariant, data.gradeId, data.variantId, data.trimId, data.materialCode, function (images) {
13677                    $scope.bp.data.vehicleImage = images.threeSixtyImagesDesktop[2];
13678                });
13679
13680                $scope.bp.data.paintResolved = false;
13681            });
13682
13683            $scope.$on(BuildPriceService.events.threeSixtyLoaded, function (event, data) {
13684                $scope.bp.data.paintResolved = true;
13685            });
13686
13687            $scope.$on(BuildPriceService.events.userSelection, function (event, data) {
13688
13689                //HttpPendingRequestsService.cancelAll();
13690
13691                var delay = 250;  // milliseconds to delay the loading.
13692
13693                $scope.bp.data.driveAwayPriceLoadingTimeout = $timeout(function () {
13694                    $scope.bp.data.driveAwayPriceLoading = true;
13695                }, delay); // Don't bother showing "loading" for time in delay
13696
13697                $scope.bp.data.vehicleLoadingTimeout = $timeout(function () {
13698                    $scope.bp.data.vehicleLoading = true;
13699                }, delay); // Don't bother showing "loading" for time in delay
13700
13701                console.log('BuildPriceService.events.userSelection: ', data, '\n driveAwayPriceLoading', $scope.bp.data.driveAwayPriceLoading, '\n vehicleLoading: ', $scope.bp.data.vehicleLoading);
13702            });
13703
13704            $scope.$on(BuildPriceService.events.priceLoad, function (event, data) {
13705                if ($scope.bp.data.driveAwayPriceLoadingTimeout) {
13706                    $timeout.cancel($scope.bp.data.driveAwayPriceLoadingTimeout);
13707                }
13708                $scope.bp.data.driveAwayPriceLoadingTimeout = $timeout(function() {
13709                    $scope.bp.data.driveAwayPriceLoading = true;
13710                }, 100); // Don't bother showing "loading" for <100ms
13711            });
13712
13713            $scope.$on(BuildPriceService.events.priceDone, function (event, data) {
13714
13715                if ($scope.bp.data.driveAwayPriceLoadingTimeout) {
13716                    $timeout.cancel($scope.bp.data.driveAwayPriceLoadingTimeout);
13717                }
13718                $scope.bp.data.driveAwayPriceLoading = false;
13719
13720            });
13721
13722            $scope.$on(BuildPriceService.events.priceReceived, function (event, data) {
13723                $scope.bp.data.apiResults.price = data;
13724                $scope.bp.data.pricing.driveAway = data.driveAway;
13725                $scope.bp.data.pricing.driveAwayPerMonth = data.installmentPerMonth;
13726                $scope.bp.data.pricing.itemised.rrp = data.listPriceIncGST;
13727                $scope.bp.data.pricing.itemised.premiumPaint = data.paintPrice;
13728                $scope.bp.data.pricing.itemised.enhancementPack = data.enhancementPackValue;
13729                $scope.bp.data.pricing.itemised.dealerDelivery = data.dealerDelivery;
13730                $scope.bp.data.pricing.onroads.total = data.onRoadCosts;
13731                $scope.bp.data.pricing.onroads.registration = data.registrationAmount;
13732                $scope.bp.data.pricing.onroads.ctp = data.ctpInsuranceAmount;
13733                $scope.bp.data.pricing.onroads.stampDuty = data.stampDuty;
13734                $scope.bp.data.pricing.onroads.luxuryCarTax = data.luxuryCarTax;
13735            });
13736
13737            $scope.$on(BuildPriceService.events.priceError, function (event, data) {
13738                console.log('priceError: ', data);
13739
13740                $scope.bp.data.pricing.driveAway = 'n/a';
13741
13742            });
13743
13744            $scope.$on(BuildPriceService.events.vehicleDataReceived, function (event, data) {
13745                BuildPriceService.vehicleData = data;
13746                $scope.$broadcast(BuildPriceService.events.vehicleDataRelay, bp.data.apiResults.vehicleData);
13747            });
13748
13749            $scope.$on(BuildPriceService.events.enhancementPacksReceived, function (event, data) {
13750                bp.data.apiResults.enhancementPacks = data;
13751            });
13752
13753            $scope.$on(BuildPriceService.events.trimsReceived, function (event, data) {
13754                bp.data.apiResults.trims = data;
13755            });
13756
13757            $scope.$on(BuildPriceService.events.paintsReceived, function (event, data) {
13758                bp.data.apiResults.colours = data;
13759                $scope.$broadcast(BuildPriceService.events.paintDataRelay, bp.data.apiResults.colours);
13760            });
13761
13762            $scope.$on(BuildPriceService.events.invalidSelectionError, function (event, data) {
13763                var clearDownstreamTargets = (data.clearDownstreamTargets != null ? data.clearDownstreamTargets : [1, 2, 3, 4, 5]);
13764                var downstreamTargets = ['carmodel', 'engineVariant', 'grade', 'enhancementPack', 'trim', 'colour'];
13765
13766
13767                if (!$scope.bp.data.userInteraction) {
13768                    $scope.navigateToPage(0);
13769                }
13770                var message;
13771                if (!$scope.bp.data.userInteraction) {
13772                    //deep link invalid case
13773                    console.warn("Deep link caused validation error downstream from " + data.controllerName);
13774                    message = "One or more of your preferences is unavailable for your current selection. Please reconfigure your vehicle.";
13775                } else {
13776                    switch (data.controllerName) {
13777                        case BuildPriceService.stepIds.MODEL:
13778                            console.warn("Reselection caused validation error downstream from " + data.controllerName);
13779                            message = "One or more of your preferences is unavailable for your current selection. Please reconfigure your vehicle.";
13780                            clearDownstreamTargets = $scope.bp.data.userInteraction ? [1, 2, 3, 4, 5] : clearDownstreamTargets;
13781                            break;
13782                        case BuildPriceService.stepIds.GRADE:
13783                            console.warn("Reselection caused validation error downstream from " + data.controllerName);
13784                            message = "One or more of your preferences is unavailable for your current selection. Please reconfigure your vehicle.";
13785                            clearDownstreamTargets = $scope.bp.data.userInteraction ? [2, 3, 4, 5] : clearDownstreamTargets;
13786                            break;
13787                        case BuildPriceService.stepIds.ENHANCEMENT_PACK:
13788                            console.warn("Reselection caused validation error downstream from " + data.controllerName);
13789                            message = "One or more of your preferences is unavailable for your current selection. Please reconfigure your vehicle.";
13790                            clearDownstreamTargets = $scope.bp.data.userInteraction ? [3, 4, 5] : clearDownstreamTargets;
13791                            break;
13792                        case BuildPriceService.stepIds.PAINT_TRIM:
13793                            console.warn("Reselection caused validation error downstream from " + data.controllerName);
13794                            message = "One or more of your preferences is unavailable for your current selection. Please reconfigure your vehicle.";
13795                            clearDownstreamTargets = $scope.bp.data.userInteraction ? [4, 5] : clearDownstreamTargets;
13796                            break;
13797                    }
13798                }
13799                for (var i = 0; i < clearDownstreamTargets.length; i++) {
13800                    $scope.bp.data.vehicle[downstreamTargets[clearDownstreamTargets[i]]] = null;
13801                }
13802
13803                $scope.saveCurrentState();
13804                $scope.$emit('open-modal-window', {
13805                    content: message,
13806                    wrapper: ["<p style='padding: 20px 65px 0px 20px; text-align: left'>", "</p>"]
13807                });
13808
13809            });
13810            //#endregion
13811
13812            $scope.$watch('bp.data.vehicle', function (newValue, oldValue) {
13813                console.log("%c bp.data.vehicle changed:", "color: blue;", newValue);
13814            });
13815
13816            $scope.$watchGroup([ 'bp.data.vehicle.carmodel', 'bp.data.vehicle.engineVariant', 'bp.data.vehicle.specifics.paintId', 'bp.data.vehicle.colourId'], function (newData, oldData, $scope) {
13817
13818
13819                for (var i in newData) {
13820                    if (!newData[i]) {
13821                        return; //I only care if all values are populated
13822                    }
13823                }
13824
13825                if ($scope.bp.data.activeIndex == 0) {
13826                    $scope.$broadcast(BuildPriceService.events.gtmEvent, { source: 'Default', data: $scope.bp.data.vehicle });
13827                }
13828
13829                $timeout.cancel($scope.bp.data.vehicleLoadingTimeout);
13830
13831                //Set Vehicle loading to false if all of these items are available
13832                $scope.bp.data.vehicleLoading = false;
13833
13834
13835
13836            }, true);
13837
13838            $scope.saveCurrentState = function () {
13839                $scope.pushURI($scope.bp.data.vehicle);
13840                $scope.$emit('vehicle-preference-change', {
13841                    model: $scope.bp.data.vehicle.carmodel,
13842                    grade: $scope.bp.data.vehicle.grade,
13843                    engineVariant: $scope.bp.data.vehicle.engineVariant,
13844                    enhancementPack: $scope.bp.data.vehicle.enhancementPack,
13845                    trim: $scope.bp.data.vehicle.trim,
13846                    colour: $scope.bp.data.vehicle.colour,
13847                    // colourId stores the paint's materialCode value, set in paintSelected handler via paintObj.materialCode
13848                    materialCode: $scope.bp.data.vehicle.colourId
13849                });
13850            }
13851
13852            //$scope.$on('bp-did-select-postcode', function (event, data) {
13853            //    console.log('rangeDealerLookupController on(bp-did-select-postcode): ' + data);
13854            //});
13855
13856            //$scope.broadcastGetPostcode = function (data) {
13857            //    $scope.$broadcast('bp-get-postcode', data);
13858            //}
13859
13860            $scope.$watchGroup(['bp.data.vehicle.carmodel', 'bp.data.vehicle.engineVariant', 'bp.data.postcodes'], function (newDataArray, oldDataArray, $scope) {
13861
13862                if ($scope.bp.data.vehicle.carmodel && $scope.bp.data.vehicle.engineVariant) {
13863                    $scope.bp.data.steps.forEach(
13864                        function (step) {
13865                            step.disabled = false;
13866                        });
13867                } else {
13868                    $scope.bp.data.steps.forEach(
13869                        function (step) {
13870                            step.disabled = true;
13871                        });
13872                    $scope.bp.data.steps[0].disabled = false;
13873                }
13874
13875                //A CarModel Changed happened Broadcast to the top Nav
13876                if (newDataArray[0]) {
13877                    // create and dispatch the event
13878                    if(typeof document !== "undefined") {
13879                        const event = new CustomEvent(BuildPriceService.events.modelSelectedUpdateMenu, {
13880                            detail: newDataArray[0]
13881                        });
13882                        document.dispatchEvent(event);
13883                    }
13884                    $rootScope.$broadcast(BuildPriceService.events.modelSelectedUpdateMenu, newDataArray[0]);
13885                }
13886
13887            });
13888
13889            function updateVehicleWithPostcodeData(postcodeData) {
13890                $scope.bp.data.vehicle.postcode = postcodeData.postcode;
13891                $scope.bp.data.vehicle.suburb = postcodeData.suburb;
13892                $scope.bp.data.vehicle.state = postcodeData.state;
13893                $scope.bp.data.vehicle.pricingZone = postcodeData.pricingZone;
13894            }
13895            //#endregion
13896        }
13897
13898    }])
13899    .controller('lxBuildPriceRouteController', ['$scope', '$location', 'BuildPriceService', function ($scope, $location, BuildPriceService) {
13900        $scope.bp = $scope.bp || {};
13901        $scope.bp.routing = BuildPriceService.routingSettings;
13902
13903        $scope.routeInit = function (obj) {
13904            if (obj && obj.baseUrl) {
13905                $scope.bp.routing.baseUrl = obj.baseUrl;
13906            }
13907            //parseURI();
13908        };
13909
13910        function ensureSuffix(suffix, value) {
13911            return value + (value[value.length - 1] === suffix ? '' : suffix);
13912        }
13913
13914        $scope.getArgPath = function () {
13915            var baseUrl = ensureSuffix('/', $scope.bp.routing.baseUrl);
13916            return ensureSuffix('/', $location.$$path).replace(baseUrl, '');
13917        };
13918
13919        //to do: passed in variables require validation
13920        function parseURI() {
13921            var argPath = $scope.getArgPath();
13922            if (argPath == '') return;
13923
13924            $scope.bp.routing.currentUri = BuildPriceService.encodeUriFragment(argPath);
13925
13926            var args = argPath.split('/');
13927            //ensure object path exists
13928            (($scope.bp = $scope.bp || {}).data = $scope.bp.data || {}
13928).vehicle = $scope.bp.data.vehicle || {};
13929
13930            var i;
13931            for (i = 0; i < args.length; i++) {
13932                //set values in url
13933                //set upper case as the deep link steps are strict types
13934                if(i===0){
13935                    $scope.bp.data.vehicle[$scope.bp.routing.argIdx[i]] = BuildPriceService.decodeUriFragment(args[i].toUpperCase());
13936                }else{
13937                    $scope.bp.data.vehicle[$scope.bp.routing.argIdx[i]] = BuildPriceService.decodeUriFragment(args[i]);
13938                }
13939            }
13940            for (i = i; i < $scope.bp.routing.argIdx.length; i++) {
13941                //clear remaining values
13942                $scope.bp.data.vehicle[$scope.bp.routing.argIdx[i]] = null;
13943            }
13944
13945            //additional parameters - jump to step
13946            if (args.length > $scope.bp.routing.argIdx.length) {
13947                var step = parseInt(args[$scope.bp.routing.argIdx.length]);
13948                if (!isNaN(step)) {
13949                    step--;
13950                    BuildPriceService.waitFor.call($scope, 'bp.data.steps', BuildPriceService.waitCondition.NOTNULL)
13951                        .then(function (value) {
13952                            if (value.length > step) {
13953                                $scope.navigateToPage(step,false);
13954                            }
13955                        });
13956                }
13957            }
13958
13959            $scope.$broadcast(BuildPriceService.events.gtmEvent, { source: 'Initial', data: $scope.bp.data.vehicle });
13960
13961        }
13962
13963        $scope.pushURI = function (vehicleModel) {
13964            // TODO: Migrate to use the same data as _Navigation.cshtml i.e. RangeBuildPriceRepository.GetRangeBuildPriceData()
13965            //
13966            // ...because the URIs you get from clicking each step in _Navigation.cshtml
13967            // do not have hyphens in them like these URIs do.
13968            //
13969            // This means email links will NOT work if you just clicked on a step, but WILL work just after
13970            // saveCurrentState (i.e. 'bp-did-select-engine-variant' / 'bp-did-select-model' events).
13971
13972            var argPath = "";
13973            var fullSpecification = true;
13974            for (var i = 0; i < $scope.bp.routing.argIdx.length; i++) {
13975                if (!$scope.bp.data.vehicle[$scope.bp.routing.argIdx[i]]) {
13976                    fullSpecification = false;
13977                    break;
13978                }
13979                argPath += "/" + BuildPriceService.encodeUriFragment($scope.bp.data.vehicle[$scope.bp.routing.argIdx[i]]);
13980            }
13981
13982            //if fully specified, also push "go to last page" into url
13983            if (fullSpecification) {
13984                argPath += "/" + $scope.bp.data.steps.length;
13985            }
13986
13987            var uri = BuildPriceService.encodeUriPath(argPath);
13988
13989            $scope.bp.routing.currentUri = uri;
13990
13991            if (history.pushState) {
13992                $location.path($scope.bp.routing.baseUrl + uri);
13993            }
13994
13995        }
13996
13997        $scope.$on(BuildPriceService.events.overviewDataReceived, function () {
13998            parseURI();
13999        });
14000
14001    }]);
14002
14003}());
14004;
14005
14006(function (undefined) {
14007    "use strict";
14008    /* Directives */
14009    angular.module('Lexus.Directives')
14010    .config(["$rootScopeProvider", function ($rootScopeProvider) {
14011        $rootScopeProvider.digestTtl(1000);
14012    }])
14013    .directive('lxRangePreLaunch', [function () {
14014        rangePreLaunchController.$inject = ['$scope', '$rootScope', '$timeout', '$location', '$http', '$filter', 'UserPreferencesService', 'DealersService', 'PricingService', 'AddressSuggestService', 'BuildPriceService', 'ngDialog', '$anchorScroll', 'PreLaunchAPIService', 'EloquaService', 'HttpPendingRequestsService', 'Geodecode'];
14015        return {
14016            scope: true,
14017            restrict: 'A',
14018            controller: rangePreLaunchController,
14019            controllerAs: 'pl',
14020            bindToController: true,
14021            link: function (scope, element, attrs) {
14022                scope.pl = scope.pl || {};
14023                try {
14024                    scope.pl.settings = JSON.parse(attrs.plSettings);
14025                } catch (ex) {
14026                    console.error(ex.message);
14027                }
14028                scope.pl.settings = scope.pl.settings || {};               
14029                scope.pl.data = {                    
14030                    vehicle: {
14031                        carmodel: null,
14032                        engineVariant: null,
14033                        grade: null,
14034                        postcode: '',
14035                        enhancementPack: null,
14036                        colour: null,
14037                        trim: null,
14038                        colourId: null,
14039                    },
14040                    lifestyle: null,
14041                    overviewDataReceived: false,
14042                };
14043                scope.init();
14044            }
14045        };
14046
14047        function rangePreLaunchController($scope, $rootScope, $timeout, $location, $http, $filter, UserPreferencesService, DealersService, PricingService, AddressSuggestService, BuildPriceService, ngDialog, $anchorScroll, PreLaunchAPIService, EloquaService, HttpPendingRequestsService, Geodecode) {
14048            var pl = this;
14049            $scope.UserPreferencesService = UserPreferencesService;
14050          
14051            $scope.init = function () {
14052                $scope.loadUserPreferences();
14053                var personalization = $scope.UserPreferencesService.getPersonalizationState();
14054                $scope.pl.data.lifestyle = personalization;                
14055                PreLaunchAPIService.getModels().then($scope.ReceiveModels);
14056            };
14057             
14058            $scope.loadUserPreferences = function () {
14059                var vCookie = $scope.UserPreferencesService.getLexusVehicle();
14060                var sbdv = $scope.pl.data.vehicle;//For Brevity
14061                
14062                if (vCookie) {
14063                    sbdv.carmodel = sbdv.carmodel ? sbdv.carmodel : vCookie.model;
14064                    sbdv.engineVariant = sbdv.engineVariant ? sbdv.engineVariant : (sbdv.carmodel ? vCookie.engineVariant : null);                                 
14065                }
14066            }
14067
14068            $scope.ReceiveModels = function () {                
14069                pl.vehicleData = PreLaunchAPIService.data;
14070                pl.data.overviewDataReceived = true;
14071                console.info("%c 0. lxRangePreLaunch -> ReceivedOverviewData: ", "color: darkblue; ", PreLaunchAPIService.data);                
14072            };
14073
14074            $rootScope.$on('personalization-change', function (event, data) {
14075                if (data && data.lifestyle) {
14076                    $scope.pl.data.lifestyle = data;
14077                }
14078            });
14079
14080            $rootScope.$on('vehicle-preference-change', function (event, data) {
14081                if (data && data.engineVariant) {
14082                    $scope.pl.data.vehicle.engineVariant = data.engineVariant;
14083                }
14084            });
14085
14086            $scope.$on('pre-launch-gtm-event', function (event, data) {
14087                updateVehicle(data);
14088                $rootScope.$broadcast('gtm-preLaunch', angular.copy(data));
14089            });
14090
14091            function updateVehicle(data) {
14092                var vehicle = data.data;
14093                for (var vProp in vehicle) {
14094                    pl.data.vehicle[vProp] = vehicle[vProp];
14095                }
14096                data.data = pl.data.vehicle;
14097            }
14098
14099        }
14100
14101    }])
14102    .controller('lxPreLaunchRouteController', ['$scope', '$location', 'BuildPriceService', function ($scope, $location, BuildPriceService) {
14103        $scope.pl = $scope.pl || {};        
14104
14105        $scope.routeInit = function (obj) {            
14106        };        
14107        
14108    }]);
14109
14110}());;
14111//Only Allowed to watch 
14112//BuildPriceAPIService.data
14113
14114//Only Allowed to emit 
14115//model selected
14116//engine variant selected
14117
14118(function (undefined) {
14119
14120    "use strict";
14121    /* Directives */
14122    angular.module('Lexus.Directives')
14123        .directive('lxPreLaunchInterior', [function () {
14124            preLaunchInteriorController.$inject = ['$scope', '$timeout', 'PreLaunchVehicleService', 'PreLaunchAPIService'];
14125            return {
14126
14127                scope: {
14128                    selectedModel: '<carmodel',
14129                    selectedEngineVariant: '<variant',
14130                    overviewDataReceived:'<overview'
14131                },
14132                restrict: 'A',
14133                controller: preLaunchInteriorController,
14134                controllerAs: 'plInterior',
14135                bindToController: true,
14136                transclude: true,
14137                link: function (scope, element, attrs, ctrl, transclude) {
14138                    transclude(scope, function (clone) {
14139                        element.append(clone);
14140                    });
14141                }
14142            };
14143
14144
14145
14146            function preLaunchInteriorController($scope, $timeout, PreLaunchVehicleService, PreLaunchAPIService) {
14147                var plInterior = this;
14148                plInterior.vehicleData = null;
14149                plInterior.gradesList = null;
14150                plInterior.trims = null;
14151                plInterior.currentGradeIndex = 0;
14152
14153                $scope.$watch('plInterior.overviewDataReceived', function (newValue, oldValue) {
14154                    if (newValue != null && newValue === true) {
14155                        plInterior.gradesList = PreLaunchAPIService.getGradesList(plInterior.selectedEngineVariant);
14156                        $scope.gradeChange(plInterior.gradesList[0], true);
14157                    }
14158                });
14159
14160                $scope.$watch('plInterior.selectedEngineVariant', function (newValue, oldValue) {
14161                    if (newValue != null && plInterior.overviewDataReceived) {
14162                        plInterior.gradesList = PreLaunchAPIService.getGradesList(newValue);
14163                        $scope.gradeChange(plInterior.gradesList[0]);
14164                    }
14165                });
14166
14167                function init() {
14168
14169                }
14170
14171                $scope.activeTab = function (grade) {
14172                    return grade == $scope.filterKey;
14173                }
14174
14175                $scope.gradeChange = function (grade, firstCall) {
14176                    var gradeIndex = plInterior.gradesList.indexOf(grade);
14177                    plInterior.currentGradeIndex = gradeIndex > 0 ? gradeIndex : 0;
14178                    setGrade(grade, firstCall);
14179                }
14180
14181                $scope.nextGrade = function () {
14182                    if (plInterior.currentGradeIndex + 1 < plInterior.gradesList.length) {
14183                        plInterior.currentGradeIndex++;
14184                        setGrade(plInterior.gradesList[plInterior.currentGradeIndex]);
14185                    }
14186                }
14187
14188                $scope.prevGrade = function () {
14189                    if (plInterior.currentGradeIndex - 1 >= 0) {
14190                        plInterior.currentGradeIndex--;
14191                        setGrade(plInterior.gradesList[plInterior.currentGradeIndex]);
14192                    }
14193                }
14194
14195                function setGrade(grade, firstCall) {
14196                    $scope.filterKey = grade;
14197                    plInterior.trims = PreLaunchAPIService.getInteriorsByGradeName(plInterior.selectedEngineVariant, grade)
14198                        || [];
14199                    plInterior.activeTrim = plInterior.trims[0];
14200                    if (plInterior.trims.length > 0) {
14201                        $scope.$emit('pre-launch-gtm-event', { source: (firstCall == true) ? 'Initial' : 'interior', data: { grade: grade, trim: plInterior.activeTrim.name } });
14202                    }
14203                }
14204
14205                init();
14206            }
14207        }]);
14208
14209}());;
14210//Only Allowed to watch 
14211//BuildPriceAPIService.data
14212
14213//Only Allowed to emit 
14214//model selected
14215//engine variant selected
14216
14217(function (undefined) {
14218
14219    "use strict";
14220    /* Directives */
14221    angular.module('Lexus.Directives')
14222        .directive('lxPreLaunchExterior', [function () {
14223            preLaunchExteriorController.$inject = ['$scope', '$timeout', 'PreLaunchVehicleService', 'PreLaunchAPIService'];
14224            return {
14225
14226                scope: {
14227                    selectedModel: '<carmodel',
14228                    selectedEngineVariant: '<variant',
14229                    overviewDataReceived: '<overview',
14230                    lifestyle: '<lifestyle',
14231                },
14232                restrict: 'A',
14233                controller: preLaunchExteriorController,
14234                controllerAs: 'plExterior',
14235                bindToController: true,
14236                transclude: true,
14237                link: function (scope, element, attrs, ctrl, transclude) {
14238                    transclude(scope, function (clone) {
14239                        element.append(clone);
14240                    });
14241                }
14242            };
14243
14244
14245
14246            function preLaunchExteriorController($scope, $timeout, PreLaunchVehicleService, PreLaunchAPIService) {
14247                var plExterior = this;
14248                plExterior.gradesList = null;
14249                plExterior.paints = null;
14250                plExterior.lifestyleImg = null;
14251                plExterior.currentGradeIndex = 0;
14252
14253                $scope.$watch('plExterior.overviewDataReceived', function (newValue, oldValue) {
14254                    if (newValue != null && newValue === true) {
14255                        plExterior.gradesList = PreLaunchAPIService.getGradesList(plExterior.selectedEngineVariant);
14256                        $scope.gradeChange(plExterior.gradesList[0], true);
14257                    }
14258                });
14259
14260                $scope.$watch('plExterior.selectedEngineVariant', function (newValue, oldValue) {
14261                    if (newValue != null && plExterior.overviewDataReceived) {
14262                        plExterior.gradesList = PreLaunchAPIService.getGradesList(newValue);
14263                        $scope.gradeChange(plExterior.gradesList[0]);
14264                    }
14265                });
14266
14267                $scope.$watch('plExterior.lifestyle', function (newValue, oldValue) {
14268                    if (newValue != null && newValue.lifestyle) {
14269                        plExterior.lifestyleImg = newValue.lifestyle.image;
14270                    }
14271                });
14272
14273                function init() {
14274
14275                }
14276
14277                $scope.activeTab = function (grade) {
14278                    return grade == $scope.filterKey;
14279                }
14280
14281                $scope.nextGrade = function () {
14282                    if (plExterior.currentGradeIndex + 1 < plExterior.gradesList.length) {
14283                        plExterior.currentGradeIndex++;
14284                        setGrade(plExterior.gradesList[plExterior.currentGradeIndex]);
14285                    }
14286                }
14287
14288                $scope.prevGrade = function () {
14289                    if (plExterior.currentGradeIndex - 1 >= 0) {
14290                        plExterior.currentGradeIndex--;
14291                        setGrade(plExterior.gradesList[plExterior.currentGradeIndex]);
14292                    }
14293                }
14294
14295                $scope.gradeChange = function (grade, firstCall) {
14296                    var gradeIndex = plExterior.gradesList.indexOf(grade);
14297                    plExterior.currentGradeIndex = gradeIndex > 0 ? gradeIndex : 0;
14298                    setGrade(grade, firstCall);                    
14299                }
14300
14301                function setGrade(grade, firstCall) {
14302                    $scope.filterKey = grade;
14303                    plExterior.paints = PreLaunchAPIService.getExteriorsByGradeName(plExterior.selectedEngineVariant, grade)
14304                        || [];
14305                    plExterior.activePaint = plExterior.paints[0];
14306                    if (plExterior.paints.length > 0) {
14307                        $scope.$emit('pre-launch-gtm-event', { source: (firstCall == true) ? 'Initial' : 'exterior', data: { grade: grade, colour: plExterior.activePaint.name } });
14308                    }
14309                }
14310
14311                init();
14312            }
14313        }]);
14314
14315}());;
14316// Only Allowed to watch
14317// Only Allowed to emit
14318// model selected
14319// engine variant selected
14320
14321(function(undefined) {
14322    'use strict';
14323
14324    var EVENT_CAROUSEL_ITEMS_FINISHED_RENDERING = 'EVENT_CAROUSEL_ITEMS_FINISHED_RENDERING';
14325
14326    /* Directives */
14327    angular
14328        .module('Lexus.Directives')
14329        .directive('lxGradeSelector', [
14330            function() {
14331                gradeSelectorController.$inject = [
14332                    '$scope',
14333                    '$element',
14334                    '$timeout',
14335                    'PreLaunchVehicleService',
14336                    'PreLaunchAPIService'
14337                ];
14338
14339                return {
14340                    scope: {
14341                        selectedModel: '<carmodel',
14342                        selectedEngineVariant: '<variant',
14343                        overviewDataReceived: '<overview',
14344                        startPosition: '=?startPosition'
14345                    },
14346                    restrict: 'A',
14347                    controller: gradeSelectorController,
14348                    controllerAs: 'gi',
14349                    bindToController: true,
14350                    transclude: true,
14351                    link: function(scope, element, attrs, ctrl, transclude) {
14352                        transclude(scope, function(clone) {
14353                            element.append(clone);
14354                        });
14355                    }
14356                };
14357
14358                function gradeSelectorController(
14359                    $scope,
14360                    $element,
14361                    $timeout,
14362                    PreLaunchVehicleService,
14363                    PreLaunchAPIService
14364                ) {
14365                    var gi = this;
14366                    var $owlCarousel;
14367                    var carouselOptions;
14368
14369                    gi.gradesList = null;
14370                    carouselOptions = {
14371                        responsive: {
14372                            0: {
14373                                items: 1,
14374                                autoWidth: false,
14375                                dots: true,
14376                                stagePadding: 20
14377                            },
14378                            768: {
14379                                items: 2
14380                            },
14381                            992: {
14382                                items: 3,
14383                                dots: false
14384                            }
14385                        },
14386                        margin: 10,
14387                        startPosition: $scope.startPosition || 0
14388                    };
14389
14390                    $scope.$watch('gi.overviewDataReceived', function(newValue, oldValue) {
14391                        if (newValue !== null && newValue === true) {
14392                            gi.gradesList = PreLaunchAPIService.getGradesByEngineVariant('UX 200');
14393                        }
14394                    });
14395
14396                    $scope.$watch('gi.selectedEngineVariant', function(newValue, oldValue) {
14397                        if (newValue !== null && gi.overviewDataReceived) {
14398                            if (typeof $owlCarousel !== 'undefined') {
14399                                $owlCarousel.trigger('destroy.owl.carousel');
14400                                $owlCarousel.find('.owl-stage-outer').children().unwrap();
14401                            }
14402
14403                            gi.gradesList = PreLaunchAPIService.getGradesByEngineVariant(newValue);
14404                        }
14405                    });
14406
14407                    $scope.$on(EVENT_CAROUSEL_ITEMS_FINISHED_RENDERING, function() {
14408                        if (typeof $owlCarousel === 'undefined') {
14409                            $owlCarousel = $($element).find('.js-owl-carousel');
14410                        }
14411
14412                        $timeout(function() {
14413                            $owlCarousel.owlCarousel(carouselOptions);
14414                        });
14415                    });
14416                }
14417            }
14418        ])
14419        .directive('lxGradeSelectorCarouselItems', [
14420            function() {
14421                return {
14422                    restrict: 'A',
14423                    link: function($scope) {
14424                        if ($scope.$last) {
14425                            $scope.$emit(EVENT_CAROUSEL_ITEMS_FINISHED_RENDERING);
14426                        }
14427                    }
14428                };
14429            }
14430        ]);
14431})();
14432;
14433(function () {
14434    "use strict";
14435
14436    angular.module('Lexus.Directives')
14437        .directive('lxSwatchChooser', [SwatchChooserDirective]);
14438
14439    function SwatchChooserDirective() {
14440        SwatchChooserController.$inject = ['$scope', '$window'];
14441        return {
14442            scope: {
14443                'swatchChooserModel': '=swatchChooserModel',
14444                'selectedSwatch': '=selectedSwatch'
14445            },
14446            controller: SwatchChooserController,
14447            controllerAs: '$ctrl',
14448            restrict: 'A',
14449            transclude: true,
14450
14451            link: function(scope, element, attr, ctrl, transclude) {
14452                transclude(scope, function(clone, scope) {
14453                    element.append(clone);
14454                });
14455            }
14456        };
14457
14458        
14459        function SwatchChooserController($scope, $window) {
14460            var $ctrl = this;
14461            $ctrl.selectedSwatch = $scope.selectedSwatch;
14462        }
14463    }
14464}());;
14465//Only Allowed to watch 
14466//BuildPriceAPIService.data
14467
14468//Only Allowed to emit 
14469//models selected
14470//engine variants selected
14471
14472(function (undefined) {
14473
14474    "use strict";
14475    /* Directives */
14476    angular.module('Lexus.Directives')
14477    .directive('lxCompareVehiclesModel', [function () {
14478      compareVehiclesModelController.$inject = ['$scope', '$timeout', 'VehicleService', 'BuildPriceService', 'BuildPriceAPIService'];
14479      return {
14480    
14481        scope: {
14482            selectedModel: '<carmodel',
14483            selectedEngineVariant: '<variant',
14484            modelsBrowsed: '<modelsbrowsed',
14485            compareVariantsSelected: '<variantsselected',
14486            compareVehiclesCount: '<compareVehiclesCount',
14487            compareModelsSelected: '<modelsselected',
14488            compareVehicleTriggered: '<comparecomplete'
14489        },
14490        restrict: 'A',
14491        controller: compareVehiclesModelController,
14492        controllerAs: 'cvModel',
14493        bindToController: true,
14494        transclude: true,
14495        link: function (scope, element, attrs, ctrl, transclude) {
14496            transclude(scope, function (clone) {
14497                element.append(clone);
14498            });
14499        }
14500      };
14501
14502      
14503
14504      function compareVehiclesModelController($scope, $timeout, VehicleService, BuildPriceService, BuildPriceAPIService) {
14505        var cvModel = this;
14506        cvModel.vehicleData = null;
14507        cvModel.modelSelected = false;
14508                        
14509        $scope.setModelHeights = function () {
14510            $timeout(function () {
14511                if (window.innerWidth > 767) {
14512                    var choosers = angular.element('.lx-range-compare-model-chooser__models');
14513
14514                    for (var c = 0, clen = choosers.length; c < clen; c++) {
14515                        var models = choosers[c].querySelectorAll('.lx-range-compare-model-chooser-model');
14516                        var modelHeights = [], lastHeight = 0;
14517                        for (var m = 0, mlen = models.length; m < mlen; m++) { modelHeights.push(models[m].clientHeight); }
14518                        modelHeights.sort();
14519                        lastHeight = modelHeights[modelHeights.length - 1];
14520                        if (modelHeights[0] != lastHeight) {
14521                            for (var m = 0, mlen = models.length; m < mlen; m++) { models[m].setAttribute('style', 'height:' + lastHeight + 'px;'); }
14522                        }
14523                    }
14524                }
14525            }, 0, false);
14526        }
14527
14528        $scope.emitEngineVariantSelected = function (engineVariant, currentSelection, userInitiated, carModel) {
14529            var data = {
14530                selection: engineVariant.uriName,
14531                clearDownstream: (engineVariant != currentSelection && currentSelection != null),
14532                userInitiated: userInitiated,
14533                carModel: {
14534                    name: carModel.name,
14535                    imageSrc: carModel.imageSrc,
14536                    imageAlt: carModel.imageAlt
14537                }
14538            };            
14539            $scope.$emit(BuildPriceService.events.engineVariantSelected, data);
14540        };
14541
14542        $scope.variantHasOffer = function (model) {
14543            var hasOffer = false;
14544            model.modelEngineVariants.forEach(function (variant) {
14545                if ($scope.isOnOffer(variant)) {
14546                    hasOffer = true;
14547                }
14548            });
14549
14550            return hasOffer;
14551        };
14552
14553        $scope.isOnOffer = function (obj) {
14554            var bool = obj.onOffer;
14555            return bool;
14556        }
14557
14558        $scope.emitModelSelected = function (model, currentSelection, userInitiated, modelElement, bodyType) {
14559
14560            if (userInitiated) {
14561                var shortModel = modelElement.replace(/\s+/g, '');
14562                setTimeout(function () {
14563                    $('html, body').animate({ scrollTop: $(shortModel).offset().top - 10 }, 250)
14564                }, 500);
14565            }
14566
14567            $scope.$emit(BuildPriceService.events.onOfferText, null);
14568
14569            var data = {
14570                selection: currentSelection === model.uriName ? null : model.uriName,
14571                clearDownstream: (model != currentSelection && currentSelection != null),
14572                userInitiated: userInitiated,
14573                modelTouched: model.uriName,
14574                bodyType: bodyType
14575            };            
14576            $scope.$emit(BuildPriceService.events.modelSelected, data);
14577        };
14578
14579        $scope.ReceiveModels = function () {
14580
14581            cvModel.vehicleData = BuildPriceAPIService.data;
14582            console.info("%c 0. buildPriceModelController -> ReceiveModels: ", "color: darkblue; ", BuildPriceAPIService.data);
14583            $scope.$emit(BuildPriceService.events.overviewDataReceived, BuildPriceAPIService.data);
14584            $scope.setModelHeights();
14585        };
14586
14587        $scope.$on(BuildPriceService.events.pageNavigationBroadcast, function (e, target) {
14588            var isCurrent = target.id == 'model'; 
14589            cvModel.isCurrentStep = isCurrent;
14590        });
14591
14592        $scope.$watch('cvModel.selectedModel', function (newValue, oldValue) {
14593            if (newValue != null && !cvModel.modelSelected) {
14594                var shortModel = '.contains-model--' + newValue.replace(/\s+/g, '');                                                
14595                setTimeout(function () {
14596                    $('html, body').animate({ scrollTop: $(shortModel).offset().top - 10 }, 250);
14597                }, 600);
14598
14599                cvModel.modelSelected = true;
14600            }
14601        });
14602
14603        function init() {            
14604            BuildPriceAPIService.getModels().then($scope.ReceiveModels);
14605        }
14606
14607        init();
14608      }
14609    }]);
14610
14611}());;
14612"use strict";
14613/* Directives */
14614angular.module('Lexus.Directives')
14615.directive('lxCompareVehiclesFooter', [function () {
14616    compareVehiclesFooterController.$inject = ['$scope', '$window', '$element', 'BuildPriceService'];
14617    return {
14618        scope: {
14619            compareVariantsSelected: '<variantsselected',
14620            compareVehiclesCount: '<compareVehiclesCount',
14621            vehicleData: '<vehicleData',
14622            removeVariant: '&removeSelectedVariant',
14623            hideFooter: '<hideFooter'
14624        },
14625        restrict: 'A',
14626        controller: compareVehiclesFooterController,
14627        controllerAs: 'cvFtr',
14628        bindToController: true,
14629        transclude: true,
14630        link: function(scope, element, attrs, ctrl, transclude) {
14631            transclude(scope, function (clone) {
14632                element.append(clone);
14633            });
14634            scope.init();
14635        }
14636    }
14637
14638    
14639    function compareVehiclesFooterController($scope, $window, $element, BuildPriceService) {
14640        var cvFtr = this;
14641                                              
14642        $scope.init = function () {
14643            
14644        }
14645
14646        $scope.compareVehicles = function () {            
14647            $scope.$emit(BuildPriceService.events.onCompareVehicles, {});
14648        }
14649
14650        $scope.getImageSource = function (index) {
14651            if (!cvFtr.compareVariantsSelected)
14652                return Lexus.Paths.RangeCompareSilhouette + '?w=600';
14653
14654            return ((cvFtr.compareVariantsSelected.length > index && cvFtr.compareVariantsSelected[index] != ''
14655                && cvFtr.vehicleData[cvFtr.compareVariantsSelected[index]] != 'undefined') ?
14656                cvFtr.vehicleData[cvFtr.compareVariantsSelected[index]].imageSrc : Lexus.Paths.RangeCompareSilhouette) + '?w=600';
14657        }
14658
14659        $scope.getImageAlt = function (index) {
14660            if (!cvFtr.compareVariantsSelected)
14661                return '';
14662
14663            return ((cvFtr.compareVariantsSelected.length > index && cvFtr.compareVariantsSelected[index] != '') ?
14664                cvFtr.vehicleData[cvFtr.compareVariantsSelected[index]].imageAlt: '');
14665        }
14666    }
14667}]);;
14668"use strict";
14669/* Directives */
14670angular.module('Lexus.Directives')
14671.directive('lxCompareVehiclesVehicle', [function () {
14672    compareVehiclesVehicleController.$inject = ['$scope', '$rootScope', '$window', '$element', '$timeout', 'BuildPriceAPIService', 'BuildPriceService', 'PricingService'];
14673    return {
14674        scope: {
14675            compareModelsSelected: '<modelsselected',
14676            compareVariantsSelected: '<variantsselected',
14677            compareVehiclesCount: '<compareVehiclesCount',
14678            vehicleData: '<vehicleData',
14679            removeVariant: '&removeSelectedVariant',
14680            activeVehicleIndex: '<activeVehicleIndex',
14681            postcode: '<postcode',
14682            vehicleSelected: '&onVehicleSelected',
14683        },
14684        restrict: 'A',
14685        controller: compareVehiclesVehicleController,
14686        controllerAs: 'ffc',
14687        bindToController: true,
14688        transclude: true,
14689        link: function(scope, element, attrs, ctrl, transclude) {
14690            transclude(scope, function (clone) {
14691                element.append(clone);
14692            });
14693            scope.ffc = scope.ffc || {};
14694            scope.ffc.vehicleIndex = attrs.vehicleIndex || "0";
14695            scope.ffc.engineVariant = null;
14696            scope.ffc.carModelObject = null;
14697            scope.ffc.variantInit = false;
14698            scope.ffc.imageTransitionInProgress = false;
14699            scope.ffc.grade = null;
14700            scope.ffc.gradeId = null;
14701            scope.ffc.driveAwayPrice = null;
14702            scope.ffc.driveAwayPriceLoading = false;
14703            scope.ffc.pricingSuccess = true;
14704            
14705            //defaults
14706            scope.ffc.form = {
14707                data: {
14708                    carmodel: null,
14709                    grade: null,
14710                    engineVariant: null,
14711                    engineVariantAndGrade: null,
14712                    enhancementPack: null,
14713                    trim: null,
14714                    colour: null,
14715                },
14716                settings: {
14717                    hideDefaultSelectOption: true
14718                },
14719            };
14720            scope.init();
14721        }
14722    }
14723
14724    
14725    function compareVehiclesVehicleController($scope, $rootScope, $window, $element, $timeout, BuildPriceAPIService, BuildPriceService, PricingService) {
14726        var cvv = this;
14727
14728        $scope.$watch("ffc.postcode", BuildPriceService.watchDebounce(function (newValue, oldValue) {
14729            if (newValue != null) {
14730                console.info("%c compareVehiclesVehicleController received POSTCODE: ", "color: green; ", newValue);
14731                getPriceByGrade();
14732            }
14733        }, 50), true);
14734
14735        $scope.$watch("ffc.gradeId", BuildPriceService.watchDebounce(function (newValue, oldValue) {
14736            if (newValue != null) {
14737                console.info("%c compareVehiclesVehicleController gradeID Changed: ", "color: blue; ", newValue);                
14738                getPriceByGrade();
14739            }
14740        }, 50), true);
14741
14742        function getPriceByGrade() {
14743
14744            if ($scope.ffc.gradeId && $scope.ffc.postcode) {
14745                $scope.ffc.driveAwayPriceLoading = true;
14746                PricingService.driveAwayByGrade($scope.ffc.gradeId,
14747                    $scope.ffc.postcode).then(function (data) {
14748                        if (data.status === "Success") {
14749                            if (data.data && data.data.driveAway) {
14750                                $scope.ffc.driveAwayPrice = data.data.driveAway;
14751                                $scope.ffc.pricingSuccess = true;
14752                            }
14753                            else {
14754                                $scope.ffc.driveAwayPrice = 'n/a';
14755                                $scope.ffc.pricingSuccess = false;
14756                            }                                                        
14757                    }
14758                    else {
14759                            $scope.ffc.driveAwayPrice = 'n/a';
14760                            $scope.ffc.pricingSuccess = false;
14761                    }
14762                    }, function (reason) {
14763                        $scope.ffc.driveAwayPrice = 'n/a';
14764                        $scope.ffc.pricingSuccess = false;
14765                }).finally(function (data) {                    
14766                    $scope.ffc.driveAwayPriceLoading = false;
14767                    pricingReport();
14768                });;
14769            }
14770        }
14771
14772        function pricingReport() {
14773            if ($scope.ffc.pricingSuccess) {
14774                $element.removeClass('pricing-error');
14775            } else {
14776                $element.addClass('pricing-error');
14777            }
14778        }
14779
14780        function getImageSource(index) {
14781            if (!cvv.compareVariantsSelected)
14782                return Lexus.Paths.RangeCompareSilhouette + '?w=600';
14783
14784            return ((cvv.compareVariantsSelected.length > index && cvv.compareVariantsSelected[index] != ''
14785                && cvv.vehicleData[cvv.compareVariantsSelected[index]] != 'undefined') ?
14786                cvv.vehicleData[cvv.compareVariantsSelected[index]].imageSrc : Lexus.Paths.RangeCompareSilhouette) + '?w=600';
14787        }
14788
14789        function updateVehicleImageAndAnimate() {
14790            $scope.ffc.imageTransitionInProgress = true;
14791            $timeout(function () {
14792                $scope.ffc.vehicleImageSource = $scope.ffc.carModelObject.imageSrc+ '?w=600';
14793            }, 250);
14794            $timeout(function () {
14795                $scope.ffc.imageTransitionInProgress = false;
14796            }, 500);
14797        }
14798        
14799        function setEngineVariantObjects() {
14800
14801            if (!$scope.ffc.variantInit) {
14802                //OnInit Get details from compareState
14803                if ($scope.ffc.compareVariantsSelected && $scope.ffc.compareVariantsSelected.length > 0) {
14804                    $scope.ffc.variantInit = true;
14805                    $scope.ffc.engineVariant = BuildPriceAPIService.findEngineVariantInVehicleData($scope.ffc.compareVariantsSelected[$scope.ffc.vehicleIndex]);
14806                }                
14807            }
14808            else if ($scope.ffc.form.data.engineVariantAndGrade == null) {
14809                //When Updated Model
14810                $scope.ffc.engineVariant = ($scope.ffc.carModelObject && $scope.ffc.carModelObject.modelEngineVariants
14811                            && $scope.ffc.carModelObject.modelEngineVariants.length > 0) ? $scope.ffc.carModelObject.modelEngineVariants[0] : null;                 
14812            }
14813            else {
14814                //From FormsCarModelEngineVariantGradeAndPacks variant dropdown model
14815                $scope.ffc.engineVariant = BuildPriceAPIService.findEngineVariantInVehicleData($scope.ffc.form.data.engineVariant);
14816                $scope.ffc.grade = getGradeFromVariant($scope.ffc.form.data.grade);
14817                $scope.ffc.gradeId = $scope.ffc.grade.gradeID;
14818                return;//do not set
14819            }
14820            
14821            $scope.ffc.form.data.engineVariantAndGrade = getEngineVariantAndGrade();
14822            $scope.ffc.grade = getGradeFromVariant(null);
14823            $scope.ffc.gradeId = $scope.ffc.grade.gradeID;
14824            
14825        }
14826
14827        function getGradeFromVariant(grade) {
14828            //Default Grade
14829            if ($scope.ffc.engineVariant && $scope.ffc.engineVariant.grades
14830                           && $scope.ffc.engineVariant.grades.length > 0) {
14831
14832                var gradeMappedArray = $scope.ffc.engineVariant.grades.map(function (currentValue, index, array) {
14833                    return {
14834                        name: currentValue.name,
14835                        gradeID: currentValue.id,
14836                        gradeMDM: currentValue.gradeID,
14837                        paints: currentValue.availablePaints,
14838                        trims:currentValue.availableTrims
14839                    }
14840                });
14841
14842                return (grade == null) ? gradeMappedArray[0] : findGradeInGrades(grade, gradeMappedArray);
14843            }
14844            
14845            return null;
14846        }
14847
14848        function findGradeInGrades(grade, grades) {
14849            var gradeObj = grades.find(function (gradeItem) {
14850                return gradeItem.name === grade;
14851            });
14852            return gradeObj;
14853        };
14854        
14855        function getEngineVariantAndGrade() {                        
14856            //For FormsCarModelEngineVariantGradeAndPacks support
14857            var grade = getGradeFromVariant(null);
14858            return $scope.ffc.engineVariant.name + ";" + ((grade) ? grade.name : '');
14859        }
14860
14861        function enhancementPacksReceived(response) {
14862            if (response && response.length > 0) {
14863                $scope.ffc.form.data.enhancementPack = response[0].uriName;
14864
14865                $scope.$emit(BuildPriceService.events.enhancementPackSelected, {                    
14866                    enhancementPack: $scope.ffc.form.data.enhancementPack,                    
14867                    vehicleIndex: $scope.ffc.vehicleIndex
14868                });
14869            }
14870        }
14871
14872        $rootScope.$on('vehicle-preference-change', function (event, data) {
14873            //This is a global Event, Update only if it is meant for this vehicle
14874            if (data.vehicleIndex == $scope.ffc.vehicleIndex) {                                
14875                $scope.ffc.carModelObject = BuildPriceAPIService.findModelInVehicleData(data.model);
14876                updateVehicleImageAndAnimate($scope);                                
14877                setEngineVariantObjects();
14878                BuildPriceAPIService.getVariants($scope.ffc.engineVariant.name, $scope.ffc.gradeId, enhancementPacksReceived);
14879
14880                $scope.$emit(BuildPriceService.events.gradeSelected, { 
14881                    carmodel:$scope.ffc.carModelObject.name,
14882                    engineVariant:$scope.ffc.engineVariant.name,                    
14883                    grade: $scope.ffc.grade.name, 
14884                    enhancementPack:$scope.ffc.form.data.enhancementPack,
14885                    trim:$scope.ffc.grade.trims[0].shortName,
14886                    colour:$scope.ffc.grade.paints[0].shortName,
14887                    gradeId: $scope.ffc.grade.gradeMDM, 
14888                    vehicleIndex: $scope.ffc.vehicleIndex });
14889            }            
14890        });
14891
14892        $scope.init = function () {
14893            $scope.ffc.vehicleImageSource = getImageSource($scope.ffc.vehicleIndex);
14894            $scope.ffc.form.data.carmodel = ($scope.ffc.compareModelsSelected && $scope.ffc.compareModelsSelected.length > 0) ?
14895                $scope.ffc.compareModelsSelected[$scope.ffc.vehicleIndex] : '';
14896            $scope.ffc.carModelObject = BuildPriceAPIService.findModelInVehicleData($scope.ffc.form.data.carmodel);
14897            $scope.ffc.engineVariant = BuildPriceAPIService.findEngineVariantInVehicleData($scope.ffc.compareVariantsSelected[$scope.ffc.vehicleIndex]);            
14898            $rootScope.$broadcast('field-car-model-engine-variant-update-selected', { vehicleIndex: $scope.ffc.vehicleIndex });
14899            setTimeout(function () {
14900                window.scrollTo(0, angular.element('.lx-range-compare-vehicles')[0].offsetTop);
14901            }, 600);
14902            
14903        }
14904                
14905        $scope.getImageAlt = function (index) {
14906            if (!cvv.compareVariantsSelected)
14907                return '';
14908
14909            return ((cvv.compareVariantsSelected.length > index && cvv.compareVariantsSelected[index] != '') ?
14910                cvv.vehicleData[cvv.compareVariantsSelected[index]].imageAlt : '');
14911        }
14912
14913        $scope.onVehicleSectionClick =  function(){
14914            $scope.$emit(BuildPriceService.events.compareVehicleActiveSelection, $scope.ffc.vehicleIndex);
14915        }        
14916
14917        $scope.updatePostCode = function () {
14918            $scope.$emit(BuildPriceService.events.openPostcodeDialog);
14919        }
14920
14921        $scope.headerLink = function (configuredPath, fallBack) {
14922            if (!configuredPath) {
14923                return fallBack + "/" + $scope.getBuildPriceLink();
14924            }
14925            return configuredPath;
14926        }
14927
14928        $scope.getBuildPriceLink = function () {
14929            //No deeplink if epack is not set
14930            if ($scope.ffc.form && $scope.ffc.form.data && $scope.ffc.form.data.enhancementPack)
14931            {
14932                var vehicle = {
14933                    carmodel: $scope.ffc.carModelObject.name,
14934                    engineVariant: $scope.ffc.engineVariant.name,
14935                    grade: $scope.ffc.grade.name,
14936                    enhancementPack: $scope.ffc.form.data.enhancementPack,
14937                    colour: ($scope.ffc.grade.paints.length > 0) ? $scope.ffc.grade.paints[0].shortName : '',
14938                    trim: ($scope.ffc.grade.trims.length > 0) ? $scope.ffc.grade.trims[0].shortName : ''
14939                };
14940
14941                return BuildPriceService.getBuildPriceLink(vehicle, 3);
14942
14943            }
14944            return '';
14945        }
14946
14947        
14948    }
14949}]);;
14950"use strict";
14951/* Directives */
14952angular.module('Lexus.Directives')
14953.directive('lxCompareRangeSpec', [function () {
14954    compareRangeSpecController.$inject = ['$scope', '$http', '$filter', '$timeout', '$interval',
14955        '$location', '$rootScope', 'API', '$sce', '$log', 'BuildPriceService'];
14956    return {
14957        scope: {
14958            compareGradesSelected: '<gradesselected',
14959        },
14960        restrict: 'A',
14961        controller: compareRangeSpecController,
14962        controllerAs: 'rtd',
14963        bindToController: true,
14964        transclude: true,
14965        link: function (scope, element, attrs, ctrl, transclude) {
14966            transclude(scope, function (clone) {
14967                element.append(clone);
14968            });
14969            scope.init();
14970            scope.rtd.mdmIds = attrs.mdmList || "";
14971        }
14972    }
14973
14974    function compareRangeSpecController($scope, $http, $filter, $timeout, $interval, $location, $rootScope, API, $sce, $log, BuildPriceService) {
14975        var rtd = this;
14976        var APPEND_GRADE_ID = "_V2";
14977
14978        $scope.rtd.urlParameters = $location.search();
14979
14980        $scope.$watch("rtd.compareGradesSelected", BuildPriceService.watchDebounce(function (newValue, oldValue) {
14981            if (newValue != null) {
14982                console.info("%c compareGradesSelected grade updated: ", "color: green; ", newValue.join('+'));
14983                fetchTechDataDetails();
14984            }
14985        }, 50), true);
14986
14987        rtd.apiService = API;
14988
14989        $scope.init = function () {
14990            $scope.rtd = $scope.rtd || {};
14991
14992            $scope.rtd.data = {
14993                featuresData: null,
14994                featuresByCategory: []
14995            };
14996            $scope.rtd.filteredFeatures = [];
14997            $scope.rtd.rawCustomizedFeatures = [];
14998            $scope.rtd.data.engineVariants = [];
14999            $scope.rtd.comparingSameVehicle = false;
15000            $scope.rtd.allFilter = true;
15001
15002            $scope.rtd.featuresByCategoryRendering = false;
15003
15004            if ($scope.rtd.compareGradesSelected.length >= 2) {
15005                fetchTechDataDetails();
15006            }
15007        };
15008
15009        $scope.findValueByKey = function (grade, feature) {
15010            if (feature.specs) {
15011                return (grade.indexOf(APPEND_GRADE_ID) > -1) ? feature.specs[grade.replace(APPEND_GRADE_ID, '')] : feature.specs[grade];
15012            }
15013
15014        }
15015
15016        $scope.filterFeatures = function (diff) {
15017            if (diff) {
15018                $scope.rtd.allFilter = false;
15019                var filteredArray = [];
15020
15021                $scope.rtd.rawCustomizedFeatures.map(function (arr) {
15022                    var filteredFeatures = [];
15023                    var modifiedArray = angular.copy(arr, {});
15024
15025                    var same = false;
15026
15027                    var keys = Object.keys(arr.grades);
15028                    var filteredFeatures = [];
15029                    arr.features.map(function (feature) {
15030
15031                        if (feature.specs) {
15032                            same = $scope.findValueByKey(keys[0], feature) == $scope.findValueByKey(keys[1], feature);
15033                        }
15034                        if (!same) {
15035                            filteredFeatures.push(feature);
15036                            same = false;
15037                        }
15038
15039                    });
15040                    modifiedArray.features = filteredFeatures;
15041                    filteredArray.push(modifiedArray);
15042
15043                });
15044                $scope.rtd.filteredFeatures = filteredArray;
15045                return;
15046            }
15047
15048            $scope.rtd.allFilter = true;
15049            $scope.rtd.filteredFeatures = $scope.rtd.rawCustomizedFeatures.map(function (a) { return angular.copy(a, {}); });
15050        }
15051
15052        function fetchTechDataDetails() {
15053            if ($scope.rtd.compareGradesSelected.length < 2) {
15054                return;
15055            }
15056
15057            $scope.rtd.featuresByCategoryLoading = true;
15058            $scope.rtd.comparingSameVehicle = $scope.rtd.compareGradesSelected[0] == $scope.rtd.compareGradesSelected[1];
15059
15060            var getRequest = ($scope.rtd.comparingSameVehicle) ? API.get($filter('formatArgs')(Lexus.API.Lookup.RangeTechDataByGrade, [
15061                Lexus.ID.Site,
15062                $scope.rtd.compareGradesSelected[0]
15063            ])) : API.get($filter('formatArgs')(Lexus.API.Lookup.RangeTechDataByGrade, [
15064                Lexus.ID.Site,
15065                $scope.rtd.compareGradesSelected[0],
15066                $scope.rtd.compareGradesSelected[1]
15067            ]));
15068
15069            getRequest
15070                .then(function (data) {
15071                    if (!data) {
15072                        return;
15073                    }
15074
15075                    $scope.rtd.featuresByCategoryRendering = true;
15076
15077                    $timeout(function () {
15078
15079                        $scope.rtd.data.featuresByCategory = data.data;
15080
15081                        var cloneData = data.data.map(function (a) { return angular.copy(a, {}); });
15082
15083                        $scope.rtd.rawCustomizedFeatures = filterKeyFeatures(cloneData, "id", $scope.rtd.mdmIds);
15084
15085                        $scope.filterFeatures(!$scope.rtd.allFilter);
15086
15087                        $scope.rtd.featuresByCategoryRendering = false;
15088                    }, 1);
15089
15090                    $scope.rtd.featuresByCategoryLoading = false;
15091                }, function (error) {
15092                    $log.error(error);
15093                    $scope.rtd.featuresByCategoryLoading = false;
15094                });
15095        };
15096
15097        function filterKeyFeatures(rawArray, propId, filterContent) {
15098            //if empty returns the raw
15099            if (!filterContent) {
15100                if ($scope.rtd.comparingSameVehicle) {
15101                    transformSpecsForSameVehicle(rawArray);
15102                }
15103                return rawArray;
15104            }
15105
15106
15107            var filteredArray = [];
15108
15109            rawArray.map(function (arr) {
15110                var filteredFeatures = [];
15111                var content = false;
15112
15113                arr.features.map(function (feature) {
15114
15115                    if (!feature.hasOwnProperty(propId))
15116                        return;
15117
15118                    var matchExpr = new RegExp("(?:^|\\b)(" + feature[propId] + ")(?=\\b|$)");
15119
15120                    if (filterContent.match(matchExpr) != null) {
15121                        filteredFeatures.push(feature);
15122                        content = true;
15123                    }
15124
15125                });
15126                if (content) {
15127                    //update features
15128                    arr.features = filteredFeatures;
15129                    //Duplicate the grades - FE
15130                    if ($scope.rtd.comparingSameVehicle) {
15131                        transformSpecsForSameVehicle([arr]);
15132                    }
15133
15134                    filteredArray.push(arr);
15135                    content = false;
15136                }
15137
15138            });
15139
15140            return filteredArray;
15141        };
15142
15143        function transformSpecsForSameVehicle(arr) {
15144            arr.map(function (a) {
15145                var keys = Object.keys(a.grades);
15146                if (keys.length == 1) {
15147                    a.grades[keys[0] + APPEND_GRADE_ID] = a.grades[keys[0]];
15148                }
15149            });
15150
15151
15152            return arr;
15153        }
15154    }    
15155}]);
15156
15157
15158
15159;
15160"use strict";
15161/* Directives */
15162angular.module('Lexus.Directives')
15163.directive('lxCompareVehiclesCta', [function () {
15164    compareVehiclesCtaController.$inject = ['$scope', '$rootScope', '$window', '$element', '$timeout', 'BuildPriceAPIService', 'BuildPriceService', 'PricingService'];
15165    return {
15166        scope: {
15167            compareModelsSelected: '<modelsselected',
15168            compareVariantsSelected: '<variantsselected',
15169            compareGradesSelected: '<gradesselected',
15170            compareEpacksSelected: '<epacksselected',
15171            comparePaintsSelected: '<paintsselected',
15172            compareTrimsSelected: '<trimsselected',
15173            vehicleSelected: '&onVehicleSelected',
15174        },
15175        restrict: 'A',
15176        controller: compareVehiclesCtaController,
15177        controllerAs: 'cvcta',
15178        bindToController: true,
15179        transclude: true,
15180        link: function(scope, element, attrs, ctrl, transclude) {
15181            transclude(scope, function (clone) {
15182                element.append(clone);
15183            });
15184            scope.cvcta = scope.cvcta || {};                        
15185        }
15186    }
15187
15188    
15189    function compareVehiclesCtaController($scope, $rootScope, $window, $element, $timeout, BuildPriceAPIService, BuildPriceService, PricingService) {
15190        var cvcta = this;
15191                                                                                        
15192        $scope.headerLink = function (configuredPath, fallBack, vehicleIndex) {
15193            if (!configuredPath) {
15194                return fallBack + "/" + $scope.getBuildPriceLink(vehicleIndex);
15195            }
15196            return configuredPath;
15197        }
15198
15199        $scope.getBuildPriceLink = function (vehicleIndex) {
15200
15201            var vehicle = {
15202                carmodel: $scope.cvcta.compareModelsSelected[vehicleIndex],
15203                engineVariant: $scope.cvcta.compareVariantsSelected[vehicleIndex],
15204                grade: $scope.cvcta.compareGradesSelected[vehicleIndex],
15205                enhancementPack: $scope.cvcta.compareEpacksSelected[vehicleIndex],
15206                colour: $scope.cvcta.comparePaintsSelected[vehicleIndex],
15207                trim: $scope.cvcta.compareTrimsSelected[vehicleIndex],
15208            };
15209
15210            return BuildPriceService.getBuildPriceLink(vehicle, 3);
15211        }
15212
15213        
15214    }
15215}]);;
15216
15217(function (undefined) {
15218    "use strict";
15219    /* Directives */
15220    angular.module('Lexus.Directives')
15221    .config(["$rootScopeProvider", function ($rootScopeProvider) {
15222        $rootScopeProvider.digestTtl(1000);
15223    }])
15224    .directive('lxRangeCompareVehicles', [function () {
15225        rangeCompareVehiclesController.$inject = ['$scope', '$rootScope', '$timeout', '$location', '$http', '$filter', 'UserPreferencesService', 'BuildPriceService', '$anchorScroll', 'BuildPriceAPIService', 'HttpPendingRequestsService', 'ngDialog', 'AddressSuggestService'];
15226        return {
15227            scope: true,
15228            restrict: 'A',
15229            controller: rangeCompareVehiclesController,
15230            controllerAs: 'cv',
15231            bindToController: true,
15232            link: function (scope, element, attrs) {
15233                scope.cv = scope.cv || {};
15234                try {
15235                    scope.cv.settings = JSON.parse(attrs.bpSettings);
15236                } catch (ex) {
15237                    console.error(ex.message);
15238                }                
15239                scope.cv.settings = scope.cv.settings || {};
15240                scope.cv.settings.debug = scope.cv.settings.debug === 'true' ? true : false;
15241                scope.cv.settings.disableDiffPricing = scope.cv.settings.disableDiffPricing === 'true' ? true : false;
15242                scope.cv.data = {                    
15243                    userInteraction: false,
15244                    activeIndex: 0,
15245                    activeVehicleIndex:0,
15246                    compareVehiclesTriggered:false,
15247                    vehicleLoading: false,
15248                    onOfferText: null,
15249                    compareVehicles: {
15250                        selectedCount: 0,
15251                        variantsAdded: [],
15252                        modelsBrowsed: [],
15253                        modelsAdded: [],
15254                        gradesAdded: [],
15255                        trimsAdded: [],
15256                        paintsAdded: [],
15257                        ePacksAdded: [],
15258                        gradeIdsAdded: [],
15259                        vehiclesData: {}
15260                    },
15261                    vehicle: {
15262                        carmodel: (scope.cv && scope.cv.data && scope.cv.data.vehicle && scope.cv.data.vehicle.carmodel) ? scope.cv.data.vehicle.carmodel : null,
15263                        engineVariant: (scope.cv && scope.cv.data && scope.cv.data.vehicle && scope.cv.data.vehicle.engineVariant) ? scope.cv.data.vehicle.engineVariant : null,
15264                        grade: (scope.cv && scope.cv.data && scope.cv.data.vehicle && scope.cv.data.vehicle.grade) ? scope.cv.data.vehicle.grade : null,
15265                        postcode: '',
15266                        enhancementPack: (scope.cv && scope.cv.data && scope.cv.data.vehicle && scope.cv.data.vehicle.enhancementPack) ? scope.cv.data.vehicle.enhancementPack : null,
15267                        colour: (scope.cv && scope.cv.data && scope.cv.data.vehicle && scope.cv.data.vehicle.colour) ? scope.cv.data.vehicle.colour : null,
15268                        trim: (scope.cv && scope.cv.data && scope.cv.data.vehicle && scope.cv.data.vehicle.trim) ? scope.cv.data.vehicle.trim : null,
15269                        colourId: (scope.cv && scope.cv.data && scope.cv.data.vehicle && scope.cv.data.vehicle.colourId) ? scope.cv.data.vehicle.colourId : null,
15270                        bodyType: (scope.cv && scope.cv.data && scope.cv.data.vehicle && scope.cv.data.vehicle.bodyType) ? scope.cv.data.vehicle.bodyType : null,
15271                    },
15272                    vehicleImage: '',
15273                    postcodes: []
15274                };
15275                scope.init();
15276            }
15277        };
15278
15279        function rangeCompareVehiclesController($scope, $rootScope, $timeout, $location, $http, $filter, UserPreferencesService, BuildPriceService, $anchorScroll, BuildPriceAPIService, HttpPendingRequestsService, ngDialog, AddressSuggestService) {
15280            var cv = this;
15281
15282
15283            $scope.UserPreferencesService = UserPreferencesService;
15284            $scope.BuildPriceService = BuildPriceService;
15285
15286            $scope.init = function () {                
15287                var visitor = $scope.UserPreferencesService.getLexusVisitor();
15288                $scope.tagsLoading = false;
15289                if (visitor && visitor.postCode && visitor.suburb && visitor.state) {
15290                    var postcodeSelection = UserPreferencesService.getAutocompleteTagFromVisitor(visitor);
15291                    $scope.cv.data.postcodes.push(postcodeSelection);
15292                    $scope.cv.data.vehicle.postcode = visitor.postCode;
15293                }
15294            };
15295             
15296            function getBodyTypeByModel(carmodel, vehicleData) {
15297                var bodyType = "";
15298                if (!carmodel) return bodyType;
15299
15300                for (var i = 0; i < vehicleData.length; i++) {
15301
15302                    var modelMap = vehicleData[i].models.map(function (model) {
15303                        return model.uriName == carmodel;
15304                    })
15305
15306                    if (modelMap.length > 0 && modelMap.indexOf(true) > -1) {
15307                        return vehicleData[i].uriName;
15308                    }
15309                }
15310                return bodyType;
15311            }
15312
15313            $scope.loadUserPreferences = function (vehicledata) {               
15314                var sbdv = $scope.cv.data.vehicle;                                
15315
15316                //TODO: If carmodel is preselected then add that car as default model to compare.
15317                var vCookie = $scope.UserPreferencesService.getLexusVehicle();
15318                var params = (vCookie.model) ? { model: vCookie.model } : null;
15319                var vCompareVehicleCookie = $scope.UserPreferencesService.getCompareVehiclesState(params);
15320
15321                if (vCompareVehicleCookie) {
15322                    sbdv.carmodel = sbdv.carmodel ? sbdv.carmodel : vCompareVehicleCookie.model;                                        
15323                }
15324                if (!sbdv.bodyType) {
15325                    sbdv.bodyType = getBodyTypeByModel(sbdv.carmodel, vehicledata);
15326                }
15327                $scope.cv.data.compareVehicles.modelsBrowsed.push(sbdv.carmodel + ":" + sbdv.bodyType);
15328            }
15329
15330            $scope.loadTags = function (query) {
15331                $scope.tagsLoading = true;
15332                AddressSuggestService.setParam('term', query);
15333                return AddressSuggestService.GetResults($scope.tagsLoaded);
15334            };
15335
15336            $scope.tagsLoaded = function () {
15337                $scope.tagsLoading = false;
15338            }
15339
15340            $scope.autocompleteLoading = function () {
15341                return $scope.tagsLoading;
15342            };
15343
15344            $scope.updatePostcode = function ($tag) {
15345                $scope.cv.data.postcodes = [$tag];
15346                $scope.cv.data.vehicle.postcode = $tag.data.postcode;
15347                $scope.$emit('visitor-details-cookie-change', {
15348                    postCode: $tag.data.postcode,
15349                    suburb: $tag.data.suburb,
15350                    state: $tag.data.state
15351                });
15352                //close after update
15353                $timeout(function () { ngDialog.close(); }, 300);
15354            };
15355
15356            $scope.clearPostcode = function () {
15357                $scope.cv.data.postcodes = [];
15358                $scope.cv.data.vehicle.postcode = '';
15359            };
15360                                                                      
15361            
15362            $scope.clearDownstream = function (level, data) {
15363
15364                if(data.userInitiated)
15365                    HttpPendingRequestsService.cancelAll();
15366
15367                //Model = 5
15368                if (level >= 5) $scope.cv.data.vehicle.engineVariant = null;
15369                //Variant = 4
15370                if (level >= 4) $scope.cv.data.vehicle.grade = null;
15371                //Grade = 3
15372                if (level >= 3) $scope.cv.data.vehicle.enhancementPack = null;
15373                if (level >= 3) $scope.cv.data.vehicleImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAsYAAAGRCAQAAADMAr3sAAAEOUlEQVR42u3UIQEAAAzDsM+/6SsYHkkkFDQHwFwkADBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjCQDMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAHMGAAzBjBjAMwYwIwBMGMAMwbAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwAzBsCMAcwYADMGMGMAzBjAjAEwYwDMGMCMATBjADMGwIwBzBgAMwYwYwDMGMCMATBjADMGoHq/RgGSYZc9owAAAABJRU5ErkJggg==';
15374
15375                //Enhancement pack = 2
15376                if (level >= 2) $scope.cv.data.vehicle.colour = null;
15377                if (level >= 2) $scope.cv.data.vehicle.trim = null;
15378                if (level >= 2) $scope.cv.data.vehicleImageLoading = true;
15379               
15380                //Trim = 1
15381                if (level >= 1) $scope.cv.data.vehicle.colourId = null;
15382                
15383            };
15384            
15385
15386            //#region EVENTS                       
15387
15388            $scope.$on(BuildPriceService.events.userInteraction, function () {
15389                $scope.cv.data.userInteraction = true;
15390            });
15391
15392            $scope.$on(BuildPriceService.events.overviewDataReceived, function (event, vehicledata) {
15393                $scope.loadUserPreferences(vehicledata);
15394            });
15395                                    
15396
15397            $scope.$on(BuildPriceService.events.onOfferText, function (event, data) {
15398                $scope.cv.data.onOfferText = data;
15399            });
15400
15401            $scope.$on(BuildPriceService.events.openPostcodeDialog, function () {               
15402                $scope.ngDialog = ngDialog;
15403                ngDialog.open({
15404                    template: 'cvPostcodeDialogTemplate',
15405                    controller: '',
15406                    className: 'ngdialog-theme-default',
15407                    scope: $scope, // this line wasn't here before
15408                    plain: false,
15409                    showClose: true,
15410                    closeByDocument: true,
15411                    closeByEscape: true,
15412                    appendTo: false,
15413                    preCloseCallback: function (value) {
15414                        
15415                    }
15416                });
15417            });
15418                                    
15419
15420            $scope.setPageVisited = function () {
15421                $scope.cv.settings.firstVisit = false;
15422                return true;
15423            }                        
15424
15425            $scope.setUserInteraction = function (data) {
15426                if (data.userInitiated) {
15427                    $scope.cv.data.userInteraction = true;
15428                }
15429            }            
15430            //#region vehicle selection listeners
15431            function SetModelsBrowsedState(data) {
15432                if (!data.selection) {
15433                    var index = $scope.cv.data.compareVehicles.modelsBrowsed.indexOf(data.modelTouched + ":" + data.bodyType);
15434                    if (index > -1) {
15435                        $scope.cv.data.compareVehicles.modelsBrowsed.splice(index, 1);
15436                    }
15437                }
15438                else {
15439                    if ($scope.cv.data.compareVehicles.modelsBrowsed.indexOf(data.modelTouched + ":" + data.bodyType) < 0) {
15440                        var match = [];
15441                        angular.forEach($scope.cv.data.compareVehicles.modelsBrowsed, function (model) {
15442                            var existingBodyType = model.split(":")[1];
15443
15444                            if (existingBodyType == data.bodyType) {
15445                                match.push(model);
15446                            }
15447                        });
15448                        angular.forEach(match, function (model) {
15449                            var index = $scope.cv.data.compareVehicles.modelsBrowsed.indexOf(model);
15450                            if (index > -1) {
15451                                $scope.cv.data.compareVehicles.modelsBrowsed.splice(index, 1);
15452                            }
15453                        });
15454                        $scope.cv.data.compareVehicles.modelsBrowsed.push(data.modelTouched + ":" + data.bodyType);
15455                    }
15456                    else {
15457                        var index = $scope.cv.data.compareVehicles.modelsBrowsed.indexOf(data.modelTouched + ":" + data.bodyType);
15458                        if (index > -1) {
15459                            $scope.cv.data.compareVehicles.modelsBrowsed.splice(index, 1);
15460                        }
15461                    }
15462                }
15463            }
15464            function SetCompareVariantsState(data) {
15465                if ($scope.cv.data.compareVehicles.selectedCount >= 2) {
15466                    $scope.cv.data.compareVehicles.variantsAdded.splice(1, 1);
15467                    $scope.cv.data.compareVehicles.modelsAdded.splice(1, 1);
15468                    $scope.cv.data.compareVehicles.selectedCount--;
15469                }
15470                else {
15471                    $scope.cv.data.compareVehicles.variantsAdded.splice($scope.cv.data.activeIndex, 1, data.selection);
15472                    $scope.cv.data.compareVehicles.modelsAdded.splice($scope.cv.data.activeIndex, 1, data.carModel.name);
15473                    $scope.cv.data.activeIndex++;
15474                    $scope.cv.data.compareVehicles.vehiclesData[data.selection] = data.carModel;
15475                    $scope.cv.data.compareVehicles.selectedCount++;
15476                }
15477            }
15478
15479            function SetCompareGradesState(data) {                
15480                $scope.cv.data.compareVehicles.modelsAdded.splice(+data.vehicleIndex, 1, data.carmodel);
15481                $scope.cv.data.compareVehicles.variantsAdded.splice(+data.vehicleIndex, 1, data.engineVariant);
15482                $scope.cv.data.compareVehicles.gradesAdded.splice(+data.vehicleIndex, 1, data.grade);
15483                $scope.cv.data.compareVehicles.paintsAdded.splice(+data.vehicleIndex, 1, data.colour);
15484                $scope.cv.data.compareVehicles.trimsAdded.splice(+data.vehicleIndex, 1, data.trim);                
15485                $scope.cv.data.compareVehicles.gradeIdsAdded.splice(+data.vehicleIndex, 1, data.gradeId);                                
15486            }
15487            //each listener sets the selected value, then sets null on all downstream vehicle selections
15488            $scope.$on(BuildPriceService.events.modelSelected, function (event, data) {
15489                if (data.clearDownstream) $scope.clearDownstream(5, data);
15490
15491                $scope.setUserInteraction(data);
15492                $scope.cv.data.vehicle.carmodel = data.selection;
15493                $scope.cv.data.vehicle.bodyType = data.bodyType;
15494                
15495                SetModelsBrowsedState(data);                
15496                $scope.saveCurrentState();
15497            });
15498
15499            $scope.$on(BuildPriceService.events.engineVariantSelected, function (event, data) {
15500                if (data.clearDownstream) $scope.clearDownstream(4, data);
15501                $scope.setUserInteraction(data);
15502                $scope.cv.data.vehicle.engineVariant = data.selection;
15503
15504                SetCompareVariantsState(data);
15505                $scope.saveCurrentState();
15506            });
15507
15508            $scope.$on(BuildPriceService.events.gradeSelected, function (event, data) {
15509                if (data.clearDownstream) $scope.clearDownstream(3, data);
15510
15511                $scope.setUserInteraction(data);
15512                $scope.cv.data.vehicle.grade = data.grade;
15513                SetCompareGradesState(data);
15514                $scope.saveCurrentState();
15515            });
15516
15517            $scope.$on(BuildPriceService.events.enhancementPackSelected, function (event, data) {                
15518                $scope.cv.data.vehicle.enhancementPack = data.grade;
15519                $scope.cv.data.compareVehicles.ePacksAdded.splice(+data.vehicleIndex, 1, data.enhancementPack);
15520                $scope.saveCurrentState();
15521            });
15522            
15523            //#endregion
15524
15525            $scope.$on(BuildPriceService.events.onCompareVehicles, function () {
15526                $scope.cv.data.compareVehiclesTriggered = true;
15527            });
15528
15529            $scope.$on(BuildPriceService.events.compareVehicleActiveSelection, function (evenet, index) {
15530                $scope.cv.data.activeVehicleIndex = index;
15531            });
15532
15533            $scope.$watch('cv.data.vehicle', function (newValue, oldValue) {
15534                console.log("%c cv.data.vehicle changed:", "color: blue;", newValue);
15535            });
15536                        
15537            $scope.saveCurrentState = function () {
15538                //$scope.pushURI($scope.cv.data.vehicle);
15539                $scope.$emit('compare-vehicle-change', {
15540                    model: $scope.cv.data.vehicle.carmodel,
15541                    bodyType:$scope.cv.data.vehicle.bodyType,
15542                    modelsBrowsed: $scope.cv.data.compareVehicles.modelsBrowsed.join("|"),
15543                    variantsSelected: $scope.cv.data.compareVehicles.variantsAdded.join("|")
15544                });
15545
15546            }                        
15547            //#endregion
15548            function SetActiveIndex(index) {
15549                //Check if needs to reset
15550                if ($scope.cv.data.compareVehicles.variantsAdded.join("") == "") {
15551                    $scope.cv.data.activeIndex = 0;
15552                    return;
15553                }
15554                $scope.cv.data.activeIndex = index;
15555            }
15556
15557            $scope.removeSelectedVariant = function (index) {
15558                if (index < 0) return;
15559                $scope.cv.data.compareVehicles.variantsAdded.splice(index, 1, "");
15560                $scope.cv.data.compareVehicles.modelsAdded.splice(index, 1, "");
15561                $scope.cv.data.compareVehicles.selectedCount--;
15562                SetActiveIndex(index);
15563                $scope.saveCurrentState();
15564            }
15565
15566            $scope.onVehicleSelected = function (vehicleIndex) {
15567                $scope.$emit('vehicle-preference-change', {
15568                    model: $scope.cv.data.compareVehicles.modelsAdded[vehicleIndex],
15569                    engineVariant: $scope.cv.data.compareVehicles.variantsAdded[vehicleIndex],
15570                    grade: $scope.cv.data.compareVehicles.gradesAdded[vehicleIndex]
15571                });
15572            }
15573
15574        }
15575
15576    }])    
15577}());;
15578"use strict";
15579angular.module('Lexus.Directives')
15580    .directive('lxSaveBuildPrice', [function () {
15581        saveBuildPriceController.$inject = ['$scope', '$rootScope', '$filter', 'UserPreferencesService', 'API', 'BuildPriceService', 'BuildPriceAPIService'];
15582        return {
15583            scope: {
15584                model: '<carmodel',
15585                engineVariant: '<variant',
15586                enhancementPack: '<enhancement',
15587                grade: '<grade',
15588                colour: '<colour',
15589                trim: '<trim',
15590                vehicle: '<vehicle',
15591                buildPriceUri:'<baseUri',
15592                vehicleData: '<overviewData',
15593                vehicleImage: '<vehicleImage',
15594                openSbp: '<openSbp'
15595            },
15596            restrict: 'A',
15597            controllerAs: 'sbp',
15598            bindToController: true,
15599            controller: saveBuildPriceController,
15600            transclude: true,
15601            link: function (scope, element, attrs, ctrl, transclude) {
15602                transclude(scope, function (clone) {
15603                    element.append(clone);
15604                });
15605                scope.sbp = scope.sbp || {};
15606                scope.sbp.loggedIn = false;
15607                scope.sbp.status = "Retrieve Saved Vehicles";
15608                scope.sbp.initState = false;
15609                scope.sbp.loadingVehicles = false;
15610                scope.sbp.imageTransitionInProgress = false;
15611                scope.sbp.showCurrentVehicle = true;
15612                scope.sbp.vehicleMaxSavedId = 0;
15613                scope.sbp.vehicleMaxLimitToSave = 3;
15614                scope.sbp.data = {
15615                    vehicleToSave: {
15616                        carmodel: null,
15617                        engineVariant: null,
15618                        grade: null,
15619                        postcode: null,
15620                        enhancementPack: null,
15621                        colour: null,
15622                        trim: null,
15623                        colourId: null,
15624                        carImageSource: null,
15625                        id: null,
15626                        timeStamp: null
15627                    },
15628                    currentVehicle: {
15629                        name: null,
15630                        imgSrc: null
15631                    },
15632                    savedVehicles: [],
15633                    engineVariantObj: null,
15634                };
15635
15636                scope.init(attrs);
15637            }
15638        }
15639        function saveBuildPriceController($scope, $rootScope, $filter, UserPreferencesService, API, BuildPriceService, BuildPriceAPIService) {
15640            var sbp = this;
15641            
15642            function getDefaultVehicleState() {
15643                //TODO: Sitecore
15644                return {
15645                    name: "Configure",
15646                    imgSrc: "Assets/Lexus/MainSite/images/placeholders/save-build/placeholder.png",
15647                    buildPriceLink: $scope.sbp.buildPriceUri,
15648                    id: null
15649                };
15650            }
15651
15652            function updateStatus(savedVehicles) {                
15653                $scope.sbp.status = (!$scope.sbp.loggedIn) ? "Retrieve Saved Vehicles" : (savedVehicles && savedVehicles.length > 0) ? "Saved Vehicle(S)" : "No Saved Vehicle(S)";
15654            }
15655
15656            function getSavedVehiclesRendering(vehicles) {
15657                var savedArr = [];
15658                if (!vehicles)
15659                    return savedArr;
15660
15661                vehicles.map(function (vehicle) {
15662                    savedArr.push(getSavedVehicleRendering(vehicle, vehicle.id));
15663                });
15664
15665                return savedArr;
15666            }
15667
15668            function getSavedVehicleRendering(vehicle, id) {
15669                try {                    
15670                    $scope.sbp.data.engineVariantObj = BuildPriceAPIService.findEngineVariantInVehicleData(vehicle.engineVariant);                    
15671                    return {
15672                        name: vehicle.engineVariant,
15673                        imgSrc: vehicle.carImageSource,
15674                        buildPriceLink: $scope.sbp.buildPriceUri + "/" + BuildPriceService.getBuildPriceLink(vehicle, 5),
15675                        id: id,
15676                    }
15677                }
15678                catch (e) {
15679                    return getDefaultVehicleState();
15680                }
15681                return null;                
15682            }
15683
15684            function paintsResolved(vehicleId) {
15685                console.info("%c 9. Exterior Images  Resolved", "color: blue; ", "Complete");                
15686                //Mutate the DOM saved Vehicles section
15687                $scope.sbp.data.savedVehicles = getSavedVehiclesRendering($scope.virtualSavedStates);
15688                $scope.sbp.loadingVehicles = false;                
15689            }
15690
15691            function callVehicleTree(vehicle, vehicleId) {
15692                //if (!vehicle) return;
15693                $scope.sbp.loadingVehicles = true;
15694                //save car image source , need not cal vehicle tree to dig the paint
15695                paintsResolved();
15696                return;
15697
15698                //Deep Nesting is required if we do not save the Vehicle Image in xDB
15699
15700                console.info("%c 2. Loading Vehicles  ", "color: blue; ", $scope.sbp.loadingVehicles);
15701                try {
15702                    var grade = BuildPriceAPIService.getGradeByName(vehicle.engineVariant, vehicle.grade);
15703                    console.info("%c 2.1.Grade  ", "color: blue; ", vehicle.grade);
15704                    var variant = BuildPriceAPIService.getVariantByName(vehicle.engineVariant, grade.id, vehicle.enhancementPack);
15705                    console.info("%c 2.2.Variant  ", "color: blue; ", grade.id);
15706                    if (!variant) {
15707                        var ep = function (variantsResp) {
15708                            console.info("%c 4. Variants Resolved  ", "color: blue; ", "");
15709                            variant = BuildPriceAPIService.getVariantByName(vehicle.engineVariant, grade.id, vehicle.enhancementPack);
15710                            var trim = null;
15711                            if (!trim) {
15712                                var tr = function (trimsResp) {
15713                                    console.info("%c 5. Trims Resolved  ", "color: blue; ", "");
15714                                    trim = BuildPriceAPIService.getTr
15714imByName(vehicle.engineVariant, grade.id, variant.mdmid, vehicle.trim);
15715                                    var paint = null;
15716                                    if (!paint) {
15717                                        var pt = function (paintResponse) {
15718                                            console.info("%c 7. paints resolved  ", "color: blue; ", "");
15719                                            paint = BuildPriceAPIService.getPaintByName(vehicle.engineVariant, grade.id, variant.mdmid, trim.id, vehicle.colour);
15720                                            console.info("%c 8. Calling Exterior Images  ", "color: blue; ", paint.materialCode);
15721                                            BuildPriceAPIService.getExteriorImages(vehicle.engineVariant, grade.id, variant.mdmid, trim.id, paint.materialCode, paintsResolved.bind(null, vehicleId));
15722                                        };
15723                                        console.info("%c 6. Calling paints  ", "color: blue; ", trim.id);
15724                                        BuildPriceAPIService.getPaints(vehicle.engineVariant, grade.id, variant.mdmid, trim.id, pt);
15725                                    }
15726                                };
15727                                console.info("%c 5. Calling Trims  ", "color: blue; ", variant.mdmid);
15728                                BuildPriceAPIService.getTrims(vehicle.engineVariant, grade.id, variant.mdmid, tr);
15729                            }
15730                        };
15731                        console.info("%c 3. Calling Variants  ", "color: blue; ", vehicle.engineVariant);
15732                        BuildPriceAPIService.getVariants(vehicle.engineVariant, grade.id, ep);
15733                    }
15734                    else {
15735                        //Resolve Paints as the tree might have fetched
15736                        paintsResolved(vehicleId);
15737                    }
15738                    
15739                }
15740                catch (e) {
15741                    return null;
15742                }
15743                finally {                    
15744                }
15745                return null;
15746
15747            }
15748            
15749            $scope.init = function (attrs) {
15750                //Virtual DOM for saved states
15751                $scope.virtualSavedStates = [];               
15752
15753                var analyticsCookie = UserPreferencesService.getAnalyticsCookie();
15754
15755                if (analyticsCookie) {
15756                    $scope.userEmail = analyticsCookie.identifier;
15757                    $scope.sbp.loggedIn = analyticsCookie.identifier != null && analyticsCookie.identifier != "";
15758                    console.info("%c Logged in  ", "color: green; ", $scope.userEmail);
15759                }
15760                if (attrs) {
15761                    $scope.vehicleState = attrs.vehicleState;                                        
15762                    $scope.sbp.vehicleMaxLimitToSave = +attrs.vehicleMaxLimit || $scope.sbp.vehicleMaxLimitToSave;
15763                    var vIds = (attrs.vehicleIds) ? attrs.vehicleIds.split("|") : null;
15764                    var sArr = (attrs.vehicleState) ? attrs.vehicleState.split("|") : null;
15765
15766                    if (sArr && sArr.length > 0) {
15767                        for (var i = 0; i < sArr.length; i++) {
15768                            if (sArr[i]) {
15769                                $scope.virtualSavedStates.push(JSON.parse(sArr[i] ? sArr[i] : null));
15770                                $scope.virtualSavedStates[i].id = vIds[i];
15771                            }                            
15772                        }
15773                    }
15774                    updateStatus($scope.virtualSavedStates);
15775                }                
15776                
15777            };
15778            
15779            $scope.$watch(function (scope) { return scope.sbp.vehicleData; }, function (newVal, oldVal) {
15780                if (newVal && $scope.sbp.loggedIn) {
15781                    //call vehicleTree for saved Vehicles                    
15782                    callVehicleTree();
15783                }
15784            });
15785
15786            $scope.$watch(function (scope) { return scope.sbp.vehicleImage; }, function (newVal, oldVal) {
15787                if (newVal) {
15788
15789                    if (newVal.indexOf("data:image") > -1)
15790                        return;
15791
15792                    $scope.sbp.showCurrentVehicle = true;
15793                    $scope.sbp.loadingVehicles = false;
15794                    $scope.sbp.imageTransitionInProgress = false;
15795                    $scope.sbp.data.currentVehicle = { name: $scope.sbp.vehicle.engineVariant, imgSrc: newVal.indexOf("data:image") > -1 ? null : newVal.replace(/jpg/g, 'png') };
15796                    setVehicle($scope.sbp.vehicle);
15797                } 
15798            })
15799
15800            $scope.$watch(function (scope) { return scope.sbp.model; }, function (newVal, oldVal) {
15801                if (newVal) {                    
15802                    if (!$scope.sbp.vehicle.engineVariant) {
15803                        $scope.sbp.showCurrentVehicle = false;
15804                        $scope.sbp.data.currentVehicle = getDefaultVehicleState();
15805                        $scope.sbp.imageTransitionInProgress = true;
15806                    }                    
15807                }
15808                else {
15809                    $scope.sbp.showCurrentVehicle = false;
15810                    $scope.sbp.data.currentVehicle = getDefaultVehicleState();
15811                }
15812            })
15813
15814            $scope.$watch(function (scope) { return scope.sbp.engineVariant; }, function (newVal, oldVal) {
15815                if (newVal) {                    
15816                    $scope.sbp.imageTransitionInProgress = false;
15817                    $scope.sbp.loadingVehicles = true;                  
15818                }
15819                else {
15820                    setVehicle($scope.sbp.vehicle);
15821                    $scope.sbp.showCurrentVehicle = false;
15822                }
15823            })
15824            
15825            $scope.$on(BuildPriceService.events.openSaveBuildPriceDialog, function (e, vehicle) {
15826                openNav(vehicle);
15827            });
15828
15829            $scope.$watch(function (scope) { return scope.sbp.openSbp; }, function (newVal, oldVal) {
15830                if (newVal === false) {
15831                    $scope.closeNav();
15832                }
15833            });
15834
15835            $scope.$on(BuildPriceService.events.closeSaveBuildPriceDialog, function (e, vehicle) {
15836                $scope.closeNav();
15837            });
15838
15839            $scope.saveBuildAndPrice = function (email, close) {
15840
15841                if (!email || $scope.sbp.data.currentVehicle.name == "Configure") {
15842                    return;
15843                }
15844                //disable Add vehicle until current request processed .
15845                $scope.sbp.showCurrentVehicle = false;
15846
15847                UserPreferencesService.setAnalyticsCookie({ identifier: email });
15848
15849                var postData = { VehicleId: updateAndRetrieveVehicleId(), EmailId: email, DeepLink: getData() };
15850                var postAnalytics = API.post(Lexus.API.Analytics.PostSavedVehicles,
15851                    postData);
15852
15853                postAnalytics
15854                    .then(function (data) {
15855                        if (data.Data) {
15856                            console.log('save B&P' + data.Data);
15857
15858                            $scope.sbp.loggedIn = true;
15859                            $scope.userEmail = email;                            
15860                            mutateSavedVehicles(getSavedVehicleRendering(JSON.parse(data.Data.DeepLink), data.Data.VehicleId), data.Data.VehicleId);
15861                            updateStatus($scope.sbp.data.savedVehicles);
15862                            updateAndRetrieveVehicleId();
15863                            if (close) {
15864                                $scope.closeNav();
15865                            }
15866                        }
15867
15868                    }, function (error) {
15869                        $log.error(error);                       
15870                    }).finally(function () {
15871                        $scope.sbp.showCurrentVehicle = true;
15872                    });
15873            };
15874
15875            $scope.deleteSaveBuildEntry = function (id) {
15876                
15877                var postData = { VehicleId: id, EmailId: $scope.userEmail, DeepLink: null };                
15878                var postAnalytics = API.del($filter('formatArgs')(Lexus.API.Analytics.DeleteSavedVehicle, [id, $scope.userEmail]));
15879                $scope.sbp.showCurrentVehicle = false;
15880
15881                postAnalytics
15882                    .then(function (data) {                        
15883                        mutateSavedVehicles(null, postData.VehicleId);
15884                        updateStatus($scope.sbp.data.savedVehicles);
15885                        $scope.sbp.activeModel = null;
15886                    }, function (error) {
15887                        $log.error(error);                        
15888                    }).finally(function () {
15889                        $scope.sbp.showCurrentVehicle = true;
15890                    });
15891            };
15892
15893            function getData() {
15894                //Set the ID and Time Stamp before Save/Update
15895                $scope.sbp.data.vehicleToSave.id = getVehicleId();
15896                $scope.sbp.data.vehicleToSave.timeStamp = (new Date()).getTime();
15897
15898                if ($scope.sbp.data.vehicleToSave)
15899                {                    
15900                    return JSON.stringify($scope.sbp.data.vehicleToSave);
15901                }
15902
15903                var vCookie = UserPreferencesService.getLexusVehicle() || { model: '' };
15904                var seperator = "~";
15905                var vts = $scope.sbp.data.vehicleToSave;                
15906
15907                if (vCookie) {
15908                    if (vCookie.model)
15909                        vts.carmodel = vCookie.model;
15910                    if (vCookie.engineVariant)
15911                        vts.engineVariant = vCookie.engineVariant;
15912                    if (vCookie.grade)
15913                        vts.grade = vCookie.grade;
15914                    if (vCookie.enhancementPack)
15915                        vts.enhancementPack = vCookie.enhancementPack;
15916                    if (vCookie.trim)
15917                        vts.trim = vCookie.trim;
15918                    if (vCookie.colour)
15919                        vts.colour = vCookie.colour;
15920                }
15921
15922                return JSON.stringify(vts);
15923            }
15924
15925            function getVehicleId() {
15926                return ($scope.sbp.activeModel) ? $scope.sbp.activeModel : null;
15927            }
15928
15929            //do not call this outside of AJAX calls
15930            function updateAndRetrieveVehicleId() {
15931                //active selection
15932                if ($scope.sbp.activeModel)
15933                    return $scope.sbp.activeModel;
15934
15935                if ($scope.sbp.data.savedVehicles.length >= $scope.sbp.vehicleMaxLimitToSave) {
15936                    //Set to first vehicle if max limit reached
15937                    $scope.sbp.activeModel = $scope.sbp.data.savedVehicles[0].id;
15938                    return $scope.sbp.activeModel;
15939                }
15940
15941                return null;
15942            }            
15943
15944            function mutateSavedVehicles(vehicle, vehicleId) {
15945                var isExisting = false;
15946                var changedIdx = 0;
15947
15948                //mutation
15949                $scope.sbp.data.savedVehicles.map(function (savedVehicle, index) {
15950                    if (savedVehicle.id == vehicleId) {
15951                        isExisting = true;
15952                        savedVehicle = vehicle;
15953                        changedIdx = index;
15954                    }
15955                });                
15956                //insert at last
15957                if (!isExisting) {
15958                    var index = $scope.sbp.data.savedVehicles.length;
15959                    (vehicle) ? $scope.sbp.data.savedVehicles.splice(index, 1, vehicle) : $scope.sbp.data.savedVehicles.splice(index, 1);
15960                }//update
15961                else {
15962                    (vehicle) ? $scope.sbp.data.savedVehicles.splice(changedIdx, 1, vehicle) : $scope.sbp.data.savedVehicles.splice(changedIdx, 1);                    
15963                }
15964            }
15965
15966            function onGetVehicleInfoResponse(response) {
15967                $scope.sbp.changeEmail = false;
15968
15969                if (response && response.Data && response.Data.Vehicles.length == 0) {                    
15970                    $scope.virtualSavedStates = [];
15971                    $scope.sbp.data.savedVehicles = [];
15972                    $scope.sbp.activeModel = null;
15973                    updateStatus($scope.sbp.data.savedVehicles);
15974                    return;
15975                }
15976                
15977                $scope.vehicleState = response.Data.Vehicles[0].DeepLink;
15978                $scope.virtualSavedStates = [];
15979                var vehicleCount = response.Data.Vehicles.length;
15980
15981                for (var i = 0; i < vehicleCount; i++) {
15982                    //To support old data n/a in live
15983                    var _v = JSON.parse(response.Data.Vehicles[i].DeepLink);
15984
15985                    if (_v && !_v.id) {
15986                        _v.id = response.Data.Vehicles[i].VehicleId;
15987                    }
15988                    $scope.virtualSavedStates.push(_v);
15989                }
15990
15991                $scope.sbp.activeModel = (vehicleCount >= $scope.sbp.vehicleMaxLimitToSave) ? $scope.virtualSavedStates[0].id : null;
15992                callVehicleTree(JSON.parse(response.Data.Vehicles[0].DeepLink), response.Data.Vehicles[0].VehicleId);
15993                updateStatus($scope.sbp.data.savedVehicles);
15994            }
15995
15996            $scope.getVehicleInfo = function (email) {
15997                if (!email) {
15998                    return;
15999                }
16000
16001                $scope.sbp.showCurrentVehicle = false;
16002                var getAnalytics = API.get($filter('formatArgs')(Lexus.API.Analytics.GetSavedVehicles, ["1", email]));
16003
16004                getAnalytics
16005                    .then(function (response) {
16006                        $scope.sbp.loggedIn = true;
16007                        $scope.userEmail = email;
16008                        UserPreferencesService.setAnalyticsCookie({ identifier: email });
16009                        onGetVehicleInfoResponse(response);                                                
16010                    }, function (error) {                        
16011                        $scope.sbp.loggedIn = false;
16012                        updateStatus($scope.sbp.data.savedVehicles);
16013                        $log.error(error);                        
16014                    }).finally(function () {
16015                        $scope.sbp.showCurrentVehicle = true;
16016                    });
16017            };
16018           
16019            function openNav(vehicle) {
16020                $("#slide-out-tray").addClass("show");
16021                $('body').removeClass("save-modal-open").addClass("save-modal-open");
16022                onNavOpened(vehicle);
16023            }
16024
16025            function onNavOpened(vehicle) {
16026                setVehicle(vehicle);
16027            }
16028
16029            function setVehicle(vehicle) {
16030                if (!vehicle) {
16031                    return;
16032                }
16033
16034                var vts = $scope.sbp.data.vehicleToSave;
16035                vts.carmodel = vehicle.carmodel;
16036                vts.engineVariant = vehicle.engineVariant;
16037                vts.grade = vehicle.grade;
16038                vts.enhancementPack = vehicle.enhancementPack;
16039                vts.trim = vehicle.trim;
16040                vts.colour = vehicle.colour;
16041                vts.id = $scope.sbp.activeModel;
16042                vts.carImageSource = $scope.sbp.data.currentVehicle.imgSrc;
16043
16044                $scope.sbp.data.currentVehicle = (vehicle.carmodel && vehicle.engineVariant) ? getSavedVehicleRendering(vts) : getDefaultVehicleState();
16045                $scope.sbp.imageTransitionInProgress = false;                
16046
16047                if (!$scope.sbp.initState && !$scope.sbp.loadingVehicles) {
16048                    $scope.sbp.data.savedVehicles = getSavedVehiclesRendering($scope.virtualSavedStates);
16049                    updateAndRetrieveVehicleId();
16050                    $scope.sbp.initState = true;
16051                }
16052            }            
16053
16054            /* Set the width of the side navigation to 0 */
16055            $scope.closeNav = function () {
16056                $scope.$emit(BuildPriceService.events.triggerCloseSaveBuildPriceDialog, {});
16057                $("#slide-out-tray").removeClass("show");
16058                $('body').removeClass("save-modal-open");
16059            }            
16060        }
16061    }]);
16062;
16063"use strict";
16064angular.module('Lexus.Directives')
16065    .directive('lxTranslate', function () {
16066        lxTranslateToastController.$inject = ['$scope', '$attrs', '$window', '$rootScope', '$cookies', '$timeout', 'UserPreferencesService', 'TranslationService'];
16067        return {
16068            scope: true,
16069            restrict: 'C',
16070            controller: lxTranslateToastController,
16071            controllerAs: 'ltt',
16072            bindToController: true
16073        }
16074
16075        function lxTranslateToastController($scope, $attrs, $window, $rootScope, $cookies, $timeout, UserPreferencesService, TranslationService) {
16076            var translateUrl = $attrs.translateUrl;
16077            var pageGuid = $attrs.pageGuid;
16078            var window = angular.element($window);
16079            var throttlescroll;
16080
16081            $scope.showToast = false;
16082
16083            var languagePref = UserPreferencesService.getLanguagePref(pageGuid);
16084            if (languagePref == null || languagePref.translate == null) {
16085                $scope.showToast = true;
16086            }
16087
16088            $scope.translate = function (translate) {
16089                $scope.showToast = false;
16090
16091                if (translate) {
16092                    UserPreferencesService.setLanguagePref({ translate: true }, pageGuid);
16093                    $window.location.href = translateUrl + "?translate=true";
16094                }
16095                else {
16096                    UserPreferencesService.setLanguagePref({ translate: false }, pageGuid);
16097                    $rootScope.$broadcast('show-confirmation');
16098                }
16099            }
16100
16101            window.bind("scroll", function () {
16102                clearTimeout(throttlescroll);
16103                if ($scope.showToast == true) {
16104                    throttlescroll = $timeout(checkAmountScrolled, 100);
16105                }
16106            });
16107
16108            function checkAmountScrolled() {
16109                var pageYOffset = $window.pageYOffset;
16110                if ($attrs.scrollOffset < pageYOffset) {
16111                    UserPreferencesService.setLanguagePref({ translate: false }, pageGuid);
16112                    $scope.showToast = false;
16113                }
16114            }
16115        }
16116    });;
16117"use strict";
16118
16119angular.module('Lexus.Services')
16120.service('TranslationService', [
16121    function () {
16122    var self = this;
16123    self.isBrowserLanguageSupported = function (browserLanguages, supportedLanguagesStr) {
16124        var supportedLanguages = supportedLanguagesStr.split(",");
16125        if (browserLanguages && browserLanguages.length > 0) {
16126            for (var i = 0; i < browserLanguages.length; i++) {
16127                var browserLanguage = browserLanguages[i];
16128                var langArray = browserLanguage.split(';');
16129                if (langArray && supportedLanguages.indexOf(langArray[0].toLowerCase()) >= 0) {
16130                    return true;
16131                }
16132            }
16133        }
16134        return false;
16135    }
16136
16137    return self;
16138}]);
16139
16140;
16141"use strict";
16142angular.module('Lexus.Directives')
16143    .directive('lxLanguageCheck', ['TranslationService', function (TranslationService) {
16144        return {
16145            scope: true,
16146            restrict: 'A',
16147            link: function (scope, element, attrs) {
16148                var browserLanguageSupported = true;
16149                var supportedLanguages = attrs.lxLanguageCheck;
16150                if (supportedLanguages.length > 0) {
16151                    var browserLanguages = angular.fromJson(attrs.browserLanguages);
16152                    browserLanguageSupported = TranslationService.isBrowserLanguageSupported(browserLanguages, supportedLanguages);
16153                }
16154                if (browserLanguageSupported) {
16155                    element.addClass("visible");
16156                }
16157            }
16158        }
16159    }]);;
16160"use strict";
16161angular.module('Lexus.Directives')
16162    .directive('lxTranslateConfirm', function () {
16163        lxTranslateConfirmController.$inject = ['$scope', '$attrs', '$rootScope', '$window', '$cookies', '$timeout', '$location', 'UserPreferencesService', 'TranslationService'];
16164        return {
16165            scope: true,
16166            restrict: 'C',
16167            controller: lxTranslateConfirmController,
16168            controllerAs: 'ltt',
16169            bindToController: true
16170        }
16171
16172        function lxTranslateConfirmController($scope, $attrs, $rootScope, $window, $cookies, $timeout, $location, UserPreferencesService, TranslationService) {
16173            $scope.showToast = false;
16174            var window = angular.element($window);
16175            var throttlescroll;
16176            var showTranslateConfirmation = $location.search().translate;
16177            if (showTranslateConfirmation == "true") {
16178                $scope.showToast = true;
16179            }
16180
16181            window.bind("scroll", function () {
16182                clearTimeout(throttlescroll);
16183                if ($scope.showToast == true) {
16184                    throttlescroll = $timeout(checkAmountScrolled, 100);
16185                }
16186            });
16187
16188            $rootScope.$on('show-confirmation', function (event, data) {
16189                $timeout(function () {
16190                    $scope.showToast = true;
16191                }, 100);
16192            });
16193
16194            function checkAmountScrolled() {
16195                var pageYOffset = $window.pageYOffset;
16196                if ($attrs.scrollOffset < pageYOffset) {
16197                    $scope.showToast = false;
16198                }
16199            }
16200        }
16201    });;
16202"use strict";
16203angular.module('Lexus.Directives')
16204    .directive('lxOpeningHours', ['$window', '$timeout', '$filter', '$http', function ($window, $timeout, $filter, $http) {
16205        openingHoursController.$inject = ['$scope', '$http', '$filter', '$timeout', '$window', '$interval', '$rootScope'];
16206        return {
16207            scope: true,
16208            restrict: 'A',
16209            controller: openingHoursController,
16210            controllerAs: 'oh',
16211            bindToController: true,
16212            link: function (scope, element, attrs, filter, http) {
16213            }
16214        };
16215
16216        function openingHoursController($scope, $http, $filter, $timeout, $window, $interval, $rootScope) {
16217            var oh = this;
16218            $scope.init = function (openingHour, closingHour, isClosed) {
16219                oh.IsOpenNow = false;
16220                oh.OpenNowText = "Open now:";
16221                var openingHourElements = openingHour.split(":");
16222                var closingHourElements = closingHour.split(":");
16223                if (!isClosed === true && openingHourElements.length === 2 && closingHourElements.length === 2) {
16224                    var now = new Date();
16225                    var year = now.getFullYear();
16226                    var month = now.getMonth();
16227                    var day = now.getDate();
16228
16229                    var dateOpeningHour = new Date(year, month, day, openingHourElements[0], openingHourElements[1]);
16230                    var dateClosingHour = new Date(year, month, day, closingHourElements[0], closingHourElements[1]);
16231
16232                    oh.IsOpenNow = dateOpeningHour.getTime() < now.getTime() && dateClosingHour.getTime() > now.getTime();
16233                }
16234                oh.OpenNowText = oh.IsOpenNow ? "Open now:" : "Closed";
16235            };
16236
16237            oh.simplifyTime = function (time) {
16238                var result = time;
16239                var timeElements = time.split(":");
16240
16241                if (timeElements.length === 2) {
16242                    var hour = timeElements[0];
16243                    if (hour - 12 >= 0) {
16244                        result = (hour - 12) + ':' + timeElements[1] + "pm";
16245                    }
16246                    else {
16247                        result = hour + ':' + timeElements[1] + "am";
16248                    }
16249                    if (timeElements[1].indexOf("00") >= 0) {
16250                        result = result.replace(':00', '');
16251                    }
16252                }
16253                
16254                return result;
16255            };
16256
16257        }
16258    }]);
16259
16260
16261;
16262'use strict';
16263angular
16264    .module('Lexus.Components')
16265    .component('threeDotToggleButton', {
16266        bindings: {
16267            'isActive': '<',
16268            'screenReaderText': '@',
16269            'cssClass': '@'
16270        },
16271        template: [
16272            '<button class="lx-three-dot-toggle-button {{$ctrl.cssClass}}" ng-class="{\'is-active\' : $ctrl.isActive}">',
16273                '<span class="lx-three-dot-toggle-button__screen-reader-text">{{$ctrl.screenReaderText}}</span>',
16274                '<span class="lx-three-dot-toggle-button__dots"><span class="lx-three-dot-toggle-button__dot"></span></span>',
16275            '</button>'
16276        ].join('')
16277    });
16278;
16279"use strict";
16280angular
16281    .module('Lexus.Components')
16282    .component('welcomeMessage', {
16283        bindings: {
16284            hoursOfDay: '@hours',
16285            cssClass: '@cssClass'
16286        },
16287        controller: ['API', '$filter', function (API, $filter) {
16288            var ctrl = this;
16289            this.$onInit = function () {
16290
16291                API.get($filter('formatArgs')(Lexus.API.Lookup.GetWelcomeMessage, [
16292                    Lexus.ID.Site,
16293                    ctrl.hoursOfDay
16294                ])).then(function (data) {
16295                    if (data && data.Data) {
16296                        ctrl.message = data.Data.Message;
16297                    }
16298                },
16299                    function (error) {
16300                        ctrl.message = null;
16301                    });
16302            };
16303        }],
16304        template: '<span class="{{$ctrl.cssClass}}">{{$ctrl.message}}</span>'
16305    });;
16306(function () {
16307
16308  "use strict";
16309
16310  angular.module('Lexus')
16311    .config(['$locationProvider', function ($locationProvider) {
16312      $locationProvider.html5Mode(
16313        {
16314          enabled: true,
16315          rewriteLinks: 'data-route',
16316          requireBase: false
16317        });
16318    }])
16319    .config(['tagsInputConfigProvider', function (tagsInputConfigProvider) {
16320        tagsInputConfigProvider.setDefaults('tagsInput', {
16321            placeholder: '',
16322            addOnPaste: true,
16323            addOnBlur: true,
16324            maxTags: 1,
16325            replaceSpacesWithDashes: false,
16326            //enableEditingLastTag: true,
16327            selectOnBackspace: false,
16328            addFromAutocompleteOnly: true,
16329            allowLeftoverText: true,
16330            debounceDelay: 500
16331        });
16332        tagsInputConfigProvider.setDefaults('autoComplete', {
16333            minLength: 2
16334        });
16335        tagsInputConfigProvider.setActiveInterpolation('tagsInput', { placeholder: true });
16336    }])
16337}());;
16338(function () {
16339
16340	"use strict";
16341
16342	angular.module('Lexus');
16343    //UTM variables should not be persisted due to an incompatibility in how eloqua handles utm_campaigns
16344    /*.run([
16345		"$location", "UserPreferencesService",
16346		function ($location, userPreferencesService) {
16347			var params = $location.search();
16348            
16349			var utmParameters = userPreferencesService.getUtmParameters();
16350
16351			if (params.utm_source) { utmParameters.source = params.utm_source; }
16352
16353			if (params.utm_medium) { utmParameters.medium = params.utm_medium; }
16354
16355			if (params.utm_campaign) { utmParameters.campaign = params.utm_campaign; }
16356
16357			if (params.utm_content) { utmParameters.content = params.utm_content; }
16358
16359			userPreferencesService.setUtmParameters(utmParameters); 
16360		}
16361	]);*/
16362}());;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.