Fix copy&paste typo in b2bWhenScrollEnds
[portal.git] / ecomp-portal-FE-common / client / bower_components_external / b2b / js / b2b-angular / b2b-library.min.js
1 /*! b2b-angular-library - v1.0.5 - Last updated: 2017-05-24. Copyright (c) 2016 AT&T Services, Inc. */ 
2 angular.module("b2b.att.tpls", ['b2bTemplate/audioPlayer/audioPlayer.html', 'b2bTemplate/audioRecorder/audioRecorder.html', 'b2bTemplate/backToTop/backToTop.html', 'b2bTemplate/boardstrip/b2bAddBoard.html', 'b2bTemplate/boardstrip/b2bBoard.html', 'b2bTemplate/boardstrip/b2bBoardstrip.html', 'b2bTemplate/calendar/datepicker-popup.html', 'b2bTemplate/calendar/datepicker.html', 'b2bTemplate/coachmark/coachmark.html', 'b2bTemplate/dropdowns/b2bDropdownDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownListDesktop.html', 'b2bTemplate/fileUpload/fileUpload.html', 'b2bTemplate/flyout/flyout.html', 'b2bTemplate/flyout/flyoutContent.html', 'b2bTemplate/footer/footer_column_switch_tpl.html', 'b2bTemplate/horizontalTable/horizontalTable.html', 'b2bTemplate/hourPicker/b2bHourpicker.html', 'b2bTemplate/hourPicker/b2bHourpickerPanel.html', 'b2bTemplate/hourPicker/b2bHourpickerValue.html', 'b2bTemplate/leftNavigation/leftNavigation.html', 'b2bTemplate/listbox/listbox.html', 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html', 'b2bTemplate/modalsAndAlerts/b2b-window.html', 'b2bTemplate/monthSelector/monthSelector-popup.html', 'b2bTemplate/monthSelector/monthSelector.html', 'b2bTemplate/monthSelector/monthSelectorLink.html', 'b2bTemplate/pagination/b2b-pagination.html', 'b2bTemplate/paneSelector/paneSelector.html', 'b2bTemplate/paneSelector/paneSelectorPane.html', 'b2bTemplate/profileCard/profileCard-addUser.html', 'b2bTemplate/profileCard/profileCard.html', 'b2bTemplate/searchField/searchField.html', 'b2bTemplate/seekBar/seekBar.html', 'b2bTemplate/slider/slider.html', 'b2bTemplate/spinButton/spinButton.html', 'b2bTemplate/statusTracker/statusTracker.html', 'b2bTemplate/stepTracker/stepTracker.html', 'b2bTemplate/switches/switches-spanish.html', 'b2bTemplate/switches/switches.html', 'b2bTemplate/tableMessages/tableMessage.html', 'b2bTemplate/tables/b2bTable.html', 'b2bTemplate/tables/b2bTableBody.html', 'b2bTemplate/tables/b2bTableHeaderSortable.html', 'b2bTemplate/tables/b2bTableHeaderUnsortable.html', 'b2bTemplate/tableScrollbar/tableScrollbar.html', 'b2bTemplate/tabs/b2bTab.html', 'b2bTemplate/tabs/b2bTabset.html', 'b2bTemplate/treeNav/groupedTree.html', 'b2bTemplate/treeNav/treeMember.html', 'b2bTemplate/treeNav/ungroupedTree.html', 'b2bTemplate/treeNodeCheckbox/groupedTree.html', 'b2bTemplate/treeNodeCheckbox/treeMember.html', 'b2bTemplate/treeNodeCheckbox/ungroupedTree.html']);angular.module("b2b.att", ["b2b.att.tpls", 'b2b.att.addressInputTemplate','b2b.att.arrows','b2b.att.audioPlayer','b2b.att.audioRecorder','b2b.att.backToTop','b2b.att.badgesForAlerts','b2b.att.boardstrip','b2b.att.bootstrapGridTemplate','b2b.att.breadcrumbs','b2b.att.buttonGroups','b2b.att.buttons','b2b.att.calendar','b2b.att.checkboxes','b2b.att.coachmark','b2b.att.configurationSection','b2b.att.directoryListingTemplate','b2b.att.dropdowns','b2b.att.fileUpload','b2b.att.filters','b2b.att.flyout','b2b.att.footer','b2b.att.header','b2b.att.headingsAndCopy','b2b.att.horizontalTable','b2b.att.hourPicker','b2b.att.inputTemplate','b2b.att.leftNavigation','b2b.att.links','b2b.att.listbox','b2b.att.loaderAnimation','b2b.att.messageWrapper','b2b.att.modalsAndAlerts','b2b.att.monthSelector','b2b.att.multiLevelNavigation','b2b.att.multipurposeExpander','b2b.att.notesMessagesAndErrors','b2b.att.notificationCardTemplate','b2b.att.orderConfirmationTemplate','b2b.att.pagination','b2b.att.paneSelector','b2b.att.phoneNumberInput','b2b.att.profileBlockTemplate','b2b.att.profileCard','b2b.att.radios','b2b.att.searchField','b2b.att.seekBar','b2b.att.separators','b2b.att.slider','b2b.att.spinButton','b2b.att.staticRouteTemplate','b2b.att.statusTracker','b2b.att.stepTracker','b2b.att.switches','b2b.att.tableDragAndDrop','b2b.att.tableMessages','b2b.att.tables','b2b.att.tableScrollbar','b2b.att.tabs','b2b.att.tagBadges','b2b.att.textArea','b2b.att.timeInputField','b2b.att.tooltipsForForms','b2b.att.treeNav','b2b.att.treeNodeCheckbox','b2b.att.utilities']);/**
3  * @ngdoc directive
4  * @name Template.att:Address Input
5  *
6  * @description
7  *  <file src="src/addressInputTemplate/docs/readme.md" />
8  *
9  * @usage
10
11  *
12  * @example
13  *  <section id="code">   
14  <example module="b2b.att">
15  <file src="src/addressInputTemplate/docs/demo.html" />
16  <file src="src/addressInputTemplate/docs/demo.js" />
17  </example>
18  </section>
19  *
20  */
21 angular.module('b2b.att.addressInputTemplate', ['ngMessages']);
22 /**
23  * @ngdoc directive
24  * @name Buttons, links & UI controls.att:arrows
25  *
26  * @description
27  *  <file src="src/arrows/docs/readme.md" />
28  *
29  * @usage
30  *   Please refer demo.html tab in Example section below.
31  *
32  * @example
33  *  <section id="code">
34         <example module="b2b.att">
35             <file src="src/arrows/docs/demo.html" />
36             <file src="src/arrows/docs/demo.js" />
37        </example>
38     </section>
39  *
40  */
41 angular.module('b2b.att.arrows', []);
42 /**
43  * @ngdoc directive
44  * @name Videos, audio & animation.att:Audio Player
45  * @scope
46  * @param {string} audioSrcUrl - MP3 audio source URL or Blob URL
47  * @description
48  *  <file src="src/audioPlayer/docs/readme.md" />
49  *
50  * @usage
51  * 
52  <div b2b-audio audio-src-url='audioSrcUrl'></div>
53  *
54  * @example
55  *  <section id="code">
56         <example module="b2b.att">
57             <file src="src/audioPlayer/docs/demo.html" />
58             <file src="src/audioPlayer/docs/demo.js" />
59        </example>
60     </section>
61  *
62  */
63  
64 angular.module('b2b.att.audioPlayer', ['b2b.att.utilities', 'b2b.att.seekBar'])
65         .constant('AudioPlayerConfig', {
66                 'defaultVolume': 50,
67                 'timeShiftInSeconds': 5
68         })
69         .filter('trustedAudioUrl', ['$sce', function ($sce) {
70                 return function (audioFileFullPath) {
71                         return audioFileFullPath ? $sce.trustAsResourceUrl(audioFileFullPath) : 'undefined';
72                 };
73         }])
74         .directive('b2bAudio', ['$log', '$timeout', 'AudioPlayerConfig', '$compile', 'events', function ($log, $timeout, AudioPlayerConfig, $compile, events) {
75                 return {
76                         restrict: 'EA',
77                         replace: true,
78                         scope: {
79                                 audioSrcUrl: '='
80                         },
81                         templateUrl: 'b2bTemplate/audioPlayer/audioPlayer.html',
82                         controller: function ($scope) {
83
84                                 $scope.audio = {};
85
86                                 if (!angular.isDefined($scope.audioSrcUrl)) {
87                                         $log.warn('b2b-audio : audio-src-url undefined');
88                                         $scope.audioSrcUrl = undefined;
89                                         $scope.audio.mp3 = undefined;
90                                 }
91
92                         },
93                         link: function (scope, element) {
94                                 var audioElement = angular.element(element[0].querySelector('audio'))[0];
95                                 scope.audio.audioElement = audioElement;
96                                 
97                                 function setAttributes(element, attributes) {
98                                         Object.keys(attributes).forEach(function (name) {
99                                                 element.setAttribute(name, attributes[name]);
100                                         });
101                                 }
102
103                                 $timeout(function () {
104                                         // TODO: Replace with DDA Tooltip
105                                         var seekBarKnob = element[0].querySelector('.b2b-seek-bar-knob');
106                                         var tooltipObject = {
107                                                 'tooltip': '{{timeFormatter(audio.currentTime)}}',
108                                                 'tooltip-placement': 'above',
109                                                 'tooltip-style': 'blue',
110                                                 'tooltip-trigger': 'mousedown',
111                                                 'tooltip-append-to-body': 'false',
112                                                 'tooltip-offset': '-10',
113                                                 'refer-by': 'seek-bar-tooltip'
114                                         };
115                                         setAttributes(seekBarKnob, tooltipObject);
116                                         $compile(seekBarKnob)(scope);
117                                 });
118
119                                 if (angular.isDefined(scope.audioSrcUrl)) {
120                                         scope.audio.mp3 = scope.audioSrcUrl;
121                                 }
122
123                                 scope.audio.currentTime = 0;
124                                 scope.audio.currentVolume = AudioPlayerConfig.defaultVolume;
125                                 scope.audio.timeShiftInSeconds = AudioPlayerConfig.timeShiftInSeconds;
126                                 scope.isPlayInProgress = false;
127                                 scope.isReady = false;
128                                 scope.isAudioDragging = false;
129
130                                 $timeout(function () {
131                                         audioElement.load();
132                                         audioElement.volume = scope.audio.currentVolume / 100;
133                                 });
134
135                                 scope.$watch('audioSrcUrl', function (newVal, oldVal) {
136                                         if (newVal !== oldVal) {
137                                                 if (!newVal) {
138                                                         $log.warn('b2b-audio : audio-src-url undefined. Please provide a valid URL');
139                                                 }
140                                                 
141                                                 scope.audio.mp3 = newVal;
142                                                 $timeout(function () {
143                                                         audioElement.load();
144                                                 });
145                                         }
146                                 });
147
148                                 scope.playAudio = function () {
149                                         if (scope.isReady) {
150                                                 audioElement.play();
151                                         }
152                                 };
153
154                                 audioElement.onplay = function () {
155                                         scope.isPlayInProgress = true;
156                                         scope.$digest();
157                                 };
158
159                                 scope.pauseAudio = function () {
160                                         audioElement.pause();
161                                 };
162
163                                 audioElement.onpause = function () {
164                                         scope.isPlayInProgress = false;
165                                         scope.$digest();
166                                 };
167
168                                 scope.toggleAudio = function () {
169                                         if (audioElement.paused) {
170                                                 scope.playAudio();
171                                         } else {
172                                                 scope.pauseAudio();
173                                         }
174                                 };
175
176                                 scope.volumeUp = function (delta) {
177                                         if (!delta) {
178                                                 delta = 0.1;
179                                         } else {
180                                                 delta = delta / 100;
181                                         }
182                                         audioElement.muted = false;
183                                         if (audioElement.volume < 1) {
184                                                 audioElement.volume = Math.min((Math.round((audioElement.volume + delta) * 100) / 100), 1);
185                                         }
186                                         scope.audio.currentVolume = audioElement.volume * 100;
187                                         return audioElement.volume;
188                                 };
189
190                                 scope.volumeDown = function (delta) {
191                                         if (!delta) {
192                                                 delta = 0.1;
193                                         } else {
194                                                 delta = delta / 100;
195                                         }
196                                         audioElement.muted = false;
197                                         if (audioElement.volume > 0) {
198                                                 audioElement.volume = Math.max((Math.round((audioElement.volume - delta) * 100) / 100), 0);
199                                         }
200                                         scope.audio.currentVolume = audioElement.volume * 100;
201                                         return audioElement.volume;
202                                 };
203
204                                 var volumeHandler = function (e) {
205                                         events.preventDefault(e);
206                                         if ((e.wheelDelta && e.wheelDelta > 0) || (e.detail && e.detail < 0)) {
207                                                 scope.volumeUp();
208                                         } else {
209                                                 scope.volumeDown();
210                                         }
211                                         scope.$digest();
212                                 };
213
214                                 
215
216                                 scope.$watch('audio.currentVolume', function (newVal, oldVal) {
217                                         if (newVal !== oldVal) {
218                                                 audioElement.volume = newVal / 100;
219                                         }
220                                 });
221
222                                 scope.setCurrentTime = function (timeInSec) {
223                                         audioElement.currentTime = timeInSec;
224                                 };
225
226                                 scope.setAudioPosition = function (val) {
227                                         if (scope.isReady) {
228                                                 scope.setCurrentTime(val);
229                                                 scope.isAudioDragging = false;
230                                         }
231                                 };
232
233                                 function getTimestampArray(timestamp) {
234                                         var d = Math.abs(timestamp) / 1000; // delta
235                                         var r = {}; // result
236                                         var s = { // structure
237                                                 day: 86400,
238                                                 hour: 3600,
239                                                 minute: 60,
240                                                 second: 1
241                                         };
242
243                                         Object.keys(s).forEach(function (key) {
244                                                 r[key] = Math.floor(d / s[key]);
245                                                 d -= r[key] * s[key];
246                                         });
247
248                                         return r;
249                                 };
250
251                                 scope.timeFormatter = function (timeInSec) {
252                                         var formattedTime = '00:00';
253
254                                         if (!timeInSec || timeInSec < 1) {
255                                                 return formattedTime;
256                                         }
257
258                                         if (typeof timeInSec === 'string') {
259                                                 return timeInSec;
260                                         }
261
262                                         var dateArray = getTimestampArray(timeInSec * 1000);
263                                         Object.keys(dateArray).forEach(function (key) {
264                                                 if (dateArray[key] === 0) {
265                                                         dateArray[key] = '00';
266                                                 } else if (dateArray[key] < 10) {
267                                                         dateArray[key] = '0' + dateArray[key];
268                                                 }
269                                         });
270
271                                         formattedTime = dateArray['minute'] + ':' + dateArray['second'];
272
273                                         if (dateArray['hour'] !== '00') {
274                                                 formattedTime = dateArray['hour'] + ':' + formattedTime;
275                                         }
276
277                                         if (dateArray['day'] !== '00') {
278                                                 formattedTime = dateArray['day'] + ':' + formattedTime;
279                                         }
280
281                                         return formattedTime;
282                                 };
283
284                                 audioElement.onloadedmetadata = function () {
285                                         scope.audio.duration = audioElement.duration;
286                                         scope.$digest();
287                                 };
288
289                                 audioElement.ontimeupdate = function () {
290                                         if (!scope.isAudioDragging) {
291                                                 scope.audio.currentTime = audioElement.currentTime;
292                                                 scope.$digest();
293                                         }
294                                 };
295
296                                 audioElement.onended = function () {
297                                         scope.setCurrentTime(0);
298                                         scope.audio.currentTime = 0;
299                                         if (!audioElement.paused) {
300                                                 scope.pauseAudio();
301                                         }
302                                         scope.$digest();
303                                 };
304
305                                 audioElement.oncanplay = function () {
306                                         scope.isReady = true;
307                                         scope.isPlayInProgress = !audioElement.paused;
308                                         scope.$digest();
309                                 };
310
311                                 var onloadstart = function () {
312                                         scope.isReady = false;
313                                         scope.isPlayInProgress = !audioElement.paused;
314                                         scope.audio.currentTime = 0;
315                                         scope.audio.duration = 0;
316                                         scope.$digest();
317                                 };
318                                 audioElement.addEventListener("loadstart", onloadstart);
319                         }
320                 };
321         }]);
322 /**
323  * @ngdoc directive
324  * @name Videos, audio & animation.att:Audio Recorder
325  * @scope
326  * @param {function} callback - A callback to handle the WAV blob
327  * @param {object} config - A config object with properties startRecordingMessage & whileRecordingMessage
328  * @description
329  *  <file src="src/audioRecorder/docs/readme.md" />
330  *
331  *
332  * @example
333  *  <section id="code">
334         <example module="b2b.att">
335             <file src="src/audioRecorder/docs/demo.html" />
336             <file src="src/audioRecorder/docs/demo.js" />
337        </example>
338     </section>
339  *
340  */
341 angular.module('b2b.att.audioRecorder', ['b2b.att.utilities'])
342     .constant('AudioRecorderConfig', {
343         'startRecordingMessage': 'Click on REC icon to being recording',
344         'whileRecordingMessage': 'Recording...'
345     })
346     .directive('b2bAudioRecorder', ['$interval', 'AudioRecorderConfig', 'b2bUserAgent', 'b2bRecorder', function($interval, AudioRecorderConfig, b2bUserAgent, b2bRecorder) {
347         return {
348             restrict: 'EA',
349             replace: true,
350             scope: {
351                 callback: '&'
352             },
353             templateUrl: 'b2bTemplate/audioRecorder/audioRecorder.html',
354             controller: function($scope) {
355
356                 function hasGetUserMedia() {
357                     return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
358                         navigator.mozGetUserMedia || navigator.msGetUserMedia);
359                 }
360
361                 if (!hasGetUserMedia()) {
362                     throw new Error('Your broswer does not support MediaRecorder API');
363                 }
364
365                 if (!(b2bUserAgent.isFF() || b2bUserAgent.isChrome())) {
366                                         throw new Error('b2bAudioRecorder does not support this browser!');
367                                 }
368
369             },
370             link: function(scope, element) {
371                 scope.elapsedTime = 0;
372                 scope.isRecording = false;
373                 scope.config = {};
374                 scope.config.startRecordingMessage = AudioRecorderConfig.startRecordingMessage;
375                 scope.config.whileRecordingMessage = AudioRecorderConfig.whileRecordingMessage;
376                 
377
378                 var timer = undefined; // Interval promise
379                 navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
380                 var stream;
381                 var audio = angular.element(element[0].querySelector('audio'))[0];
382                 var recorder = undefined;
383
384                 function startRecording() {
385                     scope.isRecording = true;
386                     navigator.mediaDevices.getUserMedia({
387                         audio: true
388                     }).then(function(stream) {
389                         //create the MediaStreamAudioSourceNode
390                         context = new AudioContext();
391                         source = context.createMediaStreamSource(stream);
392                         recorder = new b2bRecorder(source);
393                         recorder.record();
394
395                         timer = $interval(function() {
396                             scope.elapsedTime += 1;
397                         }, 1000, 0);
398                     }).catch(function(err) {
399                         angular.noop();
400                     });
401
402                 };
403
404                 function stopRecording() {
405                     scope.isRecording = false;
406                     recorder.stop();
407                     var audio = {};
408                     recorder.exportWAV(function(s) {
409                         audio.src = window.URL.createObjectURL(s);
410                         context.close().then(function() {
411                                 if (timer) {
412                                         $interval.cancel(timer);
413                                     }
414                                     scope.elapsedTime = 0;
415                                     
416                                     recorder.clear();
417                                     recorder = undefined;
418                             });
419                             if (angular.isFunction(scope.callback)){
420                                                         scope.callback({'data': audio});        
421                                                 }
422                     });
423                     
424                     
425                 }
426
427                 scope.toggleRecording = function() {
428                     if (scope.isRecording) {
429                         stopRecording();
430                     } else {
431                         startRecording();
432                     }
433                 };
434
435
436
437                 //TODO: Move this into utilities
438                 function getTimestampArray(timestamp) {
439                     var d = Math.abs(timestamp) / 1000; // delta
440                     var r = {}; // result
441                     var s = { // structure
442                         day: 86400,
443                         hour: 3600,
444                         minute: 60,
445                         second: 1
446                     };
447
448                     Object.keys(s).forEach(function(key) {
449                         r[key] = Math.floor(d / s[key]);
450                         d -= r[key] * s[key];
451                     });
452
453                     return r;
454                 };
455                 scope.timeFormatter = function(timeInSec) {
456                     var formattedTime = '00:00';
457
458                     if (!timeInSec || timeInSec < 1) {
459                         return formattedTime;
460                     }
461
462                     if (typeof timeInSec === 'string') {
463                         return timeInSec;
464                     }
465
466                     var dateArray = getTimestampArray(timeInSec * 1000);
467                     Object.keys(dateArray).forEach(function(key) {
468                         if (dateArray[key] === 0) {
469                             dateArray[key] = '00';
470                         } else if (dateArray[key] < 10) {
471                             dateArray[key] = '0' + dateArray[key];
472                         }
473                     });
474
475                     formattedTime = dateArray['minute'] + ':' + dateArray['second'];
476
477                     if (dateArray['hour'] !== '00') {
478                         formattedTime = dateArray['hour'] + ':' + formattedTime;
479                     }
480
481                     if (dateArray['day'] !== '00') {
482                         formattedTime = dateArray['day'] + ':' + formattedTime;
483                     }
484
485                     return formattedTime;
486                 };
487
488                 scope.$on('$destroy', function() {
489                     if (timer) {
490                         $interval.cancel(timer);
491                     }
492                 });
493             }
494         };
495     }]);
496
497 /**
498  * @ngdoc directive
499  * @name Navigation.att:Back To Top
500  * @scope
501  * @description
502  *  <file src="src/backToTop/docs/readme.md" />
503  * @param {integer} scrollSpeed - Scroll speed in seconds, default is 1
504 *
505  * @usage
506  * 
507         <div ng-controller="backToTopController">
508                 <div b2b-backtotop></div>
509         </div>
510  * 
511  * @example
512  *  <section id="code">
513         <example module="b2b.att">
514             <file src="src/backToTop/docs/demo.html" />
515             <file src="src/backToTop/docs/demo.js" />
516        </example>
517     </section>
518  *
519  */
520  
521 angular.module('b2b.att.backToTop', ['b2b.att.utilities','b2b.att.position'])
522         .directive('b2bBacktotopButton', [function () {
523                 return {
524                         restrict: 'EA',
525                         replace: true,
526                         templateUrl: 'b2bTemplate/backToTop/backToTop.html',
527                         link: function (scope, elem, attr) {
528                                 elem.bind('click', function(evt) {
529                                         var scrollSpeed = parseInt(attr.scrollSpeed) || 1;
530                     TweenLite.to(window, scrollSpeed, {scrollTo:{x: 0, y: 0}});
531                 });
532                         }
533                 };
534         }]);
535 /**
536  * @ngdoc directive
537  * @name Messages, modals & alerts.att:badgesForAlerts
538  *
539  * @description
540  *  <file src="src/badgesForAlerts/docs/readme.md" />
541  * @example
542  *  <section id="code">
543         <example module="b2b.att">
544             <file src="src/badgesForAlerts/docs/demo.html" />
545             <file src="src/badgesForAlerts/docs/demo.js" />
546        </example>
547         </section>
548  *
549  */
550 angular.module('b2b.att.badgesForAlerts', []);
551 /**
552  * @ngdoc directive
553  * @name Misc.att:boardstrip
554  *
555  * @description
556  *  <file src="src/boardstrip/docs/readme.md" />
557  *
558  * @usage
559  * See demo section
560  * @example
561     <section id="code">
562         <b>HTML + AngularJS</b>
563         <example module="b2b.att">
564             <file src="src/boardstrip/docs/demo.html" />
565             <file src="src/boardstrip/docs/demo.js" />
566         </example>
567     </section>
568  */
569 angular.module('b2b.att.boardstrip', ['b2b.att.utilities'])
570         .constant('BoardStripConfig', {
571                 'maxVisibleBoards': 4,
572                 'boardsToScroll': 1,
573                 /* These parameters are non-configurable and remain unaltered, until there is a change in corresponding CSS */
574                 'boardLength': 140,
575                 'boardMargin': 15
576         })
577         .directive('b2bBoard', [function () {
578                 return {
579                         restrict: 'AE',
580                         replace: true,
581                         transclude: true,
582                         require: '^b2bBoardStrip',
583                         scope: {
584                                 boardIndex: '=',
585                                 boardLabel: '='
586                         },
587                         templateUrl: 'b2bTemplate/boardstrip/b2bBoard.html',
588                         link: function (scope, element, attrs, ctrls) {
589
590                                 var parentCtrl = ctrls;
591
592                                 scope.getCurrentIndex = function () {
593                                         return parentCtrl.getCurrentIndex();
594                                 };
595                                 scope.selectBoard = function (boardIndex) {
596                                         if (!isNaN(boardIndex)) {
597                                                 parentCtrl.setCurrentIndex(boardIndex);
598                                         }
599                                 };
600                         }
601                 };
602         }])
603         .directive('b2bBoardStrip', ['BoardStripConfig', '$timeout', function (BoardStripConfig, $timeout) {
604                 return {
605                         restrict: 'AE',
606                         replace: true,
607                         transclude: true,
608                         require: ['?ngModel', 'b2bBoardStrip'],
609                         scope: {
610                                 boardsMasterArray: '=',
611                                 onAddBoard: '&?'
612                         },
613                         templateUrl: 'b2bTemplate/boardstrip/b2bBoardstrip.html',
614                         controller: function ($scope) {
615                                 if (!angular.isDefined($scope.boardsMasterArray)) {
616                                         $scope.boardsMasterArray = [];
617                                 }
618
619                                 this.rectifyMaxVisibleBoards = function () {
620                                         if (this.maxVisibleIndex >= $scope.boardsMasterArray.length) {
621                                                 this.maxVisibleIndex = $scope.boardsMasterArray.length - 1;
622                                         }
623
624                                         if (this.maxVisibleIndex < 0) {
625                                                 this.maxVisibleIndex = 0;
626                                         }
627                                 };
628
629                                 this.resetBoardStrip = function () {
630                                         $scope.currentIndex = 0;
631
632                                         this.maxVisibleIndex = BoardStripConfig.maxVisibleBoards - 1;
633                                         this.minVisibleIndex = 0;
634
635                                         this.rectifyMaxVisibleBoards();
636                                 };
637
638                                 this.getCurrentIndex = function () {
639                                         return $scope.currentIndex;
640                                 };
641                                 this.setCurrentIndex = function (indx) {
642                                         $scope.currentIndex = indx;
643                                 };
644
645                                 this.getBoardsMasterArrayLength = function () {
646                                         return $scope.boardsMasterArray.length;
647                                 };
648
649                                 $scope.addBoardPressedFlag = false;
650                                 this.getAddBoardPressedFlag = function () {
651                                         return $scope.addBoardPressedFlag;
652                                 };
653                                 this.setAddBoardPressedFlag = function (booleanValue) {
654                                         $scope.addBoardPressedFlag = booleanValue;
655                                 };
656
657                         },
658                         link: function (scope, element, attrs, ctrls) {
659
660                                 var ngModelCtrl = ctrls[0];
661                                 var ctrl = ctrls[1];
662
663                                 var oldTimeout;
664                                 var animationTimeout = 1000;
665
666                                 var getBoardViewportWidth = function (numberOfVisibleBoards) {
667                                         return numberOfVisibleBoards * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin);
668                                 };
669                                 if (element[0].querySelector(".board-viewport")) {
670                                         angular.element(element[0].querySelector(".board-viewport")).css({
671                                                 "width": getBoardViewportWidth(BoardStripConfig.maxVisibleBoards) + "px"
672                                         });
673                                 }
674
675                                 var getBoardstripContainerWidth = function (totalNumberOfBoards) {
676                                         return totalNumberOfBoards * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin);
677                                 };
678                                 if (element[0].querySelector(".boardstrip-container")) {
679                                         angular.element(element[0].querySelector(".boardstrip-container")).css({
680                                                 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
681                                         });
682                                         angular.element(element[0].querySelector(".boardstrip-container")).css({
683                                                 "left": "0px"
684                                         });
685                                 }
686
687                                 var calculateAndGetBoardstripContainerAdjustment = function () {
688
689                                         var calculatedAdjustmentValue;
690
691                                         if (ctrl.getBoardsMasterArrayLength() <= BoardStripConfig.maxVisibleBoards) {
692                                                 calculatedAdjustmentValue = 0;
693                                         } else {
694                                                 calculatedAdjustmentValue = (ctrl.minVisibleIndex * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin)) * -1;
695                                         }
696
697                                         return calculatedAdjustmentValue;
698                                 };
699
700                                 var animateBoardstripContainerAdjustment = function (elementToFocusAfterAnimation) {
701                                         var oldContainerAdjustment = angular.element(element[0].querySelector(".boardstrip-container"))[0].style.left;
702                                         var containerAdjustment = calculateAndGetBoardstripContainerAdjustment();
703                                         if (oldContainerAdjustment !== containerAdjustment + 'px') {
704                                                 angular.element(element[0].querySelector(".boardstrip-container")).css({
705                                                         "left": containerAdjustment + "px"
706                                                 });
707
708                                                 $timeout.cancel(oldTimeout);
709                                                 oldTimeout = $timeout(function () {
710                                                         elementToFocusAfterAnimation.focus();
711                                                 }, animationTimeout);
712                                         } else {
713                                                 elementToFocusAfterAnimation.focus();
714                                         }
715                                 };
716
717                                 var updateBoardsTabIndex = function (boardArray, minViewIndex, maxViewIndex) {
718                                         for (var i = 0; i < boardArray.length; i++) {
719                                                 angular.element(boardArray[i]).attr('tabindex', '-1');
720                                         }
721                                         for (var j = minViewIndex; j <= maxViewIndex; j++) {
722                                                 angular.element(boardArray[j]).attr('tabindex', '0');
723                                         }
724                                 };
725
726                                 $timeout(function () {
727                                         var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
728                                         updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
729                                 });
730
731                                 scope.$watchCollection('boardsMasterArray', function (newVal, oldVal) {
732                                         if (newVal !== oldVal) {
733                                                 /* When a board is removed */
734                                                 if (newVal.length < oldVal.length) {
735                                                         ctrl.resetBoardStrip();
736                                                         $timeout(function () {
737
738                                                                 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
739                                                                 if (currentBoardArray.length !== 0) {
740                                                                         animateBoardstripContainerAdjustment(currentBoardArray[0]);
741                                                                 } else {
742                                                                         element[0].querySelector('div.boardstrip-item--add').focus();
743                                                                 }
744
745                                                                 angular.element(element[0].querySelector(".boardstrip-container")).css({
746                                                                         "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
747                                                                 });
748                                                                 /* Update tabindecies to ensure keyboard navigation behaves correctly */
749                                                                 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
750                                                         });
751                                                 }
752                                                 /* When a board is added */
753                                                 else {
754                                                         if (ctrl.getAddBoardPressedFlag()) {
755                                                                 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
756                                                                 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
757
758                                                                 ctrl.setCurrentIndex(ctrl.maxVisibleIndex);
759
760                                                                 $timeout(function () {
761                                                                         angular.element(element[0].querySelector(".boardstrip-container")).css({
762                                                                                 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
763                                                                         });
764
765                                                                         var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
766                                                                         animateBoardstripContainerAdjustment(currentBoardArray[currentBoardArray.length - 1]);
767                                                                         /* Update tabindecies to ensure keyboard navigation behaves correctly */
768                                                                         updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
769                                                                 });
770                                                         } else {
771                                                                 if (ctrl.minVisibleIndex === 0 && ctrl.getBoardsMasterArrayLength() < BoardStripConfig.maxVisibleBoards + 1) {
772                                                                         ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
773                                                                         ctrl.rectifyMaxVisibleBoards();
774                                                                 }
775
776                                                                 $timeout(function () {
777                                                                         angular.element(element[0].querySelector(".boardstrip-container")).css({
778                                                                                 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
779                                                                         });
780
781                                                                         var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
782                                                                         /* Update tabindecies to ensure keyboard navigation behaves correctly */
783                                                                         updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
784                                                                 });
785                                                         }
786
787                                                         ctrl.setAddBoardPressedFlag(false);
788                                                 }
789                                         }
790                                 });
791
792                                 scope.nextBoard = function () {
793                                         ctrl.maxVisibleIndex += BoardStripConfig.boardsToScroll;
794                                         ctrl.rectifyMaxVisibleBoards();
795                                         ctrl.minVisibleIndex = ctrl.maxVisibleIndex - (BoardStripConfig.maxVisibleBoards - 1);
796
797                                         $timeout.cancel(oldTimeout);
798                                         angular.element(element[0].querySelector(".boardstrip-container")).css({
799                                                 "left": calculateAndGetBoardstripContainerAdjustment() + "px"
800                                         });
801
802                                         $timeout(function () {
803                                                 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
804
805                                                 /* Remove tabindex from non-visible boards */
806                                                 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
807
808                                                 if (!(scope.isNextBoard())) {
809                                                         try {
810                                                                 currentBoardArray[currentBoardArray.length - 1].focus();
811                                                         } catch (e) { /* IE8 may throw exception */ }
812                                                 }
813                                         }, animationTimeout);
814                                 };
815                                 scope.prevBoard = function () {
816
817                                         ctrl.minVisibleIndex -= BoardStripConfig.boardsToScroll;
818                                         if (ctrl.minVisibleIndex < 0) {
819                                                 ctrl.minVisibleIndex = 0;
820                                         }
821
822                                         ctrl.maxVisibleIndex = ctrl.minVisibleIndex + BoardStripConfig.maxVisibleBoards - 1;
823                                         ctrl.rectifyMaxVisibleBoards();
824
825                                         $timeout.cancel(oldTimeout);
826                                         angular.element(element[0].querySelector(".boardstrip-container")).css({
827                                                 "left": calculateAndGetBoardstripContainerAdjustment() + "px"
828                                         });
829
830                                         $timeout(function () {
831                                                 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
832
833                                                 /* Remove tabindex from non-visible boards */
834                                                 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
835
836                                                 if (ctrl.minVisibleIndex === 0) {
837                                                         try {
838                                                                 element[0].querySelector('div.boardstrip-item--add').focus();
839                                                         } catch (e) { /* IE8 may throw exception */ }
840                                                 }
841                                         });
842                                 };
843
844                                 scope.isPrevBoard = function () {
845                                         return (ctrl.minVisibleIndex > 0);
846                                 };
847                                 scope.isNextBoard = function () {
848                                         return (ctrl.getBoardsMasterArrayLength() - 1 > ctrl.maxVisibleIndex);
849                                 };
850
851                                 ngModelCtrl.$render = function () {
852                                         if (ngModelCtrl.$viewValue || ngModelCtrl.$viewValue === 0) {
853                                                 var newCurrentIndex = ngModelCtrl.$viewValue;
854
855                                                 if (!(newCurrentIndex = parseInt(newCurrentIndex, 10))) {
856                                                         newCurrentIndex = 0;
857                                                 }
858
859                                                 if (newCurrentIndex <= 0) {
860                                                         ctrl.resetBoardStrip();
861                                                         newCurrentIndex = 0;
862
863                                                         var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
864                                                         if (currentBoardArray.length !== 0) {
865                                                                 animateBoardstripContainerAdjustment(currentBoardArray[0]);
866                                                         } else {
867                                                                 element[0].querySelector('div.boardstrip-item--add').focus();
868                                                         }
869                                                         /* Update tabindecies to ensure keyboard navigation behaves correctly */
870                                                         updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
871                                                 } else if (newCurrentIndex >= ctrl.getBoardsMasterArrayLength()) {
872                                                         ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
873                                                         ctrl.rectifyMaxVisibleBoards();
874                                                         ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
875
876                                                         newCurrentIndex = ctrl.maxVisibleIndex;
877
878                                                         $timeout(function () {
879                                                                 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
880                                                                 animateBoardstripContainerAdjustment(currentBoardArray[newCurrentIndex]);
881                                                                 /* Update tabindecies to ensure keyboard navigation behaves correctly */
882                                                                 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
883                                                         });
884                                                 } else {
885
886                                                         if (!(newCurrentIndex >= ctrl.minVisibleIndex && newCurrentIndex <= ctrl.maxVisibleIndex)) {
887                                                                 ctrl.minVisibleIndex = newCurrentIndex;
888                                                                 ctrl.maxVisibleIndex = ctrl.minVisibleIndex + BoardStripConfig.maxVisibleBoards - 1;
889                                                                 ctrl.rectifyMaxVisibleBoards();
890
891                                                                 if (ctrl.getBoardsMasterArrayLength() < BoardStripConfig.maxVisibleBoards) {
892                                                                         ctrl.minVisibleIndex = 0;
893                                                                 } else {
894                                                                         ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
895                                                                 }
896
897                                                                 $timeout(function () {
898                                                                         var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
899                                                                         animateBoardstripContainerAdjustment(currentBoardArray[newCurrentIndex]);
900                                                                         /* Update tabindecies to ensure keyboard navigation behaves correctly */
901                                                                         updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
902                                                                 });
903                                                         }
904                                                 }
905                                                 scope.currentIndex = newCurrentIndex;
906                                                 ngModelCtrl.$setViewValue(newCurrentIndex);
907                                         } else {
908                                                 ctrl.resetBoardStrip();
909                                                 ngModelCtrl.$setViewValue(0);
910                                         }
911                                 };
912
913                                 scope.$watch('currentIndex', function (newVal, oldVal) {
914                                         if (newVal !== oldVal && ngModelCtrl && ngModelCtrl.$viewValue !== newVal) {
915                                                 ngModelCtrl.$setViewValue(newVal);
916                                         }
917                                 });
918                         }
919                 };
920         }])
921         .directive('b2bAddBoard', ['BoardStripConfig', '$parse', function (BoardStripConfig, $parse) {
922                 return {
923                         restrict: 'AE',
924                         replace: true,
925                         require: '^b2bBoardStrip',
926                         scope: {
927                                 onAddBoard: '&?'
928                         },
929                         templateUrl: 'b2bTemplate/boardstrip/b2bAddBoard.html',
930                         link: function (scope, element, attrs, ctrl) {
931                                 scope.addBoard = function () {
932                                         if (attrs['onAddBoard']) {
933                                                 scope.onAddBoard = $parse(scope.onAddBoard);
934                                                 scope.onAddBoard();
935                                                 ctrl.setAddBoardPressedFlag(true);
936                                         }
937                                 };
938                         }
939                 };
940         }])
941         .directive('b2bBoardNavigation', ['keymap', 'events', function (keymap, events) {
942                 return {
943                         restrict: 'AE',
944                         link: function (scope, elem) {
945
946                                 var prevElem = keymap.KEY.LEFT;
947                                 var nextElem = keymap.KEY.RIGHT;
948
949                                 elem.bind('keydown', function (ev) {
950
951                                         if (!(ev.keyCode)) {
952                                                 ev.keyCode = ev.which;
953                                         }
954
955                                         switch (ev.keyCode) {
956                                         case nextElem:
957                                                 events.preventDefault(ev);
958                                                 events.stopPropagation(ev);
959
960                                                 if (elem[0].nextElementSibling && parseInt(angular.element(elem[0].nextElementSibling).attr('tabindex')) >= 0) {
961                                                         angular.element(elem[0])[0].nextElementSibling.focus();
962                                                 } else {
963                                                         /* IE8 fix */
964                                                         var el = angular.element(elem[0])[0];
965                                                         do {
966                                                                 if (el.nextSibling) {
967                                                                         el = el.nextSibling;
968                                                                 } else {
969                                                                         break;
970                                                                 }
971                                                         } while (el && el.tagName !== 'LI');
972
973                                                         if (el.tagName && el.tagName === 'LI' && parseInt(angular.element(el).attr('tabindex')) >= 0) {
974                                                                 el.focus();
975                                                         }
976                                                 }
977
978                                                 break;
979                                         case prevElem:
980                                                 events.preventDefault(ev);
981                                                 events.stopPropagation(ev);
982
983                                                 if (elem[0].previousElementSibling && parseInt(angular.element(elem[0].previousElementSibling).attr('tabindex')) >= 0) {
984                                                         angular.element(elem[0])[0].previousElementSibling.focus();
985                                                 } else {
986                                                         /* IE8 fix */
987                                                         var el1 = angular.element(elem[0])[0];
988                                                         do {
989                                                                 if (el1.previousSibling) {
990                                                                         el1 = el1.previousSibling;
991                                                                 } else {
992                                                                         break;
993                                                                 }
994                                                         } while (el1 && el1.tagName !== 'LI');
995
996                                                         if (el1.tagName && el1.tagName === 'LI' && parseInt(angular.element(el1).attr('tabindex')) >= 0) {
997                                                                 el1.focus();
998                                                         }
999                                                 }
1000                                                 break;
1001                                         default:
1002                                                 break;
1003                                         }
1004                                 });
1005                         }
1006                 };
1007         }]);
1008 /** 
1009  * @ngdoc directive 
1010  * @name Template.att:Bootstrap Grid Template
1011  * 
1012  * @description 
1013  *  <file src="src/bootstrapGridTemplate/docs/readme.md" /> 
1014  * 
1015  * @example 
1016  *  <section id="code">
1017         <example module="b2b.att"> 
1018             <file src="src/bootstrapGridTemplate/docs/demo.html" /> 
1019             <file src="src/bootstrapGridTemplate/docs/demo.js" /> 
1020        </example> 
1021     </section>    
1022  * 
1023  */
1024 angular.module('b2b.att.bootstrapGridTemplate', [])
1025   
1026 /**
1027  * @ngdoc directive
1028  * @name Navigation.att:breadcrumbs
1029  *
1030  * @description
1031  *  <file src="src/breadcrumbs/docs/readme.md" />
1032  * @usage
1033     <ul class="breadcrumb">
1034         <li ng-repeat="link in breadCrumbsLink"><a tabindex="{{(idx==$index)?-1:0}}" href='javascript:void(0)' ng-click="clickActive($index)" ng-class="{'active':idx==$index, '': idx!=$index}">{{link.title}}</a></li>
1035     </ul>
1036  * @example
1037  <example module="b2b.att">
1038  <file src="src/breadcrumbs/docs/demo.html" />
1039  <file src="src/breadcrumbs/docs/demo.js" />
1040  </example>
1041  */
1042 angular.module('b2b.att.breadcrumbs',[])
1043 /**
1044  * @ngdoc directive
1045  * @name Buttons, links & UI controls.att:buttonGroups
1046  *
1047  * @description
1048  *  <file src="src/buttonGroups/docs/readme.md" />
1049  *
1050  * @usage
1051 <h2>Radio Aproach</h2>
1052 <div class="btn-group" b2b-key prev="37,38" next="39,40" circular-traversal role="radiogroup">
1053     <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 1'" ng-model="radioModel" b2b-btn-radio="'Button 1'" tabindex="{{(!radioModel || 'Button 1'===radioModel)?0:-1}}">Button 1</button>
1054     <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 2'" ng-model="radioModel" b2b-btn-radio="'Button 2'" tabindex="{{(!radioModel || 'Button 2'===radioModel)?0:-1}}">Button 2</button>
1055     <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 3'" ng-model="radioModel" b2b-btn-radio="'Button 3'" tabindex="{{(!radioModel || 'Button 3'===radioModel)?0:-1}}">Button 3</button>
1056 </div>
1057
1058 <h2>Checkbox Aproach</h2>
1059 <span b2b-button-group class="btn-group btn-fullwidth" role="group" max-select="3" ng-model="checkModel1">
1060     <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button1" b2b-btn-checkbox>Button1</button>
1061     <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button2" b2b-btn-checkbox>Button2</button>
1062     <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button3" b2b-btn-checkbox>Button3</button>
1063     <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button4" b2b-btn-checkbox>Button4</button>
1064     <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button5" b2b-btn-checkbox>Button5</button>
1065 </span>
1066  *
1067  * @example
1068  *  <section id="code">
1069         <example module="b2b.att">
1070             <file src="src/buttonGroups/docs/demo.html" />
1071             <file src="src/buttonGroups/docs/demo.js" />
1072        </example>
1073         </section>
1074  *
1075  */
1076 angular.module('b2b.att.buttonGroups', ['b2b.att.utilities'])
1077     .constant('buttonConfig', {
1078         activeClass: 'active',
1079         toggleEvent: 'click'
1080     })
1081     .directive('b2bBtnRadio', ['buttonConfig', function (buttonConfig) {
1082         var activeClass = buttonConfig.activeClass || 'active';
1083         var toggleEvent = buttonConfig.toggleEvent || 'click';
1084
1085         return {
1086             require: 'ngModel',
1087             link: function (scope, element, attrs, ngModelCtrl) {
1088                 var notMobile = !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
1089
1090                 if (notMobile) {
1091                     element.bind('focus', function () {
1092                         scope.$apply(function () {
1093                             ngModelCtrl.$setViewValue(scope.$eval(attrs.b2bBtnRadio));
1094                             ngModelCtrl.$render();
1095                         });
1096                     });
1097                 }
1098
1099                 element.attr('role', 'radio');
1100
1101                 //model -> UI
1102                 ngModelCtrl.$render = function () {
1103                     element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.b2bBtnRadio)));
1104                     if (angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.b2bBtnRadio))) {
1105                         element.attr("aria-checked", true);
1106                     } else {
1107                         element.attr("aria-checked", false);
1108                     }
1109                 };
1110
1111                 //ui->model
1112                 element.bind(toggleEvent, function () {
1113                     if (!element.hasClass(activeClass)) {
1114                         scope.$apply(function () {
1115                             ngModelCtrl.$setViewValue(scope.$eval(attrs.b2bBtnRadio));
1116                             ngModelCtrl.$render();
1117                         });
1118                     }
1119                 });
1120             }
1121         };
1122     }])
1123     .directive('b2bBtnCheckbox', ['buttonConfig', function (buttonConfig) {
1124         var activeClass = buttonConfig.activeClass || 'active';
1125         var toggleEvent = buttonConfig.toggleEvent || 'click';
1126
1127         return {
1128             require: ['ngModel', '^^b2bButtonGroup'],
1129             link: function (scope, element, attrs, ctrls) {
1130
1131                 var ngModelCtrl = ctrls[0];
1132                 var parentCtrl = ctrls[1];
1133
1134                 element.attr('role', 'checkbox');
1135                 element.attr('aria-describedby', parentCtrl.getStateDescriptionElemId());
1136
1137                 function getTrueValue() {
1138                     var trueValue = scope.$eval(attrs.b2bBtnCheckboxTrue);
1139                     return angular.isDefined(trueValue) ? trueValue : true;
1140                 }
1141
1142                 function getFalseValue() {
1143                     var falseValue = scope.$eval(attrs.b2bBtnCheckboxFalse);
1144                     return angular.isDefined(falseValue) ? falseValue : false;
1145                 }
1146
1147                 //model -> UI
1148                 ngModelCtrl.$render = function () {
1149                     element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
1150                     if ((angular.equals(ngModelCtrl.$modelValue, getTrueValue()))) {
1151                         element.attr("aria-checked", true);
1152                     } else {
1153                         element.attr("aria-checked", false);
1154                     }
1155                 };
1156
1157                 //ui->model
1158                 element.bind(toggleEvent, function () {
1159                     scope.$apply(function () {
1160                         ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? getFalseValue() : getTrueValue());
1161                         ngModelCtrl.$render();
1162                     });
1163                 });
1164             }
1165         };
1166     }])
1167     .directive('b2bButtonGroup', ['$timeout', '$compile', function ($timeout, $compile) {
1168         return {
1169             restrict: 'A',
1170             scope: {
1171                 maxSelect: "=",
1172                 ngModelButtonState: '=ngModel'
1173             },
1174             controller: ['$scope', '$element', function ($scope, $element) {
1175                 $scope.nSel = 0;
1176
1177                 var stateDescriptionElem = angular.element('<span id="b2b_button_group_' + $scope.$id + '" class="hide" aria-hidden="true">{{nSel}} of {{maxSelect}} options selected.</span>');
1178                 $compile(stateDescriptionElem)($scope);
1179                 $element.after(stateDescriptionElem);
1180
1181                 this.getStateDescriptionElemId = function () {
1182                     return stateDescriptionElem.attr('id');
1183                 };
1184             }],
1185             link: function (scope, element) {
1186
1187
1188                 var executeFxn = function () {
1189                     scope.nSel = 0;
1190                     angular.forEach(scope.ngModelButtonState, function (value, key) {
1191                         if (value === true) {
1192                             scope.nSel += 1;
1193                         }
1194                     });
1195
1196                     if (scope.nSel >= scope.maxSelect) {
1197                         angular.forEach(element.children(), function (chd) {
1198                             if (chd.className.indexOf('active') < 0) {
1199                                 chd.disabled = true;
1200                                 chd.setAttribute('aria-disabled', true);
1201                             }
1202                         });
1203                     } else {
1204                         angular.forEach(element.children(), function (chd) {
1205                             chd.disabled = false;
1206                             chd.setAttribute('aria-disabled', false);
1207                         });
1208                     }
1209                     scope.$digest();
1210                 };
1211
1212                 $timeout(function () {
1213                     executeFxn();
1214                 });
1215                 element.bind('click', executeFxn);
1216             }
1217         };
1218     }]);
1219 /**
1220  * @ngdoc directive
1221  * @name Buttons, links & UI controls.att:buttons
1222  * @element input
1223  * @function
1224  *
1225  * @description
1226  *  <file src="src/buttons/docs/readme.md" />
1227  * @usage
1228  *
1229 Button shape
1230 <button class="btn" type="button">Button</button> button.btn (button shape only)
1231 <button aria-label="Custom aria label" class="btn" type="button">Button</button> button.btn (button shape only) with custom aria label
1232 <button aria-label="Click on button/Press enter" class="btn" type="button" onclick="javascript:alert('It works!');">Click on button/Press enter</button> button.btn with click functionality
1233 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn" role="button">Button</a> a.btn (button shape only)
1234 <button class="btn btn-primary">Button</button> .btn-primary
1235 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-primary" role="button">Button</a> a.btn-primary
1236
1237 5 Button colors
1238 <button class="btn btn-secondary">Button</button> .btn-secondary
1239 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-secondary" role="button">Button</a> a.btn-secondary
1240 <button class="btn btn-alt">Button</button> .btn-alt
1241 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-alt" role="button">Button</a> a.btn-alt
1242 <button class="btn btn-specialty">Button</button> .btn-specialty
1243 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-specialty" role="button">Button</a> a.btn-specialty
1244 <button class="btn btn-specialty" disabled="">Button</button> disabled="disabled"
1245 <a b2b-keyup-click="32" aria-disabled="true" href="javascript:void(0)" class="btn btn-primary disabled" role="button">Button</a> a.disabled
1246
1247 3 button heights
1248 <button class="btn btn-secondary">Button</button> .btn is default and 46px height
1249 <button class="btn btn-secondary btn-medium">Button</button> .btn-medium is 42px
1250 <button class="btn btn-secondary btn-small">Button</button> .btn-small is 36px
1251
1252 .row-nowrap 2 up buttons
1253 <div class="row-nowrap">
1254     <button class="btn btn-secondary btn-fullwidth" type="button">Cancel</button>
1255     <button class="btn btn-primary btn-fullwidth" type="button">Continue</button>
1256 </div>
1257
1258 .row 2 up buttons (desktop) stacked (mobile) (different order)
1259 <div class="row cta-button-group">
1260     <button class="span btn btn-secondary btn-fullwidth hidden-phone" type="button">Cancel</button>
1261     <button class="span btn btn-primary btn-fullwidth" type="button">Continue</button>
1262     <button class="span btn btn-secondary btn-fullwidth visible-phone" type="button">Cancel</button>
1263 </div>
1264
1265  * @example
1266  *  <section id="code">
1267                <b>HTML + AngularJS</b>
1268  *              <example module="b2b.att">
1269  *              <file src="src/buttons/docs/demo.html" />
1270                  <file src="src/buttons/docs/demo.js" />
1271  *              </example>
1272             </section>
1273  *
1274  */
1275 angular.module('b2b.att.buttons', ['b2b.att.utilities']);
1276 /**
1277  * @ngdoc directive
1278  * @name Forms.att:calendar
1279  *
1280  * @description
1281  *  <file src="src/calendar/docs/readme.md" />
1282  * @usage
1283  *  <input type="text" ng-model="dt" b2b-datepicker>
1284  *
1285  * @example
1286    <section id="code">
1287     <b>HTML + AngularJS</b>
1288     <example module="b2b.att">
1289      <file src="src/calendar/docs/demo.html" />
1290      <file src="src/calendar/docs/demo.js" />
1291     </example>
1292    </section>
1293  */
1294 angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities'])
1295
1296 .constant('b2bDatepickerConfig', {
1297     dateFormat: 'MM/dd/yyyy',
1298     dayFormat: 'd',
1299     monthFormat: 'MMMM',
1300     yearFormat: 'yyyy',
1301     dayHeaderFormat: 'EEEE',
1302     dayTitleFormat: 'MMMM yyyy',
1303     disableWeekend: false,
1304     disableSunday: false,
1305     disableDates: null,
1306     onSelectClose: null,
1307     startingDay: 0,
1308     minDate: null,
1309     maxDate: null,
1310     dueDate: null,
1311     fromDate: null,
1312     legendIcon: null,
1313     legendMessage: null,
1314     calendarDisabled: false,
1315     collapseWait: 0,
1316     orientation: 'right',
1317     inline: false,
1318     helperText: 'The date you selected is $date. In case of mobile double tap to open calendar. Select a date to close the calendar.',
1319     datepickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation'],
1320     datepickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
1321     datepickerFunctionAttributes: ['disableDates', 'onSelectClose']
1322 })
1323
1324 .factory('b2bDatepickerService', ['b2bDatepickerConfig', 'dateFilter', function (b2bDatepickerConfig, dateFilter) {
1325     var setAttributes = function (attr, elem) {
1326         if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
1327             var attributes = b2bDatepickerConfig.datepickerEvalAttributes.concat(b2bDatepickerConfig.datepickerWatchAttributes, b2bDatepickerConfig.datepickerFunctionAttributes);
1328             for (var key in attr) {
1329                 var val = attr[key];
1330                 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
1331                     elem.attr(key.toSnakeCase(), key);
1332                 }
1333             }
1334         }
1335     };
1336
1337     var bindScope = function (attr, scope) {
1338         if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
1339             var evalFunction = function (key, val) {
1340                 scope[key] = scope.$parent.$eval(val);
1341             };
1342
1343             var watchFunction = function (key, val) {
1344                 scope.$parent.$watch(val, function (value) {
1345                     scope[key] = value;
1346                 });
1347                 scope.$watch(key, function (value) {
1348                     scope.$parent[val] = value;
1349                 });
1350             };
1351
1352             var evalAttributes = b2bDatepickerConfig.datepickerEvalAttributes;
1353             var watchAttributes = b2bDatepickerConfig.datepickerWatchAttributes;
1354             for (var key in attr) {
1355                 var val = attr[key];
1356                 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
1357                     evalFunction(key, val);
1358                 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
1359                     watchFunction(key, val);
1360                 }
1361             }
1362         }
1363     };
1364
1365     return {
1366         setAttributes: setAttributes,
1367         bindScope: bindScope
1368     };
1369 }])
1370
1371 .controller('b2bDatepickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bDatepickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
1372     var format = {
1373             date: getValue($attrs.dateFormat, dtConfig.dateFormat),
1374             day: getValue($attrs.dayFormat, dtConfig.dayFormat),
1375             month: getValue($attrs.monthFormat, dtConfig.monthFormat),
1376             year: getValue($attrs.yearFormat, dtConfig.yearFormat),
1377             dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
1378             dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
1379             disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
1380             disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday)
1381         },
1382         startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
1383
1384     if($attrs.disableDates !== undefined) {
1385         format.disableDates = $attrs.disableDates;
1386     } else {
1387        format.disableDates =  dtConfig.disableDates;     
1388     }
1389     $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
1390     $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
1391     $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
1392     $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
1393     $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
1394     $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
1395     $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
1396     $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
1397     $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
1398     $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
1399
1400     $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
1401
1402     function getValue(value, defaultValue) {
1403         return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
1404     }
1405
1406     function getDaysInMonth(year, month) {
1407         return new Date(year, month, 0).getDate();
1408     }
1409
1410     function getDates(startDate, n) {
1411         var dates = new Array(n);
1412         var current = startDate,
1413             i = 0;
1414         while (i < n) {
1415             dates[i++] = new Date(current);
1416             current.setDate(current.getDate() + 1);
1417         }
1418         return dates;
1419     }
1420
1421     this.updatePosition = function (b2bDatepickerPopupTemplate) {
1422         $scope.position = $position.offset($element);
1423         $scope.position.top = $scope.position.top + $element.prop('offsetHeight');
1424         $scope.position.left = $scope.position.left - (((b2bDatepickerPopupTemplate && b2bDatepickerPopupTemplate.prop('offsetWidth')) || 290) - $element.prop('offsetWidth'));
1425     };
1426
1427     this.isDateInRange = function(date) {
1428         if ((compare(date, $scope.minDate) >= 0) && (compare(date, $scope.maxDate) <= 0)) {
1429             return true;
1430         } else  {
1431             return false;
1432         }
1433         return false;
1434     }
1435
1436     this.isDisbaledDate = function(date) {
1437         if ($attrs.from && !angular.isDate($scope.fromDate)) {
1438             return true;
1439         }
1440         if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
1441             return true;
1442         }
1443         if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
1444             return true;
1445         }
1446     
1447         return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || ($scope.datesCallBack({
1448             date: date
1449         })));
1450
1451     }
1452     function isSelected(dt) {
1453         if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
1454             return true;
1455         }
1456         return false;
1457     }
1458
1459     function isFromDate(dt) {
1460         if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
1461             return true;
1462         }
1463         return false;
1464     }
1465
1466     function isDateRange(dt) {
1467         if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
1468             return true;
1469         } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
1470             return true;
1471         }
1472         return false;
1473     }
1474
1475     function isOld(date, currentMonthDate) {
1476         if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() < new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
1477             return true;
1478         } else {
1479             return false;
1480         }
1481     }
1482
1483     function isNew(date, currentMonthDate) {
1484         if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() > new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
1485             return true;
1486         } else {
1487             return false;
1488         }
1489     }
1490
1491     function isPastDue(dt) {
1492         if ($scope.dueDate) {
1493             return (dt > $scope.dueDate);
1494         }
1495         return false;
1496     }
1497
1498     function isDueDate(dt) {
1499         if ($scope.dueDate) {
1500             return (dt.getTime() === $scope.dueDate.getTime());
1501         }
1502         return false;
1503     }
1504
1505     var isDisabled = function (date, currentMonthDate) {
1506         if ($attrs.from && !angular.isDate($scope.fromDate)) {
1507             return true;
1508         }
1509         if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
1510             return true;
1511         }
1512         if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
1513             return true;
1514         }
1515         if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
1516             return true;
1517         }
1518         return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || ($scope.datesCallBack({
1519             date: date
1520         })));
1521     };
1522
1523     var compare = function (date1, date2) {
1524         return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
1525     };
1526
1527     function isMinDateAvailable(startDate, endDate) {
1528         if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
1529             $scope.disablePrev = true;
1530             $scope.visibilityPrev = "hidden";
1531         } else {
1532             $scope.disablePrev = false;
1533             $scope.visibilityPrev = "visible";
1534         }
1535     }
1536
1537     function isMaxDateAvailable(startDate, endDate) {
1538         if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
1539             $scope.disableNext = true;
1540             $scope.visibilityNext = "hidden";
1541         } else {
1542             $scope.disableNext = false;
1543             $scope.visibilityNext = "visible";
1544         }
1545     }
1546
1547     function getLabel(label) {
1548         if (label) {
1549             var labelObj = {
1550                 pre: label.substr(0, 1).toUpperCase(),
1551                 post: label
1552             };
1553             return labelObj;
1554         }
1555         return;
1556     }
1557
1558     function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
1559         return {
1560             date: date,
1561             label: dateFilter(date, dayFormat),
1562             header: dateFilter(date, dayHeaderFormat),
1563             selected: !!isSelected,
1564             fromDate: !!isFromDate,
1565             dateRange: !!isDateRange,
1566             oldMonth: !!isOld,
1567             nextMonth: !!isNew,
1568             disabled: !!isDisabled,
1569             dueDate: !!dueDate,
1570             pastDue: !!pastDue,
1571             focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
1572         };
1573     }
1574
1575     this.modes = [
1576         {
1577             name: 'day',
1578             getVisibleDates: function (date) {
1579                 var year = date.getFullYear(),
1580                     month = date.getMonth(),
1581                     firstDayOfMonth = new Date(year, month, 1),
1582                     lastDayOfMonth = new Date(year, month + 1, 0);
1583                 var difference = startingDay - firstDayOfMonth.getDay(),
1584                     numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
1585                     firstDate = new Date(firstDayOfMonth),
1586                     numDates = 0;
1587
1588                 if (numDisplayedFromPreviousMonth > 0) {
1589                     firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
1590                     numDates += numDisplayedFromPreviousMonth; // Previous
1591                 }
1592                 numDates += getDaysInMonth(year, month + 1); // Current
1593                 numDates += (7 - numDates % 7) % 7; // Next
1594
1595                 var days = getDates(firstDate, numDates),
1596                     labels = new Array(7);
1597                 for (var i = 0; i < numDates; i++) {
1598                     var dt = new Date(days[i]);
1599                     days[i] = makeDate(dt,
1600                         format.day,
1601                         format.dayHeader,
1602                         isSelected(dt),
1603                         isFromDate(dt),
1604                         isDateRange(dt),
1605                         isOld(dt, date),
1606                         isNew(dt, date),
1607                         isDisabled(dt, date),
1608                         isDueDate(dt),
1609                         isPastDue(dt));
1610                 }
1611                 for (var j = 0; j < 7; j++) {
1612                     labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
1613                 }
1614                 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
1615                 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
1616                 return {
1617                     objects: days,
1618                     title: dateFilter(date, format.dayTitle),
1619                     labels: labels
1620                 };
1621             },
1622             split: 7,
1623             step: {
1624                 months: 1
1625             }
1626         }
1627     ];
1628 }])
1629
1630 .directive('b2bDatepicker', ['$parse', '$log', '$timeout', '$document', '$documentBind', '$isElement', '$templateCache', '$compile', 'trapFocusInElement', '$position', '$window', '$filter', 'b2bDatepickerConfig', function ($parse, $log, $timeout, $document, $documentBind, $isElement, $templateCache, $compile, trapFocusInElement, $position, $window, $filter, b2bDatepickerConfig) {
1631     return {
1632         restrict: 'EA',
1633         scope: { 
1634             model: '=ngModel',
1635             datesCallBack: '&disableDates',
1636             onSelectClose: '&',
1637             disabledInput: '=?ngDisabled'
1638         },
1639         require: ['b2bDatepicker', 'ngModel', '?^b2bDatepickerGroup'],
1640         controller: 'b2bDatepickerController',
1641         link: function (scope, element, attrs, ctrls) {
1642             var datepickerCtrl = ctrls[0],
1643                 ngModel = ctrls[1],
1644                 b2bDatepickerGroupCtrl = ctrls[2];
1645             var b2bDatepickerPopupTemplate;
1646             var isCalendarOpened = false;
1647             if(scope.disabledInput === undefined || scope.disabledInput === '') {
1648                 scope.disabledInput = false;
1649             } 
1650             if(attrs.inline == 'true'){     
1651                 element.after($compile($templateCache.get('b2bTemplate/calendar/datepicker-popup.html'))(scope));
1652                 var temp = element.after();
1653                 element.remove();
1654                 element = temp; 
1655             } else {
1656                 var buttonTabIndex =  scope.disabledInput===true ? -1 : 0; 
1657
1658                 element.after($compile('<button class="btn-calendar-icon" ng-disabled='+scope.disabledInput+' ><i class="icon-primary-calendar b2b-calendar-icon" aria-haspopup="true" aria-expanded="false" ng-class=\"{\'disabled\': '+scope.disabledInput+'}\" ></i></button>')(scope));
1659                 element.attr('placeholder', 'MM/dd/yyyy');
1660                 element.attr('b2b-format-date', b2bDatepickerConfig.dateFormat); 
1661             }
1662             scope.$watch('model', function(val) {
1663
1664                 if(val !== undefined && val !== '') {
1665                     var date_regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/ ;
1666
1667                     if(!date_regex.test(element[0].value)) {
1668                         ngModel.$setValidity('datePattern', false);
1669                     } else {    
1670                         ngModel.$setValidity('datePattern', true);
1671                     }
1672                     
1673                 } else {
1674                     ngModel.$setValidity('datePattern', true);
1675                 }
1676                 
1677             });
1678
1679             if (!ngModel) {
1680                 $log.error("ng-model is required.");
1681                 return; // do nothing if no ng-model
1682             }
1683
1684             if(scope.model !== undefined && scope.model !== '') {
1685                 element[0].value = $filter('date')(scope.model, "MM/dd/yyyy");
1686             }
1687
1688             // Configuration parameters
1689             var mode = 0,
1690                 selected;
1691             scope.isOpen = false;
1692             var isValidDate = false;
1693
1694             scope.headers = [];
1695             scope.footers = [];
1696
1697             if (b2bDatepickerGroupCtrl) {
1698                 b2bDatepickerGroupCtrl.registerDatepickerScope(scope);
1699             }
1700
1701              var calendarButton = angular.element(element[0].nextElementSibling);
1702
1703             calendarButton.bind('click',function(){
1704                 openCalendarPopup = false;
1705                 if (!scope.ngDisabled) {
1706                     scope.isOpen = !scope.isOpen;
1707                     toggleCalendar(scope.isOpen);
1708                     scope.$apply();
1709                     datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1710                     $timeout(function () { 
1711                        // angular.element(element[0].querySelector('.datepicker-input')).scrollTop=0;
1712                     },10);
1713                 }
1714             })
1715             var openCalendarPopup = false;
1716
1717             element.bind('blur', function() {
1718                 if(scope.model !== undefined && scope.model !== '') {
1719                     var dateEntered = scope.model;
1720
1721                       var date_regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/ ;
1722
1723                     if(date_regex.test(dateEntered)) {       
1724                         var parts = dateEntered.split('/');
1725                         var enteredDate = new Date(parts[2],parts[0]-1,parts[1]);                           
1726
1727                         if(datepickerCtrl.isDateInRange(enteredDate)) {
1728                             isValidDate -= 1;
1729                             ngModel.$setValidity('outOfRange', true);
1730                             $timeout(function(){
1731                                 ngModel.$setValidity('outOfRange', true);
1732                             },10);
1733                             isValidDate = true;
1734                             if(!datepickerCtrl.isDisbaledDate(enteredDate)) {  
1735                                $timeout(function(){
1736                                     ngModel.$setValidity('disabledDate', true);
1737                                },10);   
1738                                 scope.select(enteredDate); 
1739                                 openCalendarPopup = true;
1740                             } else {
1741                                 $timeout(function(){
1742                                     ngModel.$setValidity('disabledDate', false);
1743                                 },10);
1744                                 isValidDate = false;
1745                                 openCalendarPopup = false;
1746                             }
1747   
1748                         } else {
1749                             isValidDate += 1;
1750                             $timeout(function(){
1751                                 ngModel.$setValidity('outOfRange', false);
1752                             },10);
1753                             isValidDate = false;
1754                             openCalendarPopup = false;
1755                         }
1756
1757                     }
1758                 }
1759             });   
1760
1761             var toggleCalendar = function (flag) {
1762                 if (!scope.inline) {
1763                     if (flag) {
1764                         b2bDatepickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/calendar/datepicker-popup.html'));
1765                         b2bDatepickerPopupTemplate = $compile(b2bDatepickerPopupTemplate)(scope);
1766                         $document.find('body').append(b2bDatepickerPopupTemplate);
1767                         b2bDatepickerPopupTemplate.bind('keydown', keyPress);
1768                         $timeout(function () {
1769                             scope.getFocus = true;
1770                             trapFocusInElement(flag, b2bDatepickerPopupTemplate);
1771                             scope.$apply();
1772                             $timeout(function () {
1773                                 scope.getFocus = false; 
1774                                 scope.$apply();
1775                             }, 100);
1776                             handleTabEvent();
1777                         });
1778                         angular.element(document.querySelector('.b2b-calendar-icon')).attr('aria-expanded','true');
1779                     } else {
1780                         if(!openCalendarPopup) {
1781                             b2bDatepickerPopupTemplate.unbind('keydown', keyPress);
1782                             b2bDatepickerPopupTemplate.remove();
1783                         }
1784                         element[0].focus();
1785                         scope.getFocus = false;
1786                         angular.element(document.querySelector('.b2b-calendar-icon')).attr('aria-expanded','false');
1787                         trapFocusInElement(flag, b2bDatepickerPopupTemplate);
1788                     }
1789                 }
1790             };
1791
1792             var handleTabEvent = function(){
1793                 b2bDatepickerPopupTemplate.find('td').on('keydown', function (e) {
1794                     if (e.keyCode == '9') {
1795                         if(e.shiftKey){
1796                             if(b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.next')){
1797                                 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.next').focus();
1798                             }else{
1799                                 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.datepicker-switch').focus()
1800                             }        
1801                         }else{
1802                             if(b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.prev')){
1803                                 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.prev').focus();
1804                             }else{
1805                                 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.datepicker-switch').focus()
1806                             }
1807                         }
1808                         
1809                         e.preventDefault();
1810                         e.stopPropagation();
1811                     }
1812                 });
1813             }
1814
1815             var outsideClick = function (e) {
1816                 var isElement = $isElement(angular.element(e.target), element, $document);
1817                 var isb2bDatepickerPopupTemplate = $isElement(angular.element(e.target), b2bDatepickerPopupTemplate, $document);
1818                 if (!(isElement || isb2bDatepickerPopupTemplate)) {
1819                     scope.isOpen = false;
1820                     toggleCalendar(scope.isOpen);
1821                     scope.$apply();
1822                 }
1823             };
1824
1825             var keyPress = function (ev) {
1826                 if (!ev.keyCode) {
1827                     if (ev.which) {
1828                         ev.keyCode = ev.which;
1829                     } else if (ev.charCode) {
1830                         ev.keyCode = ev.charCode;
1831                     }
1832                 }
1833                 if (ev.keyCode) {
1834                     if (ev.keyCode === 27) {
1835                         scope.isOpen = false;
1836                         toggleCalendar(scope.isOpen);
1837                         ev.preventDefault();
1838                         ev.stopPropagation();
1839                     } else if (ev.keyCode === 33) {
1840                         !scope.disablePrev && scope.move(-1);
1841                         $timeout(function () {
1842                             scope.getFocus = true;
1843                             scope.$apply();
1844                             $timeout(function () {
1845                                 scope.getFocus = false;
1846                                 scope.$apply();
1847                             }, 100);
1848                         });
1849                         ev.preventDefault();
1850                         ev.stopPropagation();
1851                     } else if (ev.keyCode === 34) {
1852                         !scope.disableNext && scope.move(1);
1853                         $timeout(function () {
1854                             scope.getFocus = true;
1855                             scope.$apply();
1856                             $timeout(function () {
1857                                 scope.getFocus = false;
1858                                 scope.$apply();
1859                             }, 100);
1860                         });
1861                         ev.preventDefault();
1862                         ev.stopPropagation();
1863                     }
1864                     scope.$apply();
1865                 }
1866             };
1867
1868             $documentBind.click('isOpen', outsideClick, scope);
1869
1870             var modalContainer = angular.element(document.querySelector('.modalwrapper'));
1871             var modalBodyContainer = angular.element(document.querySelector('.modal-body'));
1872             if (modalContainer) {
1873                 modalContainer.bind('scroll', function () {
1874                     if (b2bDatepickerPopupTemplate) {
1875                         datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1876                         scope.$apply();
1877                     }
1878                 });
1879             }
1880             if (modalBodyContainer) {
1881                 modalBodyContainer.bind('scroll', function () {
1882                     if (b2bDatepickerPopupTemplate) {
1883                         datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1884                         var datepickerTextfield = $position.offset(element.find('input'));
1885                         var modalBodyPosition = $position.offset(modalBodyContainer);
1886
1887                         if (((datepickerTextfield.top + datepickerTextfield.height) < modalBodyPosition.top || datepickerTextfield.top > (modalBodyPosition.top + modalBodyPosition.height)) && scope.isOpen) {
1888                             scope.isOpen = false;
1889                             toggleCalendar(scope.isOpen);
1890                         }
1891                         scope.$apply();
1892                     }
1893                 });
1894             }
1895             var window = angular.element($window);
1896             window.bind('resize', function () {
1897                 if (b2bDatepickerPopupTemplate) {
1898                     datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1899                     scope.$apply();
1900                 }
1901             });
1902
1903             scope.$on('$destroy', function () {
1904                 if (scope.isOpen) {
1905                     scope.isOpen = false;
1906                     toggleCalendar(scope.isOpen);
1907                 }
1908             });
1909
1910             scope.resetTime = function (date) {
1911                 if (typeof date === 'string') {
1912                     date = date + 'T12:00:00';
1913                 }
1914                 var dt;
1915                 if (!isNaN(new Date(date))) {
1916                     dt = new Date(date);
1917                 } else {
1918                     return null;
1919                 }
1920                 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
1921             };
1922
1923             if (attrs.min) {
1924                 scope.$parent.$watch($parse(attrs.min), function (value) {
1925                     scope.minDate = value ? scope.resetTime(value) : null;
1926                     refill();
1927                 });
1928             }
1929             if (attrs.max) {
1930                 scope.$parent.$watch($parse(attrs.max), function (value) {
1931                     scope.maxDate = value ? scope.resetTime(value) : null;
1932                     refill();
1933                 });
1934             }
1935             if (attrs.due) {
1936                 scope.$parent.$watch($parse(attrs.due), function (value) {
1937                     scope.dueDate = value ? scope.resetTime(value) : null;
1938                     refill();
1939                 });
1940             }
1941             if (attrs.from) {
1942                 scope.$parent.$watch($parse(attrs.from), function (value) {
1943                     scope.fromDate = value ? scope.resetTime(value) : null;
1944                     refill();
1945                 });
1946             }
1947
1948             if (attrs.legendIcon) {
1949                 scope.$parent.$watch(attrs.legendIcon, function (value) {
1950                     scope.legendIcon = value ? value : null;
1951                     refill();
1952                 });
1953             }
1954             if (attrs.legendMessage) {
1955                 scope.$parent.$watch(attrs.legendMessage, function (value) {
1956                     scope.legendMessage = value ? value : null;
1957                     refill();
1958                 });
1959             }
1960             if (attrs.ngDisabled) {
1961                 scope.$parent.$watch(attrs.ngDisabled, function (value) {
1962                     scope.ngDisabled = value ? value : null;
1963                 });
1964             }
1965
1966             // Split array into smaller arrays
1967             function split(arr, size) {
1968                 var arrays = [];
1969                 while (arr.length > 0) {
1970                     arrays.push(arr.splice(0, size));
1971                 }
1972                 return arrays;
1973             }
1974
1975             function refill(date) {
1976                 if (angular.isDate(date) && !isNaN(date)) {
1977                     selected = new Date(date);
1978                 } else {
1979                     if (!selected) {
1980                         selected = new Date();
1981                     }
1982                 }
1983
1984                 if (selected) {
1985                     var currentMode = datepickerCtrl.modes[mode],
1986                         data = currentMode.getVisibleDates(selected);
1987                     scope.rows = split(data.objects, currentMode.split);
1988                     var flag = false;
1989                     var startFlag = false;
1990                     var firstSelected = false;
1991                     for (var i = 0; i < scope.rows.length; i++) {
1992                         for (var j = 0; j < scope.rows[i].length; j++) {
1993
1994                             if (scope.rows[i][j].label === "1" && !firstSelected) {
1995                                 firstSelected = true;
1996                                 var firstDay = scope.rows[i][j];
1997                             }
1998
1999                             if (scope.rows[i][j].selected === true) {
2000                                 flag = true;
2001                                 break;
2002                             }
2003                         }
2004                         if (flag) {
2005                             break;
2006                         }
2007                     }
2008                     if (!flag) {
2009                         firstDay.firstFocus = true;
2010                     }
2011
2012                     scope.labels = data.labels || [];
2013                     scope.title = data.title;
2014
2015                     datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
2016                 }
2017             }
2018
2019             scope.select = function (date) {
2020                 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
2021                 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
2022                         date: dt
2023                     }) !== false)) {
2024                     scope.currentDate = dt;
2025                     element[0].value = $filter('date')(dt, "MM/dd/yyyy");
2026                     ngModel.$setValidity('outOfRange', true);
2027                     if (angular.isNumber(scope.collapseWait)) {
2028                         $timeout(function () {
2029                             scope.isOpen = false;
2030                             toggleCalendar(scope.isOpen);
2031                         }, scope.collapseWait);
2032                     } else {
2033                         scope.isOpen = false;
2034                         toggleCalendar(scope.isOpen);
2035                     }
2036                 }
2037             };
2038
2039             scope.move = function (direction,$event) {
2040                 var step = datepickerCtrl.modes[mode].step;
2041                 selected.setDate(1);
2042                 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
2043                 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
2044                 refill();
2045
2046                 $timeout(function () {
2047                     trapFocusInElement();
2048                     handleTabEvent();
2049                 }, 100);
2050
2051                 $event.preventDefault();
2052                 $event.stopPropagation();
2053             };
2054
2055             scope.trapFocus = function () {
2056                 $timeout(function () {
2057                     trapFocusInElement();
2058                 }, 100);
2059             };
2060
2061             scope.$watch('currentDate', function (value) {
2062                 if (angular.isDefined(value) && value !== null) {
2063                     refill(value);
2064                 } else {
2065                     refill();
2066                 }
2067                 ngModel.$setViewValue(value);
2068             });
2069
2070             ngModel.$render = function () {
2071                 scope.currentDate = ngModel.$viewValue;
2072             };
2073
2074             var stringToDate = function (value) {
2075                 if (!isNaN(new Date(value))) {
2076                     value = new Date(value);
2077                 }
2078                 return value;
2079             };
2080             ngModel.$formatters.unshift(stringToDate);
2081         }
2082     };
2083 }])
2084
2085
2086 .directive('b2bDatepickerGroup', [function () {
2087     return {
2088         restrict: 'EA',
2089         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2090             this.$$headers = [];
2091             this.$$footers = [];
2092             this.registerDatepickerScope = function (datepickerScope) {
2093                 datepickerScope.headers = this.$$headers;
2094                 datepickerScope.footers = this.$$footers;
2095             };
2096         }],
2097         link: function (scope, elem, attr, ctrl) {}
2098     };
2099 }])
2100
2101 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
2102     return {
2103         restrict: 'A',
2104         require: 'ngModel',
2105         link: function (scope, elem, attr, ctrl) {
2106             var b2bFormatDate = "";
2107             attr.$observe('b2bFormatDate', function (value) {
2108                 b2bFormatDate = value;
2109             });
2110             var dateToString = function (value) {
2111                 if (!isNaN(new Date(value))) {
2112                     return dateFilter(new Date(value), b2bFormatDate);
2113                 }
2114                 return value;
2115             };
2116             ctrl.$formatters.unshift(dateToString);
2117         }
2118     };
2119 }])
2120
2121 .directive('b2bDatepickerHeader', [function () {
2122     return {
2123         restrict: 'EA',
2124         require: '^b2bDatepickerGroup',
2125         transclude: true,
2126         replace: true,
2127         template: '',
2128         compile: function (elem, attr, transclude) {
2129             return function link(scope, elem, attr, ctrl) {
2130                 if (ctrl) {
2131                     ctrl.$$headers.push(transclude(scope, function () {}));
2132                 }
2133                 elem.remove();
2134             };
2135         }
2136     };
2137 }])
2138
2139 .directive('b2bDatepickerFooter', [function () {
2140     return {
2141         restrict: 'EA',
2142         require: '^b2bDatepickerGroup',
2143         transclude: true,
2144         replace: true,
2145         template: '',
2146         compile: function (elem, attr, transclude) {
2147             return function link(scope, elem, attr, ctrl) {
2148                 if (ctrl) {
2149                     ctrl.$$footers.push(transclude(scope, function () {}));
2150                 }
2151                 elem.remove();
2152             };
2153         }
2154     };
2155 }]);
2156 /**
2157  * @ngdoc directive
2158  * @name Forms.att:checkboxes
2159  *
2160  * @description
2161  *  <file src="src/checkboxes/docs/readme.md" />
2162  * @usage
2163  * See demo section
2164  * @example
2165  <example module="b2b.att">
2166  <file src="src/checkboxes/docs/demo.html" />
2167  <file src="src/checkboxes/docs/demo.js" />
2168  </example>
2169  */
2170 angular.module('b2b.att.checkboxes', ['b2b.att.utilities'])
2171 .directive('b2bSelectGroup', [function (){
2172         return {
2173             restrict: 'A',
2174             require: 'ngModel',
2175             scope: {
2176                 checkboxes: "="
2177             },
2178             link: function (scope, elem, attr, ctrl) {
2179                 elem.bind('change', function () {
2180                     var isChecked = elem.prop('checked');
2181                     angular.forEach(scope.checkboxes, function (item) {
2182                         item.isSelected = isChecked;
2183                     });
2184                     scope.$apply();
2185                 });
2186                 scope.$watch('checkboxes', function () {
2187                     var setBoxes = 0;
2188                     if(scope.checkboxes === undefined) {
2189                         return;
2190                     }
2191                     angular.forEach(scope.checkboxes, function (item) {
2192                         if (item.isSelected) {
2193                             setBoxes++; 
2194                         } 
2195                     });
2196                     elem.prop('indeterminate', false);
2197                     if ( scope.checkboxes !==undefined && setBoxes === scope.checkboxes.length && scope.checkboxes.length > 0) { 
2198                         ctrl.$setViewValue(true); 
2199                         elem.removeClass('indeterminate');
2200                     } else if (setBoxes === 0) { 
2201                        ctrl.$setViewValue(false); 
2202                        elem.removeClass('indeterminate');
2203                     } else { 
2204                         ctrl.$setViewValue(false); 
2205                         elem.addClass('indeterminate');
2206                         elem.prop('indeterminate', true); 
2207                     }
2208                     ctrl.$render();
2209                 }, true);
2210             }
2211         };
2212     }]);
2213 /**
2214  * @ngdoc directive
2215  * @name Misc.att:coachmark
2216  *
2217  * @description
2218  * <file src="src/coachmark/docs/readme.md" />
2219  *
2220  * @usage
2221  *
2222 <button b2b-coachmark start-coachmark-callback="startCoachmark()" end-coachmark-callback="endCoachmark()" action-coachmark-callback="actionCoachmark(action)" coachmark-index="coachmarkIndex" coachmarks="coachmarkElements" id="coachmark0" class="btn btn-alt">Initiate tour</button>
2223
2224  * @example
2225     <section id="code">   
2226         <b>HTML + AngularJS</b>
2227         <example module="b2b.att">
2228             <file src="src/coachmark/docs/demo.html" />
2229             <file src="src/coachmark/docs/demo.js" />
2230         </example>
2231     </section>
2232  */
2233
2234 angular.module('b2b.att.coachmark', ['b2b.att.utilities','b2b.att.position'])
2235         
2236         .directive('b2bCoachmark', ['$document', '$compile', '$position', '$timeout', 'b2bViewport', 'keymap', function($document, $compile, $position, $timeout, b2bViewport, keymap) {
2237         return {
2238             restrict: 'A',
2239                          scope: {
2240                                 coachmarks: '=',
2241                                 coachmarkIndex: '=',
2242                                 startCoachmarkCallback: '&',
2243                                 endCoachmarkCallback: '&',
2244                                 actionCoachmarkCallback: '&'
2245                         },
2246             link: function (scope, element, attrs, ctrl) {
2247                                 var coachmarkItems = scope.coachmarks;
2248                                 var body = $document.find('body').eq(0);
2249                                 var coackmarkJqContainer;
2250                                 var coackmarkContainer;
2251                                 var coachMarkElement;
2252                                 var backdropjqLiteEl;
2253                                 var coachmarkHighlight;
2254                                 var initaitedCoachmark = false;
2255                                 scope.coackmarkElPos ={
2256                                         'top':'',
2257                                         'left':''
2258                                 };
2259                                 
2260                                 scope.currentCoachmark = {};
2261                                 
2262                                 
2263                 var coachmarkBackdrop = function(){
2264                                         backdropjqLiteEl = angular.element('<div class="b2b-modal-backdrop fade in hidden-by-modal"></div>');
2265                                         body.append(backdropjqLiteEl);
2266
2267                                         backdropjqLiteEl.bind('click', function() {
2268                                                 scope.closeCoachmark();
2269                                                 scope.$apply();
2270                                         });
2271                                 };
2272                                 
2273                                 
2274                                 scope.closeButtonFocus = function(){
2275                                         if(document.getElementsByClassName('b2b-coachmark-header').length >0){
2276                                                 document.getElementsByClassName('b2b-coachmark-header')[0].scrollLeft = 0;
2277                                                 document.getElementsByClassName('b2b-coachmark-header')[0].scrollTop = 0;
2278                                         }
2279                                 }
2280
2281                                 scope.actionCoachmark = function(action){
2282                                         scope.actionCoachmarkCallback({
2283                                                 'action':action
2284                                         })
2285                                 };
2286                                 
2287                                 scope.closeCoachmark = function(){
2288                                         initaitedCoachmark = false;
2289                                         backdropjqLiteEl.remove();      
2290                                         coackmarkContainer.remove();
2291                                         coachmarkHighlight.remove();
2292                                         if(coachMarkElement !== undefined && coachMarkElement !==""){
2293                                                 coachMarkElement.removeClass('b2b-coachmark-label')
2294                                         }
2295                                         if (angular.isFunction(scope.endCoachmarkCallback)){
2296                                                 scope.endCoachmarkCallback();   
2297                                         }
2298                                         element[0].focus();
2299                                 }
2300
2301                                 var realStyle = function(_elem, _style) {
2302                                     var computedStyle;
2303                                     if ( typeof _elem.currentStyle != 'undefined' ) {
2304                                         computedStyle = _elem.currentStyle;
2305                                     } else {
2306                                         computedStyle = document.defaultView.getComputedStyle(_elem, null);
2307                                     }
2308
2309                                     return _style ? computedStyle[_style] : computedStyle;
2310                                 };
2311
2312                                 var copyComputedStyle = function(src, dest) {
2313                                     var s = realStyle(src);
2314                                     for ( var i in s ) {
2315                                         // Do not use `hasOwnProperty`, nothing will get copied
2316                                         if ( typeof i == "string" && i != "cssText" && !/\d/.test(i) && i.indexOf('webkit') !== 0 ) {
2317                                             // The try is for setter only properties
2318                                             try {
2319                                                 dest.style[i] = s[i];
2320                                                 // `fontSize` comes before `font` If `font` is empty, `fontSize` gets
2321                                                 // overwritten.  So make sure to reset this property. (hackyhackhack)
2322                                                 // Other properties may need similar treatment
2323                                                 if ( i == "font" ) {
2324                                                     dest.style.fontSize = s.fontSize;
2325                                                 }
2326                                             } catch (e) {}
2327                                         }
2328                                     }
2329                                 };
2330                                 
2331                                 function showCoachmark(targetElement) {
2332
2333                                         scope.currentCoachmark = targetElement;
2334                                         if(coachMarkElement !== undefined && coachMarkElement !==""){
2335                                                 coachMarkElement.removeClass('b2b-coachmark-label')
2336                                                 coackmarkContainer.remove();
2337                                                 coachmarkHighlight.remove();
2338                                         }
2339                                         coachMarkElement = angular.element(document.querySelector(targetElement.elementId));
2340                                         
2341                                         var     elementPosition = $position.offset(coachMarkElement);
2342                                         
2343                                         coachmarkHighlight = angular.element('<div class="b2b-coachmark-highlight"></div><div class="b2b-coachmark-highlight b2b-coachmark-highlight-mask"></div>');
2344                                         coachmarkHighlight.css({
2345                                                 'width': (elementPosition.width + 25) +'px',
2346                                                 'top': (elementPosition.top -10) + 'px',
2347                                                 'left': (elementPosition.left - 10) + 'px',
2348                                                 'height': (elementPosition.height + 20) +'px'
2349                                         });
2350                                         if(targetElement.cloneHtml){
2351                                                 var copy = coachMarkElement[0].cloneNode(true);
2352                                                 copy.id = "b2b-unique-"+targetElement.elementId.slice(1);
2353                                                 copyComputedStyle(coachMarkElement[0],copy);
2354                                                 var copychildNodes = copy.childNodes;
2355                                                 var coachmarkChildNodes = coachMarkElement[0].childNodes;
2356                                                 for(i=0;i<copychildNodes.length;i++){
2357                                                         if(copychildNodes[i].nodeType === '3'){
2358                                                                 copyComputedStyle(coachmarkChildNodes[i],copychildNodes[i])
2359                                                         }
2360                                                 }
2361                                                 coachmarkHighlight[0].appendChild(copy); // IE11 only supports appendChild, not append
2362                                         }else{
2363                                                 coachMarkElement.addClass('b2b-coachmark-label');
2364                                         }
2365                                         
2366                                         body.append(coachmarkHighlight);
2367                                         
2368                                         scope.coackmarkElPos.top = (elementPosition.top + elementPosition.height + 32) + 'px';
2369                                         scope.coackmarkElPos.left = (elementPosition.left - 158 + elementPosition.width / 2 ) + 'px';
2370                                         coackmarkJqContainer = angular.element('<div b2b-coachmark-container b2b-trap-focus-inside-element="true"></div>');
2371                                         coackmarkContainer = $compile(coackmarkJqContainer)(scope);
2372                                         body.append(coackmarkContainer);
2373                                         
2374                                         
2375                                         $timeout(function () {
2376                                                 var currentCoachmarkContainer = document.getElementsByClassName('b2b-coachmark-container')[0];
2377                                                 currentCoachmarkContainer.focus();
2378
2379                                                 newElem = angular.element(currentCoachmarkContainer);
2380                                                 newElem.bind('keydown', function (e) {
2381                                                         if(e.keyCode == keymap.KEY.TAB){
2382                                                             if(e.shiftKey) {
2383                                                                 if(e.target.className === 'b2b-coachmark-container'){
2384                                                                         e.preventDefault();
2385                                                                         e.stopPropagation();
2386                                                                     }
2387                                                             }
2388                                                         }
2389                                                 });
2390                                                 var coachmarkHeight = window.getComputedStyle(currentCoachmarkContainer).height.split('px')[0];
2391                                                 var newOffsetHeight = Math.round(elementPosition.top) - elementPosition.height;
2392                                                 
2393                                                 // We need a slight offset to show the lightboxed item
2394                                                 if(!targetElement.cloneHtml){
2395                                                         TweenLite.to(window, 2, {scrollTo:{x: (scope.coackmarkElPos.left.split('px')[0] - 100), y: newOffsetHeight-200}});
2396                                                 }
2397                                         }, 200);
2398                                 }
2399                                 
2400                                 element.bind('click', function (e) {
2401                                         initaitedCoachmark = true;
2402                     
2403                                         scope.$watch('coachmarkIndex', function () {
2404                                                 if(initaitedCoachmark === true){
2405                             if(scope.coachmarkIndex === -1){
2406                                                                 scope.closeCoachmark();
2407                                                         }else{
2408                                 findAvailableCoachmark(scope.coachmarkIndex);
2409                                 showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2410                                                         }
2411                                                 }
2412                                         });
2413                                         coachmarkBackdrop();
2414                     var findAvailableCoachmark = function(index){
2415                         if(index === -1){
2416                                 scope.coachmarkIndex = 0;
2417                         } else if(!angular.isDefined(scope.coachmarks[index]) || angular.element(document.querySelector(scope.coachmarks[index].elementId)).length === 0){
2418                             findAvailableCoachmark(index-1);
2419                         } else {
2420                             scope.coachmarkIndex = index;
2421                         }
2422                     }
2423                                         if (angular.isFunction(scope.startCoachmarkCallback)){
2424                                                 scope.startCoachmarkCallback(); 
2425                                         }
2426                     findAvailableCoachmark(scope.coachmarkIndex);
2427                     showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2428                     
2429                                         $document.bind('keydown', function (evt) {
2430                                                 if (evt.which === 27 && initaitedCoachmark) {
2431                                                         scope.closeCoachmark();
2432                                                         scope.$apply(); 
2433                                                 }
2434                                         });
2435                                 });
2436                                 //performance technique to ensure scroll event doesn't cause lag
2437                                 var throttle = function(type, name, obj) {
2438                                 obj = obj || window; 
2439                                 var running = false; 
2440                                 var func = function() { 
2441                                     if (running) { return; } 
2442                                     running = true; 
2443                                      requestAnimationFrame(function() { 
2444                                         obj.dispatchEvent(new CustomEvent(name)); 
2445                                         running = false; 
2446                                     }); 
2447                                 }; 
2448                                 obj.addEventListener(type, func); 
2449                             };
2450                          
2451                             scope.viewportWidth = b2bViewport.viewportWidth(); 
2452                             /* init - you can init any event */ 
2453                             throttle("resize", "optimizedResize"); 
2454                             window.addEventListener("optimizedResize", function() {
2455                                 if(initaitedCoachmark){
2456                         showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2457                                         scope.viewportWidth = b2bViewport.viewportWidth(); 
2458                                         scope.$digest();
2459                                 }
2460                             });
2461             }
2462         };
2463     }])
2464         .directive('b2bCoachmarkContainer', ['$document', '$position', function($document, $position) {
2465                 return {
2466                         restrict: 'A',
2467                         transclude: true,
2468             replace: true,
2469                         templateUrl: 'b2bTemplate/coachmark/coachmark.html',
2470                         link: function (scope, element, attrs, ctrl) {
2471                                                         
2472                         }
2473                 };      
2474         }]);
2475         
2476
2477 /** 
2478  * @ngdoc directive 
2479  * @name Template.att:Configuration Section 
2480  * 
2481  * @description 
2482  *  <file src="src/configurationSection/docs/readme.md" /> 
2483  * 
2484  * @example 
2485  *  <section id="code"> 
2486         <b>HTML + AngularJS</b> 
2487         <example module="b2b.att"> 
2488             <file src="src/configurationSection/docs/demo.html" /> 
2489             <file src="src/configurationSection/docs/demo.js" /> 
2490        </example> 
2491     </section>    
2492  * 
2493  */
2494 angular.module('b2b.att.configurationSection', [])
2495   
2496 /** 
2497  * @ngdoc directive 
2498  * @name Template.att:Directory Listing 
2499  * 
2500  * @description 
2501  *  <file src="src/directoryListingTemplate/docs/readme.md" /> 
2502  * 
2503  * @example 
2504  *  <section id="code"> 
2505         <b>HTML + AngularJS</b> 
2506         <example module="b2b.att"> 
2507             <file src="src/directoryListingTemplate/docs/demo.html" /> 
2508             <file src="src/directoryListingTemplate/docs/demo.js" /> 
2509        </example> 
2510     </section>    
2511  * 
2512  */
2513 angular.module('b2b.att.directoryListingTemplate', [])
2514   
2515 /**
2516  * @ngdoc directive
2517  * @name Forms.att:dropdowns
2518  *
2519  * @description
2520  *  <file src="src/dropdowns/docs/readme.md" />
2521  * @usage
2522  *
2523  * @example
2524    <section id="code">
2525     <example module="b2b.att">
2526      <file src="src/dropdowns/docs/demo.html" />
2527      <file src="src/dropdowns/docs/demo.js" />
2528     </example>
2529    </section>
2530  */
2531 angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'ngSanitize'])
2532
2533 .constant('b2bDropdownConfig', {
2534     prev: '37,38',
2535     next: '39,40',
2536     menuKeyword: 'menu',
2537     linkMenuKeyword: 'link-menu',
2538     largeKeyword: 'large',
2539     smallKeyword: 'small'
2540 })  
2541
2542 .directive("b2bDropdown", ['$timeout', '$compile', '$templateCache', 'b2bUserAgent', 'b2bDropdownConfig', '$position', function ($timeout, $compile, $templateCache, b2bUserAgent, b2bDropdownConfig, $position) {
2543     return {
2544         restrict: 'A',
2545         scope: true,
2546         require: 'ngModel',
2547         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2548             scope.isInputDropdown = true;
2549             scope.placeHoldertext = attr.placeholderText;
2550             if (attr.type) {
2551                 if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1 || attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
2552                     scope.isInputDropdown = false;
2553                     if (attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
2554                         scope.dropdownType = b2bDropdownConfig.linkMenuKeyword;
2555                     } else if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1) {
2556                         scope.dropdownType = b2bDropdownConfig.menuKeyword;
2557                     }
2558                 }
2559                 if (attr.type.indexOf(b2bDropdownConfig.largeKeyword) > -1) {
2560                     scope.dropdownSize = b2bDropdownConfig.largeKeyword;
2561                 } else if (attr.type.indexOf(b2bDropdownConfig.smallKeyword) > -1) {
2562                     scope.dropdownSize = b2bDropdownConfig.smallKeyword;
2563                 }
2564             }
2565
2566             scope.labelText = attr.labelText;
2567
2568             scope.setBlur = function () {
2569                 scope.setTouched();
2570             };
2571
2572             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2573                 var formCtrl = elem.controller('form');
2574                 scope.setNgModelController = function (name, ngModelCtrl) {
2575                     if (name && formCtrl && ngModelCtrl) {
2576                         formCtrl[name] = ngModelCtrl;
2577                     }
2578                 };
2579                 scope.setOptionalCta = function (optionalCta) {
2580                     scope.optionalCta = optionalCta;
2581                 };
2582                 var innerHtml = angular.element('<div></div>').append(elem.html());
2583                 innerHtml = ($compile(innerHtml)(scope)).html();
2584                 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownDesktop.html'));
2585                 template.find('ul').eq(0).append(innerHtml);
2586                 template = $compile(template)(scope);
2587                 elem.replaceWith(template);
2588             } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2589                 elem.css({
2590                     'opacity': '0',
2591                     'filter': 'alpha(opacity=0)'
2592                 });
2593                 elem.addClass('awd-select isWrapped');
2594                 elem.wrap('<span class="selectWrap"></span>');
2595                 var cover = angular.element('<span aria-hidden="true"><i class="icon-primary-down" aria-hidden="true"></i></span>');
2596                 elem.parent().append(cover);
2597                 elem.parent().append('<i class="icon-primary-down" aria-hidden="true"></i>');
2598                 var set = function () {
2599                     var sel = elem[0] ? elem[0] : elem;
2600                     var selectedText = "";
2601                     var selIndex = sel.selectedIndex;
2602                     if (typeof selIndex !== 'undefined') {
2603                         selectedText = sel.options[selIndex].text;
2604                     }
2605                     cover.text(selectedText).append('<i class="icon-primary-down" aria-hidden="true"></i>');
2606                 };
2607                 var update = function (value) {
2608                     $timeout(set, 100);
2609                 };
2610
2611                 if (attr.ngModel) {
2612                     scope.$watch(attr.ngModel, function (newVal, oldVal) {
2613                         update();
2614                     });
2615                 }
2616                 elem.bind('keyup', function (ev) {
2617                     if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2618                         return;
2619                     }
2620                     set();
2621                 });
2622             }
2623
2624         }],  
2625         link: function (scope, elem, attr, ctrl) {
2626             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2627                 scope.updateModel = function () {
2628                     ctrl.$setViewValue(scope.currentSelected.value);
2629                     if (scope.dropdownRequired && scope.currentSelected.value === '') {
2630                         scope.setRequired(false);
2631                     } else {
2632                         scope.setRequired(true);
2633                     }
2634
2635                     if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2636                         $timeout(function () {
2637                             scope.appendCaretPositionStyle();
2638                         }, 100);
2639                     }
2640                 };
2641                 ctrl.$render = function () {
2642
2643                 $timeout(function () {
2644
2645                         if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && (angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '')) {
2646                             scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2647                         } else if ((angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '') && ctrl.$viewValue !== '' ) {
2648                             scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2649                         } else if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && scope.placeHoldertext !== '' )  {
2650                             scope.currentSelected.text = scope.placeHoldertext;
2651                             ctrl.$setViewValue(scope.placeHoldertext);
2652                         } else {
2653                             scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2654                         }
2655
2656                     }, 100);
2657                 };
2658
2659                 scope.disabled = false;
2660                 scope.dropdownName = attr.name;
2661                 scope.dropdownId = attr.id;
2662                 scope.labelId = attr.ariaLabelledby;
2663                 scope.dropdownDescribedBy = attr.ariaDescribedby;
2664                 if (attr.required) {
2665                     scope.dropdownRequired = true;
2666                 } else {
2667                     scope.dropdownRequired = false;
2668                 }
2669                 elem.removeAttr('name');
2670                 elem.removeAttr('id');
2671                 scope.$parent.$watch(attr.ngDisabled, function (val) {
2672                     scope.disabled = val;
2673                 });
2674             }
2675         }
2676     };
2677 }])
2678
2679 .directive("b2bDropdownToggle", ['$document', '$documentBind', '$isElement', 'b2bDropdownConfig', 'keymap', 'b2bUtilitiesConfig', '$timeout', '$position', function ($document, $documentBind, $isElement, b2bDropdownConfig, keymap, b2bUtilitiesConfig, $timeout, $position) {
2680     return {
2681         restrict: 'A',
2682         require: '?^b2bKey',
2683         link: function (scope, elem, attr, ctrl) {
2684             scope.appendCaretPositionStyle = function () {
2685                 while (document.querySelector('style.b2bDropdownCaret')) {
2686                     document.querySelector('style.b2bDropdownCaret').remove();
2687                 };
2688                 var caretPosition = $position.position(elem).width - 26;
2689                 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2690                     var template = angular.element('<style class="b2bDropdownCaret" type="text/css">.linkSelectorModule .active+.moduleWrapper:before {left: ' + caretPosition + 'px;}</style>');
2691                     $document.find('head').append(template);
2692                 }
2693             };
2694
2695             if (scope.isInputDropdown && (scope.labelText !== undefined)) {
2696                 elem.attr('aria-label', scope.labelText);
2697             }
2698
2699             scope.toggleFlag = false;
2700             scope.dropdownLists = {};
2701             scope.dropdownListValues = [];
2702             scope.dropdown = {
2703                 totalIndex: -1
2704             };
2705             scope.currentSelected = {
2706                 value: '',
2707                 text: '',
2708                 label: '',
2709                 index: -1
2710             };
2711             scope.dropdownTextList = [];
2712             var searchString = '';
2713
2714             scope.removeItem = function(value){
2715                 delete scope.dropdownLists[value];
2716                 var index = scope.dropdownListValues.indexOf(value);
2717                 scope.dropdownListValues.splice(index,1);
2718                 scope.dropdownTextList=[];
2719                 scope.dropdown.totalIndex = scope.dropdownListValues.length-1; 
2720             };
2721             var getDropdownText = function(){
2722                 var dropdownItems = elem.parent().find('ul').children();
2723                 var count = dropdownItems.length;
2724                 for(var i=0;i<count;i++){
2725                     scope.dropdownTextList.push(dropdownItems.eq(i).text());
2726                 }
2727             };
2728             var searchElement = function (searchExp) {
2729                  if(scope.dropdownTextList.length ==0){
2730                     getDropdownText ();
2731                 }
2732                 var regex = new RegExp("^" + searchExp, "i");
2733                 var position = scope.dropdownTextList.regexIndexOf(regex, scope.currentSelected.index + 1, true);
2734                 if (position > -1) {
2735                     return position;
2736                 }
2737                 return undefined;
2738             };
2739             var startTimer = function (time) {
2740                 if (searchString === '') {
2741                     $timeout(function () {
2742                         searchString = '';
2743                     }, time);
2744                 }
2745             };
2746             scope.toggleDropdown = function (toggleFlag) {
2747                 if (!scope.disabled) {
2748                     if (angular.isDefined(toggleFlag)) {
2749                         scope.toggleFlag = toggleFlag;
2750                     } else {
2751                         scope.toggleFlag = !scope.toggleFlag;
2752                     }
2753                     if (!scope.toggleFlag) {
2754                         if (scope.isInputDropdown) {
2755                             elem.parent().find('input')[0].focus();
2756                         } else {
2757                             elem.parent().find('button')[0].focus();
2758                         }
2759                     } else {
2760                         scope.dropdown.highlightedValue = scope.currentSelected.value;
2761                         if (ctrl && ctrl.enableSearch) {
2762                             if (angular.isDefined(scope.dropdownLists[scope.currentSelected.value])) {
2763                                 ctrl.resetCounter(scope.dropdownLists[scope.currentSelected.value][2]);
2764                             }
2765                         }
2766                         $timeout(function () {
2767                             if(scope.dropdownLists[scope.currentSelected.value] !== undefined){
2768                                 (scope.dropdownLists[scope.currentSelected.value][1])[0].focus();
2769                             } else {
2770                                 if (scope.isInputDropdown) {
2771                                     elem.parent().find('input')[0].focus();
2772                                 } else {
2773                                     elem.parent().find('button')[0].focus();
2774                                 }
2775                             }
2776                         }, 100);
2777                         if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2778                             scope.appendCaretPositionStyle();
2779                         }
2780                     }
2781                 }
2782             };
2783
2784             elem.bind('keydown', function (ev) {
2785                 if (!ev.keyCode) {
2786                     if (ev.which) {
2787                         ev.keyCode = ev.which;
2788                     } else if (ev.charCode) {
2789                         ev.keyCode = ev.charCode;
2790                     }
2791                 }
2792                 if (!scope.toggleFlag) {
2793                     if (ev.keyCode) {
2794                         var currentIndex = scope.currentSelected.index;
2795                         if (ev.keyCode === keymap.KEY.DOWN) {
2796                             scope.toggleDropdown(true);
2797                             ev.preventDefault();
2798                             ev.stopPropagation();
2799                         } else if (b2bDropdownConfig.prev.split(',').indexOf(ev.keyCode.toString()) > -1) {
2800                             angular.isDefined(scope.dropdownListValues[currentIndex - 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex - 1]][0].updateDropdownValue() : angular.noop();
2801                             ev.preventDefault();
2802                             ev.stopPropagation();
2803                         } else if (b2bDropdownConfig.next.split(',').indexOf(ev.keyCode.toString()) > -1) {
2804                             angular.isDefined(scope.dropdownListValues[currentIndex + 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex + 1]][0].updateDropdownValue() : angular.noop();
2805                             ev.preventDefault();
2806                             ev.stopPropagation();
2807                         } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
2808                             startTimer(b2bUtilitiesConfig.searchTimer);
2809                             searchString = searchString + (keymap.MAP[ev.keyCode] || '');
2810                             var position = searchElement(searchString);
2811                             angular.isDefined(scope.dropdownListValues[position]) ? scope.dropdownLists[scope.dropdownListValues[position]][0].updateDropdownValue() : angular.noop();
2812                             ev.preventDefault();
2813                             ev.stopPropagation();
2814                         }
2815                     }
2816                 } else {
2817                     if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
2818                         scope.toggleDropdown(false);
2819                         ev.preventDefault();
2820                         ev.stopPropagation();
2821                     } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2822                         scope.toggleDropdown(false);
2823                         ev.preventDefault();
2824                         ev.stopPropagation();
2825                     }
2826                 }
2827                 scope.$apply(); // TODO: Move this into each block to avoid expensive digest cycles
2828             });
2829             var outsideClick = function (e) {
2830                 var isElement = $isElement(angular.element(e.target), elem.parent(), $document);
2831                 if (!isElement) {
2832                     scope.toggleDropdown(false);
2833                     scope.$apply();
2834                 }
2835             };
2836             $documentBind.click('toggleFlag', outsideClick, scope);
2837             $documentBind.touch('toggleFlag', outsideClick, scope);
2838         }
2839     };
2840 }])
2841
2842 .directive("b2bDropdownGroup", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
2843     return {
2844         restrict: 'A',
2845         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2846             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2847                 var innerHtml = angular.element('<div></div>').append(elem.html());
2848                 innerHtml = ($compile(innerHtml)(scope)).html();
2849                 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html'));
2850                 template.attr('ng-repeat', attr.optGroupRepeat);
2851                 template.attr('label', elem.attr('label'));
2852                 template.find('ul').append(innerHtml);
2853                 elem.replaceWith(template);
2854             } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2855                 var template = angular.element(elem.prop('outerHTML'));
2856                 template.attr('ng-repeat', attr.optGroupRepeat);
2857                 template.removeAttr('b2b-dropdown-group');
2858                 template.removeAttr('opt-group-repeat');
2859                 template = $compile(template)(scope);
2860                 elem.replaceWith(template);
2861             }
2862         }]
2863     };
2864 }])
2865
2866 .directive("b2bDropdownGroupDesktop", [function () {
2867     return {
2868         restrict: 'A',
2869         scope: true,
2870         link: function (scope, elem, attr, ctrl) {
2871             scope.groupHeader = attr.label;
2872         }
2873     };
2874 }])
2875
2876 .directive("b2bDropdownList", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
2877     return {
2878         restrict: 'A',
2879         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {           
2880             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2881                 var innerHtml = angular.element('<div></div>').append(elem.html());
2882                 innerHtml = ($compile(innerHtml)(scope)).html();
2883                 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownListDesktop.html'));
2884                 template.attr('ng-repeat', attr.optionRepeat);
2885                 template.attr('value', elem.attr('value'));
2886                 template.attr('search-key', elem.text());
2887                 if (elem.attr('aria-describedby')){
2888                     template.attr('aria-describedby', attr.ariaDescribedby);
2889                 }
2890                 if (elem.attr('imgsrc')) {
2891                     if (elem.attr('imgalt')) {
2892                         template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt="' + elem.attr('imgalt') + '"/>');
2893                     } else {
2894                         template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt=""/>');
2895                     }
2896                 }
2897                 template.append(innerHtml);
2898                 elem.replaceWith(template);
2899             } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2900                 var template = angular.element(elem.prop('outerHTML'));
2901                 template.attr('ng-repeat', attr.optionRepeat);
2902                 if (elem.attr('aria-describedby')){
2903                     template.attr('aria-describedby', attr.ariaDescribedby);
2904                 }
2905                 template.removeAttr('b2b-dropdown-list');
2906                 template.removeAttr('option-repeat');
2907                 template = $compile(template)(scope);
2908                 elem.replaceWith(template);
2909             }
2910         }]
2911     };
2912 }])
2913
2914 .directive("b2bDropdownListDesktop", ['$sce', 'keymap', 'b2bDropdownConfig', function ($sce, keymap, b2bDropdownConfig) {
2915     return {
2916         restrict: 'A',
2917         scope: true,
2918
2919         link: function (scope, elem, attr, ctrl) {
2920             var dropdownListValue = scope.dropdownListValue = attr.value;
2921             scope.dropdown.totalIndex++;
2922             var dropdownListIndex = scope.dropdown.totalIndex;
2923             scope.dropdownListValues.push(dropdownListValue);
2924             scope.dropdownLists[dropdownListValue] = [];
2925             scope.dropdownLists[dropdownListValue][0] = scope;
2926             scope.dropdownLists[dropdownListValue][1] = elem;
2927             scope.dropdownLists[dropdownListValue][2] = dropdownListIndex;
2928             scope.$parent.$parent.dropdownTextList=[];                                
2929             scope.updateDropdownValue = function () {
2930                 scope.currentSelected.value = dropdownListValue;
2931                 if (scope.isInputDropdown) {
2932                     scope.currentSelected.text = elem.text();
2933                     scope.currentSelected.label = elem.text();
2934                 } else if ((scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) || (scope.dropdownType === b2bDropdownConfig.menuKeyword && scope.dropdownSize === b2bDropdownConfig.smallKeyword)) {
2935                     scope.currentSelected.text = dropdownListValue;
2936                     scope.currentSelected.label = dropdownListValue;
2937                 } else if (scope.dropdownType === b2bDropdownConfig.menuKeyword) {
2938                     scope.currentSelected.text = $sce.trustAsHtml(elem.html());
2939                     scope.currentSelected.label = elem.text();
2940                 }
2941                 scope.currentSelected.index = dropdownListIndex;
2942                 scope.updateModel();
2943             };
2944             scope.selectDropdownItem = function () {
2945                 scope.setDirty();
2946                 scope.updateDropdownValue();
2947                 scope.toggleDropdown(false);
2948             };
2949             scope.highlightDropdown = function () {
2950                 scope.dropdown.highlightedValue = dropdownListValue;
2951             };
2952             elem.bind('mouseover', function (ev) {
2953                 elem[0].focus();
2954             });
2955
2956             elem.bind('keydown', function (ev) {
2957                 if (!ev.keyCode) {
2958                     if (ev.which) {
2959                         ev.keyCode = ev.which;
2960                     } else if (ev.charCode) {
2961                         ev.keyCode = ev.charCode;
2962                     }
2963                 }
2964                 if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
2965                     scope.toggleDropdown(false);
2966                     ev.preventDefault();
2967                     ev.stopPropagation();
2968                 } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2969                     scope.toggleDropdown(false);
2970                     ev.preventDefault();
2971                     ev.stopPropagation();
2972                 }
2973                 scope.$apply();
2974             });
2975             scope.$on('$destroy',function(){
2976                 scope.removeItem(dropdownListValue);
2977             });
2978         }
2979     };
2980 }])
2981
2982 .directive("b2bDropdownRepeat", ['$compile', 'b2bUserAgent', function ($compile, b2bUserAgent) {
2983     return {
2984         restrict: 'A',
2985         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2986             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2987                 var innerHtml = angular.element('<div></div>').append(elem.html());
2988                 innerHtml = ($compile(innerHtml)(scope)).html();
2989                 var template = angular.element('<div></div>');
2990                 template.attr('ng-repeat', attr.b2bDropdownRepeat);
2991                 template.append(innerHtml);
2992                 elem.replaceWith(template);
2993             } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2994                 angular.noop();
2995             }
2996         }]
2997     };
2998 }])
2999
3000 .directive("b2bDropdownValidation", ['$timeout', function ($timeout) {
3001     return {
3002         restrict: 'A',
3003         require: 'ngModel',
3004         link: function (scope, elem, attr, ctrl) {
3005             $timeout(function () {
3006                 scope.setNgModelController(attr.name, ctrl);
3007             }, 100);
3008             scope.setDirty = function () {
3009                 if (ctrl.$dirty === false) {
3010                     ctrl.$dirty = true;
3011                     ctrl.$pristine = false;
3012                 }
3013             };
3014             scope.setTouched = function () {
3015                 if (ctrl.$touched === false) {
3016                     ctrl.$touched = true;
3017                     ctrl.$pristine = false;
3018                 }
3019             };
3020             scope.setRequired = function (flag) {
3021                 ctrl.$setValidity('required', flag);
3022             };
3023         }
3024     };
3025 }])
3026
3027 .directive('b2bDropdownOptionalCta', [function () {
3028     return {
3029         restrict: 'EA',
3030         transclude: true,
3031         replace: true,
3032         template: '',
3033         compile: function (elem, attr, transclude) {
3034             return function link(scope, elem, attr, ctrl) {
3035                 if (scope.setOptionalCta) {
3036                     scope.setOptionalCta(transclude(scope, function () {}));
3037                 }
3038                 elem.remove();
3039             };
3040         }
3041     };
3042 }]);
3043 /**
3044  * @ngdoc directive
3045  * @name Forms.att:File Upload
3046  *
3047  * @description
3048  *  <file src="src/fileUpload/docs/readme.md" />
3049  *
3050  * @usage
3051  * 
3052 <form id="dragDropFile">                
3053     <div b2b-file-drop file-model="fileModel" on-drop="triggerFileUpload()"  align="center">
3054         <p>
3055             <br>To upload a file, drag & drop it here or 
3056                         <span b2b-file-link file-model="fileModel" on-file-select="triggerFileUpload()" >
3057                                 click here to select from your computer.
3058                         </span><br>
3059         </p>
3060     </div>
3061 </form>
3062  *
3063  * @example
3064  *  <section id="code">
3065         <example module="b2b.att">
3066             <file src="src/fileUpload/docs/demo.html" />
3067             <file src="src/fileUpload/docs/demo.js" />
3068        </example>
3069     </section>
3070  *
3071  */
3072 angular.module('b2b.att.fileUpload', ['b2b.att.utilities'])
3073     .directive('b2bFileDrop', [function() {
3074             return {
3075                 restrict: 'EA',
3076                 scope: {
3077                     fileModel: '=',
3078                     onDrop: '&'
3079                 },
3080                                 controller: ['$scope', '$attrs', function($scope, $attrs) {
3081                     this.onDrop = $scope.onDrop;
3082                 }],
3083                 link: function(scope, element) {
3084                     element.addClass('b2b-dragdrop');
3085                     element.bind(
3086                         'dragover',
3087                         function(e) {
3088                             if (e.originalEvent) {
3089                                 e.dataTransfer = e.originalEvent.dataTransfer;
3090                             }
3091                             e.dataTransfer.dropEffect = 'move';
3092                             // allows us to drop
3093                             if (e.preventDefault) {
3094                                 e.preventDefault();
3095                             }
3096                             element.addClass('b2b-dragdrop-over');
3097                             return false;
3098                         }
3099                     );
3100                     element.bind(
3101                         'dragenter',
3102                         function(e) {
3103                             // allows us to drop
3104                             if (e.preventDefault) {
3105                                 e.preventDefault();
3106                             }
3107                             element.addClass('b2b-dragdrop-over');
3108                             return false;
3109                         }
3110                     );
3111                     element.bind(
3112                         'dragleave',
3113                         function() {
3114                             element.removeClass('b2b-dragdrop-over');
3115                             return false;
3116                         }
3117                     );
3118                     element.bind(
3119                         'drop',
3120                         function(e) {
3121                             // Stops some browsers from redirecting.
3122                             if (e.preventDefault) {
3123                                 e.preventDefault();
3124                             }
3125                             if (e.stopPropagation) {
3126                                 e.stopPropagation();
3127                             }
3128                             if (e.originalEvent) {
3129                                 e.dataTransfer = e.originalEvent.dataTransfer;
3130                             }
3131                             element.removeClass('b2b-dragdrop-over');
3132                             if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
3133                                 scope.fileModel = e.dataTransfer.files[0];
3134                                 scope.$apply();
3135                                 if (angular.isFunction(scope.onDrop)) {
3136                                     scope.onDrop();
3137                                 }
3138                             }
3139                             return false;
3140                         }
3141                     );
3142                 }
3143             };
3144         }])
3145         .directive('b2bFileLink', [function() {
3146             return {
3147                 restrict: 'EA',
3148                 require: '^?b2bFileDrop',
3149                 replace: true,
3150                 transclude: true,
3151                 templateUrl: 'b2bTemplate/fileUpload/fileUpload.html',
3152                 scope: {
3153                     fileModel: '=?',
3154                     onFileSelect: '&'
3155                 },
3156                 controller: ['$scope', function($scope) {
3157                     this.setFileModel = function(fileModel) {
3158                         if ($scope.takeFileModelFromParent) {
3159                             $scope.$parent.fileModel = fileModel;
3160                             $scope.$parent.$apply();
3161                         } else {
3162                             $scope.fileModel = fileModel;
3163                             $scope.$apply();
3164                         }
3165                     };
3166                     this.callbackFunction = function() {
3167                         if (angular.isFunction($scope.onFileSelect)) {
3168                             $scope.onFileSelect();
3169                         }
3170                     };
3171                 
3172                 }],
3173                 link: function(scope, element, attr, b2bFileDropCtrl) {
3174                     scope.takeFileModelFromParent = false;
3175                     if (!(attr.fileModel) && b2bFileDropCtrl) {
3176                         scope.takeFileModelFromParent = true;
3177                     }
3178                     if (!(attr.onFileSelect) && b2bFileDropCtrl) {
3179                         scope.onFileSelect = b2bFileDropCtrl.onDrop;
3180                     }
3181                 }
3182             };
3183         }])
3184         .directive('b2bFileChange', ['$log', '$rootScope', function($log, $rootScope) {
3185             return {
3186                 restrict: 'A',
3187                 require: '^b2bFileLink',
3188                 link: function(scope, element, attr, b2bFileLinkCtrl) {
3189                     element.bind('change', changeFileModel);
3190
3191                     function changeFileModel(e) {
3192                         if (e.target.files && e.target.files.length > 0) {
3193                             b2bFileLinkCtrl.setFileModel(e.target.files[0]);
3194                             b2bFileLinkCtrl.callbackFunction();
3195                         } else {
3196                             var strFileName = e.target.value;
3197                             try {
3198                                 var objFSO = new ActiveXObject("Scripting.FileSystemObject");
3199                                 b2bFileLinkCtrl.setFileModel(objFSO.getFile(strFileName));
3200                                 b2bFileLinkCtrl.callbackFunction();
3201                             } catch (e) {
3202                                 var errMsg = "There was an issue uploading " + strFileName + ". Please try again.";
3203                                 $log.error(errMsg);
3204                                 $rootScope.$broadcast('b2b-file-link-failure', errMsg);
3205                             }
3206                         }
3207                     }
3208                 }
3209             };
3210         }]);
3211 /**
3212  * @ngdoc directive
3213  * @name Navigation.att:filters
3214  *
3215  * @description
3216  *  <file src="src/filters/docs/readme.md" />
3217  *
3218  * @usage
3219  *  <div b2b-filters></div>
3220  *
3221  * @example
3222  *      <section id="code">
3223            <b>HTML + AngularJS</b>
3224             <example module="b2b.att">
3225             <file src="src/filters/docs/demo.html" />
3226             <file src="src/filters/docs/demo.js" />
3227        </example>
3228         </section>
3229  * 
3230  */
3231 angular.module('b2b.att.filters', ['b2b.att.utilities', 'b2b.att.multipurposeExpander'])
3232     .filter('filtersSelectedItemsFilter', [function () {
3233         return function (listOfItemsArray) {
3234
3235             if (!listOfItemsArray) {
3236                 listOfItemsArray = [];
3237             }
3238
3239             var returnArray = [];
3240
3241             for (var i = 0; i < listOfItemsArray.length; i++) {
3242                 for (var j = 0; j < listOfItemsArray[i].filterTypeItems.length; j++) {
3243                     if (listOfItemsArray[i].filterTypeItems[j].selected && !listOfItemsArray[i].filterTypeItems[j].inProgress) {
3244                         returnArray.push(listOfItemsArray[i].filterTypeItems[j]);
3245                     }
3246                 }
3247             }
3248
3249             return returnArray;
3250         };
3251     }]);
3252 /**
3253  * @ngdoc directive
3254  * @name Messages, modals & alerts.att:flyout
3255  *
3256  * @description
3257  *  <file src="src/flyout/docs/readme.md" />
3258  * @example
3259  *  <section id="code">
3260         <example module="b2b.att">
3261             <file src="src/flyout/docs/demo.html" />
3262             <file src="src/flyout/docs/demo.js" />
3263        </example>
3264     </section>
3265  *
3266  */
3267 angular.module('b2b.att.flyout', ['b2b.att.utilities', 'b2b.att.position'])
3268     .directive('b2bFlyout', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
3269         return {
3270             restrict: 'EA',
3271             transclude: true,
3272             templateUrl: 'b2bTemplate/flyout/flyout.html',
3273             controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
3274                 scope.flyoutOpened = false;
3275                 var contentScope = '';
3276                 var togglerScope = '';
3277                 this.registerContentScope = function (scp) {
3278                     contentScope = scp;
3279                 };
3280                 this.registerTogglerScope = function (scp) {
3281                     togglerScope = scp;
3282                 };
3283
3284                 this.toggleFlyoutState = function () {
3285                     if (contentScope) {
3286                         contentScope.toggleFlyout();
3287                     }
3288                 };
3289                 this.getTogglerDimensions = function () {
3290                     return togglerScope.getTogglerDimensions();
3291                 }
3292                 this.setTogglerFocus = function () {
3293                     return togglerScope.setTogglerFocus();
3294                 }
3295
3296                 this.closeFlyout = function (e) {
3297                     contentScope.closeFromChild(e);
3298                 };
3299                 this.gotFocus = function () {
3300                     contentScope.gotFocus();
3301                 };
3302
3303                 this.updateAriaModel = function (val) {
3304                     scope.flyoutOpened = val;
3305                 };
3306
3307                 var firstTabableElement = undefined,
3308                     lastTabableElement = undefined;
3309
3310                 var firstTabableElementKeyhandler = function (e) {
3311                     if (!e.keyCode) {
3312                         e.keyCode = e.which;
3313                     }
3314                     if (e.keyCode === keymap.KEY.TAB && e.shiftKey && scope.flyoutOpened) { 
3315                         contentScope.gotFocus();
3316                         events.preventDefault(e);
3317                         events.stopPropagation(e);
3318                     }
3319                 };
3320
3321                 var lastTabableElementKeyhandler = function (e) {
3322                     if (!e.keyCode) {
3323                         e.keyCode = e.which;
3324                     }
3325                     if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
3326                         contentScope.gotFocus();    
3327                         events.preventDefault(e);
3328                         events.stopPropagation(e);
3329                     }
3330                 };
3331                 this.associateTabEvent = function(){
3332                     $timeout(function () {
3333                         var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
3334                         firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3335                         lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3336                         if(angular.isUndefined(firstTabableElement)){
3337                             angular.element(element).css('display','block');
3338                             firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3339                             lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3340                             angular.element(element).css('display','none');
3341                         }
3342                         angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
3343                         angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
3344                     });
3345                 }
3346                 this.updateTabbableElements = function(){
3347                     $timeout(function () {
3348                         var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
3349                         angular.element(element).css('display','block');
3350                         firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3351                         lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3352                         angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
3353                         angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
3354                         angular.element(element).css('display','none');
3355                     });
3356                 }
3357                 this.unbindTabbaleEvents = function(){
3358                     if(angular.isDefined(firstTabableElement)){
3359                         angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
3360                     }
3361
3362                     if(angular.isDefined(lastTabableElement)){
3363                         angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
3364                     }
3365                 }
3366             }],
3367             link: function (scope, element, attrs, ctrl) {
3368
3369             }
3370         };
3371     }])
3372     .directive('b2bFlyoutToggler', [function () {
3373         return {
3374             restrict: 'A',
3375             require: '^b2bFlyout',
3376             link: function (scope, element, attrs, ctrl) {
3377                 element.bind('click', function (e) {
3378                     ctrl.toggleFlyoutState();
3379                 });
3380
3381                 scope.getTogglerDimensions = function () {
3382                     return element[0].getBoundingClientRect();
3383                 }
3384
3385                 scope.setTogglerFocus = function () {
3386                     element[0].focus();
3387                 }
3388
3389                 ctrl.registerTogglerScope(scope);
3390             }
3391         };
3392     }])
3393     .directive('b2bFlyoutContent', ['$position', '$timeout', '$documentBind', '$isElement', '$document', function ($position, $timeout, $documentBind, $isElement, $document) {
3394         return {
3395             restrict: 'EA',
3396             transclude: true,
3397             replace: true,
3398             require: '^b2bFlyout',
3399             scope: {
3400                 horizontalPlacement: '@',
3401                 verticalPlacement: '@',
3402                 flyoutStyle: '@',
3403                 flyoutTitle: '@',
3404                 contentUpdated: "=?"
3405             },
3406             templateUrl: 'b2bTemplate/flyout/flyoutContent.html',
3407             link: function (scope, element, attrs, ctrl) {
3408                 var flyoutStyleArray, eachCssProperty, cssPropertyKey, cssPropertyVal, temp;
3409                 scope.openFlyout = false;
3410                 if (!scope.horizontalPlacement) {
3411                     scope.horizontalPlacement = 'center';
3412                 }
3413                 if (!scope.verticalPlacement) {
3414                     scope.verticalPlacement = 'below';
3415                 }
3416
3417                 scope.toggleFlyout = function () {
3418
3419                     scope.openFlyout = !scope.openFlyout;
3420
3421                     if (scope.openFlyout) {
3422
3423                         if (angular.isDefined(scope.flyoutStyle) && scope.flyoutStyle != "") {
3424                             flyoutStyleArray = scope.flyoutStyle.split(";");
3425                             for (i = 0; i < flyoutStyleArray.length; i++) {
3426                                 eachCssProperty = flyoutStyleArray[i].split(":");
3427                                 if (eachCssProperty.length == 2) {
3428                                     cssPropertyKey = eachCssProperty[0].trim();
3429                                     cssPropertyVal = eachCssProperty[1].trim();
3430                                     angular.element(element[0])[0].style[cssPropertyKey] = cssPropertyVal;
3431                                 }
3432                             }
3433                         }
3434
3435                         angular.element(element[0]).css({
3436                             'opacity': 0,
3437                             'display': 'block'
3438                         });
3439
3440                         var flyoutIcons = angular.element(document.querySelectorAll(".b2b-flyout-icon"));
3441                         angular.forEach(flyoutIcons, function (elm) {
3442                             angular.element(elm)[0].blur();
3443                         });
3444
3445                         $timeout(function () {
3446                             ctrl.setTogglerFocus();
3447
3448                             var togglerDimensions = ctrl.getTogglerDimensions();
3449                             var flyoutDimensions = element[0].getBoundingClientRect();
3450
3451                             switch (scope.horizontalPlacement) {
3452                             case "left":
3453                                 angular.element(element[0]).css({
3454                                     'left': ((togglerDimensions.width / 2) - 26) + 'px'
3455                                 });
3456                                 break;
3457                             case "right":
3458                                 angular.element(element[0]).css({
3459                                     'right': ((togglerDimensions.width / 2) - 23) + 'px'
3460                                 });
3461                                 break;  
3462
3463                             case "centerLeft":
3464                                 var marginLeft =  10-(flyoutDimensions.width)-20;
3465                                 angular.element(element[0]).css({
3466                                     'margin-left': marginLeft + 'px'
3467                                 });
3468                                 break;
3469                             case "centerRight":
3470                                 angular.element(element[0]).css({
3471                                     'left': ((togglerDimensions.width + 9 )) + 'px'
3472                                 });
3473                                 break;    
3474
3475                             default:
3476                                 var marginLeft = (togglerDimensions.width / 2) - (flyoutDimensions.width / 2) - 8;
3477                                 angular.element(element[0]).css({
3478                                     'margin-left': marginLeft + 'px'
3479                                 });
3480                             }
3481
3482                             switch (scope.verticalPlacement) {
3483                             case "above":
3484                                 angular.element(element[0]).css({
3485                                     'top': -(flyoutDimensions.height + 13) + 'px'
3486                                 });
3487                                 break;
3488                             case "centerLeft":
3489                                 angular.element(element[0]).css({
3490                                     'top': -((togglerDimensions.height-13))+ 'px'
3491                                 });
3492                                 break;
3493                             case "centerRight":
3494                                 angular.element(element[0]).css({
3495                                     'top': -(flyoutDimensions.height - 23)+ 'px'
3496                                 });
3497                                 break;                                    
3498                             default:
3499                                 angular.element(element[0]).css({
3500                                     'top': (togglerDimensions.height + 13) + 'px'
3501                                 });
3502                             }
3503
3504                             angular.element(element[0]).css({
3505                                 'opacity': 1
3506                             });
3507                         }, 100);
3508                     } else {
3509                         scope.hideFlyout();
3510                     }
3511                 };
3512
3513                 scope.gotFocus = function () {
3514                     scope.openFlyout = false;
3515                     scope.hideFlyout();
3516                     ctrl.setTogglerFocus();
3517                     scope.$apply();
3518                 };
3519
3520                 scope.closeFromChild = function (e) {
3521                     scope.openFlyout = false;
3522                     scope.hideFlyout();
3523                     ctrl.setTogglerFocus();
3524                     scope.$apply();
3525                 };
3526
3527                 scope.hideFlyout = function () {
3528                     angular.element(element[0]).css({
3529                         'opacity': 0,
3530                         'display': 'none'
3531                     });
3532                 };
3533
3534                 scope.closeFlyout = function (e) {
3535                     var isElement = $isElement(angular.element(e.target), element, $document);
3536                     if ((e.type === "keydown" && e.which === 27) || ((e.type === "click" || e.type==="touchend") && !isElement)) {
3537                         scope.openFlyout = false;
3538                         scope.hideFlyout();
3539                         ctrl.setTogglerFocus();
3540                         scope.$apply();
3541                     }
3542                 };
3543
3544                 scope.$watch('openFlyout', function () {
3545                     ctrl.updateAriaModel(scope.openFlyout);
3546                 });
3547
3548                 $documentBind.click('openFlyout', scope.closeFlyout, scope);
3549                 $documentBind.event('keydown', 'openFlyout', scope.closeFlyout, scope);
3550                 $documentBind.event('touchend', 'openFlyout', scope.closeFlyout, scope);
3551                 ctrl.registerContentScope(scope);
3552
3553                 if (angular.isDefined(scope.contentUpdated) && scope.contentUpdated !== null) {
3554                     scope.$watch('contentUpdated', function (newVal, oldVal) {
3555                         if(newVal){
3556                             if (newVal !== oldVal) {
3557                                 ctrl.unbindTabbaleEvents();
3558                                 ctrl.associateTabEvent();
3559                             }
3560                             scope.contentUpdated = false;
3561                         } 
3562                     });
3563                 }  
3564
3565             }
3566         };
3567     }])
3568     .directive('b2bCloseFlyout', [function () {
3569         return {
3570             restrict: 'A',
3571             require: '^b2bFlyout',
3572             scope: {
3573                 closeFlyout: '&'
3574             },
3575             link: function (scope, element, attrs, ctrl) {
3576                 element.bind('click touchstart', function (e) {
3577                     scope.closeFlyout(e);
3578                     ctrl.closeFlyout(e);
3579                 });
3580             }
3581         };
3582     }])
3583     .directive('b2bFlyoutTrapFocusInside', [function () {
3584         return {
3585             restrict: 'A',
3586             transclude: false,
3587             require: '^b2bFlyout',
3588             link: function (scope, elem, attr, ctrl) {
3589                 /* Before opening modal, find the focused element */
3590                 ctrl.updateTabbableElements();
3591             }
3592         };
3593     }]);
3594 /**
3595  * @ngdoc directive
3596  * @name Layouts.att:footer
3597  *
3598  * @description
3599  *  <file src="src/footer/docs/readme.md" />
3600  *
3601  * @usage
3602  * 
3603  <footer class="b2b-footer-wrapper" role="contentinfo" aria-label="footer">
3604         <div class="b2b-footer-container" b2b-column-switch-footer footer-link-items='footerItems'>
3605             <hr>
3606             <div class="divider-bottom-footer">
3607                 <div class="span2 dispalyInline">&nbsp;</div>
3608                 <div class="span6 dispalyInline">
3609                     <ul class="footer-nav-content">
3610                         <li><a href="Terms_of_use.html" title="Terms of use" id="foot0">Terms of use</a>|</li>
3611                         <li><a href="Privacy_policy.html" title="Privacy policy" id="foot1" class="active">Privacy policy</a>|</li>
3612                         <li><a href="Tollfree_directory_assistance.html" title="Tollfree directory assistance" id="foot2">Tollfree directory assistance</a>|</li>
3613                         <li><a href="compliance.html" title="Accessibility" id="foot3">Accessibility</a></li>
3614
3615                     </ul>
3616                     <p><a href="//www.att.com/gen/privacy-policy?pid=2587" target="_blank">© <span class="copyright">2016</span> AT&amp;T Intellectual Property</a>. All rights reserved. AT&amp;T,the AT&amp;T Globe logo and all other AT&amp;T marks contained herein are tardemarks of AT&amp;T intellectual property and/or AT&amp;T affiliated companines.
3617
3618                     </p>
3619                 </div>
3620                 <div class="span3 footerLogo dispalyInline">
3621                     <a href="index.html" class="footer-logo">
3622                         <i class="icon-primary-att-globe"><span class="hidden-spoken">A T &amp; T</span></i>
3623                         <h2 class="logo-title">AT&amp;T</h2>
3624                     </a>
3625                 </div>
3626             </div>
3627
3628         </div>  
3629     </footer>
3630
3631  * @example
3632  *  <section id="code">   
3633  <example module="b2b.att">
3634  <file src="src/footer/docs/demo.html" />
3635  <file src="src/footer/docs/demo.js" />
3636  </example>
3637  </section>
3638  *
3639  */
3640 angular.module('b2b.att.footer', ['b2b.att.utilities']).
3641         directive('b2bColumnSwitchFooter', [function() {
3642                 return {
3643                     restrict: 'A',
3644                     transclude: true,
3645                     scope: {
3646                         footerLinkItems: "="
3647                     },
3648                     templateUrl: 'b2bTemplate/footer/footer_column_switch_tpl.html',
3649                     link: function(scope) {
3650                                                 var tempFooterColumns = scope.footerLinkItems.length;
3651                                                 scope.footerColumns = 3;
3652                         if ( (tempFooterColumns === 5) || (tempFooterColumns === 4) ) {
3653                                                         scope.footerColumns = tempFooterColumns;
3654                         }
3655                     }
3656
3657                 };
3658
3659             }]);
3660      
3661
3662 /**
3663  * @ngdoc directive
3664  * @name Layouts.att:header
3665  *
3666  * @description
3667  *  <file src="src/header/docs/readme.md" />
3668  *
3669  * @usage
3670  *  <li b2b-header-menu class="header__item b2b-headermenu" ng-repeat="item in tabItems" role="presentation">
3671         <a href="#" class="menu__item" role="menuitem">{{item.title}}</a>
3672         <div class="header-secondary-wrapper">
3673             <ul class="header-secondary" role="menu">
3674                 <li class="header-subitem" b2b-header-submenu ng-repeat="i in item.subitems" role="presentation">
3675                     <a href="#" class="menu__item" aria-haspopup="true" role="menuitem">{{i.value}}</a>
3676                     <div class="header-tertiary-wrapper" ng-if="i.links">
3677                         <ul class="header-tertiary" role="menu">
3678                             <li b2b-header-tertiarymenu ng-repeat="link in i.links" role="presentation">
3679                                 <label>{{link.title}}</label>
3680                                 <div b2b-tertiary-link ng-repeat="title in link.value">
3681                                     <a href="{{link.href}}" class="header-tertiaryitem" ng-if="!title.subitems" aria-haspopup="false" role="menuitem"><span class="b2b-label-hide">{{link.title}}</span>{{title.title}}</a>
3682                                     <a href="{{link.href}}" class="header-tertiaryitem" b2b-header-togglemenu ng-if="title.subitems" aria-haspopup="true" role="menuitem"><span class="b2b-label-hide">{{link.title}}</span>{{title.title}}</a>
3683                                     <ul class="header-quarternary" role="menu"  ng-if="title.subitems">
3684                                         <li b2b-header-quarternarymenu role="presentation">
3685                                             <a href="{{nav.href}}" ng-repeat="nav in title.subitems" role="menuitem" aria-haspopup="true">{{nav.title}}</a>
3686                                         </li>
3687                                     </ul>
3688                                 </div>
3689                             </li>
3690                         </ul>
3691                     </div>
3692                 </li>
3693             </ul>
3694         </div>
3695     </li> 
3696  *
3697  * @example
3698  *  <section id="code">
3699  <example module="b2b.att.header">
3700  <file src="src/header/docs/demo.html" />
3701  <file src="src/header/docs/demo.js" />
3702  </example>
3703  </section>
3704  *
3705  */
3706 angular.module('b2b.att.header', ['b2b.att.dropdowns','b2b.att.utilities'])
3707         .directive('b2bHeaderMenu', ['keymap', '$documentBind', '$timeout', '$isElement', '$document', function (keymap, $documentBind, $timeout, $isElement, $document) {
3708         return {
3709             restrict: 'A',
3710             controller:['$scope',function($scope){
3711                 this.nextSiblingFocus = function (elObj,flag) {
3712                         if (elObj.nextElementSibling) {
3713                             if(flag){
3714                                 var nextmenuItem = this.getFirstElement(elObj.nextElementSibling,'a');
3715                                 nextmenuItem.focus();
3716                             }else{
3717                                 elObj.nextElementSibling.focus();
3718                             }
3719                         }
3720                 };
3721                 
3722                 this.previousSiblingFocus = function (elObj,flag) {
3723                         if (elObj.previousElementSibling) {
3724                             if(flag){
3725                                 var prevmenuItem = this.getFirstElement(elObj.previousElementSibling,'a');
3726                                 prevmenuItem.focus();
3727                             }else{
3728                                 elObj.previousElementSibling.focus();
3729                             }
3730                         }
3731                 };
3732                     
3733                 this.getFirstElement = function(elmObj,selector){
3734                         return elmObj.querySelector(selector);                        
3735                     };
3736             }],
3737             link: function (scope, elem,attr,ctrl) {
3738                 scope.showMenu = false;
3739                 var activeElm, subMenu, tertiaryMenu, el= angular.element(elem)[0], 
3740                         menuItem = angular.element(elem[0].children[0]);
3741                 menuItem.bind('click', function () {
3742                     elem.parent().children().removeClass('active');
3743                     elem.addClass('active');
3744                     var elems= this.parentElement.parentElement.querySelectorAll('li[b2b-header-menu]>a');
3745                     for (var i=0; i<elems.length; i++) {
3746                         elems[i].setAttribute("aria-expanded",false);
3747                     }
3748                     scope.showMenu = true;
3749                     var elmTofocus = ctrl.getFirstElement(this.parentElement,'li[b2b-header-submenu]');
3750                     elmTofocus.firstElementChild.focus();
3751                     this.setAttribute('aria-expanded',true);
3752                     scope.$apply();
3753                 });
3754                
3755                 elem.bind('keydown', function (evt) {
3756                     activeElm = document.activeElement;
3757                     subMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-submenu]');
3758                     tertiaryMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-tertiarymenu]');
3759                     switch (evt.keyCode) {
3760                         case keymap.KEY.ENTER:
3761                         case keymap.KEY.SPACE:
3762                             elem[0].click();
3763                             break;
3764                         case keymap.KEY.UP:
3765                             evt.stopPropagation();
3766                             evt.preventDefault();
3767                             if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3768                                 menuItem[0].focus();
3769                             }
3770                             break;
3771                         case keymap.KEY.DOWN:
3772                             evt.stopPropagation();
3773                             evt.preventDefault();
3774                             if (subMenu) {
3775                                 subMenu.firstElementChild.focus();
3776                             } else if (tertiaryMenu) {
3777                                 var firstSubitem = ctrl.getFirstElement(tertiaryMenu,'a.header-tertiaryitem');
3778                                 firstSubitem.focus();
3779                             }
3780                             break;
3781                         case keymap.KEY.RIGHT:
3782                             evt.stopPropagation();
3783                             evt.preventDefault();
3784                             if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3785                                 var elm = angular.element(activeElm.parentElement)[0];
3786                                 ctrl.nextSiblingFocus(elm,true);
3787                             } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
3788                                 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
3789                                 if (tertiaryLI.nextElementSibling) {
3790                                     var nextElm = ctrl.getFirstElement(tertiaryLI.nextElementSibling,"a.header-tertiaryitem");
3791                                     nextElm.focus();
3792                                 }
3793                             }
3794                             else if(activeElm.parentElement.hasAttribute('b2b-header-menu')){
3795                                 ctrl.nextSiblingFocus(el,true);
3796                             }
3797                             break;
3798                         case keymap.KEY.LEFT:
3799                             evt.stopPropagation();
3800                             evt.preventDefault();
3801                             if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3802                                 var previousElm = angular.element(activeElm.parentElement)[0];
3803                                 ctrl.previousSiblingFocus(previousElm,true);
3804                             } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
3805                                 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
3806                                 if (tertiaryLI.previousElementSibling) {
3807                                     var prevElm = ctrl.getFirstElement(tertiaryLI.previousElementSibling,"a.header-tertiaryitem");
3808                                     prevElm.focus();
3809                                 }
3810                             }
3811                             else if(activeElm.parentElement.hasAttribute('b2b-header-menu')) {
3812                                 ctrl.previousSiblingFocus(el,true);
3813                             }
3814                             break;
3815                         case keymap.KEY.ESC:
3816                             evt.stopPropagation();
3817                             evt.preventDefault();
3818                             scope.showMenu = false;
3819                             elem.removeClass('active');
3820                                                         menuItem.attr('aria-expanded',false);
3821                                                         $timeout(function(){
3822                                                                 menuItem[0].focus();
3823                                                         },100);
3824                             scope.$apply();
3825                             break;
3826                         default:
3827                             break;
3828                     }
3829                 });
3830                 var outsideClick = function (e) {
3831                     var isElement = $isElement(angular.element(e.target), elem, $document);
3832                     if (!isElement) {
3833                         scope.showMenu = false;
3834                         elem.removeClass('active');
3835                         scope.$apply();
3836                     }
3837                 };
3838                 $documentBind.click('showMenu', outsideClick, scope);
3839             }
3840         };
3841     }]).directive('b2bHeaderSubmenu', ['$timeout',function ($timeout) {
3842         return{
3843             restrict: 'A',
3844             link: function (scope, elem) {
3845                 var caretSign = angular.element("<i class='menuCaret'></i>");
3846                 $timeout(function(){
3847                                         var menuItem = angular.element(elem[0].children[0]);
3848                                         menuItem.bind('focus mouseenter', function () {
3849                                                 elem.parent().children().removeClass('active');
3850                                                 elem.addClass('active');
3851                                                 if(elem[0].childElementCount > 1){ // > 1 has third level menu
3852                                                         menuItem.attr('aria-expanded',true);
3853                                                         menuItem.attr('aria-haspopup',true);
3854                                                 }
3855                                                 var caretLeft = (elem[0].offsetLeft +  elem[0].offsetWidth/2) - 10;
3856                                                 caretSign.css({left: caretLeft + 'px'});
3857                                                 angular.element(caretSign);
3858                                                 var tertiaryItems = elem[0].querySelectorAll('[b2b-header-tertiarymenu]');
3859                                                 if(tertiaryItems.length >=1){
3860                                                         elem.append(caretSign);
3861                                                 }
3862                                         });
3863                                         menuItem.bind('blur', function () {
3864                                                 $timeout(function () {
3865                                                         var parentElm = document.activeElement.parentElement.parentElement;
3866                                                         if(parentElm){
3867                                                                 if (!(parentElm.hasAttribute('b2b-header-tertiarymenu'))) {
3868                                                                         elem.removeClass('active');
3869                                                                         if(elem[0].childElementCount > 1){ // > 1 has third level menu
3870                                                                                 menuItem.attr('aria-expanded',false);
3871                                                                         }
3872                                                                         var caret = elem[0].querySelector('.menuCaret');
3873                                                                         if(caret){
3874                                                                                 caret.remove();
3875                                                                         }   
3876                                                                 }
3877                                                         }
3878                                                 });
3879                                         });
3880                 });
3881             }
3882         };
3883     }]).directive('b2bHeaderTertiarymenu', ['$timeout','keymap', function ($timeout,keymap){
3884         return{
3885             restrict: 'A',
3886             require:'^b2bHeaderMenu',
3887             link: function (scope, elem,attr,ctrl) {
3888                 
3889                 elem.bind('keydown', function (evt) {
3890                     var activeElm = document.activeElement;
3891                     var activeParentElm = activeElm.parentElement;
3892                     var activeParentObj  = angular.element(activeParentElm)[0];
3893                     
3894                     if(activeParentElm.hasAttribute('b2b-tertiary-link')){
3895                         var quarterNav = angular.element(activeParentElm)[0].querySelector('li[b2b-header-quarternarymenu]');
3896                         if(quarterNav){
3897                             var links = ctrl.getFirstElement(angular.element(quarterNav)[0],'a');
3898                         }
3899                     }
3900                     var tertiaryMenu = activeElm.parentElement.parentElement.parentElement;
3901                     var tertiaryMenuFlag = tertiaryMenu.hasAttribute('b2b-tertiary-link');
3902                     
3903                     switch (evt.keyCode) {
3904                         case keymap.KEY.DOWN:
3905                             evt.stopPropagation();
3906                             evt.preventDefault();
3907                             if (activeParentElm.hasAttribute('b2b-tertiary-link')) {
3908                                 if(angular.element(quarterNav).hasClass('active')){
3909                                     links.focus();
3910                                 }else if(activeParentObj.nextElementSibling){
3911                                     ctrl.nextSiblingFocus(activeParentObj,true);
3912                                 }
3913                             }
3914                             else if(angular.element(activeParentElm).hasClass('active')){
3915                                 ctrl.nextSiblingFocus(activeElm);
3916                             }
3917                             break;                        
3918                         case keymap.KEY.UP:
3919                             evt.stopPropagation();
3920                             evt.preventDefault();
3921                             if(activeParentElm.hasAttribute('b2b-tertiary-link')){
3922                                 if(activeParentObj.previousElementSibling.hasAttribute('b2b-tertiary-link')){
3923                                     ctrl.previousSiblingFocus(activeParentObj,true);
3924                                 }else{
3925                                     var elm = angular.element(activeElm.parentElement.parentElement.parentElement.parentElement.parentElement)[0];
3926                                     ctrl.getFirstElement(elm,"a").focus();
3927                                 }
3928                             }else if(angular.element(activeParentElm).hasClass('active')){
3929                                     if (activeElm.previousElementSibling) {
3930                                         ctrl.previousSiblingFocus(activeElm);
3931                                     }else if (tertiaryMenuFlag) {
3932                                         var elm = angular.element(tertiaryMenu)[0];
3933                                         ctrl.getFirstElement(elm,"a.header-tertiaryitem").focus();
3934                                     }
3935                                 }
3936                             break;
3937                         default:
3938                             break;
3939                     }
3940                 });
3941             }            
3942         };          
3943     }]).directive('b2bHeaderTogglemenu', ['$timeout', 'keymap', function ($timeout, keymap) {
3944         return{
3945             restrict: 'A',
3946             require: '^b2bHeaderMenu',
3947             link: function (scope, elem, attrs, ctrl) {
3948                 var quarterNav;
3949                 $timeout(function () {
3950                     quarterNav = angular.element(elem.parent())[0].querySelector('li[b2b-header-quarternarymenu]');
3951                     elem.bind('click', function () {
3952                         angular.element(quarterNav).toggleClass('active');
3953                     });
3954                 });
3955             }
3956         };
3957     }]).directive('b2bHeaderResponsive', ['$timeout',function ($timeout) {
3958         return{
3959             restrict: 'A',
3960                         controller: function($scope){
3961                                 this.applyMediaQueries = function(value){
3962                                         document.querySelector('style').textContent += 
3963                                                 "@media screen and (max-width:950px) { \
3964                                                         .header__item.profile { right: " + value + "px; } \
3965                                                 }";
3966                                 };
3967                                 this.arrangeResponsiveHeader = function(children){
3968                                         /* 
3969                                          * clientWidth of 1090 === max-width of 1100px
3970                                          * clientWidth of 920 === max-width of 950px
3971                                          * see b2b-angular.css for rest of responsive header CSS
3972                                          */
3973                                   if (document.documentElement.clientWidth <= 920) { 
3974                                                 switch(children){
3975                                                         case 1:
3976                                                                 this.applyMediaQueries(200);                                    
3977                                                                 break;
3978                                                         case 2:
3979                                                                 this.applyMediaQueries(200);                                                    
3980                                                                 break;
3981                                                         default: // anthing above 3, however, should not have more than 3 to date
3982                                                                 this.applyMediaQueries(200);                                                                                                                                                    
3983                                                 }
3984                                         }
3985                                 }
3986                         },
3987             link: function (scope, elem, attrs, ctrl) {
3988                                 var children;
3989                                 var profile;
3990                                 
3991                                 // onload of page
3992                                 $timeout(function(){ 
3993                                         profile = document.querySelector('li.header__item.profile');
3994                                         children = angular.element(profile).children().length;
3995                                         
3996                                         ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
3997                                 });
3998
3999                                 // on screen resize
4000                                 window.addEventListener('resize', function(event){ // caret adjustmet
4001                                         var activeSubmenu = elem[0].querySelector('[b2b-header-menu] [b2b-header-submenu].active');
4002                                         var activeSubmenuEl = angular.element(activeSubmenu);
4003                                         if(activeSubmenu){
4004                                                 var caretSign = activeSubmenu.querySelector('i.menuCaret');
4005                                                 if(caretSign){
4006                                                         var caretSignEl = angular.element(caretSign);
4007                                                         var caretLeft = (activeSubmenu.offsetLeft +  activeSubmenu.offsetWidth/2) - 10;
4008                                                         caretSignEl.css({left: caretLeft + 'px'});
4009                                                 }
4010                                         }
4011
4012                                         ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
4013                                 });
4014             }
4015         };
4016         }]);
4017
4018 /**
4019  * @ngdoc directive
4020  * @name Layouts.att:headings & copy
4021  *
4022  * @description
4023  *  <file src="src/headingsAndCopy/docs/readme.md" />
4024  *
4025  * @example
4026  <section id="code">
4027     <b>HTML + AngularJS</b>
4028     <example module="b2b.att">
4029     <file src="src/headingsAndCopy/docs/demo.html" />
4030 </example>
4031 </section>
4032  */
4033
4034 var b2bLegalCopy = angular.module('b2b.att.headingsAndCopy', []);
4035 /**
4036  * @ngdoc directive
4037  * @name Tabs, tables & accordions.att:horizontalTable
4038  *
4039  * @description
4040  *  <file src="src/horizontalTable/docs/readme.md" />
4041  *
4042  * @usage
4043  * @param {int} sticky - Number of sticky columns to have. Maximum of 3.
4044  * @param {boolean} refresh - A boolean that when set to true will force a re-render of table. Only use when using 'bulk mode'
4045  * @param {string} legendContent - A string of html to fill in the legend flyout. This should generally be a <ul> with <li> and should not rely on Angular for repeating.
4046  * @param {boolean} retainColumnSet - A boolean that on re-render of the table, determines if the columns visible should reset to 0 or not. Default is false. 
4047  * @example
4048  *  <section id="code">
4049         <example module="b2b.att">
4050             <file src="src/horizontalTable/docs/demo.html" />
4051             <file src="src/horizontalTable/docs/demo.js" />
4052        </example>
4053     </section>
4054  *
4055  */
4056 angular.module('b2b.att.horizontalTable', [])
4057     .constant('b2bHorizontalTableConfig', {
4058         'maxStickyColumns': 3
4059     })
4060     .directive('b2bHorizontalTable', ['$timeout', 'b2bHorizontalTableConfig', 'b2bDOMHelper', function ($timeout, b2bHorizontalTableConfig, b2bDOMHelper) {
4061         return {
4062             restrict: 'EA',
4063             scope: true,
4064             transclude: true,
4065             scope: {
4066                 numOfStickyCols: '=?sticky',
4067                 refresh: '=?',
4068                 legendContent: '=?',
4069                 retainColumnSet: '=?'
4070
4071             },
4072             templateUrl: 'b2bTemplate/horizontalTable/horizontalTable.html',
4073             link: function (scope, element, attrs, ctrl) {
4074                 scope.numOfStickyCols = scope.numOfStickyCols || 1;
4075                 scope.viewportIndex = scope.numOfStickyCols;
4076                 scope.countDisplayText = "";
4077                 var tableElement = element.find('table');
4078                 var thElements = element.find('th');
4079                 var innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
4080                 var outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
4081
4082                 var tableColumns = [];
4083                 var tableRows = element.find('tr');
4084
4085                 var maxWidth = 0,
4086                     maxHeight = 0;
4087                 var totalWidth = element.children()[0].offsetWidth;
4088                 var lastVisibleColumn = 0;
4089                 var collectiveColumnWidth = [];
4090                 var collectiveRowHeight = [];
4091                 var columnSets = [];
4092                 var setIndex = 0; 
4093                 var stickyPixels = 0;
4094
4095                 var displayNoneCSS = {'display': 'none'};
4096                 var displayBlockCSS = {'display': 'table-cell'};
4097
4098                 var init = function() {
4099                     // Reset this from a previous execution
4100                     tableColumns = [];
4101                     collectiveColumnWidth = [];
4102                     collectiveRowHeight = [];
4103                     maxWidth = 0;
4104                     maxHeight = 0;
4105                     lastVisibleColumn = 0;
4106                     columnSets = [];
4107                     if ((!!scope.retainColumnSet)) {
4108                         setIndex = 0;
4109                     }
4110                     visibleColumns = [];
4111                     stickyPixels = 0;
4112
4113                     tableElement = element.find('table');
4114                     thElements = element.find('th');
4115                     innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
4116                     outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
4117                     totalWidth = element.children()[0].offsetWidth;
4118                     tableRows = element.find('tr');
4119
4120                     scope.disableLeft = true;
4121                     scope.disableRight = false;
4122
4123                     if (scope.numOfStickyCols > b2bHorizontalTableConfig.maxStickyColumns) {
4124                         throw new Error('Table can only support ' + b2bHorizontalTableConfig.maxStickyColumns + ' sticky columns.');
4125                     }             
4126                     scope.countDisplayText = "";
4127                     for(var count = 1; count <= scope.numOfStickyCols; count++) {
4128                         scope.countDisplayText = scope.countDisplayText + count + ", "
4129                     }
4130                     angular.forEach(tableRows, function(row, rowIndex) {
4131                         for(var j = 0; j < row.children.length; j++) {
4132                             if (tableColumns[j] === undefined) {
4133                                 tableColumns[j] = [];
4134                             }
4135                             tableColumns[j].push(row.children[j]);
4136                         }
4137                     });
4138
4139                     // We need to reset all the displayNones from previous runs, if applicable
4140                     if (attrs.refresh !== undefined && attrs.refresh !== '')  {
4141                         for (var i = scope.numOfStickyCols+1; i < tableColumns.length; i++) {
4142                             angular.element(tableColumns[i]).css(displayBlockCSS);
4143                         }
4144                     }   
4145
4146                     // We must calculate here as we need cells to be reset after re-render. 
4147                     angular.forEach(tableRows, function(row, rowIndex) {
4148                         collectiveRowHeight.push(findMax(row.children, 'height')); // BUG: Keeping this here now causes row height bugs
4149                     });
4150                     
4151                     
4152                     for (var i = 0; i < tableColumns.length; i++) {
4153                         collectiveColumnWidth.push(findMax(tableColumns[i], 'width')); //offsetWidth doesn't take into account custom css inside
4154                     }
4155                     for(var i = 0; i < scope.numOfStickyCols; i++) {
4156                         maxWidth += collectiveColumnWidth[i];
4157                     }
4158
4159                     // BUG: The code I put in to fix the table not spanning 100%  width is now preventing 
4160                     // table cells from laying out more than stickyPixels and thus we have weird wrapping
4161                     stickyPixels = totalWidth-maxWidth;
4162
4163                     // At this point, for each tr, I need to set the properties (height) and each numOfStickyCols children
4164                     // should be set with sticky properties (margin-left and width)
4165                     var width = maxWidth;
4166                     var additive = 0; 
4167
4168                     if (angular.element(document).find('html').hasClass('isIE')) {
4169                         additive = 25;
4170                     }
4171                     var thObject = undefined;
4172                     for(var i = 0; i < scope.numOfStickyCols; i++) {
4173                         for (var j = 0; j < tableRows.length; j++) {
4174                             thObject = angular.element(tableRows[j].children[i]);
4175                             angular.element(thObject).css({
4176                                 'margin-left': -(width + 2) + 'px', 
4177                                 'width': (collectiveColumnWidth[i] + 3) + 'px', // instead of taking the max width, grab max width for that column
4178                                 'height': (collectiveRowHeight[j] + additive) + 'px',
4179                                 'position': 'absolute',
4180                                 'background-color': '#F2F2F2'
4181                             });
4182                         }
4183                         width -= collectiveColumnWidth[i];
4184                     }
4185                     angular.element(tableRows[0]).css('height', collectiveRowHeight[0] + 'px');
4186                     for(var i = 0; i < tableRows.length; i++) {
4187                         angular.element(tableRows[i]).css('height', (collectiveRowHeight[i] + additive) + 'px'); 
4188                     }
4189                     
4190                     innerContainer.css({
4191                         'padding-left': (maxWidth + 2) + 'px'
4192                     });
4193
4194
4195                     // Let's precompute all the (set) combinations beforehand
4196                     columnSets = []; 
4197                     for (var i = scope.numOfStickyCols; i < tableColumns.length;) {
4198                         visibleColumns = calculateVisibleColumns(i);
4199                         if(visibleColumns === tableColumns.length){
4200                             columnSets.push([i, visibleColumns-1]);
4201                         }else{
4202                             columnSets.push([i, visibleColumns]);
4203                         }
4204                         i = visibleColumns + 1;
4205                     }
4206                     
4207                     //columnSets = [[1, 1], [2,7]]; 
4208                     
4209                     updateCellDisplay(columnSets[setIndex]);
4210                     checkScrollArrows();
4211
4212                     scope.numOfCols = tableColumns.length;
4213
4214                 }
4215
4216                 // JM520E: This is a temporary hack until I solve the ngRepeat issue
4217                 function hack() {
4218                     if (element.find('th').length < scope.numOfStickyCols) {
4219                         // DOM ngRepeat is not ready, let's check back in 10 ms
4220                         $timeout(hack, 10, false);
4221                     } else {
4222                         if (scope.refresh !== undefined) {
4223                             scope.$watch('refresh', function(oldVal, newVal) { // this watch calls too many times
4224                                 if (!angular.equals(oldVal, newVal)) { //hackFinished && oldVal < newVal
4225                                     // From testing it takes about 30 ms before ngRepeat executes, so let's set initial timeout
4226                                     // NOTE: May need to expose timeout to developers. Application is known to have digest cycle of 3-5k watches.
4227                                     $timeout(init, 100, false);
4228                                     scope.refresh = false;
4229                                 }
4230                             });
4231                         }
4232
4233                         init();
4234                     }
4235                 }
4236
4237
4238                 
4239
4240                 // Let's get started with some math!
4241                 hack();
4242
4243                 function calculateVisibleColumns(startingPoint) {
4244                     var usedWidth = 0, 
4245                         visibleColumns = startingPoint || scope.numOfStickyCols;
4246
4247                     while(usedWidth < stickyPixels && visibleColumns < collectiveColumnWidth.length) {
4248                         if (usedWidth+collectiveColumnWidth[visibleColumns] > stickyPixels) {
4249                             if (startingPoint === visibleColumns) {
4250                                 return visibleColumns; // The next cell is too large to fit, it should be only thing to fit
4251                             }
4252                             visibleColumns--;
4253                             return visibleColumns;
4254                         }
4255                         usedWidth += collectiveColumnWidth[visibleColumns];
4256                         visibleColumns++;
4257                     }
4258
4259                     if (usedWidth > stickyPixels) {
4260                         return --visibleColumns;
4261                     }
4262                     return visibleColumns;
4263                 }
4264
4265                 function updateCellDisplay(set) {
4266                     for (var i = scope.numOfStickyCols; i < tableColumns.length; i++) {
4267                         angular.element(tableColumns[i]).css(displayNoneCSS);
4268                     }
4269
4270                     for (var i = set[0]; i <= set[1]; i++) {
4271                         angular.element(tableColumns[i]).css(displayBlockCSS);
4272                     }
4273                 }
4274
4275                 function findMax(arr, prop) {
4276                     var max = 0;
4277                     var localVal = 0;
4278                     var prevDisplay;
4279                     var item;
4280                     for (var i = 0; i < arr.length; i++) {
4281                         item = arr[i];
4282                         prevDisplay = angular.element(item).css('display');
4283
4284                         if (scope.$$phase) {
4285                             scope.$digest();
4286                         }
4287                         // Remove inline styles, they will mess up calculations from original run
4288                         angular.element(item).css('height', '');
4289                         angular.element(item).css('width', ''); 
4290                         if (prop === 'width') {
4291                             // If we do not undo previous run's inline styles, this will grow widths on each re-render.
4292                             localVal = Math.ceil(parseInt(window.getComputedStyle(item).width.split('px')[0], 10)) + 30; // 30 px is padding
4293                         } else if (prop === 'offsetWidth') {
4294                             localVal = item.offsetWidth;
4295                         } else if (prop === 'height') {
4296                             //localVal = item.offsetHeight;
4297                             localVal = Math.ceil(parseInt(window.getComputedStyle(item).height.split('px')[0], 10))
4298                         }
4299
4300                         if (localVal >= max) {
4301                             max = localVal;
4302                         }
4303                     }
4304                     
4305                     return max;
4306                 }
4307
4308                 function checkScrollArrows() {
4309                     scope.disableLeft = (setIndex === 0);
4310                     scope.disableRight = !(setIndex < columnSets.length-1);
4311                 }
4312
4313                 scope.moveViewportLeft = function () {
4314                     setIndex--;
4315                     updateCellDisplay(columnSets[setIndex]);
4316                     checkScrollArrows();
4317
4318                     if (scope.disableLeft) {
4319                         element[0].querySelector('.b2b-horizontal-table-column-info').focus();
4320                     }
4321                 };
4322                 
4323                 scope.moveViewportRight = function () {
4324                     setIndex++;
4325                     updateCellDisplay(columnSets[setIndex]);
4326                     checkScrollArrows();
4327                     
4328                     if (scope.disableRight) {
4329                         element[0].querySelector('.b2b-horizontal-table-column-info').focus();
4330                     }
4331                 };
4332
4333                 scope.getColumnSet = function () {
4334                     return columnSets[setIndex];
4335                 };
4336
4337                 innerContainer.bind('scroll', function () {
4338                     $timeout(function () {
4339                         checkScrollArrows();
4340                     }, 1);
4341                 });
4342
4343             }
4344         };
4345     }]);
4346 /**
4347  * @ngdoc directive
4348  * @name Forms.att:hourPicker
4349  *
4350  * @description
4351  *  <file src="src/hourPicker/docs/readme.md" />
4352  *
4353  * @usage
4354  * <div b2b-hourpicker ng-model="hourpickerValue.value"></div>
4355     
4356  * @example
4357  *  <section id="code">
4358         <example module="b2b.att">
4359             <file src="src/hourPicker/docs/demo.html" />
4360             <file src="src/hourPicker/docs/demo.js" />
4361         </example>
4362     </section>
4363  *
4364  */
4365 angular.module('b2b.att.hourPicker', ['b2b.att.utilities'])
4366
4367 .constant('b2bHourpickerConfig', {
4368     dayOptions: [{
4369         title: 'sunday',
4370         caption: 'Sun',
4371         label: 'S',
4372         disabled: false
4373     }, {
4374         title: 'monday',
4375         caption: 'Mon',
4376         label: 'M',
4377         disabled: false
4378     }, {
4379         title: 'tuesday',
4380         caption: 'Tues',
4381         label: 'T',
4382         disabled: false
4383     }, {
4384         title: 'wednesday',
4385         caption: 'Wed',
4386         label: 'W',
4387         disabled: false
4388     }, {
4389         title: 'thursday',
4390         caption: 'Thu',
4391         label: 'T',
4392         disabled: false
4393     }, {
4394         title: 'friday',
4395         caption: 'Fri',
4396         label: 'F',
4397         disabled: false
4398     }, {
4399         title: 'saturday',
4400         caption: 'Sat',
4401         label: 'S',
4402         disabled: false
4403     }],
4404     startTimeOptions: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00'],
4405     startTimeDefaultOptionIndex: -1,
4406     startTimeDefaultMeridiem: "am",
4407     endTimeOptions: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00'],
4408     endTimeDefaultOptionIndex: -1,
4409     endTimeDefaultMeridiem: "pm",
4410     sameDayOption: true
4411 })
4412
4413 .factory('b2bNormalizeHourpickerValues', [function () {
4414     var _normalize = function (hourpickerValues) {
4415         if (angular.isDefined(hourpickerValues) && hourpickerValues != null) {
4416             var finalHourpickerValues = [];
4417             var hourpickerValue = {};
4418             var days = {};
4419             for (var i = 0; i < hourpickerValues.length; i++) {
4420                 days = hourpickerValues[i].days ? hourpickerValues[i].days : {};
4421                 hourpickerValue.startTime = hourpickerValues[i].startTime ? hourpickerValues[i].startTime : '';
4422                 hourpickerValue.startMeridiem = hourpickerValues[i].startMeridiem ? hourpickerValues[i].startMeridiem : '';
4423                 hourpickerValue.endTime = hourpickerValues[i].endTime ? hourpickerValues[i].endTime : '';
4424                 hourpickerValue.endMeridiem = hourpickerValues[i].endMeridiem ? hourpickerValues[i].endMeridiem : '';
4425                 hourpickerValue.days = [];
4426
4427                 var retrieveDaysText = function (daysDetails) {
4428                     var daysTexts = [];
4429                     var first = -1;
4430                     var last = -1;
4431                     var index = -1;
4432                     for (var i in days) {
4433                         if (days[i].value) {
4434                             daysTexts.push(i);
4435                         }
4436                     }
4437
4438                     first = daysTexts[0];
4439                     last = daysTexts[0];
4440                     index = 0;
4441                     hourpickerValue.days[index] = days[first].caption;
4442                     if (daysTexts.length > 1) {
4443                         for (var i = 1; i < daysTexts.length; i++) {
4444                             if (daysTexts[i] - last === 1) {
4445                                 last = daysTexts[i];
4446                                 hourpickerValue.days[index] = days[first].caption + ' - ' + days[last].caption;
4447                             } else {
4448                                 index++;
4449                                 first = last = daysTexts[i];
4450                                 hourpickerValue.days[index] = days[first].caption;
4451                             }
4452                         }
4453                     }
4454                 };
4455                 retrieveDaysText();
4456
4457                 finalHourpickerValues.push(angular.copy(hourpickerValue));
4458             }
4459
4460             return angular.copy(finalHourpickerValues);
4461         }
4462     };
4463
4464     return {
4465         normalize: _normalize
4466     };
4467 }])
4468
4469 .directive('b2bHourpicker', ['b2bHourpickerConfig', 'b2bNormalizeHourpickerValues', function (b2bHourpickerConfig, b2bNormalizeHourpickerValues) {
4470     return {
4471         restrict: 'EA',
4472         replace: false,
4473         scope: true,
4474         require: 'ngModel',
4475         templateUrl: 'b2bTemplate/hourPicker/b2bHourpicker.html',
4476         controller: ['$scope', function (scope) {
4477
4478         }],
4479         link: function (scope, elem, attr, ctrl) {
4480             scope.hourpicker = {};
4481             scope.hourpicker.dayOptions = attr.dayOptions ? scope.$parent.$eval(attr.dayOptions) : b2bHourpickerConfig.dayOptions;
4482             scope.hourpicker.startTimeOptions = attr.startTimeOptions ? scope.$parent.$eval(attr.startTimeOptions) : b2bHourpickerConfig.startTimeOptions;
4483             scope.hourpicker.endTimeOptions = attr.endTimeOptions ? scope.$parent.$eval(attr.endTimeOptions) : b2bHourpickerConfig.endTimeOptions;
4484             scope.hourpicker.startTimeDefaultOptionIndex = attr.startTimeDefaultOptionIndex ? scope.$parent.$eval(attr.startTimeDefaultOptionIndex) : b2bHourpickerConfig.startTimeDefaultOptionIndex;
4485             scope.hourpicker.endTimeDefaultOptionIndex = attr.endTimeDefaultOptionIndex ? scope.$parent.$eval(attr.endTimeDefaultOptionIndex) : b2bHourpickerConfig.endTimeDefaultOptionIndex;
4486             scope.hourpicker.startTimeDefaultMeridiem = attr.startTimeDefaultMeridiem ? scope.$parent.$eval(attr.startTimeDefaultMeridiem) : b2bHourpickerConfig.startTimeDefaultMeridiem;
4487             scope.hourpicker.endTimeDefaultMeridiem = attr.endTimeDefaultMeridiem ? scope.$parent.$eval(attr.endTimeDefaultMeridiem) : b2bHourpickerConfig.endTimeDefaultMeridiem;
4488             scope.hourpicker.sameDayOption = attr.sameDayOption ? scope.$parent.$eval(attr.sameDayOption) : b2bHourpickerConfig.sameDayOption;
4489             scope.hourpicker.editMode = -1;
4490
4491             scope.hourpickerValues = [];
4492             scope.finalHourpickerValues = [];
4493             scope.addHourpickerValue = function (hourpickerPanelValue) {
4494                 if (hourpickerPanelValue) {
4495                     if (scope.hourpicker.editMode > -1) {
4496                         scope.hourpickerValues[scope.hourpicker.editMode] = hourpickerPanelValue;
4497                         scope.hourpicker.editMode = -1;
4498                     } else {
4499                         scope.hourpickerValues.push(hourpickerPanelValue);
4500                     }
4501                 }
4502                 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
4503                 ctrl.$setViewValue(angular.copy(scope.hourpickerValues));
4504             };
4505             ctrl.$render = function () {
4506                 if (angular.isDefined(ctrl.$modelValue)) {
4507                     scope.hourpickerValues = angular.copy(ctrl.$modelValue);
4508                     scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
4509                 }
4510             };
4511             scope.editHourpickerValue = function (index) {
4512                 scope.hourpickerPanelValue = angular.copy(scope.hourpickerValues[index]);
4513                 scope.hourpicker.editMode = index;
4514             };
4515             scope.deleteHourpickerValue = function (index) {
4516                 scope.hourpickerValues.splice(index, 1);
4517                 scope.resetHourpickerPanelValue();
4518                 scope.addHourpickerValue();
4519             };
4520
4521             scope.setValidity = function (errorType, errorValue) {
4522                 ctrl.$setValidity(errorType, errorValue);
4523             }
4524         }
4525     }
4526 }])
4527
4528 .directive('b2bHourpickerPanel', [function () {
4529     return {
4530         restrict: 'EA',
4531         replace: false,
4532         templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerPanel.html',
4533         controller: ['$scope', function (scope) {
4534
4535         }],
4536         link: function (scope, elem, attr, ctrl) {
4537             var hourpickerPanelValueTemplate = {
4538                 days: {},
4539                 startTime: '',
4540                 startMeridiem: 'am',
4541                 endTime: '',
4542                 endMeridiem: 'pm'
4543             };
4544             for (var i = 0; i < scope.hourpicker.dayOptions.length; i++) {
4545                 hourpickerPanelValueTemplate.days[i] = {
4546                     value: false,
4547                     title: scope.hourpicker.dayOptions[i].title,
4548                     caption: scope.hourpicker.dayOptions[i].caption
4549                 };
4550             }
4551             scope.hourpickerPanelValue = {};
4552             scope.disableAddBtn = true;
4553
4554             scope.$watch('hourpickerPanelValue.days', function(){
4555                 for(var i in scope.hourpickerPanelValue.days)
4556                 {
4557                     if(scope.hourpickerPanelValue.days[i].value)
4558                     {
4559                         scope.disableAddBtn = false;
4560                         break;
4561                     }
4562                     scope.disableAddBtn = true;
4563                 }
4564             }, true);
4565
4566             scope.resetHourpickerPanelValue = function () {
4567                 scope.hourpickerPanelValue = angular.copy(hourpickerPanelValueTemplate);
4568                 if (scope.hourpicker.startTimeDefaultOptionIndex > -1) {
4569                     scope.hourpickerPanelValue.startTime = scope.hourpicker.startTimeOptions[scope.hourpicker.startTimeDefaultOptionIndex];
4570                 }
4571                 if (scope.hourpicker.endTimeDefaultOptionIndex > -1) {
4572                     scope.hourpickerPanelValue.endTime = scope.hourpicker.endTimeOptions[scope.hourpicker.endTimeDefaultOptionIndex];
4573                 }
4574                 scope.hourpickerPanelValue.startMeridiem = scope.hourpicker.startTimeDefaultMeridiem;
4575                 scope.hourpickerPanelValue.endMeridiem = scope.hourpicker.endTimeDefaultMeridiem;
4576                 scope.hourpicker.editMode = -1;
4577                 scope.setValidity('invalidHourpickerData', true);
4578                 scope.setValidity('invalidHourpickerTimeRange', true);
4579             };
4580             scope.resetHourpickerPanelValue();
4581             scope.updateHourpickerValue = function () {
4582                 if (scope.isFormValid() && !scope.isTimeOverlap()) {
4583                     scope.addHourpickerValue(angular.copy(scope.hourpickerPanelValue));
4584                     scope.resetHourpickerPanelValue();
4585                 }
4586             };
4587
4588             scope.isFormValid = function () {
4589                 var isStartTimeAvailable = scope.hourpickerPanelValue.startTime ? true : false;
4590                 var isStartMeridiemAvailable = scope.hourpickerPanelValue.startMeridiem ? true : false;
4591                 var isEndTimeAvailable = scope.hourpickerPanelValue.endTime ? true : false;
4592                 var isEndMeridiemAvailable = scope.hourpickerPanelValue.endMeridiem ? true : false;
4593                 var currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
4594                 var currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
4595                 var isTimeInProperSequence = currentEndTime > currentStartTime;
4596                 var isDayChecked = false;
4597                 for (var i in scope.hourpickerPanelValue.days) {
4598                     if (scope.hourpickerPanelValue.days[i].value) {
4599                         isDayChecked = true;
4600                         break;
4601                     }
4602                 }
4603
4604                 if (isStartTimeAvailable && isStartMeridiemAvailable && isEndTimeAvailable && isEndMeridiemAvailable && isTimeInProperSequence && isDayChecked) {
4605                     scope.setValidity('invalidHourpickerData', true);
4606                     return true;
4607                 } else {
4608                     scope.setValidity('invalidHourpickerData', false);
4609                     return false;
4610                 }
4611             };
4612             scope.isTimeOverlap = function () {
4613                 var selectedDays = [];
4614                 for (var i in scope.hourpickerPanelValue.days) {
4615                     if (scope.hourpickerPanelValue.days[i].value) {
4616                         selectedDays.push(i);
4617                     }
4618                 }
4619
4620                 var currentStartTime, currentEndTime, existingStartTime, existingEndTime;
4621                 currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
4622                 currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
4623                 for (var i = 0; i < scope.hourpickerValues.length; i++) {
4624                     
4625                     if (i === scope.hourpicker.editMode) {
4626                         continue;
4627                     }
4628
4629                     for (var j = 0; j < selectedDays.length; j++) {
4630                         existingStartTime = getTime(scope.hourpickerValues[i].startTime, scope.hourpickerValues[i].startMeridiem);
4631                         existingEndTime = getTime(scope.hourpickerValues[i].endTime, scope.hourpickerValues[i].endMeridiem);
4632                         if (scope.hourpickerValues[i].days[selectedDays[j]].value) {
4633                             if(!scope.hourpicker.sameDayOption){
4634                                 scope.setValidity('dayAlreadySelected', false);
4635                                 return true;
4636                             } else if ((currentStartTime > existingStartTime && currentStartTime < existingEndTime) || (currentEndTime > existingStartTime && currentEndTime < existingEndTime)) {
4637                                 scope.setValidity('invalidHourpickerTimeRange', false);
4638                                 return true;
4639                             } else if ((existingStartTime > currentStartTime && existingStartTime < currentEndTime) || (existingEndTime > currentStartTime && existingEndTime < currentEndTime)) {
4640                                 scope.setValidity('invalidHourpickerTimeRange', false);
4641                                 return true;
4642                             } else if ((currentStartTime === existingStartTime) && (currentEndTime === existingEndTime)) {
4643                                 scope.setValidity('invalidHourpickerTimeRange', false);
4644                                 return true;
4645                             }
4646                         }
4647                     }
4648                 }
4649
4650                 scope.setValidity('dayAlreadySelected', true);
4651                 scope.setValidity('invalidHourpickerTimeRange', true);
4652                 return false;
4653             };
4654             var getTime = function (timeString, meridiem) {
4655                 var tempDate = new Date();
4656                 if (timeString && meridiem) {
4657                     var timeSplit = timeString.split(':');
4658                     var hour = ((meridiem === 'PM' || meridiem === 'pm') && timeSplit[0] !== '12') ? parseInt(timeSplit[0], 10) + 12 : parseInt(timeSplit[0], 10);
4659                     tempDate.setHours(hour, parseInt(timeSplit[1], 10), 0, 0);
4660                 }
4661
4662                 return tempDate.getTime();
4663             };
4664         }
4665     }
4666 }])
4667
4668 .directive('b2bHourpickerValue', [function () {
4669     return {
4670         restrict: 'EA',
4671         replace: false,
4672         templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerValue.html',
4673         controller: ['$scope', function (scope) {
4674
4675         }],
4676         link: function (scope, elem, attr, ctrl) {
4677             scope.hourpickerValue = {};
4678             scope.hourpickerValue.startTime = attr.startTime ? scope.$eval(attr.startTime) : '';
4679             scope.hourpickerValue.startMeridiem = attr.startMeridiem ? scope.$eval(attr.startMeridiem) : '';
4680             scope.hourpickerValue.endTime = attr.endTime ? scope.$eval(attr.endTime) : '';
4681             scope.hourpickerValue.endMeridiem = attr.endMeridiem ? scope.$eval(attr.endMeridiem) : '';
4682             scope.hourpickerValue.days = attr.days ? scope.$eval(attr.days).join(', ') : '';
4683             scope.hourpickerValue.index = attr.b2bHourpickerValue ? scope.$eval(attr.b2bHourpickerValue) : -1;
4684         }
4685     }
4686 }]);
4687 /**
4688  * @ngdoc directive
4689  * @name Template.att:inputTemplate
4690  *
4691  * @description
4692  *  <file src="src/inputTemplate/docs/readme.md" />
4693  *
4694  * @usage
4695  *  <input type="text" id="fieldId" placeholder="placholder text here" class="span12 input-enhanced" name="fieldName">
4696  *
4697  * @example
4698  <section id="code">
4699     <b>HTML + AngularJS</b>
4700     <example module="b2b.att">
4701         <file src="src/inputTemplate/docs/demo.html" />
4702         <file src="src/inputTemplate/docs/demo.js" />
4703     </example>
4704  </section>
4705  */
4706 angular.module('b2b.att.inputTemplate', []);
4707
4708 /**
4709  * @ngdoc directive
4710  * @name Navigation.att:leftNavigation
4711  *
4712  * @description
4713  *  <file src="src/leftNavigation/docs/readme.md" />
4714  *
4715  * @usage
4716  *   <b2b-left-navigation data-menu="menuData"></b2b-left-navigation> 
4717  *
4718  * @example
4719  *  <section id="code">
4720         <example module="b2b.att">
4721             <file src="src/leftNavigation/docs/demo.html" />
4722             <file src="src/leftNavigation/docs/demo.js" />
4723        </example>
4724     </section>
4725  *
4726  */
4727 angular.module('b2b.att.leftNavigation', [])
4728     .directive('b2bLeftNavigation', [function () {
4729         return {
4730             restrict: 'EA',
4731             templateUrl: 'b2bTemplate/leftNavigation/leftNavigation.html',
4732             scope: {
4733                 menuData: '='
4734             },
4735             link: function (scope, element, attrs, ctrl) {
4736                 scope.idx = -1;
4737                 scope.itemIdx = -1;
4738                 scope.navIdx = -1;
4739                 scope.toggleNav = function (val) {
4740                     if (val === scope.idx) {
4741                         scope.idx = -1;
4742                         return;
4743                     }
4744                     scope.idx = val;
4745                 };
4746                 scope.liveLink = function (evt, val1, val2) {
4747                     scope.itemIdx = val1;
4748                     scope.navIdx = val2;
4749                     evt.stopPropagation();
4750                 };
4751             }
4752         };
4753     }]);
4754 /**
4755  * @ngdoc directive
4756  * @name Buttons, links & UI controls.att:links
4757  *
4758  * @description
4759  *  <file src="src/links/docs/readme.md" />
4760  * @usage
4761  *      <!-- See below examples for link implementation -->
4762  *      
4763  * @example
4764        <section id="code">              
4765            <b>HTML + AngularJS</b>
4766            <example module="b2b.att">
4767            <file src="src/links/docs/demo.html" />
4768             <file src="src/links/docs/demo.js" />            
4769           </example>          
4770         </section>
4771  */
4772 angular.module('b2b.att.links', []);
4773 /**
4774  * @ngdoc directive
4775  * @name Misc.att:listbox
4776  *
4777  * @description
4778  *  <file src="src/listbox/docs/readme.md" />
4779  *
4780  * @param {int} currentIndex - Current index of selected listbox item. Is not supported on multiselect listbox
4781  * @param {Array} listboxData - Data of listbox items. Should include full data regardless if HTML will be filtered.
4782
4783  * @example
4784  *  <section id="code">   
4785      <example module="b2b.att">
4786      <file src="src/listbox/docs/demo.html" />
4787      <file src="src/listbox/docs/demo.js" />
4788      </example>
4789     </section>
4790  *
4791  */
4792 angular.module('b2b.att.listbox', ['b2b.att.utilities'])
4793 .directive('b2bListBox', ['keymap', 'b2bDOMHelper', '$rootScope', function(keymap, b2bDOMHelper, $rootScope) {
4794                 return {
4795                     restrict: 'AE',
4796                     transclude: true,
4797                     replace: true,
4798                     scope: {
4799                         currentIndex: '=', 
4800                         listboxData: '='
4801                     },
4802                     templateUrl: 'b2bTemplate/listbox/listbox.html',
4803                     link: function(scope, elem, attr) {
4804
4805                         if (attr.ariaMultiselectable !== undefined || attr.ariaMultiselectable === 'true') {
4806                             scope.multiselectable = true;
4807                         } else {
4808                             scope.multiselectable = false;
4809                         }
4810
4811                         var shiftKey = false;
4812                         var elements = [];
4813                         var prevDirection = undefined; // previous direction is used for an edge case when shifting
4814                         var shiftKeyPressed = false; // Used to handle shift clicking
4815                         var ctrlKeyPressed = false;
4816
4817                         var currentIndexSet = {
4818                             'elementIndex': 0,
4819                             'listboxDataIndex': 0
4820                         };
4821
4822                         function isTrue(item) {
4823                             if (item.selected === true) {
4824                                 return true;
4825                             }
4826                         }
4827
4828                         function incrementIndex(elem) {
4829                             $rootScope.$apply();
4830
4831                             var nextElem = elem.next();
4832                             if (!angular.isDefined(nextElem) || nextElem.length === 0) {
4833                                 return;
4834                             }
4835
4836                             currentIndexSet.elementIndex += 1;
4837                             currentIndexSet.listboxDataIndex = parseInt(nextElem.attr('data-index'), 10);
4838                             scope.currentIndex = currentIndexSet.listboxDataIndex;
4839
4840                             if (currentIndexSet.elementIndex >= elements.length - 1) {
4841                                 currentIndexSet.elementIndex = elements.length-1;
4842                             }
4843                         }
4844
4845                         function decrementIndex(elem) {
4846                             $rootScope.$apply();
4847                             var prevElem = angular.element(b2bDOMHelper.previousElement(elem));
4848                             if (!angular.isDefined(prevElem) || prevElem.length === 0) {
4849                                 return;
4850                             }
4851
4852                             currentIndexSet.elementIndex -= 1;
4853                             currentIndexSet.listboxDataIndex = parseInt(prevElem.attr('data-index'), 10);
4854                             scope.currentIndex = currentIndexSet.listboxDataIndex;
4855
4856                             if (currentIndexSet.elementIndex <= 0) {
4857                                 currentIndexSet.elementIndex = 0;
4858                             }
4859                         }
4860
4861                         var focusOnElement = function(index) {
4862                             try {
4863                                 elements[index].focus();
4864                             } catch (e) {};
4865                         }
4866
4867                         function selectItems(startIndex, endIndex, forceValue) {
4868                             for (var i = startIndex; i < endIndex; i++) {
4869                                 if (forceValue === undefined) {
4870                                     // We will flip the value
4871                                     scope.listboxData[i].selected = !scope.listboxData[i].selected;
4872                                 } else {
4873                                     scope.listboxData[i].selected = forceValue;
4874                                 }
4875                             }
4876
4877                             if (!scope.$$phase) {
4878                                 scope.$apply();
4879                             }
4880                         }
4881
4882                         elem.bind('focus', function(evt) { 
4883                             // If multiselectable or not and nothing is selected, put focus on first element 
4884                             // If multiselectable and a range is set, put focus on first element of range 
4885                             // If not multiselectable and something selected, put focus on element 
4886                             elements = elem.children(); 
4887                              var selectedItems = scope.listboxData.filter(isTrue); 
4888                              var elementsIndies = Array.prototype.map.call(elements, function(item) {
4889                                 return parseInt(angular.element(item).attr('data-index'), 10);
4890                             });
4891  
4892                             if (selectedItems.length == 0) { 
4893                                 focusOnElement(0); 
4894                                 currentIndexSet.listboxDataIndex = 0;
4895                             } else if (attr.ariaMultiselectable) { 
4896                                 var index = scope.listboxData.indexOf(selectedItems[0]); 
4897                                 var indies = elementsIndies.filter(function(item) {
4898                                     return (item === index);
4899                                 });
4900
4901                                 if (indies.length === 0 || indies[0] != index) {
4902                                     // Set focused on 0
4903                                     currentIndexSet.elementIndex = elementsIndies[0]; 
4904                                     currentIndexSet.listboxDataIndex = 0;
4905                                     focusOnElement(currentIndexSet.elementIndex);
4906                                 } else {
4907                                     focusOnElement(indies[0]); 
4908                                     currentIndexSet.elementIndex = indies[0];
4909                                     currentIndexSet.listboxDataIndex = index;
4910                                 }
4911                             } else { 
4912                                 focusOnElement(currentIndexSet.elementIndex);  
4913                             }
4914                             scope.currentIndex = currentIndexSet.listboxDataIndex;
4915
4916                             if (!scope.$$phase) {
4917                                 scope.$apply();
4918                             }
4919                         });
4920                         elem.bind('keyup', function(evt) {
4921                             if (evt.keyCode === keymap.KEY.SHIFT) {
4922                                 shiftKeyPressed = false;
4923                             } else if (evt.keyCode === keymap.KEY.CTRL) {
4924                                 ctrlKeyPressed = false;
4925                             }
4926                         });
4927                 
4928                         elem.bind('keydown', function(evt) {
4929                             var keyCode = evt.keyCode;
4930                             elements = elem.children();
4931                             if (keyCode === keymap.KEY.SHIFT) {
4932                                 shiftKeyPressed = true;
4933                             } else if (evt.keyCode === keymap.KEY.CTRL) {
4934                                 ctrlKeyPressed = true;
4935                             }
4936
4937                             switch(keyCode) {
4938                                 case 65: // A key
4939                                 {
4940                                     if (scope.multiselectable && evt.ctrlKey) {
4941                                         var arr = scope.listboxData.filter(isTrue);
4942                                         var elementsIndies = Array.prototype.map.call(elements, function(item) {
4943                                             return parseInt(angular.element(item).attr('data-index'), 10);
4944                                         });
4945                                         var val = !(arr.length === scope.listboxData.length);
4946                                         for (var i = 0; i < elementsIndies.length; i++) {
4947                                             scope.listboxData[elementsIndies[i]].selected = val;
4948                                         }
4949
4950                                         if (!scope.$$phase) {
4951                                             scope.$apply();
4952                                         }
4953                                         
4954                                         evt.preventDefault();
4955                                         evt.stopPropagation();
4956                                     }
4957                                     break;
4958                                 }
4959                                 case keymap.KEY.END:
4960                                 {
4961                                     if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
4962                                         var elementsIndies = Array.prototype.map.call(elements, function(item) {
4963                                             return parseInt(angular.element(item).attr('data-index'), 10);
4964                                         }).filter(function(item) {
4965                                             return (item >= currentIndexSet.listboxDataIndex);
4966                                         });
4967                                         for (var i = 0; i < elementsIndies.length; i++) {
4968                                             scope.listboxData[elementsIndies[i]].selected = true;
4969                                         }
4970                                         evt.preventDefault();
4971                                         evt.stopPropagation();
4972
4973                                         if (!scope.$$phase) {
4974                                             scope.$apply();
4975                                         }
4976                                     }
4977                                     break;
4978                                 }
4979                                 case keymap.KEY.HOME: 
4980                                 {
4981                                     if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
4982                                         selectItems(0, currentIndexSet.listboxDataIndex+1, true); // currentIndex+1 is what is being focused on
4983                                         evt.preventDefault();
4984                                         evt.stopPropagation();
4985                                     }
4986                                     break;
4987                                 }
4988                                 case keymap.KEY.LEFT:
4989                                 case keymap.KEY.UP:
4990                                 {
4991                                     if (currentIndexSet.listboxDataIndex === 0) {
4992                                         evt.preventDefault();
4993                                         evt.stopPropagation();
4994                                         return;
4995                                     }
4996
4997                                     decrementIndex(elements.eq(currentIndexSet.elementIndex));
4998                                     if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
4999                                         if (evt.shiftKey) {
5000                                             if (prevDirection === 'DOWN') {
5001                                                 scope.listboxData[currentIndexSet.listboxDataIndex+1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex+1].selected;
5002                                             }
5003                                             scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
5004                                         }
5005                                         prevDirection = 'UP';
5006                                     } else {
5007                                         // If no modifier keys are selected, all other items need to be unselected.
5008                                         prevDirection = undefined;
5009                                         selectItems(0, scope.listboxData.length, false);
5010                                         if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){
5011                                             scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
5012                                         }
5013                                     }
5014                                     focusOnElement(currentIndexSet.elementIndex);
5015                                     if(!scope.$$phase) {
5016                                         scope.$apply();
5017                                     }
5018                                     evt.preventDefault();
5019                                     evt.stopPropagation();
5020                                     break;
5021                                 }
5022                                 case keymap.KEY.RIGHT:
5023                                 case keymap.KEY.DOWN:
5024                                 {
5025                                     if (currentIndexSet.listboxDataIndex === scope.listboxData.length-1) {
5026                                         evt.preventDefault();
5027                                         evt.stopPropagation();
5028                                         return;
5029                                     }
5030
5031                                     incrementIndex(elements.eq(currentIndexSet.elementIndex));
5032                                     
5033                                     if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
5034                                         if (evt.shiftKey) {
5035                                             if (prevDirection === 'UP') {
5036                                                 scope.listboxData[currentIndexSet.listboxDataIndex-1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex-1].selected;
5037                                             }
5038                                             
5039                                             scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;    
5040                                         }
5041                                         prevDirection = 'DOWN';
5042                                     } else {
5043                                         // If no modifier keys are selected, all other items need to be unselected.
5044                                         prevDirection = undefined;
5045                                         selectItems(0, scope.listboxData.length, false);
5046                                         if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){
5047                                             scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
5048                                         }
5049                                     }
5050
5051                                     focusOnElement(currentIndexSet.elementIndex);
5052                                     if(!scope.$$phase) {
5053                                         scope.$apply();
5054                                     }
5055                                     evt.preventDefault();
5056                                     evt.stopPropagation();
5057                                     break;
5058                                 }
5059                                 case keymap.KEY.TAB:
5060                                     if(evt.shiftKey) {
5061                                         var previousElement = b2bDOMHelper.previousElement(elem.parent().parent(), true);
5062                                         evt.preventDefault();
5063                                         previousElement.focus();
5064                                     }
5065                                     break;
5066                                 default:
5067                                     break;
5068                             }
5069                         });
5070
5071                         elem.bind('click', function(evt) {
5072                             var index = parseInt(evt.target.dataset.index, 10);
5073                             if (index === undefined || isNaN(index)) {
5074                                 return;
5075                             }
5076                             if (scope.multiselectable && currentIndexSet.listboxDataIndex !== undefined) {
5077                                 if (shiftKeyPressed) {
5078                                     var min = Math.min(index, currentIndexSet.listboxDataIndex);
5079                                     var max = Math.max(index, currentIndexSet.listboxDataIndex);
5080
5081                                     if (index === min) { // clicking up
5082                                         var firstIndex = scope.listboxData.findIndex(function(item) { return item.selected === true;});
5083                                         // Given the firstIndex, let's find the matching element to get proper element match
5084                                         elements = elem.children();
5085                                         elements.eq(firstIndex)
5086                                         var elementsThatMatch = Array.prototype.filter.call(elements, function(item) {
5087                                             if (parseInt(angular.element(item).attr('data-index'), 10) === firstIndex) {
5088                                                 return true;
5089                                             }
5090                                         });
5091                                         firstIndex = parseInt(angular.element(elementsThatMatch).attr('data-index'), 10);
5092                                         
5093                                         if (index <= firstIndex && scope.listboxData.filter(isTrue).length > 1) {
5094                                             // Break the selection into 2
5095                                             selectItems(firstIndex + 1, max + 1, undefined); // + 1 needed because selectItems only selects up to MAX
5096                                             selectItems(min, firstIndex, undefined); 
5097                                         } else if (scope.listboxData.filter(isTrue).length == 1){
5098                                             selectItems(min, max, undefined); 
5099                                         } else {
5100                                             selectItems(min + 1, max + 1, undefined);
5101                                         }
5102                                     } else { // clicking down
5103                                         selectItems(min + 1, max + 1, scope.listboxData[min].selected);
5104                                     }
5105                                 } else if (ctrlKeyPressed) {
5106                                     scope.listboxData[index].selected = !scope.listboxData[index].selected;
5107                                 } else {
5108                                     selectItems(0, scope.listboxData.length, false);
5109                                     scope.listboxData[index].selected = !scope.listboxData[index].selected;
5110                                 }
5111                             } else {
5112                                 selectItems(0, scope.listboxData.length, false);
5113                                 scope.listboxData[index].selected = !scope.listboxData[index].selected;
5114                             }
5115                             currentIndexSet.elementIndex = index;
5116                             currentIndexSet.listboxDataIndex = index;
5117                             scope.currentIndex = currentIndexSet.listboxDataIndex;
5118                             if (!scope.$$phase) {
5119                                 scope.$apply();
5120                             }
5121                             focusOnElement(index);
5122                         });
5123                     }
5124                 };
5125             }]);
5126 /**
5127  * @ngdoc directive
5128  * @name Videos, audio & animation.att:loaderAnimation
5129  *
5130  * @description
5131  *  <file src="src/loaderAnimation/docs/readme.md" />
5132  *
5133  * @usage
5134  *   <!-- Below demo js shows-->
5135  *   Angular library uses Global.css's icon-primary-spinner.
5136  *
5137  * @example
5138  *  <section id="code">
5139         <example module="b2b.att">
5140             <file src="src/loaderAnimation/docs/demo.html" />
5141             <file src="src/loaderAnimation/docs/demo.js" />
5142        </example>
5143     </section>
5144  *
5145  */
5146 angular.module('b2b.att.loaderAnimation', [])
5147     .constant('b2bSpinnerConfig', {
5148         loadingText: 'Loading...',
5149         startEvent: 'startButtonSpinner',
5150         stopEvent: 'stopButtonSpinner'
5151     })
5152     .constant("progressTrackerConfig", {
5153         loadingText: 'Loading...',
5154         minDuration: "",
5155         activationDelay: "",
5156         minDurationPromise: "",
5157         activationDelayPromise: ""
5158     })
5159
5160 .provider('progressTracker', function () {
5161     this.$get = ['$q', '$timeout', function ($q, $timeout) {
5162         function cancelTimeout(promise) {
5163             if (promise) {
5164                 $timeout.cancel(promise);
5165             }
5166         }
5167         return function ProgressTracker(options) {
5168             //do new if user doesn't
5169             if (!(this instanceof ProgressTracker)) {
5170                 return new ProgressTracker(options);
5171             }
5172
5173             options = options || {};
5174             //Array of promises being tracked
5175             var tracked = [];
5176             var self = this;
5177             //Allow an optional "minimum duration" that the tracker has to stay active for.
5178             var minDuration = options.minDuration;
5179             //Allow a delay that will stop the tracker from activating until that time is reached
5180             var activationDelay = options.activationDelay;
5181             var minDurationPromise;
5182             var activationDelayPromise;
5183             self.active = function () {
5184                 //Even if we have a promise in our tracker, we aren't active until delay is elapsed
5185                 if (activationDelayPromise) {
5186                     return false;
5187                 }
5188                 return tracked.length > 0;
5189             };
5190             self.tracking = function () {
5191                 //Even if we aren't active, we could still have a promise in our tracker
5192                 return tracked.length > 0;
5193             };
5194             self.destroy = self.cancel = function () {
5195                 minDurationPromise = cancelTimeout(minDurationPromise);
5196                 activationDelayPromise = cancelTimeout(activationDelayPromise);
5197                 for (var i = tracked.length - 1; i >= 0; i--) {
5198                     tracked[i].resolve();
5199                 }
5200                 tracked.length = 0;
5201             };
5202             //Create a promise that will make our tracker active until it is resolved.
5203             // @return deferred - our deferred object that is being tracked
5204             self.createPromise = function () {
5205                 var deferred = $q.defer();
5206                 tracked.push(deferred);
5207                 //If the tracker was just inactive and this the first in the list of promises, we reset our delay and minDuration again.
5208                 if (tracked.length === 1) {
5209                     if (activationDelay) {
5210                         activationDelayPromise = $timeout(function () {
5211                             activationDelayPromise = cancelTimeout(activationDelayPromise);
5212                             startMinDuration();
5213                         }, activationDelay);
5214                     } else {
5215                         startMinDuration();
5216                     }
5217                 }
5218                 deferred.promise.then(onDone(false), onDone(true));
5219                 return deferred;
5220
5221                 function startMinDuration() {
5222                     if (minDuration) {
5223                         minDurationPromise = $timeout(angular.noop, minDuration);
5224                     }
5225                 }
5226                 //Create a callback for when this promise is done. It will remove our tracked promise from the array if once minDuration is complete
5227                 function onDone() {
5228                     return function () {
5229                         (minDurationPromise || $q.when()).then(function () {
5230                             var index = tracked.indexOf(deferred);
5231                             tracked.splice(index, 1);
5232                             //If this is the last promise, cleanup the timeouts for activationDelay
5233                             if (tracked.length === 0) {
5234                                 activationDelayPromise = cancelTimeout(activationDelayPromise);
5235                             }
5236                         });
5237                     };
5238                 }
5239             };
5240             self.addPromise = function (promise) {
5241                 
5242 //                we cannot assign then function in other var and then add the resolve and reject 
5243                 var thenFxn = promise && (promise.then || promise.$then || (promise.$promise && promise.$promise.then));                
5244                 if (!thenFxn) {
5245                     throw new Error("progressTracker expects a promise object :: Not found");
5246                 }
5247                 var deferred = self.createPromise();
5248                 //When given promise is done, resolve our created promise
5249                 //Allow $then for angular-resource objects
5250
5251                 promise.then(function (value) {
5252                         deferred.resolve(value);
5253                         return value;
5254                     }, function (value) {
5255                         deferred.reject(value);
5256                         return $q.reject(value);
5257                     }
5258                 );
5259                 return deferred;
5260             };
5261         };
5262     }];
5263 })
5264
5265 .config(['$httpProvider', function ($httpProvider) {
5266     $httpProvider.interceptors.push(['$q', 'progressTracker', function ($q) {
5267         return {
5268             request: function (config) {
5269                 if (config.tracker) {
5270                     if (!angular.isArray(config.tracker)) {
5271                         config.tracker = [config.tracker];
5272                     }
5273                     config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];
5274
5275                     angular.forEach(config.tracker, function (tracker) {
5276                         var deferred = tracker.createPromise();
5277                         config.$promiseTrackerDeferred.push(deferred);
5278                     });
5279                 }
5280                 return $q.when(config);
5281             },
5282             response: function (response) {
5283                 if (response.config && response.config.$promiseTrackerDeferred) {
5284                     angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
5285                         deferred.resolve(response);
5286                     });
5287                 }
5288                 return $q.when(response);
5289             },
5290             responseError: function (response) {
5291                 if (response.config && response.config.$promiseTrackerDeferred) {
5292                     angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
5293                         deferred.reject(response);
5294                     });
5295                 }
5296                 return $q.reject(response);
5297             }
5298         };
5299     }]);
5300 }])
5301
5302 .directive('b2bClickSpin', ['$timeout', '$parse', '$rootScope', 'progressTracker', function ($timeout, $parse, $rootScope, progressTracker) {
5303     return {
5304         restrict: 'A',
5305         link: function (scope, elm, attrs) {
5306             var fn = $parse(attrs.b2bClickSpin);
5307             elm.on('click', function (event) {
5308                 var promise = $timeout(function () {console.log("inside Promise")}, 5000);
5309                 scope.$apply(function () {
5310                     fn(scope, {
5311                         $event: event
5312                     });
5313                 });
5314                 //comment this line if not running unit test
5315                 $rootScope.loadingTracker = progressTracker({
5316                     minDuration: 750
5317                 });
5318                 $rootScope.loadingTracker.addPromise(promise);
5319                 angular.forEach("$routeChangeSuccess $viewContentLoaded $locationChangeSuccess".split(" "), function (event) {
5320                     $rootScope.$on(event, function () {
5321
5322                         $timeout.cancel(promise);
5323                     });
5324                 });
5325             });
5326         }
5327     };
5328 }])
5329
5330 .directive('b2bProgressTracker', ['progressTrackerConfig', function (ptc) {
5331     return {
5332         restrict: 'EA',
5333         replace: true,
5334         template: '<div><div ng-show="loadingTracker.active()" style="width:100%; text-align:center"><i class=\"icon-primary-spinner\"></i></div><div ng-show="loadingTracker.active()" style="width:100%;margin-top:10px; text-align:center">'+ ptc.loadingText+'</div></div>'
5335     };
5336 }])
5337
5338 .directive('b2bLoadButton', ['b2bSpinnerConfig', '$timeout', function (spinnerConfig, $timeout) {
5339     var spinButton = function (state, element, data) {
5340         
5341         var attr = element.html() ? 'html' : 'val';
5342         state = state + 'Text';
5343         if (state === 'loadingText') {
5344             element[attr](data[state]);
5345             element.attr("disabled",'disabled');
5346             element.addClass('disabled');
5347         } else if (state === 'resetText') {
5348             element[attr](data[state]);
5349             element.removeAttr("disabled");
5350             element.removeClass('disabled');
5351         }
5352     };
5353
5354     return {
5355         restrict: 'A',
5356         replace: false,
5357         scope: {
5358             promise: '=promise',
5359             startEvent: '@startEvent',
5360             stopEvent: '@stopEvent'
5361         },
5362         link: function (scope, element, attr) {
5363             var validAttr = element.html() ? 'html' : 'val';
5364             var data = {
5365                 loadingText: '',
5366                 resetText: ''
5367             };
5368
5369             var updateLoadingText = function (val) {
5370                 var loadingText = val;
5371                 if (!angular.isDefined(loadingText) || loadingText === "") {
5372                     loadingText = spinnerConfig.loadingText;
5373                 }
5374                 data.loadingText = validAttr === 'html' ? "<i class=\"icon-primary-spinner small\"></i>" + loadingText : loadingText;
5375             };
5376             var updateResetText = function (val) {
5377                 data.resetText = val;
5378             };
5379
5380             attr.$observe('b2bLoadButton', function (val) {
5381                 updateLoadingText(val);
5382             });
5383             $timeout(function () {
5384                 updateResetText(element[validAttr]());
5385             }, 500);
5386
5387             if (!angular.isDefined(scope.startEvent) || scope.startEvent === "") {
5388                 scope.startEvent = spinnerConfig.startEvent;
5389             }
5390
5391             if (!angular.isDefined(scope.stopEvent) || scope.stopEvent === "") {
5392                 scope.stopEvent = spinnerConfig.stopEvent;
5393             }
5394
5395             scope.$watch('promise', function () {
5396                 if (angular.isDefined(scope.promise) && angular.isFunction(scope.promise.then)) {
5397                     spinButton('loading', element, data);
5398                     scope.promise.then(function () {
5399                         spinButton('reset', element, data);
5400                     }, function () {
5401                         spinButton('reset', element, data);
5402                     });
5403                 }
5404             });
5405
5406             scope.$on(scope.startEvent, function () {
5407                 spinButton('loading', element, data);
5408                 scope.$on(scope.stopEvent, function () {
5409                     spinButton('reset', element, data);
5410                 });
5411             });
5412         }
5413     };
5414 }]);
5415  /**
5416  * @ngdoc directive
5417  * @name Misc.att:messageWrapper
5418  * @scope
5419  * @param {boolean} trigger - A boolean that triggers directive to switch focus
5420  * @param {integer} delay  - Extra delay added to trigger code to allow for DOM to be ready. Default is 10ms.
5421  * @param {string} noFocus - Attribute-based API to trigger whether first focusable element receives focus on trigger or whole message (assumes tabindex="-1" set on first child)
5422  * @param {string} trapFocus - Attribute-based API to trap focus within the message. This should be enabled by default on all toast messages.
5423  * @description
5424  *  <file src="src/messageWrapper/docs/readme.md" />
5425  * @usage
5426  * <b2b-message-wrapper>Code that contains at least one focusable element and will be shown/hidden on some logic. This must have tabindex="-1".</b2b-message-wrapper>
5427  *
5428  * @example
5429  *  <section id="code">   
5430  <b>HTML + AngularJS</b>
5431  <example module="b2b.att">
5432  <file src="src/messageWrapper/docs/demo.html" />
5433  <file src="src/messageWrapper/docs/demo.js" />
5434  </example>
5435  </section>
5436  *
5437  */
5438 angular.module('b2b.att.messageWrapper', ['b2b.att.utilities'])
5439 .directive('b2bMessageWrapper', ['b2bDOMHelper', '$compile', '$timeout', '$log', function(b2bDOMHelper, $compile, $timeout, $log) {
5440   return {
5441     restrict: 'AE',
5442     scope: {
5443       trigger: '=',
5444       delay: '=?'
5445     },
5446     transclude: true,
5447     replace: true,
5448     template: '<div ng-transclude></div>',
5449     link: function(scope, elem, attrs) {
5450       scope.delay = scope.delay || 10;
5451
5452       if (attrs.trapFocus != undefined && !elem.children().eq(0).attr('b2b-trap-focus-inside-element')) {
5453         // Append b2bTrapFocusInsideElement onto first child and recompile
5454         elem.children().eq(0).attr('b2b-trap-focus-inside-element', 'false');
5455         elem.children().eq(0).attr('trigger', scope.trigger);
5456         $compile(elem.contents())(scope);
5457       }
5458
5459       var firstElement = undefined,
5460           launchingElement = undefined;
5461       
5462       scope.$watch('trigger', function(oldVal, newVal) {
5463         if (oldVal === newVal) return;
5464         if (!angular.isDefined(launchingElement)) {
5465           launchingElement = document.activeElement;
5466         }
5467         $timeout(function() {
5468           if (scope.trigger) {
5469
5470             if (attrs.noFocus === true || attrs.noFocus === "") {
5471               elem.children()[0].focus();
5472             } else {
5473               firstElement = b2bDOMHelper.firstTabableElement(elem);
5474
5475               if (angular.isDefined(firstElement)) {
5476                 firstElement.focus();
5477               }
5478             }
5479             
5480           } else {
5481             if (angular.isDefined(launchingElement) && launchingElement.nodeName !== 'BODY') {
5482               if (launchingElement === document.activeElement) {
5483                 return;
5484               }
5485
5486               if (b2bDOMHelper.isInDOM(launchingElement) && b2bDOMHelper.isTabable(launchingElement)) {
5487                   // At this point, launchingElement is still a valid element, but focus will fail and 
5488                   // activeElement will become body, hence we want to apply custom logic and find previousElement
5489                   var prevLaunchingElement = launchingElement;
5490                   launchingElement.focus();
5491
5492                   if (document.activeElement !== launchingElement || document.activeElement.nodeName === 'BODY') {
5493                     launchingElement = b2bDOMHelper.previousElement(angular.element(prevLaunchingElement), true);
5494                     launchingElement.focus();
5495                   }
5496               } else {
5497                 launchingElement = b2bDOMHelper.previousElement(launchingElement, true);
5498                 launchingElement.focus();
5499               }
5500             }
5501           }
5502         }, scope.delay); 
5503       });
5504     }
5505   };
5506 }]);
5507 /**
5508  * @ngdoc directive
5509  * @name Messages, modals & alerts.att:modalsAndAlerts
5510  *
5511  * @description
5512  *  <file src="src/modalsAndAlerts/docs/readme.md" />
5513  *
5514  * @usage
5515  *  <button class="btn" b2b-modal="b2bTemplate/modalsAndAlerts/demo_modal.html" modal-ok="ok()" modal-cancel="cancel()">Launch demo modal</button>
5516  *
5517  * @example
5518  *  <section id="code">
5519      <example module="b2b.att">
5520       <file src="src/modalsAndAlerts/docs/demo.html" />
5521       <file src="src/modalsAndAlerts/docs/demo.js" />
5522      </example>
5523     </section>
5524  *
5525  */
5526 angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transition', 'b2b.att.utilities'])
5527
5528 /**
5529  * A helper, internal data structure that acts as a map but also allows getting / removing
5530  * elements in the LIFO order
5531  */
5532 .factory('$$stackedMap', function () {
5533     return {
5534         createNew: function () {
5535             var stack = [];
5536
5537             return {
5538                 add: function (key, value) {
5539                     stack.push({
5540                         key: key,
5541                         value: value
5542                     });
5543                 },
5544                 get: function (key) {
5545                     for (var i = 0; i < stack.length; i++) {
5546                         if (key === stack[i].key) {
5547                             return stack[i];
5548                         }
5549                     }
5550                 },
5551                 keys: function () {
5552                     var keys = [];
5553                     for (var i = 0; i < stack.length; i++) {
5554                         keys.push(stack[i].key);
5555                     }
5556                     return keys;
5557                 },
5558                 top: function () {
5559                     return stack[stack.length - 1];
5560                 },
5561                 remove: function (key) {
5562                     var idx = -1;
5563                     for (var i = 0; i < stack.length; i++) {
5564                         if (key === stack[i].key) {
5565                             idx = i;
5566                             break;
5567                         }
5568                     }
5569                     return stack.splice(idx, 1)[0];
5570                 },
5571                 removeTop: function () {
5572                     return stack.splice(stack.length - 1, 1)[0];
5573                 },
5574                 length: function () {
5575                     return stack.length;
5576                 }
5577             };
5578         }
5579     };
5580 }).factory('trapFocusInElement', ['$document', '$isElement', 'b2bDOMHelper', 'keymap', function ($document, $isElement, b2bDOMHelper, keymap) {
5581     var elementStack = [];
5582     var stackHead = undefined;
5583     var firstTabableElement, lastTabableElement;
5584
5585     var trapKeyboardFocusInFirstElement = function (e) {
5586         if (!e.keyCode) {
5587             e.keyCode = e.which;
5588         }
5589
5590         if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
5591             lastTabableElement[0].focus();
5592             e.preventDefault(e);
5593             e.stopPropagation(e);
5594         }
5595
5596     };
5597
5598     var trapKeyboardFocusInLastElement = function (e) {
5599         if (!e.keyCode) {
5600             e.keyCode = e.which;
5601         }
5602
5603         if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
5604             firstTabableElement[0].focus();
5605             e.preventDefault(e);
5606             e.stopPropagation(e);
5607         }
5608     };
5609     
5610     var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
5611         var bodyElements = $document.find('body').children();
5612
5613         firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(b2bDOMHelper.firstTabableElement(stackHead));
5614         lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(b2bDOMHelper.lastTabableElement(stackHead));
5615
5616         if (flag) {
5617             for (var i = 0; i < bodyElements.length; i++) {
5618                 if (bodyElements[i] !== stackHead[0]) {
5619                     bodyElements.eq(i).attr('aria-hidden', true);
5620                 }
5621             }
5622             firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
5623             lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
5624         } else {
5625             for (var j = 0; j < bodyElements.length; j++) {
5626                 if (bodyElements[j] !== stackHead[0]) {
5627                     bodyElements.eq(j).removeAttr('aria-hidden');
5628                 }
5629             }
5630             firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
5631             lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
5632         }
5633     };
5634     var toggleTrapFocusInElement = function (flag, element) {
5635         if (angular.isDefined(flag) && angular.isDefined(element)) {
5636             if (angular.isUndefined(stackHead)) {
5637                 stackHead = element;
5638                 trapFocusInElement(flag);
5639             } else {
5640                 if (flag) {
5641                     trapFocusInElement(false);
5642                     elementStack.push(stackHead);
5643                     stackHead = element;
5644                     trapFocusInElement(true);
5645                 } else {
5646                     if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
5647                         trapFocusInElement(false);
5648                         stackHead = elementStack.pop();
5649                         if (angular.isDefined(stackHead)) {
5650                             trapFocusInElement(true);
5651                         }
5652                     }
5653                 }
5654             }
5655         }else {
5656             if (angular.isDefined(stackHead)) {
5657                 trapFocusInElement(false, firstTabableElement, lastTabableElement);
5658                 trapFocusInElement(true);
5659             }
5660         }
5661     };
5662
5663     return toggleTrapFocusInElement;
5664 }])
5665
5666 /**
5667  * A helper directive for the $modal service. It creates a backdrop element.
5668  */
5669 .directive('b2bModalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
5670     return {
5671         restrict: 'EA',
5672         replace: true,
5673         templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html',
5674         link: function (scope, element, attrs) {
5675             scope.close = function (evt) {
5676                 var modal = $modalStack.getTop();
5677                 if (modal && modal.value.backdrop && modal.value.backdrop !== 'static') {
5678                     evt.preventDefault();
5679                     evt.stopPropagation();
5680                     $modalStack.dismiss(modal.key, 'backdrop click');
5681                 }
5682             };
5683         }
5684     };
5685 }])
5686
5687 .directive('b2bModalWindow', ['$timeout', 'windowOrientation', '$window', 'keymap', function ($timeout, windowOrientation, $window, keymap) {
5688     return {
5689         restrict: 'EA',
5690         scope: {
5691             index: '@'
5692         },
5693         replace: true,
5694         transclude: true,
5695         templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-window.html',
5696         controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
5697             scope.windowClass = attrs.windowClass || '';
5698             scope.sizeClass = attrs.sizeClass || '';
5699             scope.isNotifDialog = false;
5700             scope.modalClose = attrs.modalClose || false;
5701
5702             this.setTitle = function (title) {
5703                 scope.title = title;
5704             };
5705             this.setContent = function (content) {
5706                 scope.content = content;
5707                 scope.isNotifDialog = true;
5708             };
5709             this.isDockedModal = scope.windowClass.indexOf('modal-docked') > -1;
5710         }],
5711         link: function (scope, element, attrs, ctrl) {
5712             if (ctrl.isDockedModal) {
5713                 scope.isModalLandscape = false;
5714
5715                 var window = angular.element($window);
5716                 scope.updateCss = function () {
5717                     if (windowOrientation.isPotrait()) { // Potrait Mode
5718                         scope.isModalLandscape = false;
5719                     } else if (windowOrientation.isLandscape()) { // Landscape Mode
5720                         scope.isModalLandscape = true;
5721                     }
5722                 };
5723
5724                 $timeout(function () {
5725                     scope.updateCss();
5726                     scope.$apply();
5727                 }, 100);
5728                 window.bind('orientationchange', function () {
5729                     scope.updateCss();
5730                     scope.$apply();
5731                 });
5732                 window.bind('resize', function () {
5733                     scope.updateCss();
5734                     scope.$apply();
5735                 });
5736             }else {
5737                 angular.element(element[0].querySelectorAll(".awd-select-list")).css({
5738                     "max-height": "200px"
5739                 });
5740             }
5741
5742             var isIE = /msie|trident/i.test(navigator.userAgent);
5743             if (isIE) {
5744                 if(angular.element(element[0].querySelector('.corner-button button.close')).length > 0){
5745                     angular.element(element[0].querySelector('.corner-button button.close')).bind('focus', function () {
5746                        angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollLeft = 0;
5747                        angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollTop = 0;
5748                     });
5749                 }
5750             }
5751
5752             if(scope.modalClose){
5753                 element.bind('keydown', function (e) {
5754                     if(e.keyCode == keymap.KEY.ESC){
5755                         e.preventDefault();
5756                         e.stopPropagation();
5757                     }
5758                 });
5759             }
5760         }
5761     };
5762 }])
5763
5764 .directive('b2bModalTitle', [function () {
5765     return {
5766         restrict: 'A',
5767         require: '^b2bModalWindow',
5768         link: function (scope, elem, attr, ctrl) {
5769             ctrl.setTitle(attr.id);
5770         }
5771     };
5772 }])
5773
5774 .directive('b2bModalContent', [function () {
5775     return {
5776         restrict: 'A',
5777         require: '^b2bModalWindow',
5778         link: function (scope, elem, attr, ctrl) {
5779             ctrl.setContent(attr.id);
5780         }
5781     };
5782 }])
5783
5784
5785 .directive('b2bModalBody', ['$timeout', '$position', '$document', '$window', 'windowOrientation', 'b2bAwdBreakpoints', function ($timeout, $position, $document, $window, windowOrientation, b2bAwdBreakpoints) {
5786     return {
5787         restrict: 'AC',
5788         scope: {
5789             index: '@'
5790         },
5791         require: '^b2bModalWindow',
5792         link: function (scope, element, attrs, ctrl) {
5793             var window = angular.element($window);
5794             var body = $document.find('body').eq(0);
5795             scope.setModalHeight = function () {
5796                 var modalHeaderHeight, modalFooterHeight, modalBodyHeight, windowHeight, windowWidth, modalHeight;
5797                 modalHeaderHeight = 0;
5798                 modalFooterHeight = 0;
5799                 windowHeight = $window.innerHeight;
5800                 windowWidth = $window.innerWidth;
5801                 body.css({
5802                     'height': windowHeight + 'px'
5803                 });
5804
5805                 if (ctrl.isDockedModal) {
5806                     var modalElements = element.parent().children();
5807                     for (var i = 0; i < modalElements.length; i++) {
5808                         if (modalElements.eq(i).hasClass('b2b-modal-header')) {
5809                             modalHeaderHeight = $position.position(modalElements.eq(i)).height;
5810                         } else if (modalElements.eq(i).hasClass('b2b-modal-footer')) {
5811                             modalFooterHeight = $position.position(modalElements.eq(i)).height;
5812                         }
5813                     }
5814
5815                     modalHeight = $position.position(element.parent()).height;
5816
5817                     modalBodyHeight = modalHeight - (modalHeaderHeight + modalFooterHeight) + 'px';
5818
5819                     if (windowOrientation.isPotrait()) { // Potrait Mode
5820                         element.removeAttr('style').css({
5821                             height: modalBodyHeight
5822                         });
5823                     } else if (windowOrientation.isLandscape() && windowWidth < b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Mobile
5824                         element.removeAttr('style');
5825                     } else if (windowOrientation.isLandscape() && windowWidth >= b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Non-Mobile
5826                         element.removeAttr('style').css({
5827                             height: modalBodyHeight
5828                         });
5829                     }
5830                 }
5831             };
5832
5833             $timeout(function () {
5834                 scope.setModalHeight();
5835                 scope.$apply();
5836             }, 100);
5837             window.bind('orientationchange', function () {
5838                 scope.setModalHeight();
5839                 scope.$apply();
5840             });
5841             window.bind('resize', function () {
5842                 scope.setModalHeight();
5843                 scope.$apply();
5844             });
5845         }
5846     };
5847 }])
5848
5849 .directive('b2bModalFooter', ['windowOrientation', '$window', function (windowOrientation, $window) {
5850     return {
5851         restrict: 'AC',
5852         scope: {
5853             index: '@'
5854         },
5855         link: function (scope, element, attrs) {
5856
5857         }
5858     };
5859 }])
5860
5861 .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap', '$log', '$timeout', 'trapFocusInElement', function ($document, $compile, $rootScope, $$stackedMap, $log, $timeout, trapFocusInElement) {
5862     var backdropjqLiteEl, backdropDomEl;
5863     var backdropScope = $rootScope.$new(true);
5864     var body = $document.find('body').eq(0);
5865     var html = $document.find('html').eq(0);
5866     var openedWindows = $$stackedMap.createNew();
5867     var $modalStack = {};
5868
5869     function backdropIndex() {
5870         var topBackdropIndex = -1;
5871         var opened = openedWindows.keys();
5872         for (var i = 0; i < opened.length; i++) {
5873             if (openedWindows.get(opened[i]).value.backdrop) {
5874                 topBackdropIndex = i;
5875             }
5876         }
5877         return topBackdropIndex;
5878     }
5879
5880     $rootScope.$watch(backdropIndex, function (newBackdropIndex) {
5881         backdropScope.index = newBackdropIndex;
5882     });
5883
5884     function removeModalWindow(modalInstance) {
5885         //background scroll fix
5886         html.removeAttr('style');
5887         body.removeAttr('style');
5888         body.removeClass('styled-by-modal');
5889
5890         var modalWindow = openedWindows.get(modalInstance).value;
5891         trapFocusInElement(false, modalWindow.modalDomEl);
5892
5893         //clean up the stack
5894         openedWindows.remove(modalInstance);
5895
5896         //remove window DOM element
5897         modalWindow.modalDomEl.remove();
5898
5899         //remove backdrop if no longer needed
5900         if (backdropDomEl && backdropIndex() === -1) {
5901             backdropDomEl.remove();
5902             backdropDomEl = undefined;
5903         }
5904
5905         //destroy scope
5906         modalWindow.modalScope.$destroy();
5907     }
5908
5909     $document.bind('keydown', function (evt) {
5910         var modal;
5911
5912         if (evt.which === 27) {
5913             modal = openedWindows.top();
5914             if (modal && modal.value.keyboard) {
5915                 $rootScope.$apply(function () {
5916                     $modalStack.dismiss(modal.key);
5917                 });
5918             }
5919         }
5920     });
5921
5922     $modalStack.open = function (modalInstance, modal) {
5923
5924         openedWindows.add(modalInstance, {
5925             deferred: modal.deferred,
5926             modalScope: modal.scope,
5927             backdrop: modal.backdrop,
5928             keyboard: modal.keyboard
5929         });
5930
5931         var angularDomEl = angular.element('<div b2b-modal-window></div>');
5932         angularDomEl.attr('window-class', modal.windowClass);
5933         angularDomEl.attr('size-class', modal.sizeClass);
5934         angularDomEl.attr('index', openedWindows.length() - 1);
5935         angularDomEl.attr('modal-close', modal.modalClose);
5936         angularDomEl.html(modal.content);
5937
5938         var modalDomEl = $compile(angularDomEl)(modal.scope);
5939         openedWindows.top().value.modalDomEl = modalDomEl;
5940         //background page scroll fix
5941         html.css({
5942             'overflow-y': 'hidden'
5943         });
5944         body.css({
5945             'overflow-y': 'hidden',
5946             'width': '100%',
5947             'height': window.innerHeight + 'px'
5948         });
5949         body.addClass('styled-by-modal');
5950         body.append(modalDomEl);
5951
5952         if (backdropIndex() >= 0 && !backdropDomEl) {
5953             backdropjqLiteEl = angular.element('<div b2b-modal-backdrop></div>');
5954             backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
5955             body.append(backdropDomEl);
5956         }
5957
5958         $timeout(function () {
5959
5960             if (modal.scope.$$childHead.isNotifDialog) {
5961                 angular.element(modalDomEl).find('button')[0].focus();
5962             } else {
5963                 angular.element(modalDomEl)[0].focus();
5964             }
5965             trapFocusInElement(true, angular.element(modalDomEl).eq(0));
5966         }, 200);
5967     };
5968
5969     $modalStack.close = function (modalInstance, result) {
5970         var modal = openedWindows.get(modalInstance);
5971         if (modal) {
5972             modal.value.deferred.resolve(result);
5973             removeModalWindow(modalInstance);
5974         }
5975     };
5976
5977     $modalStack.dismiss = function (modalInstance, reason) {
5978         var modalWindow = openedWindows.get(modalInstance).value;
5979         if (modalWindow) {
5980             modalWindow.deferred.reject(reason);
5981             removeModalWindow(modalInstance);
5982         }
5983     };
5984
5985     $modalStack.getTop = function () {
5986         return openedWindows.top();
5987     };
5988
5989     return $modalStack;
5990 }])
5991
5992 .provider('$modal', function () {
5993     var $modalProvider = {
5994         options: {
5995             backdrop: true, //can be also false or 'static'
5996             keyboard: true
5997         },
5998         $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack', function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
5999             var $modal = {};
6000
6001             function getTemplatePromise(options) {
6002                 return options.template ? $q.when(options.template) :
6003                     $http.get(options.templateUrl, {
6004                         cache: $templateCache
6005                     }).then(function (result) {
6006                         return result.data;
6007                     });
6008             }
6009
6010             function getResolvePromises(resolves) {
6011                 var promisesArr = [];
6012                 angular.forEach(resolves, function (value, key) {
6013                     if (angular.isFunction(value) || angular.isArray(value)) {
6014                         promisesArr.push($q.when($injector.invoke(value)));
6015                     }
6016                 });
6017                 return promisesArr;
6018             }
6019
6020             $modal.open = function (modalOptions) {
6021
6022                 var modalResultDeferred = $q.defer();
6023                 var modalOpenedDeferred = $q.defer();
6024                 //prepare an instance of a modal to be injected into controllers and returned to a caller
6025                 var modalInstance = {
6026                     result: modalResultDeferred.promise,
6027                     opened: modalOpenedDeferred.promise,
6028                     close: function (result) {
6029                         $modalStack.close(modalInstance, result);
6030                     },
6031                     dismiss: function (reason) {
6032                         $modalStack.dismiss(modalInstance, reason);
6033                     }
6034                 };
6035
6036                 //merge and clean up options
6037                 modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
6038                 modalOptions.resolve = modalOptions.resolve || {};
6039
6040                 //verify options
6041                 if (!modalOptions.template && !modalOptions.templateUrl) {
6042                     throw new Error('One of template or templateUrl options is required.');
6043                 }
6044
6045                 var templateAndResolvePromise =
6046                     $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
6047
6048
6049                 templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
6050
6051                     var modalScope = (modalOptions.scope || $rootScope).$new();
6052                     modalScope.$close = modalInstance.close;
6053                     modalScope.$dismiss = modalInstance.dismiss;
6054
6055                     var ctrlInstance, ctrlLocals = {};
6056                     var resolveIter = 1;
6057
6058                     //controllers
6059                     if (modalOptions.controller) {
6060                         ctrlLocals.$scope = modalScope;
6061                         ctrlLocals.$modalInstance = modalInstance;
6062                         angular.forEach(modalOptions.resolve, function (value, key) {
6063                             ctrlLocals[key] = tplAndVars[resolveIter++];
6064                         });
6065
6066                         ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
6067                     }
6068
6069                     $modalStack.open(modalInstance, {
6070                         scope: modalScope,
6071                         deferred: modalResultDeferred,
6072                         content: tplAndVars[0],
6073                         backdrop: modalOptions.backdrop,
6074                         keyboard: modalOptions.keyboard,
6075                         windowClass: modalOptions.windowClass,
6076                         sizeClass: modalOptions.sizeClass,
6077                         modalClose: modalOptions.modalClose
6078                     });
6079
6080                 }, function resolveError(reason) {
6081                     modalResultDeferred.reject(reason);
6082                 });
6083
6084                 templateAndResolvePromise.then(function () {
6085                     modalOpenedDeferred.resolve(true);
6086                 }, function () {
6087                     modalOpenedDeferred.reject(false);
6088                 });
6089
6090                 return modalInstance;
6091             };
6092
6093             return $modal;
6094         }]
6095     };
6096
6097     return $modalProvider;
6098 })
6099
6100 .directive("b2bModal", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
6101     return {
6102         restrict: 'A',
6103         scope: {
6104             b2bModal: '@',
6105             modalController: '@',
6106             modalOk: '&',
6107             modalCancel: '&',
6108             windowClass: '@',
6109             sizeClass: '@', 
6110             modalClose: '@'
6111         },
6112         link: function (scope, elm, attr) {
6113             elm.bind('click', function (ev) {
6114                 var currentPosition = ev.pageY - ev.clientY;
6115                 ev.preventDefault();
6116                 if (angular.isDefined(elm.attr("href")) && elm.attr("href") !== "") {
6117                     scope.b2bModal = elm.attr("href");
6118                 }
6119                 $modal.open({
6120                     templateUrl: scope.b2bModal,
6121                     controller: scope.modalController,
6122                     windowClass: scope.windowClass,
6123                     sizeClass: scope.sizeClass, 
6124                     modalClose: scope.modalClose
6125                 }).result.then(function (value) {
6126                     scope.modalOk({
6127                         value: value
6128                     });
6129                     elm[0].focus();
6130                 }, function (value) {
6131                     scope.modalCancel({
6132                         value: value
6133                     });
6134                     elm[0].focus();
6135                 });
6136             });
6137         }
6138     };
6139 }])
6140
6141 .directive("utilityFilter", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
6142     return {
6143         restrict: 'EA',
6144         scope: {
6145             utilityFilter: '@'
6146         },
6147         require: 'ngModel',
6148         templateUrl: 'b2bTemplate/modal/u-filter.html',
6149         link: function (scope, element, attribute, ctrl) {
6150             //controller to be passed to $modal service
6151             scope.options = angular.copy(scope.$parent.$eval(attribute.ngModel));
6152             scope.$parent.$watch(attribute.ngModel, function (newVal, oldVal) {
6153                 if (newVal !== oldVal) {
6154                     scope.options = newVal;
6155                 }
6156             });
6157             var modalCtrl = function ($scope, options) {
6158                 $scope.options = angular.copy(options);
6159             };
6160
6161             if (angular.isDefined(scope.utilityFilter)) {
6162                 scope.templateUrl = scope.utilityFilter;
6163             } else {
6164                 scope.templateUrl = 'b2bTemplate/modal/u-filter-window.html';
6165             }
6166             element.bind('click', function (ev) {
6167                 var currentPosition = ev.pageY - ev.clientY;
6168                 $modal.open({
6169                     templateUrl: scope.templateUrl,
6170                     controller: modalCtrl,
6171                     resolve: {
6172                         options: function () {
6173                             return scope.options;
6174                         }
6175                     }
6176                 }).result.then(function (value) {
6177                     ctrl.$setViewValue(value);
6178                     element[0].focus();
6179                     $scrollTo(0, currentPosition, 0);
6180                 }, function () {
6181                     element[0].focus();
6182                     $scrollTo(0, currentPosition, 0);
6183                 });
6184             });
6185         }
6186     };
6187 }]);
6188 /**
6189  * @ngdoc directive
6190  * @name Forms.att:monthSelector
6191  *
6192  * @description
6193  *  <file src="src/monthSelector/docs/readme.md" />
6194  *
6195  * @usage
6196  * <div b2b-monthpicker ng-model="dt" min="minDate" max="maxDate" mode="monthpicker"></div>
6197     
6198  * @example
6199  *  <section id="code">
6200         <example module="b2b.att">
6201             <file src="src/monthSelector/docs/demo.html" />
6202             <file src="src/monthSelector/docs/demo.js" />
6203         </example>
6204     </section>
6205  *
6206  */
6207 angular.module('b2b.att.monthSelector', ['b2b.att.position', 'b2b.att.utilities'])
6208
6209 .constant('b2bMonthpickerConfig', {
6210     dateFormat: 'MM/dd/yyyy',
6211     dayFormat: 'd',
6212     monthFormat: 'MMMM',
6213     yearFormat: 'yyyy',
6214     dayHeaderFormat: 'EEEE',
6215     dayTitleFormat: 'MMMM yyyy',
6216     disableWeekend: false,
6217     disableSunday: false,
6218     disableDates: null,
6219     onSelectClose: null,
6220     startingDay: 0,
6221     minDate: null,
6222     maxDate: null,
6223     dueDate: null,
6224     fromDate: null,
6225     legendIcon: null,
6226     legendMessage: null,
6227     calendarDisabled: false,
6228     collapseWait: 0,
6229     orientation: 'left',
6230     inline: false,
6231     mode:0,
6232     helperText: 'The date you selected is $date. Double tap to open calendar. Select a date to close the calendar.',
6233     descriptionText: 'Use tab to navigate between previous button, next button and month. Use arrow keys to navigate between months. Use space or enter to select a month.',
6234     MonthpickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation','mode','id'],
6235     MonthpickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
6236     MonthpickerFunctionAttributes: ['disableDates', 'onSelectClose']
6237 })
6238
6239 .factory('b2bMonthpickerService', ['b2bMonthpickerConfig', 'dateFilter', function (b2bMonthpickerConfig, dateFilter) {
6240     var setAttributes = function (attr, elem) {
6241         if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
6242             var attributes = b2bMonthpickerConfig.MonthpickerEvalAttributes.concat(b2bMonthpickerConfig.MonthpickerWatchAttributes, b2bMonthpickerConfig.MonthpickerFunctionAttributes);
6243             for (var key in attr) {
6244                 var val = attr[key];
6245                 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6246                     elem.attr(key.toSnakeCase(), key);
6247                 }
6248             }
6249         }
6250     };
6251
6252     var bindScope = function (attr, scope) {
6253         if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
6254             var evalFunction = function (key, val) {
6255                 scope[key] = scope.$parent.$eval(val);
6256             };
6257
6258             var watchFunction = function (key, val) {
6259                 scope.$parent.$watch(val, function (value) {
6260                     scope[key] = value;
6261                 });
6262                 scope.$watch(key, function (value) {
6263                     scope.$parent[val] = value;
6264                 });
6265             };
6266
6267             var evalAttributes = b2bMonthpickerConfig.MonthpickerEvalAttributes;
6268             var watchAttributes = b2bMonthpickerConfig.MonthpickerWatchAttributes;
6269             for (var key in attr) {
6270                 var val = attr[key];
6271                 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6272                     evalFunction(key, val);
6273                 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6274                     watchFunction(key, val);
6275                 }
6276             }
6277         }
6278     };
6279
6280     return {
6281         setAttributes: setAttributes,
6282         bindScope: bindScope
6283     };
6284 }])
6285
6286 .controller('b2bMonthpickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bMonthpickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
6287     var format = {
6288             date: getValue($attrs.dateFormat, dtConfig.dateFormat),
6289             day: getValue($attrs.dayFormat, dtConfig.dayFormat),
6290             month: getValue($attrs.monthFormat, dtConfig.monthFormat),
6291             year: getValue($attrs.yearFormat, dtConfig.yearFormat),
6292             dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
6293             dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
6294             disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
6295             disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday),
6296             disableDates: getValue($attrs.disableDates, dtConfig.disableDates)
6297         },
6298         startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
6299
6300     $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
6301     $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
6302     $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
6303     $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
6304     $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
6305     $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
6306     $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
6307     $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
6308     $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
6309     $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
6310     $scope.mode = getValue($attrs.mode, dtConfig.mode);
6311     
6312     $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
6313
6314     function getValue(value, defaultValue) {
6315         return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
6316     }
6317
6318     function getDaysInMonth(year, month) {
6319         return new Date(year, month, 0).getDate();
6320     }
6321
6322     function getDates(startDate, n) {
6323         var dates = new Array(n);
6324         var current = startDate,
6325             i = 0;
6326         while (i < n) {
6327             dates[i++] = new Date(current);
6328             current.setDate(current.getDate() + 1);
6329         }
6330         return dates;
6331     }
6332
6333     this.updatePosition = function (b2bMonthpickerPopupTemplate) {
6334         $scope.position = $position.offset($element);
6335         if($element.find('input').length > 0 ){
6336             $scope.position.top += $element.find('input').prop('offsetHeight');
6337         }else{
6338             $scope.position.top += $element.find('a').prop('offsetHeight');
6339         }
6340         
6341         if ($scope.orientation === 'right') {
6342             $scope.position.left -= (((b2bMonthpickerPopupTemplate && b2bMonthpickerPopupTemplate.prop('offsetWidth')) || 290) - $element.find('input').prop('offsetWidth'));
6343         }
6344     };
6345
6346     function isSelected(dt) { 
6347         if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
6348             return true;
6349         }
6350         return false;
6351     }
6352
6353     function isFromDate(dt) {
6354         if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
6355             return true;
6356         }
6357         return false;
6358     }
6359
6360     function isDateRange(dt) {
6361         if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
6362             return true;
6363         } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
6364             return true;
6365         }
6366         return false;
6367     }
6368
6369     function isOld(date, currentMonthDate) {
6370         if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() < new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
6371             return true;
6372         } else {
6373             return false;
6374         }
6375     }
6376
6377     function isNew(date, currentMonthDate) {
6378         if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() > new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
6379             return true;
6380         } else {
6381             return false;
6382         }
6383     }
6384
6385     function isPastDue(dt) {
6386         if ($scope.dueDate) {
6387             return (dt > $scope.dueDate);
6388         }
6389         return false;
6390     }
6391
6392     function isDueDate(dt) {
6393         if ($scope.dueDate) {
6394             return (dt.getTime() === $scope.dueDate.getTime());
6395         }
6396         return false;
6397     }
6398
6399     var isDisabled = function (date, currentMonthDate) {
6400         if ($attrs.from && !angular.isDate($scope.fromDate)) {
6401             return true;
6402         }
6403         if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
6404             return true;
6405         }
6406         if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
6407             return true;
6408         }
6409         if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
6410             return true;
6411         }
6412         return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
6413             date: date
6414         })));
6415     };
6416     
6417     var isDisabledMonth = function (date, currentMonthDate) {
6418         if ($attrs.from && !angular.isDate($scope.fromDate)) {
6419             return true;
6420         }
6421         if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
6422             return true;
6423         }
6424         if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
6425             return true;
6426         }
6427         return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
6428             date: date
6429         })));
6430     };    
6431          
6432     var compare = function (date1, date2) {
6433         return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
6434     };
6435
6436     function isMinDateAvailable(startDate, endDate) {
6437         if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
6438             $scope.disablePrev = true;
6439             $scope.visibilityPrev = "hidden";
6440         } else {
6441             $scope.disablePrev = false;
6442             $scope.visibilityPrev = "visible";
6443         }
6444     }
6445     
6446     function isMaxDateAvailable(startDate, endDate) {
6447         if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
6448             $scope.disableNext = true;
6449             $scope.visibilityNext = "hidden";
6450         } else {
6451             $scope.disableNext = false;
6452             $scope.visibilityNext = "visible";
6453         }
6454     }    
6455     
6456     function isYearInRange(currentYear) {
6457             
6458         if ($scope.minDate && currentYear === $scope.minDate.getFullYear()) {
6459             $scope.disablePrev = true;
6460             $scope.visibilityPrev = "hidden";
6461         } else {
6462             $scope.disablePrev = false;
6463             $scope.visibilityPrev = "visible";
6464         }
6465         
6466         if ($scope.maxDate && currentYear === $scope.maxDate.getFullYear()) {
6467             $scope.disableNext = true;
6468             $scope.visibilityNext = "hidden";
6469         } else {
6470             $scope.disableNext = false;
6471             $scope.visibilityNext = "visible";
6472         }
6473         
6474     }    
6475
6476     this.focusNextPrev = function(b2bMonthpickerPopupTemplate,init){
6477         if(init){
6478             if (!$scope.disablePrev){
6479                 b2bMonthpickerPopupTemplate[0].querySelector('th.prev').focus();
6480             }else if (!$scope.disableNext){
6481                 b2bMonthpickerPopupTemplate[0].querySelector('th.next').focus();
6482             }else{
6483                 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
6484             }
6485         }else{
6486             if ($scope.disableNext || $scope.disablePrev){
6487                 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
6488             }       
6489         }    
6490     };
6491
6492     function getLabel(label) {
6493         if (label) {
6494             var labelObj = {
6495                 pre: label.substr(0, 1).toUpperCase(),
6496                 post: label
6497             };
6498             return labelObj;
6499         }
6500         return;
6501     }
6502
6503     function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
6504         return {
6505             date: date,
6506             label: dateFilter(date, dayFormat),
6507             header: dateFilter(date, dayHeaderFormat),
6508             selected: !!isSelected,
6509             fromDate: !!isFromDate,
6510             dateRange: !!isDateRange,
6511             oldMonth: !!isOld,
6512             nextMonth: !!isNew,
6513             disabled: !!isDisabled,
6514             dueDate: !!dueDate,
6515             pastDue: !!pastDue,
6516             focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
6517         };
6518     }
6519     
6520     this.modes = [
6521         {
6522             name: 'day',
6523             getVisibleDates: function (date) {
6524                 var year = date.getFullYear(),
6525                     month = date.getMonth(),
6526                     firstDayOfMonth = new Date(year, month, 1),
6527                     lastDayOfMonth = new Date(year, month + 1, 0);
6528                 var difference = startingDay - firstDayOfMonth.getDay(),
6529                     numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
6530                     firstDate = new Date(firstDayOfMonth),
6531                     numDates = 0;
6532
6533                 if (numDisplayedFromPreviousMonth > 0) {
6534                     firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
6535                     numDates += numDisplayedFromPreviousMonth; // Previous
6536                 }
6537                 numDates += getDaysInMonth(year, month + 1); // Current
6538                 numDates += (7 - numDates % 7) % 7; // Next
6539
6540                 var days = getDates(firstDate, numDates),
6541                     labels = new Array(7);
6542                 for (var i = 0; i < numDates; i++) {
6543                     var dt = new Date(days[i]);
6544                     days[i] = makeDate(dt,
6545                         format.day,
6546                         format.dayHeader,
6547                         isSelected(dt),
6548                         isFromDate(dt),
6549                         isDateRange(dt),
6550                         isOld(dt, date),
6551                         isNew(dt, date),
6552                         isDisabled(dt, date),
6553                         isDueDate(dt),
6554                         isPastDue(dt));
6555                 }
6556                 for (var j = 0; j < 7; j++) {
6557                     labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
6558                 }
6559                 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
6560                 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
6561                 return {
6562                     objects: days,
6563                     title: dateFilter(date, format.dayTitle),
6564                     labels: labels
6565                 };
6566             },
6567             split: 7,
6568             step: {
6569                 months: 1
6570             }
6571         },
6572         {
6573             name: 'month',
6574             getVisibleDates: function(date) {
6575                 var months = [], 
6576                     labels = [], 
6577                     year = date.getFullYear();
6578                     for (var i = 0; i < 12; i++) {
6579                         var dt = new Date(year,i,1);                
6580                         months[i] = makeDate(dt,
6581                                     format.month,
6582                                     format.dayHeader,
6583                                     isSelected(dt), 
6584                                     isFromDate(dt),
6585                                     isDateRange(dt),
6586                                     false,
6587                                     false,
6588                                     isDisabledMonth(dt, date),
6589                                     isDueDate(dt),                                       
6590                                     isPastDue(dt));                                                                                                                                                         
6591                     }
6592                 isYearInRange(year);  
6593                 return {objects: months, title: dateFilter(date, format.year), labels: labels};
6594             },
6595             split:4,
6596             step: {years: 1}
6597         }
6598     ];
6599 }])
6600
6601 .directive('b2bMonthpickerPopup', ['$parse', '$log', '$timeout', '$document', '$documentBind', '$isElement', '$templateCache', '$compile','$interval', 'trapFocusInElement', 'keymap', function ($parse, $log, $timeout, $document, $documentBind, $isElement, $templateCache, $compile, $interval,trapFocusInElement, keymap) {
6602     return {
6603         restrict: 'EA',
6604         scope: {
6605           trigger: '='
6606         },
6607         replace: true,
6608         transclude: true,
6609         templateUrl: function (elem, attr) {
6610             if (attr.inline === 'true') {
6611                 return 'b2bTemplate/monthSelector/monthSelector-popup.html';
6612             }else if (attr.link === 'true') {
6613                 return 'b2bTemplate/monthSelector/monthSelectorLink.html';
6614             }else {
6615                 return 'b2bTemplate/monthSelector/monthSelector.html';
6616             }
6617         },
6618         scope: {},
6619         require: ['b2bMonthpickerPopup', 'ngModel', '?^b2bMonthpickerGroup'],
6620         controller: 'b2bMonthpickerController',
6621         link: function (scope, element, attrs, ctrls) {
6622             var MonthpickerCtrl = ctrls[0],
6623                 ngModel = ctrls[1],
6624                 b2bMonthpickerGroupCtrl = ctrls[2];
6625             var b2bMonthpickerPopupTemplate;
6626
6627             if (!ngModel) {
6628                 $log.error("ng-model is required.");
6629                 return; // do nothing if no ng-model
6630             }
6631
6632             // Configuration parameters
6633             var mode = scope.mode,
6634                 selected;
6635             scope.isOpen = false;
6636
6637             scope.headers = [];
6638             scope.footers = [];
6639             scope.triggerInterval=undefined;
6640
6641
6642             if (b2bMonthpickerGroupCtrl) {
6643                 b2bMonthpickerGroupCtrl.registerMonthpickerScope(scope);
6644             }
6645
6646             element.bind('keydown', function (ev) {                   
6647                 if (!ev.keyCode) {
6648                     if (ev.which) {
6649                         ev.keyCode = ev.which;
6650                     } else if (ev.charCode) {
6651                         ev.keyCode = ev.charCode;
6652                     }
6653                 }                                
6654                 if(ev.keyCode === keymap.KEY.ESC)
6655                 {
6656                     scope.isOpen = false;
6657                     toggleCalendar(scope.isOpen);
6658                     scope.$apply();
6659                 }
6660             });
6661             
6662             element.find('button').bind('click', function () {
6663                 onClicked();                
6664             });
6665
6666             element.find('a').bind('click', function () {
6667                 onClicked();                
6668             });
6669
6670             
6671             element.find('input').bind('click', function () {
6672                 onClicked();
6673             });
6674
6675             var onClicked = function() {        
6676                 if (!scope.ngDisabled) {
6677                     scope.isOpen = !scope.isOpen;
6678                     toggleCalendar(scope.isOpen);                    
6679                     MonthpickerCtrl.updatePosition(b2bMonthpickerPopupTemplate);
6680                     scope.$apply();
6681                 }
6682             };
6683         
6684             var toggleCalendar = function (flag) {
6685                 if (!scope.inline) {
6686                     if (flag) {
6687                         b2bMonthpickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/monthSelector/monthSelector-popup.html'));
6688                         b2bMonthpickerPopupTemplate.attr('b2b-trap-focus-inside-element', 'false');
6689                         b2bMonthpickerPopupTemplate.attr('trigger', 'true');
6690                         b2bMonthpickerPopupTemplate = $compile(b2bMonthpickerPopupTemplate)(scope);
6691                         $document.find('body').append(b2bMonthpickerPopupTemplate);
6692                         b2bMonthpickerPopupTemplate.bind('keydown', escPress);
6693                         $timeout(function () {
6694                             scope.getFocus = true;
6695                             scope.trigger=0;
6696                             scope.$apply();
6697                             $timeout(function () {
6698                                 scope.getFocus = false;
6699                                 scope.$apply();
6700                                 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,true);
6701                             }, 100);
6702                         });
6703                         scope.triggerInterval = $interval(function () {
6704                             //This value is updated to trigger init() function of directive on year change.
6705                             scope.trigger=(scope.trigger === 0 ? 1 : 0);
6706                         }, 200);
6707
6708                     } else {
6709                         b2bMonthpickerPopupTemplate.unbind('keydown', escPress);
6710                         if(scope.triggerInterval)
6711                         {
6712                             $interval.cancel(scope.triggerInterval);
6713                             scope.triggerInterval=undefined;
6714                         }
6715                         b2bMonthpickerPopupTemplate.remove();
6716                         if(element.find('button').length > 0){
6717                             element.find('button')[0].focus();
6718                         }else{
6719                             element.find('a')[0].focus();
6720                         }
6721                         
6722                         scope.getFocus = false;
6723                     }
6724                 }
6725             };
6726
6727             var outsideClick = function (e) {
6728                 var isElement = $isElement(angular.element(e.target), element, $document);
6729                 var isb2bMonthpickerPopupTemplate = $isElement(angular.element(e.target), b2bMonthpickerPopupTemplate, $document);
6730                 if (!(isElement || isb2bMonthpickerPopupTemplate)) {
6731                     scope.isOpen = false;
6732                     toggleCalendar(scope.isOpen);
6733                     scope.$apply();
6734                 }
6735             };
6736
6737             var escPress = function (ev) {
6738                 if (!ev.keyCode) {
6739                     if (ev.which) {
6740                         ev.keyCode = ev.which;
6741                     } else if (ev.charCode) {
6742                         ev.keyCode = ev.charCode;
6743                     }
6744                 }
6745                 if (ev.keyCode) {
6746                     if (ev.keyCode === keymap.KEY.ESC) {
6747                         scope.isOpen = false;
6748                         toggleCalendar(scope.isOpen);
6749                         ev.preventDefault();
6750                         ev.stopPropagation();
6751                     } else if (ev.keyCode === 33) {
6752                         !scope.disablePrev && scope.move(-1);
6753                         $timeout(function () {
6754                             scope.getFocus = true;
6755                             scope.$apply();
6756                             $timeout(function () {
6757                                 scope.getFocus = false;
6758                                 scope.$apply();
6759                             }, 100);
6760                         });
6761                         ev.preventDefault();
6762                         ev.stopPropagation();
6763                     } else if (ev.keyCode === 34) {
6764                         !scope.disableNext && scope.move(1);
6765                         $timeout(function () {
6766                             scope.getFocus = true;
6767                             scope.$apply();
6768                             $timeout(function () {
6769                                 scope.getFocus = false;
6770                                 scope.$apply();
6771                             }, 100);
6772                         });
6773                         ev.preventDefault();
6774                         ev.stopPropagation();
6775                     }
6776                     scope.$apply();
6777                 }
6778             };                          
6779                                         
6780             $documentBind.click('isOpen', outsideClick, scope);
6781
6782             scope.$on('$destroy', function () {
6783                 if (scope.isOpen) {
6784                     scope.isOpen = false;
6785                     toggleCalendar(scope.isOpen);
6786                 }
6787             });
6788
6789             scope.resetTime = function (date) {
6790                 if (typeof date === 'string') {
6791                     date = date + 'T12:00:00';
6792                 }
6793                 var dt;
6794                 if (!isNaN(new Date(date))) {
6795                     dt = new Date(date);
6796                     if(scope.mode === 1){
6797                         dt = new Date(dt.getFullYear(), dt.getMonth());
6798                     }else{
6799                         dt = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
6800                     }                                                            
6801                 } else {
6802                     return null;
6803                 }
6804                 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
6805             };
6806             
6807             if (attrs.min) {
6808                 scope.$parent.$watch($parse(attrs.min), function (value) {
6809                     scope.minDate = value ? scope.resetTime(value) : null;
6810                     refill();
6811                 });
6812             }
6813             if (attrs.max) {
6814                 scope.$parent.$watch($parse(attrs.max), function (value) {
6815                     scope.maxDate = value ? scope.resetTime(value) : null;
6816                     refill();
6817                 });
6818             }
6819             if (attrs.due) {
6820                 scope.$parent.$watch($parse(attrs.due), function (value) {
6821                     scope.dueDate = value ? scope.resetTime(value) : null;
6822                     refill();
6823                 });
6824             }
6825             if (attrs.from) {
6826                 scope.$parent.$watch($parse(attrs.from), function (value) {
6827                     scope.fromDate = value ? scope.resetTime(value) : null;
6828                     refill();
6829                 });
6830             }
6831
6832             if (attrs.legendIcon) {
6833                 scope.$parent.$watch(attrs.legendIcon, function (value) {
6834                     scope.legendIcon = value ? value : null;
6835                     refill();
6836                 });
6837             }
6838             if (attrs.legendMessage) {
6839                 scope.$parent.$watch(attrs.legendMessage, function (value) {
6840                     scope.legendMessage = value ? value : null;
6841                     refill();
6842                 });
6843             }
6844             if (attrs.ngDisabled) {
6845                 scope.$parent.$watch(attrs.ngDisabled, function (value) {
6846                     scope.ngDisabled = value ? value : null;
6847                 });
6848             }      
6849             
6850
6851             // Split array into smaller arrays
6852             function split(arr, size) {
6853                 var arrays = [];
6854                 while (arr.length > 0) {
6855                     arrays.push(arr.splice(0, size));
6856                 }
6857                 return arrays;
6858             }
6859             
6860             var moveMonth = function(selectedDate, direction) {
6861                 var step = MonthpickerCtrl.modes[scope.mode].step;
6862                 selectedDate.setDate(1);
6863                 selectedDate.setMonth(selectedDate.getMonth() + direction * (step.months || 0));
6864                 selectedDate.setFullYear(selectedDate.getFullYear() + direction * (step.years || 0));
6865
6866                 return selectedDate;
6867             };            
6868
6869             function refill(date) {
6870                 if (angular.isDate(date) && !isNaN(date)) {
6871                     selected = new Date(date);
6872                 } else {
6873                     if (!selected) {
6874                         selected = new Date();
6875                     }
6876                 }
6877
6878                 if (selected) {                    
6879                     var selectedCalendar;
6880                     if(scope.mode === 1){
6881                         if(!angular.isDate(selected))
6882                            {                           
6883                                 selected = new Date();
6884                            }
6885                         selectedCalendar = moveMonth(angular.copy(selected), -1);
6886                     } else {
6887                         selectedCalendar = angular.copy(selected);
6888                     }
6889                     
6890                     var currentMode = MonthpickerCtrl.modes[mode],
6891                         data = currentMode.getVisibleDates(selected);
6892
6893                     scope.rows = split(data.objects, currentMode.split);
6894             
6895                     var flag=false;
6896                     var startFlag=false;
6897                     var firstSelected = false;
6898                     for(var i=0; i<scope.rows.length; i++)
6899                     {
6900                         for(var j=0; j<scope.rows[i].length; j++)
6901                         {
6902                             if(!scope.rows[i][j].disabled && !firstSelected)
6903                             {
6904                                 firstSelected=true;
6905                                 var firstDay = scope.rows[i][j];
6906                             }
6907
6908                             if(scope.rows[i][j].selected)
6909                             {
6910                                 flag=true;
6911                                 break;
6912                             }                  
6913                         }
6914                         if(flag)
6915                         {
6916                             break;
6917                         }
6918                     }
6919                     if(!flag && firstSelected)
6920                     {
6921                        firstDay.firstFocus=true;
6922                     }
6923
6924                     scope.labels = data.labels || [];
6925                     scope.title = data.title;                    
6926                 }
6927             }
6928
6929             scope.select = function (date,$event) {
6930                 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
6931                 scope.currentDate = dt;
6932                 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
6933                         date: dt
6934                     }) !== false)) {
6935                     if (angular.isNumber(scope.collapseWait)) {
6936                         $timeout(function () {
6937                             scope.isOpen = false;
6938                             toggleCalendar(scope.isOpen);
6939                         }, scope.collapseWait);
6940                     } else {
6941                         scope.isOpen = false;
6942                         toggleCalendar(scope.isOpen);
6943                     }
6944                 }
6945             };
6946
6947             scope.move = function (direction,$event) {
6948                 var step = MonthpickerCtrl.modes[mode].step;
6949                 selected.setDate(1);
6950                 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
6951                 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
6952                 refill();
6953                 scope.getFocus = true;
6954                 $timeout(function () {
6955                     if (attrs.inline === 'true') {
6956                         MonthpickerCtrl.focusNextPrev(element,false); 
6957                     }else{
6958                         MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,false);
6959                     }
6960                 },100);
6961                 $event.preventDefault();
6962                 $event.stopPropagation();
6963             };
6964
6965             scope.$watch('currentDate', function (value) {
6966                 if (angular.isDefined(value) && value !== null) {
6967                     refill(value);
6968                 } else {
6969                     refill();
6970                 }
6971                 ngModel.$setViewValue(value);
6972             });
6973
6974             ngModel.$render = function () {
6975                 scope.currentDate = ngModel.$viewValue;
6976             };
6977
6978             var stringToDate = function (value) {
6979                 if (!isNaN(new Date(value))) {
6980                     value = new Date(value);
6981                 }
6982                 return value;
6983             };
6984             ngModel.$formatters.unshift(stringToDate);
6985         }
6986     };
6987 }])
6988
6989 .directive('b2bMonthpicker', ['$compile', '$log', 'b2bMonthpickerConfig', 'b2bMonthpickerService', function ($compile, $log, b2bMonthpickerConfig, b2bMonthpickerService) {
6990     return {
6991         restrict: 'A',
6992         scope: {
6993             disableDates: '&',
6994             onSelectClose: '&'
6995         },
6996         require: 'ngModel',
6997         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
6998             var dateFormatString = angular.isDefined(attr.dateFormat) ? scope.$parent.$eval(attr.dateFormat) : b2bMonthpickerConfig.dateFormat;
6999             var helperText = angular.isDefined(attr.helperText) ? scope.$parent.$eval(attr.helperText) : b2bMonthpickerConfig.helperText; 
7000             helperText = helperText.replace('$date', '{{dt | date : \'' + dateFormatString + '\'}}');
7001
7002             var descriptionText = angular.isDefined(attr.descriptionText) ? scope.$parent.$eval(attr.descriptionText) : b2bMonthpickerConfig.descriptionText;  
7003
7004
7005             var inline = false;
7006             if (elem.prop('nodeName') !== 'INPUT' && elem.prop('nodeName') !== 'A') {
7007                 inline = true;
7008             }
7009
7010             var selectedDateMessage = "";
7011             
7012             if (elem.prop('nodeName') !== 'A'){
7013                 selectedDateMessage = '<button type="button" class="span12 faux-input" ng-disabled="ngDisabled" aria-describedby="monthpicker-description'+scope.$id+'"><span class="hidden-spoken" aria-live="assertive" aria-atomic="false">' + helperText + '</span></button>';    
7014                 elem.attr('tabindex', '-1'); 
7015                 elem.attr('aria-hidden', 'true');  
7016                 elem.attr('readonly', 'true'); 
7017             }else{
7018                 selectedDateMessage = ''
7019                 elem.attr('aria-label', helperText);
7020             }
7021             
7022             var descriptionTextSpan = '<span class="offscreen-text" id="monthpicker-description'+scope.$id+'">'+descriptionText+'</span>';
7023             elem.removeAttr('b2b-Monthpicker');
7024             elem.removeAttr('ng-model');
7025             elem.removeAttr('ng-disabled');
7026             elem.addClass('Monthpicker-input');
7027             elem.attr('ng-model', 'dt');
7028             elem.attr('aria-describedby', 'monthpicker-description'+scope.$id);
7029             
7030             
7031             
7032             elem.attr('ng-disabled', 'ngDisabled');
7033             elem.attr('b2b-format-date', dateFormatString);
7034
7035             var wrapperElement = angular.element('<div></div>');
7036             wrapperElement.attr('b2b-Monthpicker-popup', '');
7037             wrapperElement.attr('ng-model', 'dt');
7038             if (inline) {
7039                 wrapperElement.attr('inline', inline);
7040             }
7041             if (elem.prop('nodeName') === 'A'){
7042                 wrapperElement.attr('link', true);
7043             }
7044             b2bMonthpickerService.setAttributes(attr, wrapperElement);
7045             b2bMonthpickerService.bindScope(attr, scope);
7046
7047             wrapperElement.html('');
7048             wrapperElement.append(selectedDateMessage);
7049             wrapperElement.append('');
7050             wrapperElement.append(descriptionTextSpan);
7051             wrapperElement.append('');
7052             wrapperElement.append(elem.prop('outerHTML'));
7053
7054             var elm = wrapperElement.prop('outerHTML');
7055             elm = $compile(elm)(scope);
7056             elem.replaceWith(elm);
7057         }],
7058         link: function (scope, elem, attr, ctrl) {
7059             if (!ctrl) {
7060                 $log.error("ng-model is required.");
7061                 return; // do nothing if no ng-model
7062             }
7063             
7064             scope.$watch('dt', function (value) {
7065                 ctrl.$setViewValue(value);
7066             });
7067             ctrl.$render = function () {
7068                 scope.dt = ctrl.$viewValue;
7069             };
7070         }
7071     };
7072 }])
7073
7074 .directive('b2bMonthpickerGroup', [function () {
7075     return {
7076         restrict: 'EA',
7077         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
7078             this.$$headers = [];
7079             this.$$footers = [];
7080             this.registerMonthpickerScope = function (MonthpickerScope) {
7081                 MonthpickerScope.headers = this.$$headers;
7082                 MonthpickerScope.footers = this.$$footers;
7083             };
7084         }],
7085         link: function (scope, elem, attr, ctrl) {}
7086     };
7087 }])
7088
7089 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
7090     return {
7091         restrict: 'A',
7092         require: 'ngModel',
7093         link: function (scope, elem, attr, ctrl) {
7094             var b2bFormatDate = "";
7095             attr.$observe('b2bFormatDate', function (value) {
7096                 b2bFormatDate = value;
7097             });
7098             var dateToString = function (value) {
7099                 if (!isNaN(new Date(value))) {
7100                     return dateFilter(new Date(value), b2bFormatDate);
7101                 }
7102                 return value;
7103             };
7104             ctrl.$formatters.unshift(dateToString);
7105         }
7106     };
7107 }])
7108
7109 .directive('b2bMonthpickerHeader', [function () {
7110     return {
7111         restrict: 'EA',
7112         require: '^b2bMonthpickerGroup',
7113         transclude: true,
7114         replace: true,
7115         template: '',
7116         compile: function (elem, attr, transclude) {
7117             return function link(scope, elem, attr, ctrl) {
7118                 if (ctrl) {
7119                     ctrl.$$headers.push(transclude(scope, function () {}));
7120                 }
7121                 elem.remove();
7122             };
7123         }
7124     };
7125 }])
7126
7127 .directive('b2bMonthpickerFooter', [function () {
7128     return {
7129         restrict: 'EA',
7130         require: '^b2bMonthpickerGroup',
7131         transclude: true,
7132         replace: true,
7133         template: '',
7134         compile: function (elem, attr, transclude) {
7135             return function link(scope, elem, attr, ctrl) {
7136                 if (ctrl) {
7137                     ctrl.$$footers.push(transclude(scope, function () {}));
7138                 }
7139                 elem.remove();
7140             };
7141         }
7142     };
7143 }]);
7144 /**
7145  * @ngdoc directive
7146  * @name Navigation.att:multiLevelNavigation
7147  *
7148  * @description
7149  *  <file src="src/multiLevelNavigation/docs/readme.md" />
7150  *
7151  * @usage
7152  *       <div class="b2b-ml-nav">
7153  *          <ul role="tree">
7154  *             <li aria-label="{{child.name}}" tabindex="-1" b2b-ml-nav="{{child.type}}" role="treeitem" ng-repeat="child in treeStructure">
7155  *                  <a tabindex="-1" href="javascript:void(0);" title="{{child.name}}">{{child.name}}<span><i class="{{child.type=='endNode'?'icon-primary-circle':'icon-primary-collapsed'}}"></i></span></a>
7156  *                      <!-- Below UL tag is RECURSIVE to generate n-childs -->
7157  *                      <ul role="group" ng-if="child.child">
7158  *                          <li aria-label="{{child.name}}" b2b-ml-nav="{{child.type}}" tabindex="-1" role="treeitem" ng-repeat="child in child.child">
7159  *                          <a tabindex="-1" href="javascript:void(0);" title="{{child.name}}">{{child.name}}<span><i class="{{child.type=='endNode'?'icon-primary-circle':'icon-primary-collapsed'}}"></i></span></a>
7160  *                               <!-- RECURSIVE UL tag goes here -->
7161  *                          </li>
7162  *                      </ul>
7163  *             </li>
7164  *           </ul>
7165  *        </div>
7166  *
7167  * @example
7168  *  <section id="code">
7169         <example module="b2b.att">
7170             <file src="src/multiLevelNavigation/docs/demo.html" />
7171             <file src="src/multiLevelNavigation/docs/demo.js" />
7172        </example>
7173     </section>
7174  *
7175  */
7176 angular.module('b2b.att.multiLevelNavigation', ['b2b.att.utilities'])
7177     //directive b2bMlNav Test coverage 100% on 5/13
7178     .directive('b2bMlNav', ['keymap', function (keymap) {
7179         return {
7180             restrict: 'EA',
7181             link: function (scope, element) {
7182                 var rootE, parentE, upE, downE, lastE, homeE, endE;
7183                 //default root tree element tabindex set zero
7184                 if (element.parent().parent().hasClass('b2b-ml-nav') && (element[0].previousElementSibling === null)) {
7185                     element.attr('tabindex', 0);
7186                 }
7187                 //check root via class
7188                 var isRoot = function (elem) {
7189                         if (elem.parent().parent().eq(0).hasClass('b2b-ml-nav')) {
7190                             return true;
7191                         } else {
7192                             return false;
7193                         }
7194
7195                     }
7196                     //for any expandable tree item on click
7197                 var toggleState = function (e) {
7198                                     
7199                     if (angular.element(e.target).attr("b2b-ml-nav") !== "endNode") {
7200                         var eLink = element.find('a').eq(0);
7201                         if (eLink.hasClass('active')) {
7202                             eLink.removeClass('active');
7203                             eLink.parent().attr("aria-expanded", "false");
7204                             eLink.find('i').eq(0).removeClass('icon-primary-expanded');
7205                             eLink.find('i').eq(0).addClass('icon-primary-collapsed');
7206                         } else {
7207                             eLink.addClass('active');
7208                             eLink.parent().attr("aria-expanded", "true");
7209                             eLink.find('i').eq(0).removeClass('icon-primary-collapsed');
7210                             eLink.find('i').eq(0).addClass('icon-primary-expanded');
7211                         }
7212                     }
7213                 };
7214                 //function finds the main root-item from particular tree-group
7215                 var findRoot = function (elem) {
7216                     if (isRoot(elem)) {
7217                         rootE = elem;
7218                         return;
7219                     }
7220                     if (elem.attr("b2b-ml-nav") === "middleNode" || elem.attr("b2b-ml-nav") === "endNode") {
7221                         parentE = elem.parent().parent();
7222                     } else {
7223                         parentE = elem;
7224                     }
7225                     if (parentE.attr("b2b-ml-nav") === "rootNode") {
7226                         rootE = parentE;
7227                     } else {
7228                         findRoot(parentE);
7229                     }
7230                 };
7231                 //finds the last visible node of the previous tree-group
7232                 var findPreActive = function (elem) {
7233                     if (!(elem.hasClass("active"))) {
7234                         return;
7235                     } else {
7236                         var childElems = angular.element(elem[0].nextElementSibling.children);
7237                         lastE = angular.element(childElems[childElems.length - 1]);
7238                         if (lastE.attr("b2b-ml-nav") === "middleNode" && lastE.find('a').eq(0).hasClass('active')) {
7239                             findPreActive(lastE.find('a').eq(0));
7240                         }
7241                         upE = lastE;
7242                     }
7243                 };
7244                 //find above visible link
7245                 var findUp = function (elem) {
7246                     if (elem[0].previousElementSibling !== null) {
7247                         upE = angular.element(elem[0].previousElementSibling);
7248                     } else {
7249                         upE = elem.parent().parent();
7250                     }
7251                     if (isRoot(elem) || (upE.attr('b2b-ml-nav') === "middleNode" && upE[0] !== elem.parent().parent()[0])) {
7252                         findPreActive(upE.find('a').eq(0)); 
7253                     }
7254                 };
7255                 //find below visible link
7256                 var findDown = function (elem) {
7257                     if (elem.hasClass('active')) {
7258                         downE = elem.next().find('li').eq(0);
7259                     } else {
7260                         if (elem.parent().next().length !== 0) {
7261                             downE = elem.parent().next().eq(0);
7262                         } else {
7263                             if (elem.parent().parent().parent().next().length !== 0) {
7264                                 downE = elem.parent().parent().parent().next().eq(0);
7265                                 return;
7266                             }
7267                             downE = elem.parent().eq(0);
7268                         }
7269                     }
7270                 };
7271                 //finds last root-group element of the tree
7272                 var findEnd = function (elem) {
7273                     findRoot(elem);
7274                     endE = angular.element(rootE.parent()[0].children[rootE.parent()[0].children.length - 1]);
7275                 };
7276                 //finds first root element of tree
7277                 var findHome = function (elem) {
7278                     findRoot(elem);
7279                     homeE = angular.element(rootE.parent()[0].children[0]);
7280                 };
7281                 element.bind('click', function (e) {
7282                     if(element.attr("b2b-ml-nav") !== "endNode") { 
7283                         toggleState(e); 
7284                     }
7285                     if (rootE==undefined){
7286                         findRoot(element);
7287                     }
7288                     var currSelected = rootE.parent()[0].querySelector('.selected');
7289                     if(currSelected){
7290                         angular.element(currSelected).removeClass('selected');
7291                     }                    
7292                     element.find('a').eq(0).addClass('selected');
7293                     e.stopPropagation();
7294                 });
7295                 element.bind('focus', function (e) {
7296                     if(element.attr("b2b-ml-nav") !== "endNode") {
7297                         if(element.find('a').eq(0).hasClass('active')) {
7298                             element.attr("aria-expanded", true);
7299                         }
7300                         else {
7301                             element.attr("aria-expanded", false);
7302                         }
7303                         
7304                     }
7305                 })
7306                 //Keyboard functionality approach:
7307                 //find keycode
7308                 //set set tabindex -1 on the current focus element
7309                 //find the next element to be focussed, set tabindex 0 and throw focus
7310                 element.bind('keydown', function (evt) {
7311                     switch (evt.keyCode) {
7312                     case keymap.KEY.ENTER:
7313                     case keymap.KEY.SPACE:
7314                         element.triggerHandler('click');
7315                         evt.stopPropagation();
7316                         evt.preventDefault();
7317                         break;
7318                     case keymap.KEY.END:
7319                         evt.preventDefault();
7320                         element.attr('tabindex', -1);
7321                         findEnd(element);
7322                         endE.eq(0).attr('tabindex', 0);
7323                         endE[0].focus();
7324                         evt.stopPropagation();
7325                         break;
7326                     case keymap.KEY.HOME:
7327                         evt.preventDefault();
7328                         element.attr('tabindex', -1);
7329                         findHome(element);
7330                         homeE.eq(0).attr('tabindex', 0);
7331                         homeE[0].focus();
7332                         evt.stopPropagation();
7333                         break;
7334                     case keymap.KEY.LEFT:
7335                         evt.preventDefault();
7336                         if (!isRoot(element)) {
7337                             element.attr('tabindex', -1);
7338                             parentE = element.parent().parent();
7339                             parentE.eq(0).attr('tabindex', 0);
7340                             parentE[0].focus();
7341                             parentE.eq(0).triggerHandler('click');
7342                         } else {
7343                             if (element.find('a').eq(0).hasClass('active')) {
7344                                 element.triggerHandler('click');
7345                             }
7346                         }
7347                         evt.stopPropagation();
7348                         break;
7349                     case keymap.KEY.UP:
7350                         evt.preventDefault();
7351                         if (!(isRoot(element) && element[0].previousElementSibling === null)) {
7352                             element.attr('tabindex', -1);
7353                             findUp(element);
7354                             upE.eq(0).attr('tabindex', 0);
7355                             upE[0].focus();
7356                         }
7357                         evt.stopPropagation();
7358                         break;
7359                     case keymap.KEY.RIGHT:
7360                         evt.preventDefault();
7361                         if (element.attr("b2b-ml-nav") !== "endNode") {
7362                             if (!element.find('a').eq(0).hasClass('active')) {
7363                                 element.triggerHandler('click');
7364                             }
7365                             element.attr('tabindex', -1);
7366                             findDown(element.find('a').eq(0));
7367                             downE.eq(0).attr('tabindex', 0);
7368                             downE[0].focus();
7369                         }
7370                         evt.stopPropagation();
7371                         break;
7372                     case keymap.KEY.DOWN:
7373                         evt.preventDefault();
7374                         element.attr('tabindex', -1);
7375                         if (!(element.attr("b2b-ml-nav") === "middleNode" && element.find('a').eq(0).hasClass('active')) && (element.next().length === 0)) {
7376                             if(element.parent().parent().attr("b2b-ml-nav") !== "rootNode" && element.parent().parent()[0].nextElementSibling !== null)
7377                             {
7378                                 findDown(element.find('a').eq(0));
7379                                 downE.eq(0).attr('tabindex', 0);
7380                                 downE[0].focus();
7381                                 evt.stopPropagation();
7382                                 break;
7383                             }
7384                             findRoot(element);
7385                             if (!(rootE.next().length === 0)) {
7386                                 rootE.next().eq(0).attr('tabindex', 0);
7387                                 rootE.next()[0].focus();
7388                             } else {
7389                                 rootE.eq(0).attr('tabindex', 0);
7390                                 rootE[0].focus();
7391                             }
7392                             evt.stopPropagation();
7393                             break;
7394                         }
7395                         findDown(element.find('a').eq(0));
7396                         downE.eq(0).attr('tabindex', 0);
7397                         downE[0].focus();
7398                         evt.stopPropagation();
7399                         break;
7400                     default:
7401                         break;
7402                     }
7403                 });
7404             }
7405         };
7406     }]);
7407 /**
7408  * @ngdoc directive
7409  * @name Tabs, tables & accordions.att:multipurposeExpander
7410  *
7411  * @description
7412  *  <file src="src/multipurposeExpander/docs/readme.md" />
7413  *
7414  * @usage
7415  * <!--With Close Other -->
7416  * <b2b-expander-group close-others="true">
7417  *  <b2b-expanders class="mpc-expanders" is-open="testmpc">            
7418  *      <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc, 'b2b-toggle-header-inactive': testmpc } ">Heading content goes here</b2b-expander-heading>               
7419  *      <b2b-expander-body>
7420             <p>body content goes here</p>
7421         </b2b-expander-body>
7422  *  </b2b-expanders>
7423  *  </b2b-expander-group>
7424  *  
7425  * <!-- Without Close Other -->
7426  *  <b2b-expanders class="mpc-expanders" is-open="testmpc2">            
7427  *      <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc2, 'b2b-toggle-header-inactive': testmpc2 } ">Heading content goes here</b2b-expander-heading>               
7428  *      <b2b-expander-body>
7429             <p>body content goes here</p>
7430         </b2b-expander-body>
7431  *  </b2b-expanders>
7432  *  
7433  * @example
7434  *  <section id="code">
7435         <example module="b2b.att.multipurposeExpander">
7436             <file src="src/multipurposeExpander/docs/demo.html" />
7437             <file src="src/multipurposeExpander/docs/demo.js" />
7438         </example>
7439     </section>
7440  *
7441  */
7442
7443 angular.module('b2b.att.multipurposeExpander', ['b2b.att', 'b2b.att.collapse'])
7444 .directive('b2bExpanderGroup', function () {
7445     return {
7446         restrict: 'EA',
7447         transclude: true,
7448         template: "<ng-transclude></ng-transclude>",
7449         controller:['$scope','$attrs', function($scope,$attrs){
7450             this.groups = [];
7451             this.index = -1;            
7452             this.scope = $scope;
7453             
7454             this.addGroup = function (groupScope) {
7455                 var that = this;
7456                 groupScope.index = this.groups.length;
7457                 this.groups.push(groupScope);
7458                 if(this.groups.length > 0){
7459                     this.index = 0;
7460                 }
7461                 groupScope.$on('$destroy', function () {
7462                 that.removeGroup(groupScope);
7463             });
7464             };
7465
7466             this.closeOthers = function (openGroup) {
7467                 var closeOthers = angular.isDefined($attrs.closeOthers);
7468                 if (closeOthers && !$scope.forceExpand) {
7469                     angular.forEach(this.groups, function (group) {
7470                         if (group !== openGroup) {
7471                             group.isOpen = false;
7472                         }
7473                     });
7474                 }
7475                 if (this.groups.indexOf(openGroup) === (this.groups.length - 1) && $scope.forceExpand) {
7476                     $scope.forceExpand = false;
7477                 }
7478             };
7479             this.removeGroup = function (group) {
7480             var index = this.groups.indexOf(group);
7481             if (index !== -1) {
7482                 this.groups.splice(this.groups.indexOf(group), 1);
7483             }
7484         };
7485         }]
7486        
7487     };
7488     
7489 })
7490 .directive('b2bExpanders', function () {
7491     return{
7492         restrict: 'EA',
7493         replace: true,
7494         require:['b2bExpanders','?^b2bExpanderGroup'],
7495         transclude: true,
7496         scope:{isOpen:'=?'},
7497         template: "<div ng-transclude></div>",
7498         controller: ['$scope', function ($scope){
7499                 var bodyScope = null;
7500                 var expanderScope = null;
7501                 this.isOpened = function(){                
7502                     if($scope.isOpen)
7503                     {
7504                         return  true;
7505                     }else
7506                     {
7507                         return false;
7508                     }
7509                 };                
7510                 this.setScope = function (scope) {
7511                     bodyScope = scope; 
7512                     bodyScope.isOpen = $scope.isOpen;                   
7513                 };                
7514                 this.setExpanderScope = function (scope) {
7515                     expanderScope = scope;                                   
7516                 };
7517                 this.toggle = function () {
7518                     $scope.isOpen = bodyScope.isOpen = !bodyScope.isOpen;                    
7519                     return bodyScope.isOpen;
7520                     
7521                 };
7522                 this.watchToggle = function(io){ 
7523                     bodyScope.isOpen = io;
7524                     expanderScope.updateIcons(io);
7525                 };  
7526             }],
7527         link: function (scope, elem, attr, myCtrl)
7528         {
7529             //scope.isOpen = false; 
7530             if(myCtrl[1]){
7531                 myCtrl[1].addGroup(scope);
7532             }
7533             scope.$watch('isOpen', function(val){                               
7534                 myCtrl[0].watchToggle(scope.isOpen);
7535                 if(val && myCtrl[1]){
7536                     myCtrl[1].closeOthers(scope);
7537                 }
7538             });            
7539         }
7540     };
7541 })
7542
7543 .directive('b2bExpanderHeading', function () {
7544     return{
7545         require: "^b2bExpanders",
7546         restrict: 'EA',
7547         replace: true,
7548         transclude: true,
7549         scope: true,
7550         template: "<div style='padding:10px 10px 10px 0 !important' ng-transclude></div>"
7551     };
7552 })
7553
7554 .directive('b2bExpanderBody', function () {
7555     return{
7556         restrict: 'EA',
7557         require: "^b2bExpanders",
7558         replace: true,
7559         transclude: true,
7560         scope: {},
7561         template: "<div b2b-collapse='!isOpen' ><div ng-transclude></div></div>",
7562         link: function (scope, elem, attr, myCtrl) {
7563             scope.isOpen = false;
7564             myCtrl.setScope(scope);
7565         }
7566     };
7567 })
7568
7569 .directive('b2bExpanderToggle', function () {
7570     return{
7571         restrict: 'EA',
7572         require: "^b2bExpanders",
7573         scope: {
7574             expandIcon: '@',
7575             collapseIcon: '@'
7576         },
7577         
7578         link: function (scope, element, attr, myCtrl)
7579         {
7580             myCtrl.setExpanderScope(scope);
7581             var isOpen = myCtrl.isOpened();   
7582
7583             scope.setIcon = function () {
7584                 element.attr("role", "button");
7585
7586                 if (scope.expandIcon && scope.collapseIcon)
7587                 {
7588                     if (isOpen) {
7589                         element.removeClass(scope.expandIcon);
7590                         element.addClass(scope.collapseIcon);
7591
7592                         element.attr("aria-expanded", "true");
7593                     }
7594                     else {
7595                         element.removeClass(scope.collapseIcon);
7596                         element.addClass(scope.expandIcon);
7597
7598                         element.attr("aria-expanded", "false");
7599                     }
7600                 }                                                               
7601             };
7602             
7603             element.bind('click', function (){
7604                 scope.toggleit();
7605             });
7606             scope.updateIcons = function(nStat){
7607                 isOpen = nStat;
7608                 scope.setIcon();                
7609             };
7610             scope.toggleit = function (){
7611                 isOpen = myCtrl.toggle();
7612                 scope.setIcon();
7613                 scope.$apply();
7614             };                    
7615             scope.setIcon();
7616         }
7617     };
7618 });
7619 /**
7620  * @ngdoc directive
7621  * @name Messages, modals & alerts.att:notesMessagesAndErrors
7622  *
7623  * @description
7624  *  <file src="src/notesMessagesAndErrors/docs/readme.md" />
7625  *
7626  * @usage
7627  *  See Demo
7628  *
7629  * @example
7630  *  <section id="code">
7631         <example module="b2b.att">
7632             <file src="src/notesMessagesAndErrors/docs/demo.html" />
7633             <file src="src/notesMessagesAndErrors/docs/demo.js" />
7634        </example>
7635         </section>
7636  *
7637  */
7638 angular.module('b2b.att.notesMessagesAndErrors', []);
7639 /** 
7640  * @ngdoc directive 
7641  * @name Template.att:Notification Card
7642  * 
7643  * @description 
7644  *  <file src="src/notificationCardTemplate/docs/readme.md" /> 
7645  * 
7646  * @example 
7647  *  <section id="code"> 
7648         <b>HTML + AngularJS</b> 
7649         <example module="b2b.att"> 
7650             <file src="src/notificationCardTemplate/docs/demo.html" /> 
7651             <file src="src/notificationCardTemplate/docs/demo.js" /> 
7652        </example> 
7653     </section>    
7654  * 
7655  */
7656 angular.module('b2b.att.notificationCardTemplate', [])
7657   
7658 /** 
7659  * @ngdoc directive 
7660  * @name Template.att:Order Confirmation Template
7661  * 
7662  * @description 
7663  *  <file src="src/orderConfirmationTemplate/docs/readme.md" /> 
7664  * 
7665  * @example 
7666  *  <section id="code"> 
7667         <b>HTML + AngularJS</b> 
7668         <example module="b2b.att"> 
7669             <file src="src/orderConfirmationTemplate/docs/demo.html" /> 
7670             <file src="src/orderConfirmationTemplate/docs/demo.js" /> 
7671        </example> 
7672     </section>    
7673  * 
7674  */
7675 angular.module('b2b.att.orderConfirmationTemplate', []);
7676   
7677 /**
7678  * @ngdoc directive
7679  * @name Navigation.att:pagination
7680  *
7681  * @description
7682  *  <file src="src/pagination/docs/readme.md" />
7683  * @param {int} total-pages - Total # of pages, set in your controller $scope
7684  * @param {int} current-page - Current selected page, set in your controller $scope
7685  * @param {function} click-handler - Handler function on click of page number, defined in your controller $scope
7686  * @param {string} input-id - _UNIQUE ID_ __MUST__ be provided for 508 compliance, set in your HTML as static text
7687  * @param {string} input-class - optional class that can be given to use for the go to page container
7688  *
7689  * @usage
7690  *   <div b2b-pagination="" input-id="goto-page-2" total-pages="totalPages1" current-page="currentPage1" click-handler="customHandler"></div> 
7691  *
7692  * @example
7693  *  <section id="code">
7694         <example module="b2b.att">
7695             <file src="src/pagination/docs/demo.html" />
7696             <file src="src/pagination/docs/demo.js" />
7697        </example>
7698         </section>
7699  *
7700  */
7701 angular.module('b2b.att.pagination', ['b2b.att.utilities', 'ngTouch'])
7702     .directive('b2bPagination', ['b2bUserAgent', 'keymap', '$window', '$timeout', function (b2bUserAgent, keymap, $window, $timeout) {
7703         return {
7704             restrict: 'A',
7705             scope: {
7706                 totalPages: '=',
7707                 currentPage: '=',
7708                 clickHandler: '=?',
7709                 inputId: '=',
7710                 isDroppable: '=?'
7711             },
7712             replace: true,
7713             templateUrl: 'b2bTemplate/pagination/b2b-pagination.html',
7714             link: function (scope, elem, attr) {
7715                 scope.isMobile = b2bUserAgent.isMobile();
7716                 scope.notMobile = b2bUserAgent.notMobile();
7717                 scope.focusedPage;
7718                 scope.meanVal = 3;
7719                 scope.inputClass = attr.inputClass;
7720                 scope.droppableAttribute = scope.isDroppable ? true : false;
7721                 scope.$watch('totalPages', function (value) {
7722                     if (angular.isDefined(value) && value !== null) {
7723                         scope.pages = [];
7724                         if (value < 1) {
7725                             scope.totalPages = 1;
7726                             return;
7727                         }
7728                         if (value <= 10) {
7729                             for (var i = 1; i <= value; i++) {
7730                                 scope.pages.push(i);
7731                             }
7732                         } else if (value > 10) {
7733                             var midVal = Math.ceil(value / 2);
7734                             scope.pages = [midVal - 2, midVal - 1, midVal, midVal + 1, midVal + 2];
7735                         }
7736                         if(scope.currentPage === undefined || scope.currentPage === 1)
7737                         {
7738                             currentPageChanged(1);
7739                         }
7740                     }
7741                 });
7742                 scope.$watch('currentPage', function (value) {
7743                     currentPageChanged(value);
7744                     callbackHandler(value);
7745                 });
7746                 var callbackHandler = function (num) {
7747                     if (angular.isFunction(scope.clickHandler)) {
7748                         scope.clickHandler(num);
7749                     }
7750                 };
7751                 var getBoundary = function(value){
7752                     if ( value < 100 ) {
7753                         return 5;
7754                     } else if ( 100 <= value && value < 1000 ) {
7755                         return 4;
7756                     } else if ( 1000 <= value ) {
7757                         return 3;
7758                     } else {
7759                         return 5; // error
7760                     }
7761                 };
7762                 function currentPageChanged(value) {
7763                     if (angular.isDefined(value) && value !== null) {
7764                         if (!value || value < 1) {
7765                             value = 1;
7766                         }
7767                         if (value > scope.totalPages) {
7768                             value = scope.totalPages;
7769                         }
7770                         if (scope.currentPage !== value) {
7771                             scope.currentPage = value;
7772                             callbackHandler(scope.currentPage);
7773                         }
7774                         if (scope.totalPages > 10) {
7775                             var val = parseInt(value);
7776                             scope.boundary = getBoundary(val);
7777                             if (val <= 6) { // Left (first) section
7778                                 scope.pages = [1, 2, 3, 4, 5, 6, 7];
7779                             } else if ( val <= (scope.totalPages - scope.boundary) ) { // Middle section
7780                                 if ( 7 <= val && val < 9 ) {
7781                                     if(scope.totalPages < 100) {
7782                                         scope.pages = [val - 3, val - 2, val - 1, val, val + 1, val + 2];
7783                                     } else if(scope.totalPages < 1000) {
7784                                         scope.pages = [val - 2, val - 1, val, val + 1, val + 2];
7785                                     } else if(scope.totalPages < 1000) {
7786                                         scope.pages = [val - 2, val - 1, val, val + 1, val + 2];
7787                                     }
7788                                 } else if ( 9 <= val && val < 100 ) {
7789                                     scope.pages = [val - 3, val - 2, val - 1, val, val + 1, val + 2];
7790                                 } else if ( 100 <= val && val < 1000 ) {
7791                                     scope.pages = [val - 2, val - 1, val, val + 1];
7792                                 } else if ( 1000 <= val ) {
7793                                     scope.pages = [val - 1, val, val + 1];
7794                                 }
7795                             } else if ( (scope.totalPages - scope.boundary) < val ) { // Right (last) section
7796                                 if ( val < 100 ) {
7797                                     scope.pages = [scope.totalPages - 5, scope.totalPages - 4, scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7798                                 } else if ( 100 <= val && val < 1000 ) {
7799                                     scope.pages = [scope.totalPages - 4, scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7800                                 } else if ( 1000 <= val ) {
7801                                     scope.pages = [scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7802                                 }
7803                             }
7804                         }
7805                         if (scope.isMobile) {
7806                             var inWidth = $window.innerWidth;
7807                             var viewLimit = 7;
7808                             if (inWidth <= 400) {
7809                                 viewLimit = 7;
7810                             } else if (inWidth > 400 && inWidth < 500) {
7811                                 viewLimit = 9;
7812                             } else if (inWidth >= 500 && inWidth < 600) {
7813                                 viewLimit = 11;
7814                             } else if (inWidth >= 600 && inWidth < 700) {
7815                                 viewLimit = 13;
7816                             } else if (inWidth >= 700 && inWidth < 800) {
7817                                 viewLimit = 15;
7818                             }
7819
7820                             var val = parseInt(value);
7821
7822                             scope.meanVal = Math.floor(viewLimit / 2);
7823                             var lowerLimit = (val - scope.meanVal) < 1 ? 1 : val - scope.meanVal;
7824                             var upperLimit = (lowerLimit + viewLimit - 1) > scope.totalPages ? scope.totalPages : lowerLimit + viewLimit - 1;
7825                             scope.pages = [];
7826                             for (var i = lowerLimit; i <= upperLimit; i++) {
7827                                 scope.pages.push(i);
7828                             }
7829                         }
7830                     }
7831                 }
7832                 scope.gotoKeyClick = function (keyEvent) {
7833                   if (keyEvent.which === keymap.KEY.ENTER) {
7834                     scope.gotoBtnClick()
7835                   }
7836                 }
7837                 scope.gotoBtnClick = function () {
7838                     currentPageChanged(parseInt(scope.gotoPage)); 
7839                     callbackHandler(scope.currentPage);
7840                     var qResult = elem[0].querySelector('button');
7841                     angular.element(qResult).attr('disabled','true');
7842                     $timeout(function(){
7843                         elem[0].querySelector('.b2b-pager__item--active').focus();
7844                     }, 50); 
7845                     scope.gotoPage = null;               
7846                 }
7847                 scope.onfocusIn = function(evt)
7848                 {
7849                     var qResult = elem[0].querySelector('button');
7850                     angular.element(qResult).removeAttr('disabled');
7851                 }
7852                 scope.onfocusOut = function(evt)
7853                 {
7854                     if(evt.target.value === "")
7855                     {
7856                         var qResult = elem[0].querySelector('button');
7857                         angular.element(qResult).attr('disabled','true');
7858                     }                    
7859                 }
7860                 scope.next = function (event) {
7861                     if (event != undefined) {
7862                         event.preventDefault();
7863                     }
7864                     if (scope.currentPage < scope.totalPages) {
7865                         scope.currentPage += 1;
7866                         callbackHandler(scope.currentPage);
7867                     }
7868                 };
7869                 scope.prev = function (event) {
7870                     if (event != undefined) {
7871                         event.preventDefault();
7872                     }
7873                     if (scope.currentPage > 1) {
7874                         scope.currentPage -= 1;
7875                         callbackHandler(scope.currentPage);
7876                     }
7877                 };
7878                 scope.selectPage = function (value, event) {
7879                     event.preventDefault();
7880                     scope.currentPage = value;
7881                     scope.focusedPage = value;
7882                     callbackHandler(scope.currentPage);
7883                 };
7884                 scope.checkSelectedPage = function (value) {
7885                     if (scope.currentPage === value) {
7886                         return true;
7887                     }
7888                     return false;
7889                 };
7890                 scope.isFocused = function (page) {
7891                     return scope.focusedPage === page;
7892                 };
7893             }
7894         };
7895     }]);
7896
7897 /**
7898  * @ngdoc directive
7899  * @name Navigation.att:paneSelector
7900  *
7901  * @description
7902  *  <file src="src/paneSelector/docs/readme.md" />
7903  *
7904  * @usage
7905  *  Please refer demo.html tab in Example section below.
7906  *
7907  * @example
7908     <section id="code">
7909         <b>HTML + AngularJS</b>
7910         <example module="b2b.att">
7911             <file src="src/paneSelector/docs/demo.html" />
7912             <file src="src/paneSelector/docs/demo.js" />
7913         </example>
7914     </section>
7915  */
7916
7917 angular.module('b2b.att.paneSelector', ['b2b.att.tabs', 'b2b.att.utilities'])
7918
7919 .filter('paneSelectorSelectedItemsFilter', [function () {
7920     return function (listOfItemsArray) {
7921
7922         if (!listOfItemsArray) {
7923             listOfItemsArray = [];
7924         }
7925
7926         var returnArray = [];
7927
7928         for (var i = 0; i < listOfItemsArray.length; i++) {
7929             if (listOfItemsArray[i].isSelected) {
7930                 returnArray.push(listOfItemsArray[i]);
7931             }
7932         }
7933
7934         return returnArray;
7935     };
7936 }])
7937
7938 .filter('paneSelectorFetchChildItemsFilter', [function () {
7939     return function (listOfItemsArray) {
7940
7941         if (!listOfItemsArray) {
7942             listOfItemsArray = [];
7943         }
7944
7945         var returnArray = [];
7946
7947         for (var i = 0; i < listOfItemsArray.length; i++) {
7948             for (var j = 0; j < listOfItemsArray[i].childItems.length; j++) {
7949                 returnArray.push(listOfItemsArray[i].childItems[j]);
7950             }
7951         }
7952
7953         return returnArray;
7954     };
7955 }])
7956
7957 .directive('b2bPaneSelector', [function () {
7958     return {
7959         restrict: 'AE',
7960         replace: true,
7961         templateUrl: 'b2bTemplate/paneSelector/paneSelector.html',
7962         transclude: true,
7963         scope: {}
7964     };
7965 }])
7966
7967 .directive('b2bPaneSelectorPane', [ function () {
7968     return {
7969         restrict: 'AE',
7970         replace: true,
7971         templateUrl: 'b2bTemplate/paneSelector/paneSelectorPane.html',
7972         transclude: true,
7973         scope: {}
7974     };
7975 }])
7976
7977 .directive('b2bTabVertical', ['$timeout', 'keymap', function ($timeout, keymap) {
7978     return {
7979         restrict: 'A',
7980         require: '^b2bTab',
7981         link: function (scope, element, attr, b2bTabCtrl) {
7982
7983             if (!b2bTabCtrl) {
7984                 return;
7985             }
7986
7987             // retreive the isolateScope
7988             var iScope = angular.element(element).isolateScope();
7989
7990             $timeout(function () {
7991                 angular.element(element[0].querySelector('a')).unbind('keydown');
7992                 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
7993
7994                     if (!(evt.keyCode)) {
7995                         evt.keyCode = evt.which;
7996                     }
7997
7998                     switch (evt.keyCode) {
7999                         case keymap.KEY.DOWN:
8000                             evt.preventDefault();
8001                             iScope.nextKey();
8002                             break;
8003
8004                         case keymap.KEY.UP:
8005                             evt.preventDefault();
8006                             iScope.previousKey();
8007                             break;
8008
8009                         default:;
8010                     }
8011                 });
8012             });
8013         }
8014     };
8015 }]);
8016 /**
8017  * @ngdoc directive
8018  * @name Forms.att:phoneNumberInput
8019  *
8020  * @description
8021  *  <file src="src/phoneNumberInput/docs/readme.md" />
8022  *
8023  * @usage
8024 <form name="userForm1">
8025     <div class="form-row" ng-class="{'error':!userForm1.text.$valid && userForm1.text.$dirty}">
8026         <label>Phone Mask<span style="color:red">*</span>: (npa) nxx-line &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Model Value: {{mask.text}}</label>
8027         <div>
8028             <input b2b-phone-mask="phoneMask" name="text" ng-model="mask.text" type="text" placeholder="e.g. (123) 456-7890" title="Phone Number" class="b2b-phone-mask-input" required />
8029             <div ng-messages="userForm1.text.$error" class="error-msg" aria-live="polite" aria-atomic="true">
8030                 <span ng-message="required" role="alert">This field is mandatory!</span>
8031                 <span ng-message="invalidPhoneNumber" role="alert">Please enter valid phone number!</span>
8032                 <span ng-message="mask" role="alert">Please enter valid phone number!</span>
8033             </div>
8034         </div>
8035     </div>
8036 </form>
8037  *
8038  * @example
8039  *  <section id="code">
8040         <example module="b2b.att">
8041             <file src="src/phoneNumberInput/docs/demo.html" />
8042             <file src="src/phoneNumberInput/docs/demo.js" />
8043        </example>
8044     </section>
8045  *
8046  */
8047 angular.module('b2b.att.phoneNumberInput', ['ngMessages', 'b2b.att.utilities'])
8048     .constant("CoreFormsUiConfig", {
8049         phoneMask: '(___) ___-____',
8050         phoneMaskDot: '___.___.____',
8051         phoneMaskHyphen: '___-___-____'
8052     })
8053     .directive('b2bPhoneMask', ['$parse', 'CoreFormsUiConfig', 'keymap', 'b2bUserAgent', function ($parse, CoreFormsUiConfig, keymap, b2bUserAgent) {
8054         return {
8055             require: 'ngModel',
8056             scope: {
8057                 ngModel: '='
8058             },
8059             link: function (scope, iElement, iAttrs, ctrl) {
8060                 
8061                 var mask = '';
8062                 var validPhoneNumber = false;
8063                 var currentKey = '';
8064                 if (b2bUserAgent.isMobile()) {
8065                     mask = "__________";
8066                 } else {
8067                     switch (iAttrs.b2bPhoneMask) {
8068                     case "phoneMask":
8069                         mask = CoreFormsUiConfig.phoneMask;
8070                         break;
8071                     case "phoneMaskDot":
8072                         mask = CoreFormsUiConfig.phoneMaskDot;
8073                         break;
8074                     case "phoneMaskHyphen":
8075                         mask = CoreFormsUiConfig.phoneMaskHyphen;
8076                         break;
8077                     default:
8078                         mask = CoreFormsUiConfig.phoneMask;
8079                     }
8080                 }
8081                 iElement.attr("maxlength", mask.length);
8082                 var checkValidity = function (unmaskedValue, rawValue) {
8083                     var valid = false;
8084                     if (angular.isUndefined(rawValue) || rawValue === '') {
8085                         valid = true;
8086                     } else if (unmaskedValue) {
8087                         valid = (unmaskedValue.length === 10);
8088                     }
8089                     ctrl.$setValidity('invalidPhoneNumber', validPhoneNumber);
8090                     ctrl.$setValidity('mask', valid);
8091                     return valid;
8092                 };
8093                 var handleKeyup = function (evt) {
8094
8095                     if (evt && evt.keyCode === keymap.KEY.SHIFT) {
8096                         return;
8097                     }
8098
8099                     var index, formattedNumber;
8100                     if (ctrl.$modelValue) {
8101                         formattedNumber = ctrl.$modelValue;
8102                     } else {
8103                         formattedNumber = iElement.val();
8104                     }
8105                     if (!formattedNumber.length && currentKey === '') {
8106                         return;
8107                     }
8108                     var maskLength, inputNumbers, maskArray, tempArray, maskArrayLength;
8109                     tempArray = [];
8110                     maskArray = mask.split("");
8111                     maskArrayLength = maskArray.length;
8112                     maskLength = formattedNumber.substring(0, mask.length);
8113                     inputNumbers = formattedNumber.replace(/[^0-9]/g, "").split("");
8114                     for (index = 0; index < maskArrayLength; index++) {
8115                         tempArray.push(maskArray[index] === "_" ? inputNumbers.shift() : maskArray[index]);
8116                         if (inputNumbers.length === 0) {
8117                             break;
8118                         }
8119                     }
8120                     formattedNumber = tempArray.join("");
8121                     if (formattedNumber === '(') {
8122                         formattedNumber = '';
8123                     }
8124
8125                     if ( (angular.isDefined(evt) && evt.which) && currentKey !== '') {
8126                         if (maskArray[0] === currentKey && formattedNumber === '') {
8127                             formattedNumber = '(';
8128                         } else if (evt.which === keymap.KEY.SPACE && currentKey === ' ') {
8129                             formattedNumber = formattedNumber + ') ';
8130                         } else if (maskArray[0] === currentKey && formattedNumber === '') {
8131                             formattedNumber = formattedNumber + currentKey;
8132                         } else if (maskArray[formattedNumber.length] === currentKey) {
8133                             formattedNumber = formattedNumber + currentKey;
8134                         }
8135                         currentKey = '';
8136                     }
8137
8138                     ctrl.$setViewValue(formattedNumber);
8139                     ctrl.$render();
8140                     return formattedNumber;
8141                 };
8142
8143
8144                 // since we are only allowing 0-9, why even let the keypress go forward?
8145                 // also added in delete... in case they want to delete :)
8146                 var handlePress = function (e) {
8147                     if (e.which) {
8148                         if ((e.which < 48 || e.which > 57) && (e.which < 96 || e.which > 105)) {
8149                             if (e.which !== keymap.KEY.BACKSPACE && e.which !== keymap.KEY.TAB && e.which !== keymap.KEY.DELETE && e.which !== keymap.KEY.ENTER && e.which !== keymap.KEY.LEFT && e.which !== keymap.KEY.RIGHT &&
8150                                 // Allow: Ctrl+V/v
8151                                 (!(e.ctrlKey) && (e.which !== '118' || e.which !== '86')) &&
8152                                 // Allow: Ctrl+C/c
8153                                 (!(e.ctrlKey) && (e.which !== '99' || e.which !== '67')) &&
8154                                 // Allow: Ctrl+X/x
8155                                 (!(e.ctrlKey) && (e.which !== '120' || e.which !== '88')) &&
8156                                 /* 229 key code will sent as placeholder key for andriod devices */
8157                                 (e.which != 229 )) {
8158                                 e.preventDefault ? e.preventDefault() : e.returnValue = false;
8159                                 validPhoneNumber = false;
8160                             }
8161                         } else {
8162                             validPhoneNumber = true;
8163                         }
8164
8165                         setCurrentKey(e);
8166                     }
8167                     scope.$apply();
8168                 };
8169                 // i moved this out because i thought i might need focus as well..
8170                 // to handle setting the model as the view changes
8171                 var parser = function (fromViewValue) {
8172                     var letters = /^[A-Za-z]+$/;
8173                     var numbers = /^[0-9]+$/;
8174                     if (angular.isUndefined(fromViewValue) || fromViewValue === '') {
8175                         validPhoneNumber = true;
8176                     } else {
8177                         if (fromViewValue.match(letters)) {
8178                             validPhoneNumber = false;
8179                         }
8180                         if (fromViewValue.match(numbers)) {
8181                             validPhoneNumber = true;
8182                         }
8183                     }
8184                     var clean = "";
8185                     if (fromViewValue && fromViewValue.length > 0) {
8186                         clean = fromViewValue.replace(/[^0-9]/g, '');
8187                     }
8188                     checkValidity(clean, fromViewValue);
8189                     return clean;
8190                 };
8191
8192                 //to handle reading the model and formatting it
8193                 var formatter = function (fromModelView) {
8194                     var input = '';
8195                     checkValidity(fromModelView);
8196                     if (fromModelView) {
8197                         input = handleKeyup();
8198                     }
8199                     return input;
8200                 };
8201
8202                 var setCurrentKey = function (e) {
8203                     switch (e.which) {
8204                     case 189:
8205                     case 109:
8206                         currentKey = '-';
8207                         break;
8208                     case 190:
8209                     case 110:
8210                         currentKey = '.';
8211                         break;
8212                     case 57:
8213                         if (e.shiftKey === true) {
8214                             currentKey = '(';
8215                         }
8216                         break;
8217                     case 48:
8218                         if (e.shiftKey === true) {
8219                             currentKey = ')';
8220                         }
8221                         break;
8222                     case 32:
8223                         currentKey = ' ';
8224                         break;
8225                     }
8226                 };
8227
8228                 if (angular.isDefined(scope.ngModel)) {
8229                     parser(scope.ngModel);
8230                 }
8231
8232                 ctrl.$parsers.push(parser);
8233                 ctrl.$formatters.push(formatter);
8234                 iElement.bind('keyup', handleKeyup);
8235                 iElement.bind('keydown', handlePress);
8236             }
8237         };
8238 }]);
8239 /** 
8240  * @ngdoc directive 
8241  * @name Template.att:Profile Blocks 
8242  * 
8243  * @description 
8244  *  <file src="src/profileBlockTemplate/docs/readme.md" /> 
8245  * @example 
8246  *  <section id="code">  
8247         <example module="b2b.att"> 
8248             <file src="src/profileBlockTemplate/docs/demo.html" /> 
8249             <file src="src/profileBlockTemplate/docs/demo.js" /> 
8250        </example>  
8251     </section>  
8252  *  
8253  */   
8254
8255 angular.module('b2b.att.profileBlockTemplate', [])
8256     
8257      
8258   
8259 /**
8260  * @ngdoc directive
8261  * @name Layouts.att:profileCard
8262  *
8263  * @description
8264  *  <file src="src/profileCard/docs/readme.md" />
8265  *
8266  * @usage
8267  *  <b2b-profile-card></b2b-profile-card>
8268  *
8269  * @example
8270     <section id="code">   
8271         <example module="b2b.att">
8272             <file src="src/profileCard/docs/demo.html" />
8273             <file src="src/profileCard/docs/demo.js" />
8274         </example>
8275     </section>
8276  */
8277
8278 angular.module('b2b.att.profileCard', ['b2b.att'])
8279 .constant('profileStatus',{
8280     status: {
8281         ACTIVE: {
8282             status: "Active",
8283             color: "green"
8284         },
8285         DEACTIVATED: {
8286             status: "Deactivated",
8287             color: "red"
8288         },
8289         LOCKED: {
8290             status: "Locked",
8291             color: "red"
8292         },
8293         IDLE: {
8294             status: "Idle",
8295             color: "yellow"
8296         },
8297         PENDING: {
8298             status: "Pending",
8299             color: "blue"
8300         }
8301     },
8302     role: "COMPANY ADMINISTRATOR"
8303
8304 })
8305 .directive('b2bProfileCard',['$http','$q','profileStatus', function($http,$q,profileStatus) {
8306    return {
8307         restrict: 'EA',
8308         replace: 'true',
8309         templateUrl: function(element, attrs){
8310             if(!attrs.addUser){
8311                 return 'b2bTemplate/profileCard/profileCard.html';
8312             }
8313             else{
8314                 return 'b2bTemplate/profileCard/profileCard-addUser.html';
8315             }
8316         },
8317         scope: {
8318             profile:'=',
8319             characterLimit: '@'
8320         },
8321         link: function(scope, elem, attr){
8322                 scope.characterLimit = parseInt(attr.characterLimit, 10) || 25;
8323                 scope.shouldClip = function(str) {
8324                         return str.length > scope.characterLimit;
8325                 };
8326
8327                 scope.showEmailTooltip = false;
8328
8329             scope.image=true;
8330             function isImage(src) {
8331                 var deferred = $q.defer();
8332                 var image = new Image();
8333                 image.onerror = function() {
8334                     deferred.reject(false);
8335                 };
8336                 image.onload = function() {
8337                     deferred.resolve(true);
8338                 };
8339                 if(src !== undefined && src.length>0 ){
8340                     image.src = src;
8341                 } else {
8342                      deferred.reject(false);
8343                 }
8344                 return deferred.promise;
8345             }
8346             if(!attr.addUser){
8347             scope.image=false;
8348             isImage(scope.profile.img).then(function(img) {
8349                 scope.image=img;
8350             });
8351             var splitName=(scope.profile.name).split(' ');
8352             scope.initials='';
8353             for(var i=0;i<splitName.length;i++){
8354                 scope.initials += splitName[i][0];
8355             }
8356             if(scope.profile.role.toUpperCase() === profileStatus.role){
8357                 scope.badge=true;
8358             }
8359             var profileState=profileStatus.status[scope.profile.state.toUpperCase()];
8360             if(profileState) {
8361                 scope.profile.state=profileStatus.status[scope.profile.state.toUpperCase()].status;
8362                 scope.colorIcon=profileStatus.status[scope.profile.state.toUpperCase()].color;
8363                 if(scope.profile.state.toUpperCase()===profileStatus.status.PENDING.status.toUpperCase()||scope.profile.state.toUpperCase()===profileStatus.status.LOCKED.status.toUpperCase()){
8364                         scope.profile.lastLogin=scope.profile.state;
8365                 }
8366             }
8367             var today=new Date().getTime();
8368             var lastlogin=new Date(scope.profile.lastLogin).getTime();
8369             var diff=(today-lastlogin)/(1000*60*60*24);
8370             if(diff<=1){
8371                 scope.profile.lastLogin="Today";
8372             }
8373             else if(diff<=2){
8374                 scope.profile.lastLogin="Yesterday";
8375             }
8376         }
8377     }
8378 };
8379 }]);
8380 /**
8381  * @ngdoc directive
8382  * @name Forms.att:radios
8383  *
8384  * @description
8385  *  <file src="src/radios/docs/readme.md" />
8386  *
8387  * @usage
8388  *  See demo section
8389  *
8390  * @param {boolean} refreshRadioGroup - A trigger that recalculates and updates the accessibility roles on radios in a group.
8391  * 
8392  * @example
8393     <section id="code">
8394                <b>HTML + AngularJS</b>
8395                <example module="b2b.att">
8396                 <file src="src/radios/docs/demo.html" />
8397                 <file src="src/radios/docs/demo.js" />
8398                </example>
8399             </section>
8400  */
8401 angular.module('b2b.att.radios', ['b2b.att.utilities'])
8402 .directive('b2bRadioGroupAccessibility', ['$timeout', 'b2bUserAgent', function($timeout, b2bUserAgent) {
8403     return {
8404         restrict: "A",
8405         scope: {
8406             refreshRadioGroup: "=",
8407         },
8408         link: function(scope, ele, attr) {
8409
8410             var roleRadioElement, radioProductSelectElement, radioInputTypeElement;
8411
8412             $timeout(calculateNumberOfRadio);
8413
8414             scope.$watch('refreshRadioGroup', function(value) {
8415                 if (value === true) {
8416                     addingRoleAttribute();
8417                     $timeout(calculateNumberOfRadio);
8418                     scope.refreshRadioGroup = false;
8419                 } else {
8420                     return;
8421                 }
8422             })
8423
8424
8425             function calculateNumberOfRadio() {
8426                 roleRadioElement = ele[0].querySelectorAll('[role="radio"]');
8427
8428                 radioProductSelectElement = ele[0].querySelectorAll('[role="radiogroup"] li.radio-box');
8429
8430                 radioInputTypeElement = ele[0].querySelectorAll('input[type="radio"]');
8431
8432                 for (var i = 0; i < radioInputTypeElement.length; i++) {
8433                     var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
8434                     var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
8435                     var numOfx = i + 1 + ' of ' + radioInputTypeElement.length;
8436                     angular.element(roleRadioElement[i]).attr({
8437                         'aria-checked': isChecked,
8438                         'aria-disabled': isDisabled,
8439                         'data-opNum': numOfx
8440                     });
8441                     if (b2bUserAgent.notMobile()) {
8442                         angular.element(roleRadioElement[i]).removeAttr("role");
8443                     }
8444
8445                     if (radioProductSelectElement.length) {
8446                         isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
8447                     }
8448
8449                     if (/Android/i.test(navigator.userAgent)) {
8450                         angular.element(roleRadioElement[i]).append('<span class="hidden-spoken">. ' + numOfx + '.</span>');
8451                     }
8452
8453
8454                     angular.element(radioInputTypeElement[i]).bind('click', radioStateChangeonClick);
8455
8456                 }
8457             }
8458
8459             function addingRoleAttribute() {
8460                 for (var i = 0; i < radioInputTypeElement.length; i++) {
8461                     if (b2bUserAgent.notMobile()) {
8462                         angular.element(roleRadioElement[i]).attr("role", "radio");
8463                     }
8464                 }
8465             }
8466
8467             function radioStateChangeonClick() {
8468                 for (var i = 0; i < radioInputTypeElement.length; i++) {
8469                     var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
8470                     var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
8471                     if (radioProductSelectElement.length) {
8472                         isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
8473                     }
8474                     angular.element(roleRadioElement[i]).attr({
8475                         'aria-checked': isChecked,
8476                         'aria-disabled': isDisabled
8477                     });
8478                 }
8479
8480             }
8481         }
8482     }
8483
8484 }]);
8485
8486 /**
8487  * @ngdoc directive
8488  * @name Forms.att:searchField
8489  *
8490  * @description
8491  *  <file src="src/searchField/docs/readme.md" />
8492  *
8493  * @usage
8494  *  <div b2b-search-field dropdown-list="listdata" on-click-callback="clickFn(value)" class="span12" input-model='typedString' config-obj='keyConfigObj'></div>
8495  *
8496  * @example
8497  <section id="code">
8498     <example module="b2b.att">
8499     <file src="src/searchField/docs/demo.html" />
8500     <file src="src/searchField/docs/demo.js" />
8501     </example>
8502 </section>
8503  */
8504
8505 angular.module('b2b.att.searchField', ['b2b.att.utilities', 'b2b.att.position'])
8506     .filter('b2bFilterInput', [function() {
8507         return function(list, str, keyArray, displayListKey, isContainsSearch, searchSeperator) {
8508             var res = [];
8509             var searchLabel;
8510             var searchCondition;
8511             var conditionCheck = function(searchSeperator, listItem, displayListKey, splitString) {
8512                 var displayTitle = null;
8513                 if (splitString) {
8514                     for (var i = 0; i < displayListKey.length; i++) {
8515                         if (i <= 0) {
8516                             displayTitle = listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase()) > -1;
8517                         } else {
8518                             displayTitle = (splitString[i]) ? displayTitle && listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase().trim()) > -1 : displayTitle;
8519                         }
8520                     }
8521                 } else {
8522                     angular.forEach(displayListKey, function(value) {
8523                         if (!displayTitle) {
8524                             displayTitle = listItem[value];
8525                         } else {
8526                             displayTitle = displayTitle + (listItem[value] ? searchSeperator + ' ' + listItem[value] : '');
8527                         }
8528                     });
8529                 }
8530                 return displayTitle;
8531             }
8532             angular.forEach(list, function(listItem) {
8533                 var splitString = str.indexOf(searchSeperator) > -1 ? str.split(searchSeperator) : false;
8534                 var displayList = conditionCheck(searchSeperator, listItem, displayListKey, splitString)
8535                 for (var i = 0; i < keyArray.length; i++) {
8536                     searchLabel = keyArray[i];
8537                     if (listItem[searchLabel]) {
8538                         if (isContainsSearch) {
8539                             var displaySearchList = listItem[searchLabel].toLowerCase().indexOf(str.toLowerCase()) > -1;
8540                             if (splitString.length > 1) {
8541                                 displaySearchList = (splitString.length <= keyArray.length) ? displayList : false;
8542                             }
8543                             searchCondition = displaySearchList;
8544                         } else {
8545                             searchCondition = listItem[searchLabel].match(new RegExp('^' + str, 'gi'));
8546                         }
8547                         if (searchCondition) {
8548                             res.push({
8549                                 'title': conditionCheck(searchSeperator, listItem, displayListKey),
8550                                 'valueObj': listItem
8551                             });
8552                             break;
8553                         }
8554                     }
8555                 }
8556             });
8557             return res;
8558         };
8559     }]).directive('b2bSearchField', ['$filter', 'b2bFilterInputFilter', 'keymap', '$documentBind', '$isElement', '$document', 'events', '$timeout', function($filter, b2bFilterInput, keymap, $documentBind, $isElement, $document, events, $timeout) {
8560         return {
8561             restrict: 'A',
8562             scope: {
8563                 dataList: '=dropdownList',
8564                 onClickCallback: '&',
8565                 inputModel: '=',
8566                 configObj: '=',
8567                 objModel: '=',
8568                 inputDeny: '=?',
8569                 disabled: '=?'
8570             },
8571             replace: true,
8572             templateUrl: 'b2bTemplate/searchField/searchField.html',
8573             controller: ['$scope', function($scope) {
8574                 this.searchKeyArray = [];
8575                 if ($scope.configObj.searchKeys) {
8576                     this.searchKeyArray = $scope.configObj.searchKeys;
8577                 }
8578                 if (angular.isUndefined($scope.disabled)) {
8579                     $scope.disabled = false;
8580                 }
8581                 this.triggerInput = function(searchString) {
8582                     $scope.originalInputModel = searchString;
8583                     if (searchString === '') {
8584                         $scope.currentIndex = -1;
8585                         $scope.filterList = [];
8586                         $scope.showListFlag = false;
8587                     } else if (searchString !== '' && !$scope.isFilterEnabled) {
8588                         $scope.filterList = $filter('b2bFilterInput')($scope.dataList, searchString, this.searchKeyArray, $scope.configObj.displayListDataKey, $scope.configObj.isContainsSearch, $scope.configObj.searchSeperator);
8589                         $scope.showListFlag = true;
8590                     }
8591                 };
8592                 this.denyRegex = function() {
8593                     return $scope.inputDeny;
8594                 };
8595             }],
8596             link: function(scope, elem) {
8597                 scope.isFilterEnabled = false;
8598                 scope.showListFlag = false;
8599                 scope.currentIndex = -1;
8600                 scope.setCurrentIdx = function(idx) {
8601                     scope.currentIndex = idx;
8602                     if (idx > -1) {
8603                         scope.inputModel = scope.filterList[idx].title;
8604                         scope.objModel = scope.filterList[idx];
8605                     }
8606                 };
8607                 scope.isActive = function(index, dropdownLength) {
8608                     scope.dropdownLength = dropdownLength;
8609                     return scope.currentIndex === index;
8610                 };
8611                 scope.selectItem = function(idx) {
8612                     scope.setCurrentIdx(idx);
8613                     scope.onClickCallback({
8614                         value: scope.inputModel,
8615                         objValue: scope.objModel
8616                     });
8617                     scope.showListFlag = false;
8618                     $timeout(function() {
8619                         elem.find('div').find('input')[0].focus();
8620                     }, 150);
8621                 };
8622                 scope.startSearch = function() {
8623                     scope.onClickCallback({
8624                         value: scope.inputModel,
8625                         objValue: scope.objModel
8626                     });
8627                 };
8628                 scope.selectPrev = function() {
8629                     if (scope.currentIndex > 0 && scope.filterList.length > 0) {
8630                         scope.currentIndex = scope.currentIndex - 1;
8631                         scope.setCurrentIdx(scope.currentIndex);
8632                     } else if (scope.currentIndex === 0) {
8633                         scope.currentIndex = scope.currentIndex - 1;
8634                         scope.inputModel = scope.originalInputModel;
8635                         scope.isFilterEnabled = true;
8636                     }
8637                 };
8638                 scope.selectNext = function() {
8639                     if (scope.currentIndex < scope.configObj.noOfItemsDisplay - 1) {
8640                         if (scope.currentIndex < scope.filterList.length - 1) {
8641                             scope.currentIndex = scope.currentIndex + 1;
8642                             scope.setCurrentIdx(scope.currentIndex);
8643                         }
8644                     }
8645                 };
8646                 scope.selectCurrent = function() {
8647                     scope.selectItem(scope.currentIndex);
8648                 };
8649                 scope.selectionIndex = function(e) {
8650                     switch (e.keyCode) {
8651                         case keymap.KEY.DOWN:
8652                             events.preventDefault(e);
8653                             scope.isFilterEnabled = true;
8654                             scope.selectNext();
8655                             break;
8656                         case keymap.KEY.UP:
8657                             events.preventDefault(e);
8658                             scope.isFilterEnabled = true;
8659                             scope.selectPrev();
8660                             break;
8661                         case keymap.KEY.ENTER:
8662                             events.preventDefault(e);
8663                             scope.isFilterEnabled = true;
8664                             scope.selectCurrent();
8665                             break;
8666                         case keymap.KEY.ESC:
8667                             events.preventDefault(e);
8668                             scope.isFilterEnabled = false;
8669                             scope.showListFlag = false;
8670                             scope.inputModel = '';
8671                             break;
8672                         default:
8673                             scope.isFilterEnabled = false;
8674                             break;
8675                     }
8676                     if (elem[0].querySelector('.filtercontainer')) {
8677                         elem[0].querySelector('.filtercontainer').scrollTop = (scope.currentIndex - 1) * 35;
8678                     }
8679                 };
8680                 scope.$watch('filterList', function(newVal, oldVal) {
8681                     if (newVal !== oldVal) {
8682                         scope.currentIndex = -1;
8683                     }
8684                 });
8685                 scope.blurInput = function() {
8686                     $timeout(function() {
8687                         scope.showListFlag = false;
8688                     }, 150);
8689                 };
8690                 var outsideClick = function(e) {
8691                     var isElement = $isElement(angular.element(e.target), elem.find('ul').eq(0), $document);
8692                     if (!isElement && document.activeElement.tagName.toLowerCase() !== 'input') {
8693                         scope.showListFlag = false;
8694                         scope.$apply();
8695                     }
8696                 };
8697                 $documentBind.click('showListFlag', outsideClick, scope);
8698             }
8699         };
8700     }])
8701     .directive('b2bSearchInput', [function() {
8702         return {
8703             restrict: 'A',
8704             require: ['ngModel', '^b2bSearchField'],
8705             link: function(scope, elem, attr, ctrl) {
8706                 var ngModelCtrl = ctrl[0];
8707                 var attSearchBarCtrl = ctrl[1];
8708                 var REGEX = ctrl[1].denyRegex();
8709                 var parser = function(viewValue) {
8710                     attSearchBarCtrl.triggerInput(viewValue);
8711                     return viewValue;
8712                 };
8713                 ngModelCtrl.$parsers.push(parser);
8714
8715                 if (REGEX !== undefined || REGEX !== '') {
8716                     elem.bind('input', function() {
8717                         var inputString = ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.replace(REGEX, '');
8718                         if (inputString !== ngModelCtrl.$viewValue) {
8719                             ngModelCtrl.$setViewValue(inputString);
8720                             ngModelCtrl.$render();
8721                             scope.$apply();
8722                         }
8723                     });
8724                 }
8725             }
8726         };
8727     }]);
8728
8729 /**
8730  * @ngdoc directive
8731  * @name Buttons, links & UI controls.att:Seek bar
8732  *
8733  * @description
8734  *  <file src="src/seekBar/docs/readme.md" />
8735  *
8736  * @usage
8737  *  Horizontal Seek Bar
8738  *      <b2b-seek-bar min="0" max="400" step="1" skip-interval="1" data-ng-model="horizontalSeekBarVal" style="width:180px; margin: auto;" on-drag-end="onDragEnd()" on-drag-init="onDragStart()"></b2b-seek-bar>
8739
8740  *      Vertical Seek Bar
8741  *      <b2b-seek-bar min="0" max="1" step="0.01" skip-interval="0.1" vertical data-ng-model="verticalSeekBarVal" style=" width: 6px; height: 180px; margin: auto;"></b2b-seek-bar>
8742  *
8743  * @example
8744     <section id="code">   
8745         <b>HTML + AngularJS</b>
8746         <example module="b2b.att">
8747             <file src="src/seekBar/docs/demo.html" />
8748             <file src="src/seekBar/docs/demo.js" />
8749         </example>
8750     </section>
8751  */
8752
8753 angular.module('b2b.att.seekBar', ['b2b.att.utilities','b2b.att.position'])
8754         .constant('b2bSeekBarConfig', {
8755             'min': 0,
8756             'max': 100,
8757             'step': 1,
8758             'skipInterval': 1
8759         })
8760         .directive('b2bSeekBar', ['$documentBind', 'events', 'b2bSeekBarConfig', 'keymap', '$compile', function($documentBind, events, b2bSeekBarConfig, keymap, $compile) {
8761                 return {
8762                     restrict: 'AE',
8763                     replace: true,
8764                     require: 'ngModel',
8765                     templateUrl: 'b2bTemplate/seekBar/seekBar.html',
8766                     scope: {
8767                         onDragEnd: '&?',
8768                         onDragInit: '&?'
8769                     },
8770                     link: function(scope, elm, attr, ngModelCtrl) {
8771                         scope.isDragging = false;
8772                         scope.verticalSeekBar = false;
8773                         var min;
8774                         var max;
8775                         var step = b2bSeekBarConfig.step;
8776                         var skipInterval = b2bSeekBarConfig.skipInterval;
8777                         var knob = angular.element(elm[0].querySelector('.b2b-seek-bar-knob-container'));
8778                         var seekBarKnob = angular.element(knob[0].querySelector('.b2b-seek-bar-knob'));
8779                         var trackContainer = angular.element(elm[0].querySelector('.b2b-seek-bar-track-container'));
8780                         var trackFill = angular.element(elm[0].querySelector('.b2b-seek-bar-track-fill'));
8781                         var trackContainerRect = {};
8782                         var axisPosition;
8783                         var trackFillOrderPositioning;
8784
8785                         if (angular.isDefined(attr.vertical)) {
8786                             scope.verticalSeekBar = true;
8787                             axisPosition = "clientY";
8788                         }
8789                         else {
8790                             scope.verticalSeekBar = false;
8791                             axisPosition = "clientX";
8792                         }
8793                         var getValidStep = function(val) {
8794                             val = parseFloat(val);
8795                             // in case $modelValue came in string number
8796                             if (angular.isNumber(val)) {
8797                                 val = Math.round((val - min) / step) * step + min;
8798                                 return Math.round(val * 1000) / 1000;
8799                             }
8800                         };
8801
8802                         var getPositionToPercent = function(x) {
8803                             if (scope.verticalSeekBar) {
8804                                 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
8805                             }
8806                             else {
8807                                 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
8808                             }
8809                         };
8810
8811                         var getPercentToValue = function(percent) {
8812                             return (min + percent * (max - min));
8813                         };
8814
8815                         var getValueToPercent = function(val) {
8816                             return (val - min) / (max - min);
8817                         };
8818
8819                         var getValidMinMax = function(val) {
8820                             return Math.max(min, Math.min(max, val));
8821                         };
8822
8823                         var updateTrackContainerRect = function() {
8824                             trackContainerRect = trackContainer[0].getBoundingClientRect();
8825                             if (scope.verticalSeekBar) {
8826                                 if (!trackContainerRect.height) {
8827                                     trackFillOrderPositioning = trackContainer[0].scrollHeight;
8828                                 } else {
8829                                     trackFillOrderPositioning = trackContainerRect.height;
8830                                 }
8831                             }
8832                             else {
8833                                 if (!trackContainerRect.width) {
8834                                     trackFillOrderPositioning = trackContainer[0].scrollWidth;
8835                                 } else {
8836                                     trackFillOrderPositioning = trackContainerRect.width;
8837                                 }
8838
8839                             }
8840
8841                         };
8842
8843                         var updateKnobPosition = function(percent) {
8844                             var percentStr = (percent * 100) + '%';
8845                             if (scope.verticalSeekBar) {
8846                                 knob.css('bottom', percentStr);
8847                                 trackFill.css('height', percentStr);
8848                             }
8849                             else {
8850                                 knob.css('left', percentStr);
8851                                 trackFill.css('width', percentStr);
8852                             }
8853                         };
8854
8855                         var modelRenderer = function() {
8856                             if (isNaN(ngModelCtrl.$viewValue)) {
8857                                 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
8858                             }
8859
8860                             var viewVal = ngModelCtrl.$viewValue;
8861                             scope.currentModelValue = viewVal;
8862
8863                             //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
8864                             if ((min || min === 0) && max && step) {
8865                                 updateKnobPosition(getValueToPercent(viewVal));
8866                             }
8867                         };
8868
8869                         var setModelValue = function(val) {
8870                             scope.currentModelValue = getValidMinMax(getValidStep(val));
8871                             ngModelCtrl.$setViewValue(scope.currentModelValue);
8872                         };
8873
8874                         var updateMin = function(val) {
8875                             min = parseFloat(val);
8876                             if(isNaN(min)){
8877                                min = b2bSeekBarConfig.min; 
8878                             }
8879                             modelRenderer();
8880                         };
8881
8882                         var updateMax = function(val) {
8883                             max = parseFloat(val);
8884                             if(isNaN(max)){
8885                                max = b2bSeekBarConfig.max; 
8886                             }
8887                             modelRenderer();
8888                         };
8889
8890                         var updateStep = function(val) {
8891                             step = parseFloat(val);
8892                             if (!attr['skipInterval']) {
8893                                 skipInterval = step;
8894                             }
8895                         };
8896
8897                         var updateSkipInterval = function(val) {
8898                             skipInterval = step * Math.ceil(val / (step!==0?step:1));
8899                         };
8900
8901                         angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(b2bSeekBarConfig.min);
8902                         angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(b2bSeekBarConfig.max);
8903                         if (angular.isDefined(attr.step)) {
8904                             attr.$observe('step', updateStep);
8905                         }
8906                         if (angular.isDefined(attr.skipInterval)) {
8907                             attr.$observe('skipInterval', updateSkipInterval);
8908                         }
8909                         scope.currentModelValue = getValidMinMax(getValidStep(min));
8910                         var onMouseDown = function(e) {
8911                             switch (e.which) {
8912                                 case 1:
8913                                     // left mouse button
8914                                     break;
8915                                 case 2:
8916                                 case 3:
8917                                     // right or middle mouse button
8918                                     return;
8919                             }
8920                             ;
8921
8922                             scope.isDragging = true;
8923                             seekBarKnob[0].focus();
8924                             updateTrackContainerRect();
8925                             if (attr['onDragInit']) {
8926                                 scope.onDragInit();
8927                             }
8928                             events.stopPropagation(e);
8929                             events.preventDefault(e);
8930                              scope.$apply(function() {
8931                                 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
8932                             });
8933                         };
8934
8935                         var onMouseUp = function() {
8936
8937                             if (attr['onDragEnd']) {
8938                                 scope.onDragEnd();
8939                             }
8940                             scope.isDragging = false;
8941                             scope.$digest();
8942                         };
8943
8944                         var onMouseMove = function(e) {
8945                             if (scope.isDragging) {
8946                                 events.stopPropagation(e);
8947                                 events.preventDefault(e);
8948
8949                                 scope.$apply(function() {
8950                                     setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
8951                                 });
8952                             }
8953                         };
8954
8955                         function onKeyDown(e) {
8956                             if (!(e.keyCode)) {
8957                                 e.keyCode = e.which;
8958                             }
8959                             var updateStep;
8960                             switch (e.keyCode) {
8961                                 case keymap.KEY.LEFT:
8962                                     if (!scope.verticalSeekBar) {
8963                                         updateStep = -skipInterval;
8964                                     }
8965                                     break;
8966                                 case keymap.KEY.RIGHT:
8967                                     if (!scope.verticalSeekBar) {
8968                                         updateStep = skipInterval;
8969                                     }
8970                                     break;
8971                                 case keymap.KEY.UP:
8972                                     if (scope.verticalSeekBar) {
8973                                         updateStep = skipInterval;
8974                                     }
8975                                     break;
8976                                 case keymap.KEY.DOWN:
8977                                     if (scope.verticalSeekBar) {
8978                                         updateStep = -skipInterval;
8979                                     }
8980                                     break;
8981                                 default:
8982                                     return;
8983                             }
8984
8985                             if (updateStep) {
8986                                 events.stopPropagation(e);
8987                                 events.preventDefault(e);
8988                                 scope.$apply(function() {
8989                                     setModelValue(ngModelCtrl.$viewValue + updateStep);
8990                                 });
8991                                 if (attr['onDragEnd']) {
8992                                 scope.onDragEnd();
8993                             }
8994                             }
8995                         }
8996
8997                         elm.on('keydown', onKeyDown);
8998                         elm.on('mousedown', onMouseDown);
8999
9000                         $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
9001                         $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
9002
9003                         ngModelCtrl.$render = function() {
9004                             if (!scope.isDragging) {
9005                                 modelRenderer();
9006                             }
9007                         };
9008                         ngModelCtrl.$viewChangeListeners.push(modelRenderer);
9009                         ngModelCtrl.$formatters.push(getValidMinMax);
9010                         ngModelCtrl.$formatters.push(getValidStep);
9011                     }
9012                 };
9013             }]);
9014 /**
9015  * @ngdoc directive
9016  * @name Layouts.att:separators
9017  *
9018  * @description
9019  *  <file src="src/separators/docs/readme.md" />
9020  *
9021  * @usage
9022
9023  *
9024  * @example
9025  *  <section id="code">   
9026  <b>HTML + AngularJS</b>
9027  <example module="b2b.att">
9028  <file src="src/separators/docs/demo.html" />
9029  <file src="src/separators/docs/demo.js" />
9030  </example>
9031  </section>
9032  *
9033  */
9034
9035 angular.module('b2b.att.separators', []);
9036 /**
9037  * @ngdoc directive
9038  * @name Buttons, links & UI controls.att:slider
9039  *
9040  * @description
9041  *  <file src="src/slider/docs/readme.md" />
9042  *
9043  * @usage
9044  *  <b2b-slider min="0" max="400" step="1" no-aria-label skip-interval="1" ng-model="horizontalSliderVal" style="width:180px; margin: auto;" on-drag-end="onDragEnd()" on-drag-init="onDragStart()" label-id="slider-label" post-aria-label="postAriaLabel"></b2b-slider>
9045  *
9046  * @example
9047     <section id="code">   
9048         <b>HTML + AngularJS</b>
9049         <example module="b2b.att">
9050             <file src="src/slider/docs/demo.html" />
9051             <file src="src/slider/docs/demo.js" />
9052         </example>
9053     </section>
9054  */
9055
9056 angular.module('b2b.att.slider', ['b2b.att.utilities'])
9057         .constant('SliderConfig', {
9058             'min': 0,
9059             'max': 100,
9060             'step': 1,
9061             'skipInterval': 1
9062         })
9063         .directive('b2bSlider', ['$documentBind', 'SliderConfig', 'keymap', '$compile', '$log', function($documentBind, SliderConfig, keymap, $compile, $log) {
9064                 return {
9065                     restrict: 'AE',
9066                     replace: true,
9067                     require: 'ngModel',
9068                     templateUrl: 'b2bTemplate/slider/slider.html',
9069                     scope: {
9070                         onDragEnd: '&?',
9071                         onDragInit: '&?',
9072                         trackFillColor: '=?',
9073                         preAriaLabel: '=?',
9074                         postAriaLabel: '=?',
9075                         onRenderEnd: '&?',
9076                         sliderSnapPoints: '=?',
9077                         customAriaLabel: '=?',
9078                         labelId: '@?'
9079                     },
9080                     link: function(scope, elm, attr, ngModelCtrl) {
9081                         scope.isDragging = false;
9082                         scope.verticalSlider = false;
9083                         var min;
9084                         var max;
9085                         var step = SliderConfig.step;
9086                         var skipInterval = SliderConfig.skipInterval;
9087                         var knob = angular.element(elm[0].querySelector('.slider-knob-container'));
9088                         var sliderKnob = angular.element(knob[0].querySelector('.slider-knob'));
9089                         var trackContainer = angular.element(elm[0].querySelector('.slider-track-container'));
9090                         var trackFill = angular.element(elm[0].querySelector('.slider-track-fill'));
9091                         var trackContainerRect = {};
9092                         var axisPosition = "clientX";
9093                         var trackFillOrderPositioning;
9094
9095                         //Forcefully disabling the vertical Slider code.
9096                         if (angular.isDefined(attr.vertical)) {
9097                             scope.verticalSlider = true;
9098                             axisPosition = "clientY";
9099                         }
9100
9101                         if (angular.isDefined(scope.noAriaLabel) && scope.noAriaLabel !== '') {
9102                             $log.warn('no-aria-label has been deprecated. This will be removed in v0.6.0.');
9103                         }
9104                         if (angular.isDefined(scope.preAriaLabel) && scope.preAriaLabel !== '') {
9105                             $log.warn('pre-aria-label has been deprecated. Please use label-id instead. This will be removed in v0.6.0.');
9106                         }
9107                         if (angular.isDefined(scope.customAriaLabel) && scope.customAriaLabel !== '') {
9108                             $log.warn('custom-aria-label has been deprecated. Please use label-id and post-aria-label instead. This will be removed in v0.6.0.');
9109                         }
9110
9111                         var binarySearchNearest = function (num, arr) {
9112                             var mid;
9113                             var lo = 0;
9114                             var hi = arr.length - 1;
9115                             
9116                             while (hi - lo > 1) {
9117                                 mid = Math.floor((lo + hi) / 2);
9118                                 if (arr[mid] < num) {
9119                                     lo = mid;
9120                                 } else {
9121                                     hi = mid;
9122                                 }
9123                             }
9124                             if (num - arr[lo] < arr[hi] - num) {
9125                                 return arr[lo];
9126                             }
9127                             return arr[hi];
9128                         };
9129                         
9130                         var getValidStep = function(val) {
9131                             val = parseFloat(val);
9132                             // in case $modelValue came in string number
9133                             if (!isNaN(val)) {
9134                                 
9135                                 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9136                                     val = binarySearchNearest(val, scope.sliderSnapPoints);
9137                                 }
9138                                 else {
9139                                     val = Math.round((val - min) / step) * step + min;
9140                                 }
9141                                 
9142                                 return Math.round(val * 1000) / 1000;
9143                             }
9144                         };
9145
9146                         var getPositionToPercent = function(x) {
9147                             if (scope.verticalSlider) {
9148                                 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
9149                             }
9150                             else {
9151                                 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
9152                             }
9153                         };
9154
9155                         var getPercentToValue = function(percent) {
9156                             return (min + percent * (max - min));
9157                         };
9158
9159                         var getValueToPercent = function(val) {
9160                             return (val - min) / (max - min);
9161                         };
9162
9163                         var getValidMinMax = function(val) {
9164                             return Math.max(min, Math.min(max, val));
9165                         };
9166
9167                         var updateTrackContainerRect = function() {
9168                             trackContainerRect = trackContainer[0].getBoundingClientRect();
9169                             if (scope.verticalSlider) {
9170                                 if (!trackContainerRect.height) {
9171                                     trackFillOrderPositioning = trackContainer[0].scrollHeight;
9172                                 } else {
9173                                     trackFillOrderPositioning = trackContainerRect.height;
9174                                 }
9175                             }
9176                             else {
9177                                 if (!trackContainerRect.width) {
9178                                     trackFillOrderPositioning = trackContainer[0].scrollWidth;
9179                                 } else {
9180                                     trackFillOrderPositioning = trackContainerRect.width;
9181                                 }
9182
9183                             }
9184
9185                         };
9186
9187                         var updateKnobPosition = function(percent) {
9188                             var percentStr = (percent * 100) + '%';
9189                             if (scope.verticalSlider) {
9190                                 knob.css('bottom', percentStr);
9191                                 trackFill.css('height', percentStr);
9192                             }
9193                             else {
9194                                 knob.css('left', percentStr);
9195                                 trackFill.css('width', percentStr);
9196                             }
9197                         };
9198
9199                         var modelRenderer = function() {
9200
9201                             if(attr['disabled']){
9202                                 return;
9203                             }
9204
9205                             if (isNaN(ngModelCtrl.$viewValue)) {
9206                                 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
9207                             }
9208
9209                             var viewVal = ngModelCtrl.$viewValue;
9210                             scope.currentModelValue = viewVal;
9211
9212                             //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
9213                             if ((min || min === 0) && max && step) {
9214                                 updateKnobPosition(getValueToPercent(viewVal));
9215                             }
9216                         };
9217
9218                         var setModelValue = function(val) {
9219                             scope.currentModelValue = getValidMinMax(getValidStep(val));
9220                             ngModelCtrl.$setViewValue(scope.currentModelValue);
9221                         };
9222
9223                         var updateMin = function(val) {
9224                             min = parseFloat(val);
9225                             if(isNaN(min)){
9226                                min = SliderConfig.min; 
9227                             }
9228                             scope.min = min;
9229                             modelRenderer();
9230                         };
9231
9232                         var updateMax = function(val) {
9233                             max = parseFloat(val);
9234                             if(isNaN(max)){
9235                                max = SliderConfig.max; 
9236                             }
9237                             scope.max = max;
9238                             modelRenderer();
9239                         };
9240
9241                         var updateStep = function(val) {
9242                             step = parseFloat(val);
9243                             if (!attr['skipInterval']) {
9244                                 skipInterval = step;
9245                             }
9246                         };
9247
9248                         var updateSkipInterval = function(val) {
9249                             skipInterval = step * Math.ceil(val / (step!==0?step:1));
9250                         };
9251
9252                         angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(SliderConfig.min);
9253                         angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(SliderConfig.max);
9254                         if (angular.isDefined(attr.step)) {
9255                             attr.$observe('step', updateStep);
9256                         }
9257                         if (angular.isDefined(attr.skipInterval)) {
9258                             attr.$observe('skipInterval', updateSkipInterval);
9259                         }
9260                         scope.currentModelValue = getValidMinMax(getValidStep(min));
9261                         var onMouseDown = function(e) {
9262
9263                             if(attr['disabled']){
9264                                 return;
9265                             }
9266
9267                             switch (e.which) {
9268                                 case 1:
9269                                     // left mouse button
9270                                     break;
9271                                 case 2:
9272                                 case 3:
9273                                     // right or middle mouse button
9274                                     return;
9275                             }
9276
9277                             scope.isDragging = true;
9278                             sliderKnob[0].focus();
9279                             updateTrackContainerRect();
9280                             if (attr['onDragInit']) {
9281                                 scope.onDragInit();
9282                             }
9283                             e.stopPropagation();
9284                             e.preventDefault();
9285                              scope.$apply(function() {
9286                                 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
9287                             });
9288                         };
9289
9290                         var onMouseUp = function() {
9291
9292                             if (attr['onDragEnd']) {
9293                                 scope.onDragEnd();
9294                             }
9295                             scope.isDragging = false;
9296                             scope.$digest();
9297                         };
9298
9299                         var onMouseMove = function(e) {
9300                             if (scope.isDragging) {
9301                                 e.stopPropagation();
9302                                 e.preventDefault();
9303
9304                                 scope.$apply(function() {
9305                                     setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
9306                                 });
9307                             }
9308                         };
9309
9310                         function onKeyDown(e) {
9311                             if (!(e.keyCode)) {
9312                                 e.keyCode = e.which;
9313                             }
9314                             var updateStep;
9315                             switch (e.keyCode) {
9316                                 case keymap.KEY.DOWN:
9317                                 case keymap.KEY.LEFT:
9318                                     if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9319                                         var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
9320                                         if (currentIndex > 0) {
9321                                             currentIndex--;
9322                                         }
9323                                         updateStep = scope.sliderSnapPoints[currentIndex];
9324                                     }
9325                                     else {
9326                                         updateStep = ngModelCtrl.$viewValue - skipInterval;
9327                                     }
9328                                     break;
9329                                 case keymap.KEY.UP:
9330                                 case keymap.KEY.RIGHT:
9331                                     if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9332                                         var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
9333                                         if (currentIndex < scope.sliderSnapPoints.length-1) {
9334                                             currentIndex++;
9335                                         }
9336                                         updateStep = scope.sliderSnapPoints[currentIndex];
9337                                     }
9338                                     else {
9339                                         updateStep = ngModelCtrl.$viewValue + skipInterval;
9340                                     }
9341                                     break;
9342                                 case keymap.KEY.END:
9343                                     if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9344                                         currentIndex = scope.sliderSnapPoints.length-1;
9345                                         updateStep = scope.sliderSnapPoints[currentIndex];
9346                                     } else {
9347                                         setModelValue(scope.max);
9348                                     }
9349                                     e.preventDefault();
9350                                     e.stopPropagation();
9351                                     break;
9352                                 case keymap.KEY.HOME:
9353                                     if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9354                                         currentIndex = 0;
9355                                         updateStep = scope.sliderSnapPoints[currentIndex];
9356                                     } else {
9357                                         setModelValue(scope.min);
9358                                     }
9359                                     e.preventDefault();
9360                                     e.stopPropagation();
9361                                     break;
9362                                 default:
9363                                     return;
9364                             }
9365
9366                             if (angular.isNumber(updateStep) && !attr['disabled']) {
9367                                 e.stopPropagation();
9368                                 e.preventDefault();
9369                                 scope.$apply(function() {
9370                                     setModelValue(updateStep);
9371                                 });
9372                                 if (attr['onDragEnd']) {
9373                                     scope.onDragEnd();
9374                                 }
9375                             }
9376                         }
9377
9378                         elm.on('keydown', onKeyDown);
9379                         elm.on('mousedown', onMouseDown);
9380
9381                         $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
9382                         $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
9383
9384                         attr.$observe('disabled', function (disabled) {
9385                             if (disabled) {
9386                                 sliderKnob.removeAttr('tabindex');
9387                             } else {
9388                                 sliderKnob.attr('tabindex', '0');
9389                                 disabled = false;
9390                             }
9391
9392                             elm.toggleClass("slider-disabled", disabled);
9393
9394                             if (angular.isDefined(attr.hideDisabledKnob)) {
9395                                 scope.hideKnob = disabled;
9396                             }
9397                         });
9398
9399                         ngModelCtrl.$render = function() {
9400                             if (!scope.isDragging) {
9401                                 modelRenderer();
9402                                 if (attr['onRenderEnd'] && !attr['disabled']) {
9403                                     scope.onRenderEnd({currentModelValue: scope.currentModelValue});
9404                                 }
9405                             }
9406                         };
9407                         ngModelCtrl.$viewChangeListeners.push(modelRenderer);
9408                         ngModelCtrl.$formatters.push(getValidMinMax);
9409                         ngModelCtrl.$formatters.push(getValidStep);
9410                     }
9411                 };
9412             }]);
9413 /**
9414  * @ngdoc directive
9415  * @name Forms.att:spinButton
9416  *
9417  * @param {String} spin-button-id - An ID for the input field
9418  * @param {Integer} min - Minimum value for the input
9419  * @param {Integer} max - Maximum value for the input
9420  * @param {Integer} step - Value by which input field increments or decrements on up/down arrow keys
9421  * @param {Integer} page-step - Value by which input field increments or decrements on page up/down keys
9422  * @param {boolean} input-model-key - Default value for input field
9423  * @param {boolean} disabled-flag - A boolean that triggers directive once the min or max value has reached
9424  *
9425  * @description
9426  *  <file src="src/spinButton/docs/readme.md" />
9427  *
9428  * @example
9429  *      <section id="code">
9430             <example module="b2b.att">
9431             <file src="src/spinButton/docs/demo.html" />
9432             <file src="src/spinButton/docs/demo.js" />
9433        </example>
9434         </section>
9435  * 
9436  */
9437 angular.module('b2b.att.spinButton', ['b2b.att.utilities'])
9438     .constant('b2bSpinButtonConfig', {
9439         min: 1,
9440         max: 10,
9441         step: 1,
9442         pageStep: 10,
9443         inputModelKey: 'value',
9444         disabledFlag: false
9445     })
9446     .directive('b2bSpinButton', ['keymap', 'b2bSpinButtonConfig', 'b2bUserAgent', function (keymap, b2bSpinButtonConfig, userAgent) {
9447         return {
9448             restrict: 'EA',
9449             require: '?ngModel',
9450             transclude: false,
9451             replace: true,
9452             scope: {
9453                 min: '=min',
9454                 max: '=max',
9455                 step: '=step',
9456                 pageStep: '=pageStep',
9457                 spinButtonId: '@',
9458                 inputValue: '=ngModel',
9459                 inputModelKey: '@',
9460                 disabledFlag: "=?"
9461             },
9462             templateUrl: 'b2bTemplate/spinButton/spinButton.html',
9463             controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
9464
9465                 scope.isMobile = userAgent.isMobile();
9466                 scope.notMobile = userAgent.notMobile();
9467
9468                 scope.min = attrs.min ? scope.min : b2bSpinButtonConfig.min;
9469                 scope.max = attrs.max ? scope.max : b2bSpinButtonConfig.max;
9470                 scope.step = attrs.step ? scope.step : b2bSpinButtonConfig.step;
9471                 scope.pageStep = attrs.pageStep ? scope.pageStep : b2bSpinButtonConfig.pageStep;
9472                 scope.inputModelKey = attrs.inputModelKey ? scope.inputModelKey : b2bSpinButtonConfig.inputModelKey;
9473                 scope.disabledFlag = attrs.disabledFlag ? scope.disabledFlag : b2bSpinButtonConfig.disabledFlag;
9474                 
9475                 if (scope.min < 0) {
9476                     scope.min = 0;
9477                 }
9478                 if (scope.max > 999) {
9479                     scope.max = 999;
9480                 }
9481
9482                 scope.isPlusDisabled = function () {
9483                     return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] >= scope.max);
9484                 };
9485                 scope.isMinusDisabled = function () {
9486                     return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] <= scope.min);
9487                 };
9488
9489                 scope.getValidateInputValue = function (value) {
9490                     if (value <= scope.min) {
9491                         return scope.min;
9492                     } else if (value >= scope.max) {
9493                         return scope.max;
9494                     } else {
9495                         return value;
9496                     }
9497                 };
9498
9499                 scope.plus = function () {
9500                     scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.step);
9501                 };
9502                 scope.minus = function () {
9503                     scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.step);
9504                 };
9505                 scope.pagePlus = function () {
9506                     scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.pageStep);
9507                 };
9508                 scope.pageMinus = function () {
9509                     scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.pageStep);
9510                 };
9511
9512             }],
9513             link: function (scope, elem) {
9514
9515                 if (scope.notMobile) {
9516                     angular.element(elem).find('input').attr('aria-live', 'off');
9517                     angular.element(elem).find('input').attr('role', 'spinbutton');
9518                 }
9519
9520                 elem.find('input').bind('keydown', function (e) {
9521                     if (e.keyCode === keymap.KEY.UP) {
9522                         scope.plus();
9523                     } else if (e.keyCode === keymap.KEY.DOWN){
9524                         scope.minus();
9525                     } else if (e.keyCode === keymap.KEY.HOME) {
9526                         scope.inputValue[scope.inputModelKey] = parseInt(scope.min);
9527                     } else if (e.keyCode === keymap.KEY.END) {
9528                         scope.inputValue[scope.inputModelKey] = parseInt(scope.max);
9529                     } else if (e.keyCode === keymap.KEY.PAGE_UP) {
9530                         scope.pagePlus();
9531                     } else if (e.keyCode === keymap.KEY.PAGE_DOWN) {
9532                         scope.pageMinus();
9533                     }
9534                     scope.$apply();
9535                 });
9536
9537                 elem.find('input').bind('keyup', function () {
9538                     if (scope.inputValue[scope.inputModelKey] === null ||
9539                         scope.inputValue[scope.inputModelKey] === '' ||
9540                         scope.inputValue[scope.inputModelKey] < scope.min) {
9541                         scope.inputValue[scope.inputModelKey] = scope.min;
9542                         scope.$apply();
9543                     } else if (angular.isUndefined(scope.inputValue[scope.inputModelKey]) || 
9544                                scope.inputValue[scope.inputModelKey] > scope.max) {
9545                         scope.inputValue[scope.inputModelKey] = scope.max;
9546                         scope.$apply();
9547                     }
9548                 });
9549
9550                 scope.focusInputSpinButton = function (evt) {
9551                     evt.preventDefault();
9552                     if (scope.notMobile) {
9553                         elem[0].querySelector('input').focus();
9554                     }
9555                 };
9556
9557             }
9558         };  
9559     }]);
9560 /** 
9561  * @ngdoc directive 
9562  * @name Template.att:Static Route
9563  * 
9564  * @description 
9565  *  <file src="src/staticRouteTemplate/docs/readme.md" /> 
9566  * 
9567  * @example 
9568  *  <section id="code"> 
9569         <example module="b2b.att"> 
9570             <file src="src/staticRouteTemplate/docs/demo.html" /> 
9571             <file src="src/staticRouteTemplate/docs/demo.js" /> 
9572        </example> 
9573     </section>    
9574  * 
9575  */
9576 angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities'])
9577   
9578 /**
9579  * @ngdoc directive
9580  * @name Progress & usage indicators.att:statusTracker
9581  *
9582  * @scope
9583  * @param {array} statusObject - An array of status objects that accept heading, estimate, description and state
9584  * @description
9585  * <file src="src/statusTracker/docs/readme.md" />
9586  *
9587  * @usage
9588  *
9589 <div ng-controller="statusTrackerController">
9590     <b2b-status-tracker statuses="statusObject"></b2b-status-tracker>
9591 </div>
9592
9593  * @example
9594     <section id="code">   
9595         <example module="b2b.att">
9596             <file src="src/statusTracker/docs/demo.html" />
9597             <file src="src/statusTracker/docs/demo.js" />
9598         </example>
9599     </section>
9600  */
9601
9602 angular.module('b2b.att.statusTracker', ['b2b.att.utilities'])
9603 .constant('b2bStatusTrackerConfig', {
9604     'maxViewItems': 3,
9605     'icons': {
9606         'complete': 'icoControls-approval',
9607         'current': 'icon-misc-time',
9608         'pending': 'icoControls-statusokay',
9609         'actionRequired': 'icon-primary-securityalerts-alert',
9610         'notAvailable': 'icoControls-restricted'
9611     }
9612 })
9613 .directive('b2bStatusTracker', ['b2bStatusTrackerConfig', function(b2bStatusTrackerConfig) {
9614         return {
9615             restrict: 'EA',
9616             transclude: false,
9617             replace: true,
9618             scope:{
9619                 statuses: '='
9620             },
9621             templateUrl: function(scope) {
9622                 return 'b2bTemplate/statusTracker/statusTracker.html';
9623             },
9624             link: function(scope, element, attr) {
9625                 scope.currentViewIndex = 0;
9626                 scope.b2bStatusTrackerConfig = b2bStatusTrackerConfig;
9627
9628                 scope.nextStatus = function() {
9629                     if (scope.currentViewIndex+1 <= scope.statuses.length) {
9630                         scope.currentViewIndex++;
9631                     }
9632                 };
9633                 scope.previousStatus = function() {
9634                     if (scope.currentViewIndex-1 >= 0) {
9635                         scope.currentViewIndex--;
9636                     }
9637                 };
9638                 scope.isInViewport = function(index) {
9639                     return (index < scope.currentViewIndex+3 && index >= scope.currentViewIndex);  // && index > scope.currentViewIndex-2
9640                 };
9641
9642                 scope.removeCamelCase = function(str) {
9643                     return str.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase();
9644                 }
9645             }
9646         };
9647     }]);
9648 /**
9649  * @ngdoc directive
9650  * @name Progress & usage indicators.att:stepTracker
9651  *
9652  * @scope
9653  * @param {array} stepsItemsObject - An array of step objects
9654  * @param {Integer} currenIindex - This indicates the current running step
9655  * @param {Integer} viewportIndex - This is optional. This can used to start the view port rather than 1 item.
9656  * @description
9657  * <file src="src/stepTracker/docs/readme.md" />
9658  *
9659  * @usage
9660  *
9661  *      <b2b-step-tracker steps-items-object="stepsObject" current-index="currentStepIndex" step-indicator-heading="stepHeading"></b2b-step-tracker>
9662  *
9663
9664  * @example
9665     <section id="code">   
9666         <b>HTML + AngularJS</b>
9667         <example module="b2b.att">
9668             <file src="src/stepTracker/docs/demo.html" />
9669             <file src="src/stepTracker/docs/demo.js" />
9670         </example>
9671     </section>
9672  */
9673 angular.module('b2b.att.stepTracker', ['b2b.att.utilities'])
9674         .constant('b2bStepTrackerConfig', {
9675                 'maxViewItems': 5
9676         })
9677         .directive('b2bStepTracker', ['b2bStepTrackerConfig', function(b2bStepTrackerConfig) {
9678                 return {
9679                         restrict: 'EA',
9680                         transclude: true,
9681                         scope:{
9682                                 stepsItemsObject:"=",
9683                                 currentIndex:"=",
9684                 viewportIndex:"=?"
9685                         },
9686                         templateUrl: 'b2bTemplate/stepTracker/stepTracker.html',
9687                         link: function(scope, ele, attr) {
9688                 if (angular.isDefined(scope.viewportIndex)) {
9689                     scope.currentViewIndex = scope.viewportIndex - 1;   
9690                 }else{
9691                     scope.currentViewIndex = 0;
9692                 }
9693                            
9694                            scope.b2bStepTrackerConfig = b2bStepTrackerConfig;
9695                            scope.nextStatus = function() {
9696                                         if (scope.currentViewIndex+1 <= scope.stepsItemsObject.length) {
9697                                                 scope.currentViewIndex++;
9698                                         }
9699                                 };
9700                                 scope.previousStatus = function() {
9701                                         if (scope.currentViewIndex-1 >= 0) {
9702                                                 scope.currentViewIndex--;
9703                                         }
9704                                 };
9705                                 scope.isInViewport = function(index) {
9706                                         return (index < scope.currentViewIndex+b2bStepTrackerConfig.maxViewItems && index >= scope.currentViewIndex);
9707                                 };
9708                         }
9709                 };
9710         }]);
9711      
9712 /**
9713  * @ngdoc directive
9714  * @name Buttons, links & UI controls.att:switches
9715  *
9716  * @description
9717  *  <file src="src/switches/docs/readme.md" />
9718  *
9719  * @usage
9720  *  
9721  *  <!-- On / Off Toggle switch -->
9722  *  <label for="switch1" class="controlled-text-wrap"> This is ON
9723  *      <input type="checkbox" role="checkbox" b2b-switches id="switch1" ng-model="switch1.value">
9724  *  </label>
9725  *
9726  *  <!-- On / Off Toggle switch and DISABLED -->
9727  *  <label for="switch2" class="controlled-text-wrap"> This is ON (disabled)
9728  *      <input type="checkbox" role="checkbox" b2b-switches id="switch2" ng-model="switch1.value" ng-disabled="true" >
9729  *  </label> 
9730  *
9731  *
9732  * @example
9733  *  <section id="code">
9734         <b>HTML + AngularJS</b>
9735         <example module="b2b.att">
9736             <file src="src/switches/docs/demo.js" />
9737             <file src="src/switches/docs/demo.html" />
9738         </example>
9739     </section>
9740  */
9741 angular.module('b2b.att.switches', ['b2b.att.utilities'])
9742     .directive('b2bSwitches', ['$compile', '$templateCache', 'keymap', 'events', function ($compile, $templateCache, keymap, events) {
9743         return {
9744             restrict: 'EA',
9745             require: ['ngModel'],
9746             link: function (scope, element, attrs, ctrl) {
9747                 var ngModelController = ctrl[0];
9748         
9749                 element.parent().bind("keydown", function (e) {
9750                     if (!attrs.disabled && (e.keyCode === keymap.KEY.ENTER || e.keyCode === keymap.KEY.SPACE)) {
9751                         events.preventDefault(e);
9752                         ngModelController.$setViewValue(!ngModelController.$viewValue);
9753                         element.prop("checked", ngModelController.$viewValue);
9754                     }
9755                 });
9756
9757                 element.wrap('<div class="btn-switch">');
9758                 //element.attr("tabindex", -1);
9759                 if (navigator.userAgent.match(/iphone/i)){
9760                     element.attr("aria-live", "polite");
9761                 }
9762                 else {
9763                     element.removeAttr('aria-live');
9764                 }
9765
9766                 var templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches.html"));
9767                 if (angular.isDefined(attrs.typeSpanish)) {
9768                     templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches-spanish.html"));
9769                 }
9770
9771                 templateSwitch = $compile(templateSwitch)(scope);
9772                 element.parent().append(templateSwitch);
9773
9774                 element.bind("focus", function (e) {
9775                     element.parent().addClass('focused');
9776                 });
9777
9778                 element.bind("blur", function (e) {
9779                     element.parent().removeClass('focused');
9780                 });
9781             }
9782         };
9783     }]);
9784 /** 
9785  * @ngdoc directive 
9786  * @name Template.att:Table with Drag and Drop
9787  * 
9788  * @description 
9789  *  <file src="src/tableDragAndDrop/docs/readme.md" /> 
9790  * 
9791  * @example 
9792  *  <section id="code"> 
9793         <example module="b2b.att"> 
9794             <file src="src/tableDragAndDrop/docs/demo.html" /> 
9795             <file src="src/tableDragAndDrop/docs/demo.js" /> 
9796        </example> 
9797     </section>    
9798  * 
9799  */
9800 angular.module('b2b.att.tableDragAndDrop', ['b2b.att.utilities','b2b.att.tables'])
9801   
9802 /**
9803  * @ngdoc directive
9804  * @name Messages, modals & alerts.att:tableMessages
9805  *
9806  * @description
9807  *  <file src="src/tableMessages/docs/readme.md" />
9808  *
9809  * @usage
9810     <!-- no matching results -->
9811     <b2b-table-message msg-type="'noMatchingResults'">
9812        <p>No Matching Results</p>
9813     </b2b-table-message>
9814   
9815     <!-- info could not load -->
9816     <b2b-table-message msg-type="'infoCouldNotLoad'" on-refresh-click="refreshClicked()">
9817     </b2b-table-message>
9818    
9819     <!-- magnify search -->
9820     <b2b-table-message msg-type="'magnifySearch'">
9821     </b2b-table-message>
9822    
9823     <!-- loading data -->
9824     <b2b-table-message msg-type="'loadingTable'">
9825           <!-- custom html -->
9826           <p>The data is currently loading...</p>
9827     </b2b-table-message>
9828
9829  * @example
9830     <section id="code">   
9831         <b>HTML + AngularJS</b>
9832         <example module="b2b.att">
9833             <file src="src/tableMessages/docs/demo.html" />
9834             <file src="src/tableMessages/docs/demo.js" />
9835         </example>
9836     </section>
9837  */
9838 angular.module('b2b.att.tableMessages', [])
9839     .directive('b2bTableMessage', [function() {
9840         return {
9841             restrict: 'AE',
9842             replace: true,
9843             transclude: true,
9844             scope: {
9845                 msgType: '=',
9846                 onRefreshClick: '&'
9847             },
9848             templateUrl: 'b2bTemplate/tableMessages/tableMessage.html',
9849             link: function(scope) {
9850                 scope.refreshAction = function(evt) {
9851                     scope.onRefreshClick(evt);
9852                 };
9853             }
9854         };
9855     }]);
9856
9857 /**
9858  * @ngdoc directive
9859  * @name Tabs, tables & accordions.att:tableScrollbar
9860  *
9861  * @description
9862  *  <file src="src/tableScrollbar/docs/readme.md" />
9863  *
9864  * @usage
9865  * 
9866 <b2b-table-scrollbar>
9867     <table>
9868         <thead type="header">
9869             <tr>
9870                 <th role="columnheader" scope="col" key="Id" id="col1">Id</th>
9871                 .....
9872             </tr>
9873         </thead>
9874         <tbody type="body">
9875             <tr>
9876                 <td id="rowheader0" headers="col1">1002</td>
9877                 .....
9878             </tr>
9879         </tbody>
9880     </table>
9881 </b2b-table-scrollbar>
9882  *
9883  * @example
9884  *  <section id="code">
9885         <example module="b2b.att">
9886             <file src="src/tableScrollbar/docs/demo.html" />
9887             <file src="src/tableScrollbar/docs/demo.js" />
9888        </example>
9889     </section>
9890  *
9891  */
9892 angular.module('b2b.att.tableScrollbar', [])
9893     .directive('b2bTableScrollbar', ['$timeout', function ($timeout) {
9894         return {
9895             restrict: 'E',
9896             scope: true,
9897             transclude: true,
9898             templateUrl: 'b2bTemplate/tableScrollbar/tableScrollbar.html',
9899             link: function (scope, element, attrs, ctrl) {
9900                 var firstThWidth, firstTdWidth, firstColumnWidth, firstColumnHeight, trHeight = 0;
9901                 var pxToScroll = '';
9902                 var tableElement = element.find('table');
9903                 var thElements = element.find('th');
9904                 var tdElements = element.find('td');
9905                 var innerContainer = angular.element(element[0].querySelector('.b2b-table-inner-container'));
9906                 var outerContainer = angular.element(element[0].querySelector('.b2b-table-scrollbar'));
9907
9908                 scope.disableLeft = true;
9909                 scope.disableRight = false;
9910
9911                 if (angular.isDefined(thElements[0])) {
9912                     firstThWidth = thElements[0].offsetWidth;
9913                 }
9914                 if (angular.isDefined(tdElements[0])) {
9915                     firstTdWidth = tdElements[0].offsetWidth;
9916                 }
9917                 firstColumnWidth = (firstThWidth > firstTdWidth) ? firstThWidth : firstTdWidth;
9918
9919                 innerContainer.css({
9920                     'padding-left': (firstColumnWidth + 2) + 'px'
9921                 });
9922
9923                 angular.forEach(element.find('tr'), function (eachTr, index) {
9924                     trObject = angular.element(eachTr);
9925                     firstColumn = angular.element(trObject.children()[0]);
9926
9927                     angular.element(firstColumn).css({
9928                         'left': '0px',
9929                         'width': (firstColumnWidth + 2) + 'px',
9930                         'position': 'absolute'
9931                     });
9932
9933                     trHeight = trObject[0].offsetHeight;
9934                     firstColumnHeight = firstColumn[0].offsetHeight;
9935                     if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
9936                         firstColumnHeight += 1;
9937                     }
9938
9939                     if (trHeight !== firstColumnHeight - 1) {
9940                         if (trHeight > firstColumnHeight) {
9941                             if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
9942                                 trHeight -= 1;
9943                             }
9944                             angular.element(firstColumn).css({
9945                                 'height': (trHeight + 1) + 'px'
9946                             });
9947                         } else {
9948                             angular.element(trObject).css({
9949                                 'height': (firstColumnHeight - 1) + 'px'
9950                             });
9951                         }
9952                     }
9953
9954                 });
9955
9956                 pxToScroll = outerContainer[0].offsetWidth - firstColumnWidth;
9957
9958                 scope.scrollLeft = function () {
9959                     innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + 20 - pxToScroll;
9960                 };
9961
9962                 scope.scrollRight = function () {
9963                     innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + pxToScroll - 20;
9964                 };
9965
9966                 scope.checkScrollArrows = function () {
9967                     if (innerContainer[0].scrollLeft == 0) {
9968                         scope.disableLeft = true;
9969                     } else {
9970                         scope.disableLeft = false;
9971                     }
9972
9973                     if (((innerContainer[0].offsetWidth - firstColumnWidth) + innerContainer[0].scrollLeft) >= tableElement[0].offsetWidth) {
9974                         scope.disableRight = true;
9975                     } else {
9976                         scope.disableRight = false;
9977                     }
9978                 };
9979
9980
9981                 innerContainer.bind('scroll', function () {
9982                     $timeout(function () {
9983                         scope.checkScrollArrows();
9984                     }, 1);
9985                 });
9986
9987             }
9988         };
9989     }]);
9990 /**
9991  * @ngdoc directive
9992  * @name Tabs, tables & accordions.att:tables
9993  *
9994  * @description
9995  *  <file src="src/tables/docs/readme.md" />
9996  *
9997  * @usage
9998  *   
9999  Table
10000  <table b2b-table table-data="tableData" search-string="searchString">
10001     <thead b2b-table-row type="header">
10002         <tr>
10003             <th b2b-table-header key="requestId" default-sort="a" id="col1">Header 1</th>
10004             <th b2b-table-header key="requestType" sortable="false" id="col2">Header 2</th>
10005         </tr>
10006     </thead>
10007     <tbody b2b-table-row type="body" row-repeat="rowData in tableData">
10008         <tr>
10009             <td b2b-table-body id="rowheader{{$index}}" headers="col1" ng-bind="rowData['requestId']"> </td>
10010             <td b2b-table-body ng-bind="rowData['requestType']"></td>
10011         </tr>
10012     </tbody>
10013  </table>
10014  *
10015  * @example
10016  *  <section id="code">
10017         <example module="b2b.att">
10018             <file src="src/tables/docs/demo.html" />
10019             <file src="src/tables/docs/demo.js" />
10020        </example>
10021     </section>
10022  *
10023  */
10024 angular.module('b2b.att.tables', ['b2b.att.utilities'])
10025     .constant('b2bTableConfig', {
10026         defaultSortPattern: false, // true for descending & false for ascending
10027         highlightSearchStringClass: 'tablesorter-search-highlight',
10028         zebraStripCutOff: 6, // > zebraStripCutOff
10029         tableBreakpoints: [ // breakpoints are >= min and < max
10030             {
10031                 min: 0,
10032                 max: 480,
10033                 columns: 2
10034             },
10035             {
10036                 min: 480,
10037                 max: 768,
10038                 columns: 3
10039             },
10040             {
10041                 min: 768,
10042                 max: 1025,
10043                 columns: 5
10044             },
10045             {
10046                 min: 1025,
10047                 max: 1920,
10048                 columns: 7
10049             }
10050         ]
10051     })
10052     .directive('b2bTable', ['$filter', '$window', 'b2bTableConfig', '$timeout', function ($filter, $window, b2bTableConfig, $timeout) {
10053         return {
10054             restrict: 'EA',
10055             replace: true,
10056             transclude: true,
10057             scope: {
10058                 tableData: "=",
10059                 viewPerPage: "=",
10060                 currentPage: "=",
10061                 totalPage: "=",
10062                 searchCategory: "=",
10063                 searchString: "=",
10064                 nextSort: '='
10065             },
10066             require: 'b2bTable',
10067             templateUrl: 'b2bTemplate/tables/b2bTable.html',
10068             controller: ['$scope', '$attrs', function ($scope, $attrs) {
10069                 this.headers = [];
10070                 this.currentSortIndex = null;
10071                 this.responsive = $scope.responsive = $attrs.responsive;
10072                 this.maxTableColumns = -1;
10073                 this.totalTableColums = 0;
10074                 this.active = $scope.active = false;
10075                 this.responsiveRowScopes = [];
10076                 this.hideColumnPriority = [];
10077                 this.hiddenColumn = [];
10078                 this.setIndex = function (headerScope, priority) {
10079                     this.headers.push(headerScope);
10080                     if (this.responsive) {
10081                         this.totalTableColums++;
10082                         if (!isNaN(priority)) {
10083                             this.hideColumnPriority[priority] = this.totalTableColums - 1;
10084                         } else {
10085                             this.hideColumnPriority[this.totalTableColums - 1] = this.totalTableColums - 1;
10086                         }
10087                     }
10088                     return this.totalTableColums - 1;
10089                 };
10090                 this.getIndex = function (headerName) {
10091                     for (var i = 0; i < this.headers.length; i++) {
10092                         if (this.headers[i].headerName === headerName) {
10093                             return this.headers[i].index;
10094                         }
10095                     }
10096                     return null;
10097                 };
10098                 this.setResponsiveRow = function (responsiveRowScope) {
10099                     this.responsiveRowScopes.push(responsiveRowScope);
10100                 }
10101                 $scope.nextSort = '';
10102                 this.sortData = function (columnIndex, reverse, externalSort) {
10103                     if ($scope.$parent && $scope.$parent !== undefined) {
10104                         $scope.$parent.columnIndex = columnIndex;
10105                         $scope.$parent.reverse = reverse;
10106                     }
10107                     this.currentSortIndex = columnIndex;
10108                     if (externalSort === true) {
10109                         if (!reverse) {
10110                             $scope.nextSort = 'd'
10111                         } else {
10112                             $scope.nextSort = 'a'
10113                         }
10114                     }
10115                     $scope.currentPage = 1;
10116                     this.resetSortPattern();
10117                 };
10118                 this.getSearchString = function () {
10119                     return $scope.searchString;
10120                 };
10121                 this.resetSortPattern = function () {
10122                     for (var i = 0; i < this.headers.length; i++) {
10123                         var currentScope = this.headers[i];
10124                         if (currentScope.index !== this.currentSortIndex) {
10125                             currentScope.resetSortPattern();
10126                         }
10127                     }
10128                 };
10129
10130                 $scope.$watch('nextSort', function (val) {
10131                     if ($scope.$parent && $scope.$parent !== undefined) {
10132                         $scope.$parent.nextSort = val;
10133                     }
10134
10135                 });
10136             }],
10137             link: function (scope, elem, attr, ctrl) {
10138                 scope.searchCriteria = {};
10139                 scope.tableBreakpoints = attr.tableConfig ? scope.$parent.$eval(attr.tableConfig) : angular.copy(b2bTableConfig.tableBreakpoints);
10140                 scope.$watchCollection('tableData', function (value) {
10141                     if (value && !isNaN(value.length)) {
10142                         scope.totalRows = value.length;
10143                     }
10144                 });
10145                 scope.$watch('currentPage', function (val) {
10146                     if (scope.$parent && scope.$parent !== undefined) {
10147                         scope.$parent.currentPage = val;
10148                     }
10149
10150                 });
10151                 scope.$watch('viewPerPage', function (val) {
10152                     if (scope.$parent && scope.$parent !== undefined) {
10153                         scope.$parent.viewPerPage = val;
10154                     }
10155                 });
10156                 scope.$watch('totalRows', function (val) {
10157                     if (scope.$parent && scope.$parent !== undefined) {
10158                         if (val > b2bTableConfig.zebraStripCutOff) {
10159                             scope.$parent.zebraStripFlag = true;
10160                         } else {
10161                             scope.$parent.zebraStripFlag = false;
10162                         }
10163                     }
10164                 });
10165                 scope.$watch(function () {
10166                     return scope.totalRows / scope.viewPerPage;
10167                 }, function (value) {
10168                     if (!isNaN(value)) {
10169                         scope.totalPage = Math.ceil(value);
10170                         scope.currentPage = 1;
10171                     }
10172                 });
10173                 var searchValCheck = function (val) {
10174                     if (angular.isDefined(val) && val !== null && val !== "") {
10175                         return true;
10176                     }
10177                 };
10178                 var setSearchCriteria = function (v1, v2) {
10179                     if (searchValCheck(v1) && searchValCheck(v2)) {
10180                         var index = ctrl.getIndex(v2);
10181                         scope.searchCriteria = {};
10182                         if (index !== null) {
10183                             scope.searchCriteria[index] = v1;
10184                         }
10185                     } else if (searchValCheck(v1) && (!angular.isDefined(v2) || v2 === null || v2 === "")) {
10186                         scope.searchCriteria = {
10187                             $: v1
10188                         };
10189                     } else {
10190                         scope.searchCriteria = {};
10191                     }
10192                 };
10193                 scope.$watch('searchCategory', function (newVal, oldVal) {
10194                     if (newVal !== oldVal) {
10195                         setSearchCriteria(scope.searchString, newVal);
10196                     }
10197                 });
10198                 scope.$watch('searchString', function (newVal, oldVal) {
10199                     if (newVal !== oldVal) {
10200                         setSearchCriteria(newVal, scope.searchCategory);
10201                     }
10202                 });
10203                 scope.$watchCollection('searchCriteria', function (val) {
10204                     if (scope.$parent && scope.$parent !== undefined) {
10205                         scope.$parent.searchCriteria = val;
10206                     }
10207                     scope.totalRows = scope.tableData && ($filter('filter')(scope.tableData, val, false)).length || 0;
10208                     scope.currentPage = 1;
10209                 });
10210                 var window = angular.element($window);
10211                 var findMaxTableColumns = function () {
10212                     var windowWidth;
10213                     windowWidth = $window.innerWidth;
10214                     ctrl.maxTableColumns = -1;
10215                     for (var i in scope.tableBreakpoints) {
10216                         if (windowWidth >= scope.tableBreakpoints[i].min && windowWidth < scope.tableBreakpoints[i].max) {
10217                             ctrl.maxTableColumns = scope.tableBreakpoints[i].columns;
10218                             break;
10219                         }
10220                     }
10221                     if (ctrl.maxTableColumns > -1 && ctrl.totalTableColums > ctrl.maxTableColumns) {
10222                         ctrl.active = true;
10223                     } else {
10224                         ctrl.active = false;
10225                     }
10226                     for (var i in ctrl.responsiveRowScopes) {
10227                         ctrl.responsiveRowScopes[i].setActive(ctrl.active);
10228                     }
10229                 };
10230                 var findHiddenColumn = function () {
10231                     var columnDiffenence = ctrl.maxTableColumns > -1 ? ctrl.totalTableColums - ctrl.maxTableColumns : 0;
10232                     ctrl.hiddenColumn = [];
10233                     if (columnDiffenence > 0) {
10234                         var tempHideColumnPriority = angular.copy(ctrl.hideColumnPriority);
10235                         for (var i = 0; i < columnDiffenence; i++) {
10236                             ctrl.hiddenColumn.push(tempHideColumnPriority.pop());
10237                         }
10238                     }
10239                 };
10240                 var resizeListener = function () {
10241                     findMaxTableColumns();
10242                     findHiddenColumn();
10243                 };
10244                 if (ctrl.responsive) {
10245                     window.bind('resize', function () {
10246                         resizeListener();
10247                         scope.$apply();
10248                     });
10249                     $timeout(function () {
10250                         resizeListener();
10251                     }, 100);
10252                 }
10253             }
10254         };
10255     }])
10256     .directive('b2bTableRow', [function () {
10257         return {
10258             restrict: 'EA',
10259             compile: function (elem, attr) {
10260                 if (attr.type === 'header') {
10261                     angular.noop();
10262                 } else if (attr.type === 'body') {
10263                     var html = elem.children();
10264                     if (attr.rowRepeat) {
10265                         html.attr('ng-repeat', attr.rowRepeat.concat(" | orderBy : (reverse?'-':'')+ columnIndex  | filter : searchCriteria : false "));
10266                     }
10267                     html.attr('ng-class', "{'odd': $odd && zebraStripFlag}");
10268                     html.attr('class', 'data-row');
10269                     html.attr('b2b-responsive-row', '{{$index}}');
10270                     elem.append(html);
10271                 }
10272             }
10273         };
10274     }])
10275     .directive('b2bTableHeader', ['b2bTableConfig', function (b2bTableConfig) {
10276         return {
10277             restrict: 'EA',
10278             replace: true,
10279             transclude: true,
10280             scope: {
10281                 sortable: '@',
10282                 defaultSort: '@',
10283                 index: '@key'
10284             },
10285             require: '^b2bTable',
10286             templateUrl: function (elem, attr) {
10287                 if (attr.sortable === 'false') {
10288                     return 'b2bTemplate/tables/b2bTableHeaderUnsortable.html';
10289                 } else {
10290                     return 'b2bTemplate/tables/b2bTableHeaderSortable.html';
10291                 }
10292             },
10293             link: function (scope, elem, attr, ctrl) {
10294                 var reverse = b2bTableConfig.defaultSortPattern;
10295                 scope.headerName = elem.text();
10296                 scope.headerId = elem.attr('id');
10297                 scope.sortPattern = null;
10298                 var priority = parseInt(attr.priority, 10);
10299                 scope.columnIndex = ctrl.setIndex(scope, priority);
10300
10301                 scope.isHidden = function () {
10302                     return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10303                 };
10304
10305                 scope.$watch(function () {
10306                     return elem.text();
10307                 }, function (value) {
10308                     scope.headerName = value;
10309                 });
10310                 scope.sort = function (sortType) {
10311                     if (typeof sortType === 'boolean') {
10312                         reverse = sortType;
10313                     }
10314                     ctrl.sortData(scope.index, reverse, false);
10315                     scope.sortPattern = reverse ? 'descending' : 'ascending';
10316                     reverse = !reverse;
10317                 };
10318                 scope.$watch(function () {
10319                     return ctrl.currentSortIndex;
10320                 }, function (value) {
10321                     if (value !== scope.index) {
10322                         scope.sortPattern = null;
10323                     }
10324                 });
10325
10326                 if (scope.sortable === undefined || scope.sortable === 'true' || scope.sortable === true) {
10327                     scope.sortable = 'true';
10328                 } else if (scope.sortable === false || scope.sortable === 'false') {
10329                     scope.sortable = 'false';
10330                 }
10331
10332                 if (scope.sortable !== 'false') {
10333                     if (scope.defaultSort === 'A' || scope.defaultSort === 'a') {
10334                         scope.sort(false);
10335                     } else if (scope.defaultSort === 'D' || scope.defaultSort === 'd') {
10336                         scope.sort(true);
10337                     }
10338                 }
10339                 scope.resetSortPattern = function () {
10340                     reverse = b2bTableConfig.defaultSortPattern;
10341                 };
10342             }
10343         };
10344     }])
10345     .directive('b2bResponsiveRow', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
10346         return {
10347             restrict: 'EA',
10348             require: '^b2bTable',
10349             controller: ['$scope', function ($scope) {
10350                 this.rowValues = $scope.rowValues = [];
10351                 this.setRowValues = function (rowValue) {
10352                     this.rowValues.push(rowValue);
10353                 };
10354                 var columnIndexCounter = -1;
10355                 this.getIndex = function () {
10356                     columnIndexCounter++;
10357                     return columnIndexCounter;
10358                 };
10359             }],
10360             link: function (scope, elem, attr, ctrl) {
10361                 if (ctrl.responsive) {
10362                     scope.rowIndex = attr.b2bResponsiveRow;
10363                     scope.active = false;
10364                     scope.expandFlag = false;
10365                     scope.headerValues = ctrl.headers;
10366                     ctrl.setResponsiveRow(scope);
10367                     var firstTd = elem.find('td').eq(0);
10368                     scope.setActive = function (activeFlag) {
10369                         scope.active = activeFlag;
10370                         if (scope.active) {
10371                             elem.addClass('has-button');
10372                             firstTd.attr('role', 'rowheader');
10373                             firstTd.parent().attr('role', 'row');
10374                         } else {
10375                             elem.removeClass('has-button');
10376                             firstTd.removeAttr('role');
10377                             firstTd.parent().removeAttr('role');
10378                         }
10379                     };
10380                     scope.toggleExpandFlag = function (expandFlag) {
10381                         if (angular.isDefined(expandFlag)) {
10382                             scope.expandFlag = expandFlag;
10383                         } else {
10384                             scope.expandFlag = !scope.expandFlag;
10385                         }
10386                         if (scope.expandFlag) {
10387                             elem.addClass('opened');
10388                         } else {
10389                             elem.removeClass('opened');
10390                         }
10391                     };
10392
10393                     firstTd.attr('scope', 'row');
10394                     firstTd.addClass('col-1');
10395                     scope.$on('$destroy', function () {
10396                         elem.next().remove();
10397                     });
10398                     $timeout(function () {
10399                         scope.firstTdId = firstTd.attr('id');
10400                         var firstTdContent = firstTd.html();
10401                         var toggleButtonTemplate = '<span ng-show="!active">' + firstTdContent + '</span><button type="button" aria-describedby="sup-actNum{{$id}}" aria-expanded="{{expandFlag}}" ng-show="active" ng-click="toggleExpandFlag()"><i ng-class="{\'icon-primary-accordion-plus\': !expandFlag, \'icon-primary-accordion-minus\': expandFlag}" aria-hidden="true"></i>' + firstTdContent + '</button><span id="sup-actNum{{$id}}" style="display:none">{{expandFlag && "Hide row below." || "Show row below."}}</span>';
10402                         toggleButtonTemplate = $compile(toggleButtonTemplate)(scope);
10403                         firstTd.html('');
10404                         firstTd.prepend(toggleButtonTemplate);
10405
10406                         var template = $templateCache.get('b2bTemplate/tables/b2bResponsiveRow.html');
10407                         template = $compile(template)(scope);
10408                         elem.after(template);
10409                     }, 100);
10410                 }
10411             }
10412         };
10413     }])
10414     .directive('b2bResponsiveList', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
10415         return {
10416             restrict: 'EA',
10417             require: '^b2bTable',
10418             link: function (scope, elem, attr, ctrl) {
10419                 scope.columnIndex = parseInt(attr.b2bResponsiveList, 10);
10420                 scope.isVisible = function () {
10421                     return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10422                 };
10423             }
10424         };
10425     }])
10426     .directive('b2bTableBody', ['$filter', '$timeout', 'b2bTableConfig', function ($filter, $timeout, b2bTableConfig) {
10427         return {
10428             restrict: 'EA',
10429             require: ['^b2bTable', '?^b2bResponsiveRow'],
10430             scope: true,
10431             replace: true,
10432             transclude: true,
10433             templateUrl: 'b2bTemplate/tables/b2bTableBody.html',
10434             link: function (scope, elem, attr, ctrl) {
10435                 var b2bTableCtrl = ctrl[0];
10436                 var b2bResponsiveRowCtrl = ctrl[1];
10437                 var highlightSearchStringClass = b2bTableConfig.highlightSearchStringClass;
10438                 var searchString = "";
10439                 var wrapElement = function (elem) {
10440                     var text = elem.text();
10441                     elem.html($filter('b2bHighlight')(text, searchString, highlightSearchStringClass));
10442                 };
10443                 var traverse = function (elem) {
10444                     var innerHtml = elem.children();
10445                     if (innerHtml.length > 0) {
10446                         for (var i = 0; i < innerHtml.length; i++) {
10447                             traverse(innerHtml.eq(i));
10448                         }
10449                     } else {
10450                         wrapElement(elem);
10451                         return;
10452                     }
10453                 };
10454                 var clearWrap = function (elem) {
10455                     var elems = elem.find('*');
10456                     for (var i = 0; i < elems.length; i++) {
10457                         if (elems.eq(i).attr('class') && elems.eq(i).attr('class').indexOf(highlightSearchStringClass) !== -1) {
10458                             var text = elems.eq(i).text();
10459                             elems.eq(i).replaceWith(text);
10460                         }
10461                     }
10462                 };
10463                 if (b2bResponsiveRowCtrl) {
10464                     scope.columnIndex = b2bResponsiveRowCtrl.getIndex();
10465                     scope.isHidden = function () {
10466                         return (b2bTableCtrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10467                     };
10468                 }
10469                 $timeout(function () {
10470                     var actualHtml = elem.children();
10471                     scope.$watch(function () {
10472                         return b2bTableCtrl.getSearchString();
10473                     }, function (val) {
10474                         searchString = val;
10475                         clearWrap(elem);
10476                         if (actualHtml.length > 0) {
10477                             traverse(elem);
10478                         } else {
10479                             wrapElement(elem);
10480                         }
10481                     });
10482                     if (b2bResponsiveRowCtrl) {
10483                         b2bResponsiveRowCtrl.setRowValues(elem.html());
10484                     }
10485                 }, 50);
10486             }
10487         };
10488     }])
10489     .directive('b2bTableSort', ['b2bTableConfig','$timeout', function (b2bTableConfig,$timeout) {
10490         return {
10491             restrict: 'EA',
10492             replace: true,
10493             require: '^b2bTable',
10494             link: function (scope, elem, attr, ctrl) {
10495                 var initialSort = '',
10496                     nextSort = '',
10497                     tempsort = '';
10498                 initialSort = attr.initialSort;
10499
10500                 scope.sortTable = function (msg,trigger) {
10501                     if(trigger == 'dropdown'){
10502                         if (nextSort === 'd' || nextSort === 'D') {                        
10503                          ctrl.sortData(msg, false, false);
10504                         }else{
10505                          ctrl.sortData(msg, true, false);
10506                         }
10507                         return;                        
10508                     }
10509                     $timeout(function(){
10510                         if (nextSort.length > 0) {
10511
10512                         if (nextSort === 'd' || nextSort === 'D') {
10513                             tempsort = nextSort
10514                             ctrl.sortData(msg, true, true);
10515                             nextSort = 'a';
10516                              $timeout(function(){
10517                                 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10518                                     angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10519                                 }   
10520                             },100);
10521                             
10522                         } else {
10523                             tempsort = nextSort
10524                             ctrl.sortData(msg, false, true);
10525                             nextSort = 'd';
10526                              $timeout(function(){
10527                                 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10528                                     angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10529                                 }   
10530                             },100);
10531                         }
10532                     } else if (initialSort.length > 0) {
10533
10534                         if (initialSort === 'd' || initialSort === 'D') {
10535                             tempsort = nextSort
10536                             ctrl.sortData(msg, true, true);
10537                             nextSort = 'a';
10538                             $timeout(function(){
10539                                 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10540                                     angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10541                                 }   
10542                             },100);
10543                              
10544                         } else {
10545                             tempsort = nextSort
10546                             ctrl.sortData(msg, false, true);
10547                             nextSort = 'd';
10548                              $timeout(function(){
10549                                 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10550                                     angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10551                                 }   
10552                             },100);
10553
10554                              
10555                         }
10556                     }
10557                     },10)
10558
10559                 };
10560
10561                 scope.sortDropdown = function(msg) {
10562
10563                     if(tempsort==='') {
10564
10565                         tempsort='a'
10566                     }
10567                     if(tempsort === 'd' || tempsort === 'D' ) {
10568                         ctrl.sortData(msg, true, false);       
10569                     } else {
10570                        ctrl.sortData(msg, false, false);
10571                     }
10572
10573                 };
10574             }
10575         };
10576     }]);
10577 /**
10578  * @ngdoc directive
10579  * @name Tabs, tables & accordions.att:tabs
10580  *
10581  * @description
10582  *  <file src="src/tabs/docs/readme.md" />
10583  *
10584  * @usage
10585  *  <b2b-tabset tab-id-selected="activeTabsId">
10586         <b2b-tab ng-repeat="tab in gTabs" tab-item="tab" 
10587                  id="{{tab.uniqueId}}" aria-controls="{{tab.tabPanelId}}"
10588                  ng-disabled="tab.disabled">
10589             {{tab.title}}
10590         </b2b-tab>
10591     </b2b-tabset>
10592  *
10593  * @example
10594  *  <section id="code">
10595         <example module="b2b.att">
10596             <file src="src/tabs/docs/demo.html" />
10597             <file src="src/tabs/docs/demo.js" />
10598         </example>
10599     </section>
10600  *
10601  */
10602
10603 angular.module('b2b.att.tabs', ['b2b.att.utilities'])
10604     .directive('b2bTabset', function () {
10605         return {
10606             restrict: 'EA',
10607             transclude: true,
10608             replace: true,
10609             scope: {
10610                 tabIdSelected: '='
10611             },
10612             templateUrl: 'b2bTemplate/tabs/b2bTabset.html',
10613             controller: ['$scope', function ($scope) {
10614
10615                 this.setTabIdSelected = function (tab) {
10616                     $scope.tabIdSelected = tab.id;
10617                 };
10618
10619                 this.getTabIdSelected = function () {
10620                     return $scope.tabIdSelected;
10621                 };
10622             }]
10623         };
10624     })
10625     .directive('b2bTab', ['keymap', function (keymap) {
10626         return {
10627             restrict: 'EA',
10628             transclude: true,
10629             replace: true,
10630             require: '^b2bTabset',
10631             scope: {
10632                 tabItem: "="
10633             },
10634             templateUrl: 'b2bTemplate/tabs/b2bTab.html',
10635             controller: [function(){}],
10636             link: function (scope, element, attr, b2bTabsetCtrl) {
10637
10638                 if (scope.tabItem && !scope.tabItem.disabled) {
10639                     scope.tabItem.disabled = false;
10640                 }
10641
10642                 scope.isTabActive = function () {
10643                     return (scope.tabItem.id === b2bTabsetCtrl.getTabIdSelected());
10644                 };
10645
10646                 scope.clickTab = function () {
10647                     if (attr.disabled) {
10648                         return;
10649                     }
10650                     b2bTabsetCtrl.setTabIdSelected(scope.tabItem);
10651                 };
10652
10653                 scope.nextKey = function () {
10654                     var el = angular.element(element[0])[0];
10655                     var elementToFocus = null;
10656                     while (el && el.nextElementSibling) {
10657                         el = el.nextElementSibling;
10658                         if (!el.querySelector('a').disabled) {
10659                             elementToFocus = el.querySelector('a');
10660                             break;
10661                         }
10662                     }
10663
10664                     if (!elementToFocus) {
10665                         var childTabs = element.parent().children();
10666                         for (var i = 0; i < childTabs.length; i++) {
10667                             if (!childTabs[i].querySelector('a').disabled) {
10668                                 elementToFocus = childTabs[i].querySelector('a');
10669                                 break;
10670                             }
10671                         }
10672                     }
10673
10674                     if (elementToFocus) {
10675                         elementToFocus.focus();
10676                     }
10677                 };
10678
10679                 scope.previousKey = function () {
10680                     var el = angular.element(element[0])[0];
10681                     var elementToFocus = null;
10682
10683                     while (el && el.previousElementSibling) {
10684                         el = el.previousElementSibling;
10685                         if (!el.querySelector('a').disabled) {
10686                             elementToFocus = el.querySelector('a');
10687                             break;
10688                         }
10689                     }
10690
10691                     if (!elementToFocus) {
10692                         var childTabs = element.parent().children();
10693                         for (var i = childTabs.length - 1; i > 0; i--) {
10694                             if (!childTabs[i].querySelector('a').disabled) {
10695                                 elementToFocus = childTabs[i].querySelector('a');
10696                                 break;
10697                             }
10698                         }
10699                     }
10700
10701                     if (elementToFocus) {
10702                         elementToFocus.focus();
10703                     }
10704                 };
10705
10706                 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
10707
10708                     if (!(evt.keyCode)) {
10709                         evt.keyCode = evt.which;
10710                     }
10711
10712                     switch (evt.keyCode) {
10713                         case keymap.KEY.RIGHT:
10714                             evt.preventDefault();
10715                             scope.nextKey();
10716                             break;
10717
10718                         case keymap.KEY.LEFT:
10719                             evt.preventDefault();
10720                             scope.previousKey();
10721                             break;
10722
10723                         default:;
10724                     }
10725                 });
10726             }
10727         };
10728     }]);
10729 /**
10730  * @ngdoc directive
10731  * @name Messages, modals & alerts.att:tagBadges
10732  *
10733  * @description
10734  *  <file src="src/tagBadges/docs/readme.md" />
10735  *
10736  * @example
10737  *  <section id="code">
10738         <example module="b2b.att">
10739             <file src="src/tagBadges/docs/demo.html" />
10740             <file src="src/tagBadges/docs/demo.js" />
10741         </example>
10742     </section>
10743  *
10744  */
10745 angular.module('b2b.att.tagBadges', ['b2b.att.utilities'])
10746         .directive('b2bTagBadge',['$timeout',function($timeout){
10747             return{
10748                 restrict: 'EA',
10749                 link: function(scope,elem,attr,ctrl){
10750                     elem.addClass('b2b-tags');
10751                     if(angular.element(elem[0].querySelector('.icon-primary-close')).length>0) {
10752                         var item = angular.element(elem[0].querySelector('.icon-primary-close'));
10753                         item.bind('click',function(){
10754                         elem.css({'height':'0','width':'0','padding':'0','border':'0'});
10755                         elem.attr('tabindex','0');
10756                         elem[0].focus();
10757                         item.parent().remove();
10758                         elem[0].bind('blur',function(){
10759                             elem[0].remove();
10760                         });
10761                     });  
10762                     }
10763                   
10764
10765
10766
10767                 }
10768             };   
10769 }]);
10770 /**
10771  * @ngdoc directive
10772  * @name Forms.att:textArea
10773  *
10774  * @description
10775  *  <file src="src/textArea/docs/readme.md" />
10776  *
10777  * @usage
10778  *  <textarea b2b-reset b2b-reset-textarea ng-model="textAreaModel" ng-disabled="disabled" ng-trim="false" placeholder="{{placeholderText}}" rows="{{textAreaRows}}" maxlength="{{textAreaMaxlength}}" role="textarea"></textarea>
10779  *
10780  * @example
10781     <section id="code">
10782         <b>HTML + AngularJS</b>
10783         <example module="b2b.att">
10784             <file src="src/textArea/docs/demo.html" />
10785             <file src="src/textArea/docs/demo.js" />
10786         </example>
10787     </section>
10788  */
10789 angular.module('b2b.att.textArea', ['b2b.att.utilities'])
10790
10791 .directive('b2bResetTextarea', [ function () {
10792     return {
10793         restrict: 'A',
10794         require: 'b2bReset',
10795         link: function (scope, element, attrs, ctrl) {
10796
10797             var resetButton = ctrl.getResetButton();
10798             
10799             var computeScrollbarAndAddClass = function () {
10800                 if (element.prop('scrollHeight') > element[0].clientHeight) {
10801                     element.addClass('hasScrollbar');
10802                 } else {
10803                     element.removeClass('hasScrollbar');
10804                 }
10805             };
10806             
10807             computeScrollbarAndAddClass();
10808
10809             element.on('focus keyup', function(){
10810                 computeScrollbarAndAddClass();
10811             });
10812         }
10813     };
10814 }]);
10815
10816 /**
10817  * @ngdoc directive
10818  * @name Forms.att:timeInputField
10819  *
10820  * @description
10821  *  <file src="src/timeInputField/docs/readme.md" />
10822  *
10823  
10824  * @example
10825  *  <section id="code">
10826         <example module="b2b.att">
10827             <file src="src/timeInputField/docs/demo.html" />
10828             <file src="src/timeInputField/docs/demo.js" />
10829        </example>
10830     </section>
10831  *
10832  */
10833 angular.module('b2b.att.timeInputField',['ngMessages', 'b2b.att.utilities']).directive('b2bTimeFormat',function(){
10834     return{
10835         restrict : 'A',
10836         require : '^ngModel',
10837         link : function(scope,elem,attr,ctrl){
10838             elem.on('keyup',function(evt){
10839                 var modelValue = ctrl.$modelValue;
10840                 var format = attr.b2bTimeFormat;
10841                  modelValue = modelValue.split(':');
10842                 if(format == "12"){
10843                     if(!(modelValue[0] <= 12 && modelValue[0] > 0 ) || !(modelValue[1] <= 59)){
10844                         ctrl.$setValidity('inValidTime',false);   
10845                     }else{
10846                         ctrl.$setValidity('inValidTime',true);
10847                     }
10848                 }else if(format =="24"){
10849                     if(!(modelValue[0] <= 23) || !(modelValue[1] <= 59)){
10850                         ctrl.$setValidity('inValidTime',false);
10851                     }else{
10852                         ctrl.$setValidity('inValidTime',true);
10853                     }
10854                 }                
10855                scope.$apply();
10856             });
10857         }
10858     }
10859 });
10860
10861 /**
10862  * @ngdoc directive
10863  * @name Forms.att:tooltipsForForms
10864  *
10865  * @description
10866  *  <file src="src/tooltipsForForms/docs/readme.md" />
10867  *
10868  * @example
10869  <example module="b2b.att">
10870  <file src="src/tooltipsForForms/docs/demo.html" />
10871  <file src="src/tooltipsForForms/docs/demo.js" />
10872  </example>
10873  */
10874 angular.module('b2b.att.tooltipsForForms', ['b2b.att.utilities'])
10875         .directive('b2bTooltip', ['$document', '$window', '$isElement', function ($document, $window, $isElement) {
10876                 return  {
10877                     restrict: 'A',
10878                     link: function (scope, elem, attr, ctrl) {
10879                         var icon = elem[0].querySelector('a.tooltip-element');
10880                         var btnIcon = elem[0].querySelector('.btn.tooltip-element');
10881                         var tooltipText = elem[0].querySelector('.helpertext');
10882                         var tooltipWrapper = elem[0].querySelector('.tooltip-size-control');
10883                         if (elem.hasClass('tooltip-onfocus')) {
10884                             var inputElm = angular.element(elem[0].querySelector("input"));
10885                             var textAreaElm = angular.element(elem[0].querySelector("textarea"));
10886                         }
10887                         angular.element(icon).attr({'aria-expanded': false});
10888                         angular.element(btnIcon).attr({'aria-expanded': false});
10889                         var calcTooltip = function () {
10890                             if (!elem.hasClass('tooltip active')) {
10891                                 if (elem.hasClass('tooltip-onfocus')) {
10892                                     angular.element(elem[0].querySelector("input")).triggerHandler('focusout');
10893                                 }
10894                                 if (elem.hasClass('tooltip-onclick')) {
10895                                     return false;
10896                                 }
10897                                 angular.element(icon).removeClass('active');
10898                                 angular.element(icon).attr({'aria-expanded': true});
10899                                 angular.element(icon).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
10900                                 angular.element(tooltipText).attr({'aria-hidden': false});
10901                                 elem.addClass('active');
10902
10903                                 var tooltipIconPos = angular.element(icon).prop('offsetLeft'),
10904                                         tooltipPosition = angular.element(tooltipText).prop('offsetWidth') / 2,
10905                                         tipOffset = (tooltipIconPos - 30) - tooltipPosition,
10906                                         maxRightPos = (($window.innerWidth - 72) - (tooltipPosition * 2)) - 14.5;
10907
10908                                 if ($window.innerWidth >= '768') {
10909                                     if (tipOffset < 0) {// if icon on far left side of page
10910                                         tipOffset = 15;
10911                                     }
10912                                     else if (tooltipIconPos > maxRightPos) {// if icon is far right side of page
10913                                         tipOffset = maxRightPos;
10914                                     }
10915                                     else {// if tooltip in the middle somewhere
10916                                         tipOffset = tipOffset;
10917                                     }
10918                                     angular.element(tooltipWrapper).css({left: tipOffset + 'px'});
10919                                 }
10920                             }
10921                         };
10922                         
10923                         // TOOLTIP LINK ONCLICK AND FOCUS
10924                         angular.element(icon).on('click mouseover mouseout focus blur', function (e) {
10925                             if (e.type == 'mouseover') {
10926                                 calcTooltip();
10927                             }
10928                             else if (e.type == 'mouseout' && elem.hasClass('active')) {
10929                                 if (!elem.hasClass('activeClick')) {
10930                                     angular.element(tooltipText).attr({
10931                                         'aria-hidden': true,
10932                                         'tabindex': '-1'
10933                                     });
10934                                     elem.removeClass('active');
10935                                 } else if (elem.hasClass('activeClick') && navigator.userAgent.match(/iphone/i)) {
10936                                     elem.removeClass('active activeClick');
10937                                 }
10938                             }
10939
10940                             else {
10941                                 if (elem.hasClass('activeClick')) {
10942                                     angular.element(icon).attr({'aria-expanded': false});
10943                                     angular.element(tooltipText).attr({'aria-hidden': true});
10944                                     angular.element(icon).removeAttr('aria-describedby');
10945                                     elem.removeClass('activeClick active');
10946                                     e.preventDefault();
10947                                 }
10948                                 else if (e.type == 'click') {
10949                                     elem.addClass('activeClick');
10950                                     calcTooltip();
10951                                     e.preventDefault();
10952                                 }
10953                                 else {
10954                                     angular.element(icon).on('keydown', function (e) {
10955                                         if (e.keyCode == '32') {
10956                                             (elem.hasClass('active')) ? elem.removeClass('active') : elem.addClass('value');
10957                                             angular.element(icon).triggerHandler('click');
10958                                             e.preventDefault();
10959                                         } else if (e.keyCode == '27') {
10960                                             (elem.hasClass('active')) ? elem.removeClass('active activeClick') : elem.addClass('value');
10961                                         }
10962                                     });
10963                                     e.preventDefault();
10964                                 }
10965                             }
10966                             e.preventDefault();
10967                         });
10968
10969                         // TOOLTIP BUTTON INSIDE A TEXT FIELD
10970                         angular.element(btnIcon).on('click', function (e) {
10971                             var $this = angular.element(this);
10972                             if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10973                                 elem.removeClass('active');
10974                                 $this.removeClass('active');
10975                                 angular.element(tooltipText).removeAttr('aria-live');
10976                                 $this.attr({'aria-expanded': 'false'});
10977                                 $this.removeAttr('aria-describedby');
10978                             } else {
10979                                 elem.addClass('active');
10980                                 $this.addClass('active');
10981                                 $this.attr({'aria-expanded': 'true', 'aria-describedby': angular.element(tooltipText).attr('id')});
10982                                 angular.element(tooltipText).attr({'aria-live': 'polite'});
10983                             }
10984                         });
10985
10986                         angular.element(btnIcon).on('blur', function (e) {
10987                             var $this = angular.element(this);
10988                             if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10989                                 elem.removeClass('active');
10990                                 $this.removeClass('active');
10991                                 angular.element(tooltipText).removeAttr('aria-live');
10992                                 $this.attr({'aria-expanded': 'false'});
10993                                 $this.removeAttr('aria-describedby');
10994                             }
10995                         });  
10996
10997                         angular.element(btnIcon).on('keydown', function (e) {
10998                             var $this = angular.element(this);
10999                             if (e.keyCode == '27') {
11000                                 var $this = angular.element(this);
11001                                 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
11002                                     elem.removeClass('active');
11003                                     $this.removeClass('active');
11004                                     angular.element(tooltipText).removeAttr('aria-live');
11005                                     $this.attr({'aria-expanded': 'false'});
11006                                     $this.removeAttr('aria-describedby');
11007                                 }
11008                             }
11009                         });
11010
11011                         // close all tooltips if clicking something else
11012                         $document.bind('click', function (e) {
11013                             var isElement = $isElement(angular.element(e.target), elem, $document);
11014                             if (!isElement) {
11015                                 elem.removeClass('active');
11016                                 angular.element(elem[0].querySelector('.tooltip-element')).removeClass('active');
11017                                 angular.element(tooltipText).removeAttr('aria-live');
11018                                 angular.element(elem[0].querySelector('.tooltip-element')).attr({'aria-expanded': 'false'});
11019                                 angular.element(elem[0].querySelector('.tooltip-element')).removeAttr('aria-describedby');
11020                             };
11021                         });
11022
11023                         angular.element(inputElm).on('keydown', function (e) {
11024                             if (e.keyCode == '27'){
11025                                 elem.removeClass('active');
11026                                 angular.element(tooltipText).css('display', 'none');
11027                                 angular.element(tooltipText).removeAttr('aria-live');
11028
11029                                 if (angular.element(this).attr('aria-describedby') === undefined){
11030
11031                                 }
11032
11033                                 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
11034
11035                                     var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
11036
11037                                     angular.element(this).attr('aria-describedby', describedByValue);
11038
11039                                 }
11040                                 else {
11041                                     angular.element(this).removeAttr('aria-describedby');
11042                                 }
11043                             }
11044                         });
11045
11046                         angular.element(textAreaElm).on('keydown', function (e) {
11047                             if (e.keyCode == '27'){
11048                                 elem.removeClass('active');
11049                                 angular.element(tooltipText).css('display', 'none');
11050                                 angular.element(tooltipText).removeAttr('aria-live');
11051                                 if (angular.element(this).attr('aria-describedby') === undefined){
11052
11053                                 }
11054
11055                                 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
11056
11057                                     var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
11058
11059                                     angular.element(this).attr('aria-describedby', describedByValue);
11060
11061                                 }
11062                                 else {
11063                                     angular.element(this).removeAttr('aria-describedby');
11064                                 }
11065                             }
11066                         });
11067
11068                         // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXT FIELD
11069                         angular.element(inputElm).on('focus', function (e) {
11070                             var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
11071                             for (var i = 0; i < allTooltip.length; i++) {
11072                                 if (angular.element(allTooltip[i]).hasClass('active')) {
11073                                     angular.element(allTooltip[i]).triggerHandler('click');
11074                                 }
11075                             };
11076                             angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
11077                             angular.element(tooltipText).css('display', 'block');
11078                             angular.element(tooltipText).attr({'aria-live': 'polite'});
11079                             elem.addClass('active');
11080                         });
11081                         angular.element(inputElm).on('blur', function (e) {
11082                             elem.removeClass('active');
11083                             angular.element(tooltipText).css('display', 'none');
11084                             angular.element(tooltipText).removeAttr('aria-live');
11085                             angular.element(this).removeAttr('aria-describedby');
11086                         });
11087
11088                         // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXTAREA
11089                         angular.element(textAreaElm).on('focus', function (e) {
11090                             var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
11091                             for (var i = 0; i < allTooltip.length; i++) {
11092                                 if (angular.element(allTooltip[i]).hasClass('active')) {
11093                                     angular.element(allTooltip[i]).triggerHandler('click');
11094                                 }
11095                             };
11096                             elem.addClass('active');
11097                             angular.element(tooltipText).css('display', 'block');
11098                             angular.element(tooltipText).attr({'aria-live': 'polite'});
11099                             angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
11100                         });
11101                         angular.element(textAreaElm).on('blur', function (e) {
11102                             elem.removeClass('active');
11103                             angular.element(tooltipText).css('display', 'none');
11104                             angular.element(tooltipText).removeAttr('aria-live');
11105                             angular.element(this).removeAttr('aria-describedby');
11106                         });
11107                     }
11108                 };
11109             }]); 
11110 /**
11111  * @ngdoc directive
11112  * @name Navigation.att:TreeNavigation
11113  *
11114  *
11115  * @scope
11116  * @param {String} setRole - This value needs to be "tree". This is required to incorporate CATO requirements.
11117  * @param {Boolean} groupIt - This value needs to be "false" for top-level tree rendered.
11118  *
11119  * @description
11120  *  <file src="src/treeNav/docs/readme.md" />
11121  *
11122  * @usage
11123  *      <div class="b2b-tree">
11124  *                <b2b-tree-nav collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-nav>
11125  *            </div>
11126  * @example
11127  *  <section id="code">
11128         <example module="b2b.att">
11129             <file src="src/treeNav/docs/demo.html" />
11130             <file src="src/treeNav/docs/demo.js" />
11131        </example>
11132     </section>
11133  *
11134  */
11135 angular.module('b2b.att.treeNav', ['b2b.att.utilities'])
11136     .directive('b2bTreeNav', function () {
11137         return {
11138             restrict: "E",
11139             replace: true,
11140             scope: {
11141                 collection: '=',
11142                 groupIt: '=',
11143                 setRole: '@'
11144             },
11145             templateUrl: function (element, attrs) {
11146                 if (attrs.groupIt === 'true') {
11147                     return "b2bTemplate/treeNav/groupedTree.html";
11148                 } else {
11149                     return "b2bTemplate/treeNav/ungroupedTree.html";
11150                 }
11151             },
11152             link: function (scope) {               
11153                 if (!(scope.setRole === 'tree')) {
11154                     scope.setRole = 'group';
11155                 }             
11156             }
11157         }
11158     })
11159     .directive('b2bMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
11160         return {
11161             restrict: "E",
11162             replace: true,
11163             scope: {
11164                 member: '=',
11165                 groupIt: '='
11166             },
11167             templateUrl: 'b2bTemplate/treeNav/treeMember.html',
11168             link: function (scope, element, attrs) {
11169                 scope.elemArr = [];
11170                 var removeRootTabIndex = function (elem) {
11171                     if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
11172                         elem.attr('tabindex', -1);                        
11173                         return;
11174                     }
11175                     removeRootTabIndex(elem.parent());
11176                 };
11177                                 scope.$watch('member.child', function(newVal, oldVal){                                  
11178                                         if(newVal !== oldVal){
11179                                                 scope.showChild();
11180                                         };
11181                                 });
11182                 scope.showChild = function () {
11183                         if (!element.hasClass('grouped')) {
11184                             if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11185                                 scope.groupIt = false;
11186                                 element.addClass('grouped');
11187                                 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11188                                 $compile(element.contents())(scope);
11189                                 if(scope.member.active && scope.member.active === true){
11190                                     element.find('i').eq(0).removeClass('icon-primary-collapsed');
11191                                 };
11192                                 if(scope.member.selected && scope.member.selected === true){
11193                                     element.attr('aria-selected', true);
11194                                     element.attr('tabindex', 0);
11195                                     removeRootTabIndex(element);
11196                                 };
11197                                 if(scope.member.active && scope.member.active == undefined){
11198                                     element.find('i').eq(0).addClass('icon-primary-collapsed');
11199                                 };
11200                             } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11201                                 element.addClass('grouped');
11202                                 scope.groupIt = true;
11203                                 // FILTER - GROUPBY - APPROACH 
11204                                 var j = 0;
11205                                 var grpName = '';
11206                                 if(scope.member.child[0].groupName !== undefined){
11207                                     grpName = scope.member.child[0].groupName;
11208                                 }
11209                                 else{
11210                                     var toSlice = scope.member.child[0].name.search(' ');
11211                                     grpName = scope.member.child[0].name.slice(0, toSlice);
11212                                 }
11213
11214                                 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11215                                     j = 0;
11216                                     for (j = j + i; j < (i + scope.member.divide); j++) {                                        
11217                                         if (j === scope.member.child.length) {
11218                                             scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11219                                             break;
11220                                             
11221                                             if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11222                                                 scope.member.child[j-1].activeGrp = true;
11223                                             };
11224                                             
11225                                         }
11226                                         if (i + scope.member.divide > scope.member.child.length) {
11227                                             scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11228                                             if(scope.member.child[j].active && scope.member.child[j].active===true){
11229                                                 scope.member.child[j].activeGrp = true;
11230                                             };
11231
11232                                         } else {
11233                                             scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
11234                                             if(scope.member.child[j].active && scope.member.child[j].active===true){
11235                                                 scope.member.child[j].activeGrp = true;
11236                                             };
11237                                         }
11238                                     }
11239                                 }
11240                                                                 if(scope.member.divide){
11241                                                                         element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11242                                                                 } else {
11243                                                                         element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11244                                                                 }
11245                                 $compile(element.contents())(scope);
11246                                 if(scope.member.active && scope.member.active === true){
11247                                     element.find('i').eq(0).removeClass('icon-primary-collapsed');
11248                                 };
11249                                 if(scope.member.selected && scope.member.selected === true){
11250                                     element.attr('aria-selected', true);
11251                                 };
11252                                 if( scope.member.active && scope.member.active == undefined){
11253                                     element.find('i').eq(0).addClass('icon-primary-collapsed');
11254                                 };
11255                             }
11256                         }
11257                 };
11258                                 //Below condition opens node for opening on json load.
11259                 if(scope.member.active && scope.member.active == true){
11260                     scope.showChild();
11261                 };
11262                 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
11263                     element.find('i').eq(0).addClass('icon-primary-collapsed');
11264                 }
11265                 else if(scope.member.child == undefined){
11266                     element.find('i').eq(0).addClass('icon-primary-circle');
11267                 };
11268                 element.bind('keydown', function (evt) {
11269                     switch (evt.keyCode) {
11270                         case keymap.KEY.ENTER:
11271                             if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
11272                                 scope.member.onSelect(scope.member);
11273                             }
11274                             evt.stopPropagation();
11275                             break;
11276                         default: 
11277                             break;                            
11278                     }
11279                     
11280                 });
11281                 //else getting true in every case .. so better use switch case .. that makes more sense you dumb.
11282                 element.bind('click', function (evt) {
11283                                         scope.showChild();
11284                                         var expandFunc = scope.member.onExpand;
11285                                         
11286                     //onSelect
11287                         if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
11288                                     scope.member.onSelect(scope.member);
11289                                 }
11290                         if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
11291                            var eValue = scope.member.onExpand(scope.member);
11292                         }
11293                         if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
11294                             scope.member.onCollapse(scope.member);
11295                         }
11296                 });
11297             }
11298         }
11299 }])
11300     .directive('b2bTreeLink', ['keymap', '$timeout', function (keymap, $timeout) {
11301         return {
11302             restrict: 'A',
11303             link: function (scope, element, attr, ctrl) {
11304                 var rootE, parentE, upE, downE;
11305                 var closeOthersUp = function (elem,isKeyPress,passiveClose) {
11306                     //For accordion functionality on sibling nodes
11307                     if (elem.find('a').eq(0).hasClass('active')) {
11308                         activeToggle(elem,isKeyPress,passiveClose);
11309                         return;
11310                     }
11311                     if (elem.hasClass('bg') && !isKeyPress) {
11312                         elem.removeClass('bg');
11313                         if (elem.attr('aria-selected')) {
11314                             elem.attr('aria-selected', 'false');
11315                         }                        
11316                     }
11317                     if (elem[0].previousElementSibling !== null) {
11318                         closeOthersUp(angular.element(elem[0].previousElementSibling),isKeyPress);
11319                     }
11320                 };
11321                 var closeOthersDown = function (elem,isKeyPress,passiveClose) {
11322                     //For accordion functionality on sibling nodes
11323                     if (elem.find('a').eq(0).hasClass('active')) {
11324                         activeToggle(elem,isKeyPress,passiveClose);
11325                         return;
11326                     }
11327                     if (elem.hasClass('bg') && !isKeyPress) {
11328                         elem.removeClass('bg');
11329                         if (elem.attr('aria-selected')) {
11330                             elem.attr('aria-selected', 'false');
11331                         }                        
11332                     }
11333                     if (elem[0].nextElementSibling !== null) {
11334                         closeOthersDown(angular.element(elem[0].nextElementSibling),isKeyPress);
11335                     }
11336                 };
11337
11338                
11339                 var removeBackground = function(elem){
11340
11341                     if(elem.hasClass('b2b-tree')){
11342                         angular.element(elem[0].getElementsByClassName('bg')).removeClass('bg');
11343                         return;
11344                     }else{
11345                         removeBackground(elem.parent().parent());
11346                     }
11347
11348                 };
11349
11350 /**
11351 * These two functions used for setting heights on parent nodes as the child node closes
11352 * Retaining this code for future reference
11353
11354                 var addParentHeight = function(e, h) {
11355                     var parentLi = e.parent().parent();
11356                     var parentUl = e.parent();
11357                     if(!parentLi.hasClass('b2b-tree')) {
11358                         var addHeight = parentUl[0].offsetHeight + h;
11359                         parentLi.find('ul').eq(0).css({
11360                             height: addHeight+'px'
11361                         })
11362                         addParentHeight(parentLi, h);
11363                     }                    
11364                 };
11365
11366                 var removeParentHeight = function(e, h) {
11367                     var parentLi = e.parent().parent();
11368                     var parentUl = e.parent();
11369                     if(!parentLi.hasClass('b2b-tree')) {
11370                         var addHeight = parentUl[0].offsetHeight - h;
11371                         parentLi.find('ul').eq(0).css({
11372                             height: addHeight+'px'
11373                         })
11374                         removeParentHeight(parentLi, h);
11375                     }
11376                 };
11377 */          
11378
11379             // isKeyPress - to notify that the function is called by Right Key press
11380             // passiveClose -  prevents firing of oncollapse events during the action
11381             // of expand function(check the function definition)
11382
11383                 var activeToggle = function (elem,isKeyPress,passiveClose) {
11384                     var element = elem.find('a').eq(0);
11385                     if (element.hasClass('active')) {
11386                         if(!isKeyPress){
11387                             elem.removeClass('bg');
11388                         }
11389                         
11390                         if (elem.attr('aria-selected') && !isKeyPress) {
11391                             elem.attr('aria-selected', 'false');
11392                         }
11393                         if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11394                             if(isKeyPress && scope.member){
11395                                 if (scope.member.onCollapse !== undefined && !passiveClose) {
11396                                     scope.member.onCollapse(scope.member);
11397                                 }
11398                             }
11399                             element.removeClass('active');
11400                             elem.attr('aria-expanded', 'false');
11401                             element.find('i').eq(0).removeClass('icon-primary-expanded');
11402                             element.find('i').eq(0).addClass('icon-primary-collapsed');
11403                             //For Animation: below commented code is used to manually set height of UL to zero 
11404                             //retaining code for future reference
11405                             /*
11406                             var totalHeight = elem.find('ul')[0].scrollHeight;
11407                             removeParentHeight(elem, totalHeight);
11408                             elem.find('ul').eq(0).css({
11409                                 height: null
11410                             });*/
11411                         }
11412                     } else {
11413                         if(!isKeyPress){
11414                             elem.addClass('bg');
11415                             elem.attr('aria-selected', 'true');
11416                         }
11417                         
11418                         if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11419                             if(isKeyPress){
11420                                 if(typeof scope.showChild === 'function' ){
11421                                 scope.showChild();
11422                                 }
11423                                 if(scope.member){
11424                                     if (scope.member.onExpand !== undefined) {
11425                                         scope.member.onExpand(scope.member);
11426                                     }
11427                                 }
11428                             }
11429                             element.addClass('active');
11430                             elem.attr('aria-expanded', 'true');
11431                             element.find('i').eq(0).removeClass('icon-primary-collapsed');
11432                             element.find('i').eq(0).addClass('icon-primary-expanded');
11433                             //For Animation: below commented code is used to manually set height of the ul generatedon the click of parent LI.
11434                             //retaining code for future reference
11435                             /*                            
11436                             var totalHeight = elem.find('ul')[0].scrollHeight;
11437                             addParentHeight(elem, totalHeight);
11438                             elem.find('ul').eq(0).css({
11439                                 height: totalHeight+'px'
11440                             });*/
11441                             
11442                         }
11443                     }
11444                 };
11445                 element.bind('click', function (evt) {
11446                     //first we close others and then we open the clicked element
11447                                         if (element[0].previousElementSibling) {
11448                                                 closeOthersUp(angular.element(element[0].previousElementSibling));
11449                                         }
11450                                         if (element[0].nextElementSibling) {
11451                                                 closeOthersDown(angular.element(element[0].nextElementSibling));
11452                                         }
11453                     removeBackground(element);
11454                                         activeToggle(element);                    
11455                     
11456                     evt.stopPropagation();                    
11457                 });
11458                 //default root tree element tabindex set zero
11459                 if (element.parent().parent().hasClass('b2b-tree') && (element.parent()[0].previousElementSibling === null)) {
11460                     element.attr('tabindex', 0);
11461                 }
11462                 //check root via class
11463                 var isRoot = function (elem) {
11464                     if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
11465                         return true;
11466                     } else {
11467                         return false;
11468                     }
11469                 };
11470                 var findRoot = function (elem) {
11471                     if (isRoot(elem)) {
11472                         rootE = elem;
11473                         return;
11474                     }
11475                     findRoot(elem.parent());
11476                 };
11477
11478                 var findPreActive = function (elem) {
11479
11480                     if (!(elem.hasClass("active"))) {
11481                         return;
11482                     } else {
11483                         var childElems = angular.element(elem[0].nextElementSibling.children);
11484                         lastE = angular.element(childElems[childElems.length - 1]);
11485                         if (lastE.find('a').eq(0).hasClass('active')) {
11486                             findPreActive(lastE.find('a').eq(0));
11487                         }
11488                         upE = lastE;
11489                     }
11490                 };
11491
11492                 var findUp = function (elem) {
11493                     if (isRoot(elem)) {
11494                         upE = elem;
11495                         return;
11496                     }
11497                     if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
11498                         upE = angular.element(elem[0].previousElementSibling);
11499                         if (upE.find('a').eq(0).hasClass('active')) {
11500                             findPreActive(upE.find('a').eq(0));
11501                         }
11502                     } else {
11503                         upE = elem.parent().parent();
11504                     }
11505                 };
11506
11507                 var downElement = function (elem) {
11508                     if (elem.next().hasClass('tree-hide')) {
11509                         downElement(elem.next());
11510                     } else {
11511                         downE = elem.next();
11512                     }
11513                 }
11514                 var isBottomElem = false;
11515                 var downParent = function(liElem){
11516                     if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree')){
11517                         isBottomElem = true;
11518                         return;
11519                     }
11520                     if(liElem.next().length !== 0){
11521                         downE = liElem.next().eq(0);
11522                         return;
11523                     }
11524                     else {
11525                         downParent(liElem.parent().parent());
11526                     }
11527                 }
11528                 
11529                 var findDown = function (elem) {
11530                     if (isRoot(elem.parent()) && !elem.hasClass('active')) {
11531                         downE = elem.parent();
11532                         return;
11533                     }
11534                     if (elem.hasClass('active')) {
11535                         downE = elem.next().find('li').eq(0);
11536                         if (downE.hasClass('tree-hide')) {
11537                             downElement(downE);
11538                         }
11539
11540                     } else {
11541                         downParent(elem.parent());
11542                         if(isBottomElem === true){
11543                             downE = elem.parent();
11544                             isBottomElem = false;
11545                         }
11546                     }
11547                 };
11548
11549
11550                 var resetTabPosition = function(element){
11551                     findRoot(element);
11552                     angular.element(rootE.parent().parent()[0].querySelector("li[tabindex='0']")).attr('tabindex','-1');
11553                     var elemToFocus =  rootE.parent().parent()[0].querySelector(".bg")|| rootE;
11554
11555                     angular.element(elemToFocus).attr('tabindex','0');
11556                 };
11557                 // Function to control the expansion of nodes when the user tabs into the tree and
11558                 // the slected node is not visible
11559                 var expand = function(elemArr){
11560                     var elem= elemArr.pop();
11561                     var element = elem.find('a').eq(0);                    
11562                     var selectedNode = elem.parent().parent()[0].querySelector(".bg");
11563                     if(selectedNode != null){
11564                         while(elem){
11565                              element = elem.find('a').eq(0);
11566                     if(!element.hasClass('active') ){
11567
11568
11569                     if (elem[0].previousElementSibling) {
11570                         closeOthersUp(angular.element(elem[0].previousElementSibling),true,true);
11571                         }
11572                         if (elem[0].nextElementSibling) {
11573                             closeOthersDown(angular.element(elem[0].nextElementSibling),true,true);
11574                         }
11575
11576                          if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11577                             if(typeof scope.showChild === 'function' ){
11578                                 scope.showChild();
11579                             }
11580                             element.addClass('active');
11581                             elem.attr('aria-expanded', 'true');
11582                             element.find('i').eq(0).removeClass('icon-primary-collapsed');
11583                             element.find('i').eq(0).addClass('icon-primary-expanded');
11584                             }
11585                           
11586                           }   
11587                           elem = elemArr.pop();
11588                         }                      
11589                         
11590                     }else{
11591                         return;
11592                     }                   
11593                 };
11594
11595                 element.find('a').eq(0).bind('mouseenter', function (evt) {
11596                     angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
11597                         angular.element(value).removeClass('activeTooltip') 
11598                     });
11599                     element.addClass('activeTooltip');
11600                 });
11601                 element.find('a').eq(0).bind('mouseleave', function (evt) {
11602                     element.removeClass('activeTooltip');
11603                 });
11604                 element.bind('focus', function (evt) {
11605                     angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
11606                         angular.element(value).removeClass('activeTooltip') 
11607                     });
11608                     element.addClass('activeTooltip');
11609                 });
11610                 element.bind('blur', function (evt) {
11611                     element.removeClass('activeTooltip');
11612                 });
11613                 element.bind('keydown', function (evt) {
11614                     switch (evt.keyCode) {
11615                     case keymap.KEY.HOME:
11616                         evt.preventDefault();
11617                         evt.stopPropagation();
11618                         element.attr('tabindex', -1);
11619                         findRoot(element);
11620                         rootE.eq(0).attr('tabindex', 0);
11621                         rootE[0].focus();
11622                         break;
11623                     case keymap.KEY.LEFT:
11624                         evt.preventDefault();
11625                         evt.stopPropagation(); 
11626                       
11627                         if(element.find('a').eq(0).hasClass('active')){
11628                             if (element[0].previousElementSibling) {
11629                                 closeOthersUp(angular.element(element[0].previousElementSibling),true);
11630                             }
11631                             if (element[0].nextElementSibling) {
11632                                 closeOthersDown(angular.element(element[0].nextElementSibling),true);
11633                              }
11634                              activeToggle(element,true);
11635                                 return;
11636                         }
11637                             element.attr('tabindex', -1);
11638                             parentE = element.parent().parent();
11639                             parentE.attr('tabindex', 0);
11640                             parentE[0].focus();
11641                         break;
11642                     case keymap.KEY.UP:
11643                         evt.preventDefault();
11644                         evt.stopPropagation();
11645                         element.attr('tabindex', -1);
11646                         findUp(element);
11647                         upE.eq(0).attr('tabindex', 0);
11648                         upE[0].focus();
11649                         break;
11650                     case keymap.KEY.RIGHT:
11651                         evt.preventDefault();
11652                         evt.stopPropagation();
11653                         if(element.find('i').eq(0).hasClass('icon-primary-circle')){
11654                             break;
11655                         }    
11656                         if (!element.find('a').eq(0).hasClass('active')) {
11657                             if (element[0].previousElementSibling) {
11658                         closeOthersUp(angular.element(element[0].previousElementSibling),true);
11659                         }
11660                         if (element[0].nextElementSibling) {
11661                             closeOthersDown(angular.element(element[0].nextElementSibling),true);
11662                         }
11663                         activeToggle(element,true);
11664                     
11665                         }
11666                         else {
11667                             element.attr('tabindex', -1);
11668                             findDown(element.find('a').eq(0));
11669                             downE.eq(0).attr('tabindex', 0);
11670                             downE[0].focus();                            
11671                         }                        
11672                         break;
11673                     case keymap.KEY.DOWN:
11674                         evt.preventDefault();
11675                         element.attr('tabindex', -1);
11676                         findDown(element.find('a').eq(0));
11677                         downE.eq(0).attr('tabindex', 0);
11678                         downE[0].focus();
11679                         evt.stopPropagation();
11680                         break;
11681                     case keymap.KEY.ENTER:
11682                         var isSelectedElem = element.hasClass('bg');
11683                         var enterFunc = function(element){
11684                             if (isSelectedElem) {
11685                                 element.removeClass('bg');
11686                                 if (element.attr('aria-selected')) {
11687                                     element.attr('aria-selected', 'false');
11688                                 }                        
11689                             }
11690                             else {
11691                                 element.addClass('bg');
11692                                 element.attr('aria-selected', 'true');                                   
11693                             }  
11694                         };                            
11695                         if (element[0].previousElementSibling) {
11696                             closeOthersUp(angular.element(element[0].previousElementSibling));
11697                         }
11698                         if (element[0].nextElementSibling) {
11699                             closeOthersDown(angular.element(element[0].nextElementSibling));
11700                         }                   
11701                         
11702                         removeBackground(element);
11703                         enterFunc(element);
11704                         evt.stopPropagation();                                                      
11705                         break;
11706                     case keymap.KEY.TAB:
11707                         $timeout(function(){
11708                             resetTabPosition(element);
11709                         },0);
11710                          evt.stopPropagation(); 
11711                         
11712                         break;
11713                     default:
11714                         break;
11715                     }
11716                 });
11717             element.bind('keyup',function(evt){
11718                 if(evt.keyCode === keymap.KEY.TAB){
11719                   
11720                         var tempElem = element;
11721                         var elemArr = [];
11722                         while(!tempElem.hasClass('b2b-tree')){
11723                             elemArr.push(tempElem);
11724                             tempElem = tempElem.parent().parent();
11725                         }
11726                         elemArr.push(tempElem);
11727                       
11728                         expand(elemArr);                    
11729                 }
11730                  evt.stopPropagation(); 
11731             });
11732             }
11733         };
11734     }]);
11735 /**
11736  * @ngdoc directive
11737  * @name Navigation.att:Tree nodes with checkboxes
11738  *
11739  * @param {String} setRole - The value needs to be "tree". This is required to incorporate CATO requirements.
11740  * @param {boolean} groupIt - The value needs to be "false" for top-level tree rendered. 
11741  * @param {Object} collection -  The JSON object of tree to be rendered.
11742  * @description
11743  *  <file src="src/treeNodeCheckbox/docs/readme.md" />
11744  *
11745  * @usage
11746  *      <div class="b2b-tree-checkbox">
11747  *                <b2b-tree-node-checkbox collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-node-checkbox>
11748  *            </div>
11749  * @example
11750  *  <section id="code">
11751         <example module="b2b.att">
11752             <file src="src/treeNodeCheckbox/docs/demo.html" />
11753             <file src="src/treeNodeCheckbox/docs/demo.js" />
11754        </example>
11755     </section>
11756  *
11757  */
11758 angular.module('b2b.att.treeNodeCheckbox', ['b2b.att.utilities'])
11759     .directive('b2bTreeNodeCheckbox', function () {
11760         return {
11761             restrict: "E",
11762             replace: true,
11763             scope: {
11764                 collection: '=',
11765                 groupIt: '=',
11766                 setRole: '@'
11767             },
11768             templateUrl: function (element, attrs) {
11769                 if (attrs.groupIt === 'true') {
11770                     return "b2bTemplate/treeNodeCheckbox/groupedTree.html";
11771                 } else {
11772                     return "b2bTemplate/treeNodeCheckbox/ungroupedTree.html";
11773                 }
11774             },
11775             link: function (scope) {
11776                 if (!(scope.setRole === 'tree')) {
11777                     scope.setRole = 'group';
11778                 }
11779             }
11780         }
11781     })
11782     .directive('b2bTreeMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
11783         return {
11784             restrict: "E",
11785             replace: true,
11786             scope: {
11787                 member: '=',
11788                 groupIt: '='
11789             },
11790             templateUrl: 'b2bTemplate/treeNodeCheckbox/treeMember.html',
11791             link: function (scope, element, attrs) {
11792                 scope.elemArr = [];
11793                 var removeRootTabIndex = function (elem) {
11794                     if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
11795                         elem.attr('tabindex', -1);                        
11796                         return;
11797                     }
11798                     removeRootTabIndex(elem.parent());
11799                 };
11800                 scope.$watch('member.child', function(newVal, oldVal){                  
11801                     if(newVal !== oldVal){
11802                         scope.showChild();
11803                     };
11804                 });
11805
11806                 var checkedCount = 0;
11807                 var nonCheckedCount = 0;
11808                 var checkBoxesCount = 0;
11809
11810                 if(element.find('a').eq(0).find('input')){
11811                     if(scope.member.indeterminate){
11812                         element.find('a').eq(0).find('input').prop('indeterminate', true);
11813                         element.attr('aria-checked',"mixed");
11814                     }
11815                     element.attr('aria-checked',scope.member.isSelected);
11816                 }
11817
11818                 element.find('a').eq(0).find('input').bind('change',function(){
11819                     scope.member.indeterminate = false;
11820                     downwardModalUpdate(scope.member);
11821                     downwardSelection(element);
11822                     upwardSelection(element);
11823                     element.attr('aria-checked',scope.member.isSelected);
11824                      if (scope.member.onSelect !== undefined) {
11825                         scope.member.onSelect(scope.member);
11826                     }
11827                 });
11828
11829                 element.find('a').eq(0).find('input').bind('click',function(){
11830                     var elem = angular.element(this);
11831                     if(scope.member.indeterminate){
11832                         scope.member.indeterminate = false;
11833                         scope.member.isSelected = true;
11834                         elem.prop('indeterminate', false);
11835                         elem.prop('checked', true);
11836                         elem.triggerHandler('change');
11837                     }
11838                 });
11839
11840                 var groupNode = false;
11841                 var checkedTreeNode = false;
11842
11843                 var isCheckboxSelected = function(elem){
11844                     checkedTreeNode = false;
11845                     checkedTreeNode = angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked;
11846                 }
11847
11848                 var findCheckbox = function(elem){
11849                     return angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox');
11850                 }
11851
11852                 var updateGrpNodeCheckboxes = function(elem, checked){
11853                     angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked = checked;
11854                 }
11855
11856                 
11857                 var isGroupNode = function(elem){
11858                     groupNode = false;
11859                     if(angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.grpTreeCheckbox')){
11860                         groupNode = true;
11861                     }
11862                 }
11863
11864                 var downwardModalUpdate = function(curMember){
11865                     angular.forEach(curMember.child, function(childMember, key) {
11866                         childMember.isSelected = curMember.isSelected;
11867                         childMember.indeterminate = false;
11868                         if(angular.isArray(childMember.child) && scope.member.child.length > 0){
11869                             downwardModalUpdate(childMember);
11870                         }
11871                     });
11872                 }
11873
11874                 var downwardSelection = function(elem){
11875                     if(findCheckbox(elem)){
11876                         isCheckboxSelected(elem)
11877                     } 
11878                     if(angular.element(elem).find('ul').length > 0){
11879                         var childNodes = angular.element(elem).find('ul').eq(0).children('li');
11880                         for(var i=0; i<childNodes.length; i++){
11881                             if(findCheckbox(childNodes[i])){
11882                                 isGroupNode(childNodes[i]);
11883                                 angular.element(findCheckbox(childNodes[i])).prop('indeterminate', false);
11884                                 angular.element(childNodes[i]).attr('aria-checked',checkedTreeNode);
11885                                 if(groupNode){
11886                                     updateGrpNodeCheckboxes(childNodes[i],checkedTreeNode);
11887                                 }else{
11888                                     angular.element(childNodes[i]).scope().member.isSelected = checkedTreeNode;
11889                                     angular.element(childNodes[i]).scope().member.indeterminate = false
11890                                     angular.element(childNodes[i]).scope().$apply();
11891                                 }
11892                                 downwardSelection(childNodes[i]);
11893                             }
11894                         }
11895
11896                     }
11897                 }
11898                 var upwardSelection = function(elem){
11899                                         if(!elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
11900                                                 var childNodes = elem.parent().parent().find('ul').eq(0).children('li');
11901                                                 checkedCount = 0;
11902                                                 nonCheckedCount = 0;
11903                                                 checkBoxesCount = 0;    
11904                                                 for(i=0; i<childNodes.length; i++){
11905                                                         if(findCheckbox(childNodes[i])){
11906                                                                 isGroupNode(childNodes[i]);
11907                                                                 isCheckboxSelected(childNodes[i]);
11908                                                                 checkBoxesCount++;
11909                                                                 if(checkedTreeNode){
11910                                                                         checkedCount++;
11911                                                                 }else if(!angular.element(angular.element(angular.element(childNodes[i]).find('a').eq(0))[0].querySelector('input.treeCheckBox')).prop('indeterminate')){
11912                                                                         nonCheckedCount++;
11913                                                                 }
11914                                                         }
11915                                                 }
11916                                                 var parentNodeScope;
11917                                                 parentNodeScope = angular.element(elem.parent().parent()).scope();
11918                                                 if(findCheckbox(elem.parent().parent())){
11919                                                         if(nonCheckedCount == checkBoxesCount){
11920                                                                 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11921                                                                 if(parentNodeScope &&  parentNodeScope.member){
11922                                                                         parentNodeScope.member.isSelected = false;
11923                                                                         parentNodeScope.member.indeterminate = false;
11924                                                                 }else{
11925                                                                         updateGrpNodeCheckboxes(elem.parent().parent(),false);
11926                                                                 }
11927                                                                 angular.element(elem.parent().parent()).attr('aria-checked',false);
11928                                                         }else if(checkedCount == checkBoxesCount){
11929                                                                 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11930                                                                 if(parentNodeScope &&  parentNodeScope.member){
11931                                                                         parentNodeScope.member.isSelected = true;
11932                                                                         parentNodeScope.member.indeterminate = false;
11933                                                                 }else{
11934                                                                         updateGrpNodeCheckboxes(elem.parent().parent(),true);
11935                                                                 }
11936                                                                 angular.element(elem.parent().parent()).attr('aria-checked',true);
11937                                                         }else{
11938                                                                 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', true);
11939                                                                 if(parentNodeScope &&  parentNodeScope.member){
11940                                                                         parentNodeScope.member.isSelected = false;
11941                                                                         parentNodeScope.member.indeterminate = true;
11942                                                                 }else{
11943                                                                         updateGrpNodeCheckboxes(elem.parent().parent(),false);
11944                                                                 }
11945                                                                 angular.element(elem.parent().parent()).attr('aria-checked',"mixed");
11946                                                         }
11947                                                         if(parentNodeScope &&  parentNodeScope.member){
11948                                                                 parentNodeScope.$apply();
11949                                                         }        
11950                                                 }
11951                                                 
11952                                                 
11953                                                 
11954                                                 if(elem.parent().parent().attr('role') == "treeitem"){
11955                                                         upwardSelection(elem.parent().parent());
11956                                                 }
11957                                         }
11958                 }
11959
11960                 scope.showChild = function () {
11961                         if (!element.hasClass('grouped')) {
11962                             if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11963                                 scope.groupIt = false;
11964                                 element.addClass('grouped');
11965                                 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
11966                                 $compile(element.contents())(scope);
11967                                 if(scope.member.active && scope.member.active === true){
11968                                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
11969                                 };
11970                                 if(scope.member.selected && scope.member.selected === true){
11971                                     element.attr('tabindex', 0);
11972                                     removeRootTabIndex(element);
11973                                 };
11974                                 if(scope.member.active && scope.member.active == undefined){
11975                                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
11976                                 };
11977                             } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11978                                 element.addClass('grouped');
11979                                 scope.groupIt = true;
11980                                 var j = 0;
11981                                 var grpName = '';
11982                                 if(scope.member.child[0].groupName !== undefined){
11983                                     grpName = scope.member.child[0].groupName;
11984                                 }
11985                                 else{
11986                                     var toSlice = scope.member.child[0].name.search(' ');
11987                                     grpName = scope.member.child[0].name.slice(0, toSlice);
11988                                 }
11989
11990                                 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11991                                     j = 0;
11992                                     for (j = j + i; j < (i + scope.member.divide); j++) {                                        
11993                                         if (j === scope.member.child.length) {
11994                                             scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11995                                             break;
11996                                             
11997                                             if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11998                                                 scope.member.child[j-1].activeGrp = true;
11999                                             };
12000                                             
12001                                         }
12002                                         if (i + scope.member.divide > scope.member.child.length) {
12003                                             scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
12004                                             if(scope.member.child[j].active && scope.member.child[j].active===true){
12005                                                 scope.member.child[j].activeGrp = true;
12006                                             };
12007
12008                                         } else {
12009                                             scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
12010                                             if(scope.member.child[j].active && scope.member.child[j].active===true){
12011                                                 scope.member.child[j].activeGrp = true;
12012                                             };
12013                                         }
12014                                     }
12015                                 }
12016                                 if(scope.member.divide){
12017                                     element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
12018                                 } else {
12019                                     element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
12020                                 }
12021                                 $compile(element.contents())(scope);
12022                                 if(scope.member.active && scope.member.active === true){
12023                                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12024                                 };
12025                                 
12026                                 if( scope.member.active && scope.member.active == undefined){
12027                                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12028                                 };
12029                             }
12030                         }
12031                         $timeout(function () {
12032                             if(!scope.member.indeterminate){
12033                                 downwardSelection(element);
12034                             }    
12035                         });  
12036
12037                 };
12038                 
12039                 if(scope.member.active && scope.member.active == true){
12040                     scope.showChild();
12041                 };
12042                 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
12043                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12044                 }
12045                 else if(scope.member.child == undefined){
12046                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-circle');
12047                     if(scope.$parent.$index === 0) {
12048                         element.find('a').eq(0).append('<span class="first-link"></span>');
12049                     };
12050                 };
12051                 
12052                 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12053                     scope.showChild();
12054                     var expandFunc = scope.member.onExpand;
12055                     if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
12056                        var eValue = scope.member.onExpand(scope.member);
12057                     }
12058                     if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
12059                         scope.member.onCollapse(scope.member);
12060                     }
12061                 });
12062
12063                 angular.element(element[0].querySelectorAll('.treeNodeName')).eq(0).bind('click', function (evt) {
12064
12065                 });
12066                 
12067             }
12068         }
12069 }])
12070     .directive('b2bTreeNodeLink', ['keymap', '$timeout', function (keymap, $timeout) {
12071         return {
12072             restrict: 'A',
12073             link: function (scope, element, attr, ctrl) {
12074                 var rootE, parentE, upE, downE;
12075                 var closeOthersUp = function (elem) {
12076                     
12077                     if (elem.find('a').eq(0).hasClass('active')) {
12078                         activeToggle(elem);
12079                         return;
12080                     }
12081                     if (elem.hasClass('bg')) {
12082                         elem.removeClass('bg');
12083                     }
12084                     if (elem[0].previousElementSibling !== null) {
12085                         closeOthersUp(angular.element(elem[0].previousElementSibling));
12086                     }
12087                 };
12088                 var closeOthersDown = function (elem) {
12089                     
12090                     if (elem.find('a').eq(0).hasClass('active')) {
12091                         activeToggle(elem);
12092                         return;
12093                     }
12094                     if (elem.hasClass('bg')) {
12095                         elem.removeClass('bg');
12096                     }
12097                     if (elem[0].nextElementSibling !== null) {
12098                         closeOthersDown(angular.element(elem[0].nextElementSibling));
12099                     }
12100                 };
12101
12102                 var removeBackgroundUp = function (elem) {
12103                     
12104                     if (elem.hasClass('b2b-tree-checkbox')) {
12105                         return;
12106                     } else {
12107                         elem.parent().parent().removeClass('bg');
12108                         removeBackgroundUp(elem.parent().parent());
12109                     }
12110                 };
12111
12112                 var removeBackgroundDown = function (elem) {
12113                     
12114                     angular.element(elem[0].querySelector('.bg')).removeClass('bg');
12115                 };
12116
12117
12118
12119                 var activeToggle = function (elem) {
12120                     var element = elem.find('a').eq(0);
12121                     if (element.hasClass('active')) {
12122                         elem.removeClass('bg');
12123                         if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12124                             element.removeClass('active');
12125                             elem.attr('aria-expanded', 'false');
12126                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-expanded');
12127                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12128                         }
12129                     } else {
12130                         elem.addClass('bg');
12131                         if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12132                             element.addClass('active');
12133                             elem.attr('aria-expanded', 'true');
12134                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12135                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-expanded');
12136                         }
12137                     }
12138                 };
12139                 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12140                     
12141                         if (element[0].previousElementSibling) {
12142                             closeOthersUp(angular.element(element[0].previousElementSibling));
12143                         }
12144                         if (element[0].nextElementSibling) {
12145                             closeOthersDown(angular.element(element[0].nextElementSibling));
12146                         }
12147
12148                         activeToggle(element);
12149
12150                     removeBackgroundDown(element);
12151                     removeBackgroundUp(element);
12152                     evt.stopPropagation();                    
12153                 });
12154                 
12155                 if (element.parent().parent().hasClass('b2b-tree-checkbox') && (element.parent()[0].previousElementSibling === null)) {
12156                     element.attr('tabindex', 0);
12157                 }
12158                 
12159                 var isRoot = function (elem) {
12160                     if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
12161                         return true;
12162                     } else {
12163                         return false;
12164                     }
12165                 };
12166                 var findRoot = function (elem) {
12167                     if (isRoot(elem)) {
12168                         rootE = elem;
12169                         return;
12170                     }
12171                     findRoot(elem.parent());
12172                 };
12173
12174                 var findPreActive = function (elem) {
12175
12176                     if (!(elem.hasClass("active"))) {
12177                         return;
12178                     } else {
12179                         var childElems = angular.element(elem[0].nextElementSibling.children);
12180                         lastE = angular.element(childElems[childElems.length - 1]);
12181                         if (lastE.find('a').eq(0).hasClass('active')) {
12182                             findPreActive(lastE.find('a').eq(0));
12183                         }
12184                         upE = lastE;
12185                     }
12186                 };
12187
12188                 var findUp = function (elem) {
12189                     if (isRoot(elem)) {
12190                         upE = elem;
12191                         return;
12192                     }
12193                     if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
12194                         upE = angular.element(elem[0].previousElementSibling);
12195                         if (upE.find('a').eq(0).hasClass('active')) {
12196                             findPreActive(upE.find('a').eq(0));
12197                         }
12198                     } else {
12199                         upE = elem.parent().parent();
12200                     }
12201                 };
12202
12203                 var downElement = function (elem) {
12204                     if (elem.next().hasClass('tree-hide')) {
12205                         downElement(elem.next());
12206                     } else {
12207                         downE = elem.next();
12208                     }
12209                 }
12210                 var isBottomElem = false;
12211                 var downParent = function(liElem){
12212                     if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
12213                         isBottomElem = true;
12214                         return;
12215                     }
12216                     if(liElem.next().length !== 0){
12217                         downE = liElem.next().eq(0);
12218                         return;
12219                     }
12220                     else {
12221                         downParent(liElem.parent().parent());
12222                     }
12223                 }
12224                 
12225                 var findDown = function (elem) {
12226                     if (isRoot(elem.parent()) && !elem.hasClass('active')) {
12227                         downE = elem.parent();
12228                         return;
12229                     }
12230                     if (elem.hasClass('active')) {
12231                         downE = elem.next().find('li').eq(0);
12232                         if (downE.hasClass('tree-hide')) {
12233                             downElement(downE);
12234                         }
12235
12236                     } else {
12237                         downParent(elem.parent());
12238                         if(isBottomElem === true){
12239                             downE = elem.parent();
12240                             isBottomElem = false;
12241                         }
12242                     }
12243                 };
12244                 element.bind('keydown', function (evt) {
12245                     switch (evt.keyCode) {
12246                     case keymap.KEY.HOME:
12247                         evt.preventDefault();
12248                         evt.stopPropagation();
12249                         element.attr('tabindex', -1);
12250                         findRoot(element);
12251                         rootE.eq(0).attr('tabindex', 0);
12252                         rootE[0].focus();
12253                         break;
12254                     case keymap.KEY.LEFT:
12255                         evt.preventDefault();
12256                         evt.stopPropagation();
12257                         if (!isRoot(element)) {
12258                             if(element.find('a').eq(0).hasClass('active')){
12259                                 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12260                                 return;
12261                             }
12262                             element.attr('tabindex', -1);
12263                             parentE = element.parent().parent();
12264                             parentE.attr('tabindex', 0);
12265                             parentE[0].focus();
12266                         } else {
12267                             if (element.find('a').eq(0).hasClass('active')) {
12268                                 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12269                             }
12270                         };
12271                         break;
12272                     case keymap.KEY.UP:
12273                         evt.preventDefault();
12274                         evt.stopPropagation();
12275                         element.attr('tabindex', -1);
12276                         findUp(element);
12277                         upE.eq(0).attr('tabindex', 0);
12278                         upE[0].focus();
12279                         break;
12280                     case keymap.KEY.RIGHT:
12281                         evt.preventDefault();
12282                         evt.stopPropagation();
12283                         if(angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')){
12284                             break;
12285                         }    
12286                         if (!element.find('a').eq(0).hasClass('active')) {
12287                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12288                         }
12289                         else {
12290                             element.attr('tabindex', -1);
12291                             findDown(element.find('a').eq(0));
12292                             downE.eq(0).attr('tabindex', 0);
12293                             downE[0].focus();                            
12294                         }                        
12295                         break;
12296                     case keymap.KEY.DOWN:
12297                         evt.preventDefault();
12298                         element.attr('tabindex', -1);
12299                         findDown(element.find('a').eq(0));
12300                         downE.eq(0).attr('tabindex', 0);
12301                         downE[0].focus();
12302                         evt.stopPropagation();
12303                         break;
12304                     case keymap.KEY.SPACE:
12305                     case keymap.KEY.ENTER:
12306                         evt.preventDefault();
12307                         evt.stopPropagation();
12308                         if(angular.isDefined(element.scope().member.isSelected)){
12309                             element.scope().member.isSelected = !element.scope().member.isSelected;
12310                             element.scope().member.indeterminate = false;
12311                             element.scope().$apply();
12312                             element.find('a').eq(0).find('input').prop('indeterminate', false);
12313                             element.find('a').eq(0).find('input').triggerHandler('change');
12314                         }
12315                         break;    
12316                     default:
12317                         break;
12318                     }
12319                 });
12320             }
12321         };
12322     }]);
12323
12324
12325 angular.module('b2b.att.collapse', ['b2b.att.transition'])
12326
12327 // The collapsible directive indicates a block of html that will expand and collapse
12328 .directive('b2bCollapse', ['$transition', function($transition) {
12329     // CSS transitions don't work with height: auto, so we have to manually change the height to a
12330     // specific value and then once the animation completes, we can reset the height to auto.
12331     // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
12332     // "collapse") then you trigger a change to height 0 in between.
12333     // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
12334
12335     var props = {
12336         open: {
12337             marginTop: null,
12338             marginBottom: null,
12339             paddingTop: null,
12340             paddingBottom: null,
12341             display: 'block'
12342         },
12343         closed: {
12344             marginTop: 0,
12345             marginBottom: 0,
12346             paddingTop: 0,
12347             paddingBottom: 0,
12348             display: 'none'
12349         }
12350     };
12351
12352     var fixUpHeight = function(scope, element, height) {
12353         // We remove the collapse CSS class to prevent a transition when we change to height: auto
12354         element.removeClass('b2bCollapse');
12355         element.css({height: height});
12356         //adjusting for any margin or padding
12357         if (height === 0) {
12358             element.css(props.closed);
12359         } else {
12360             element.css(props.open);
12361         }
12362         // It appears that  reading offsetWidth makes the browser realise that we have changed the
12363         // height already :-/
12364         var x = element[0].offsetWidth;
12365         element.addClass('b2bCollapse');
12366     };
12367
12368     return {
12369         link: function(scope, element, attrs) {
12370             var isCollapsed;
12371             var initialAnimSkip = true;
12372             scope.$watch(function() {
12373                 return element[0].scrollHeight;
12374             }, function(value) {
12375                 //The listener is called when scrollHeight changes
12376                 //It actually does on 2 scenarios: 
12377                 // 1. Parent is set to display none
12378                 // 2. angular bindings inside are resolved
12379                 //When we have a change of scrollHeight we are setting again the correct height if the group is opened
12380                 if (element[0].scrollHeight !== 0) {
12381                     if (!isCollapsed) {
12382                         if (initialAnimSkip) {
12383                             fixUpHeight(scope, element, element[0].scrollHeight + 'px');
12384                         } else {
12385                             fixUpHeight(scope, element, 'auto');
12386                             element.css({overflow: 'visible'});
12387                         }
12388                     }
12389                 }
12390             });
12391
12392             scope.$watch(attrs.b2bCollapse, function(value) {
12393                 if (value) {
12394                     collapse();
12395                 } else {
12396                     expand();
12397                 }
12398             });
12399
12400
12401             var currentTransition;
12402             var doTransition = function(change) {
12403                 if (currentTransition) {
12404                     currentTransition.cancel();
12405                 }
12406                 currentTransition = $transition(element, change);
12407                 currentTransition.then(
12408                         function() {
12409                             currentTransition = undefined;
12410                         },
12411                         function() {
12412                             currentTransition = undefined;
12413                         }
12414                 );
12415                 return currentTransition;
12416             };
12417
12418             var expand = function() {
12419                 scope.postTransition = true; 
12420                 if (initialAnimSkip) {
12421                     initialAnimSkip = false;
12422                     if (!isCollapsed) {
12423                         fixUpHeight(scope, element, 'auto');
12424                     }
12425                 } else {
12426                     //doTransition({ height : element[0].scrollHeight + 'px' })
12427                     doTransition(angular.extend({height: element[0].scrollHeight + 'px'}, props.open))
12428                             .then(function() {
12429                                 // This check ensures that we don't accidentally update the height if the user has closed
12430                                 // the group while the animation was still running
12431                                 if (!isCollapsed) {
12432                                     fixUpHeight(scope, element, 'auto');
12433                                 }
12434                             });
12435                 }
12436                 isCollapsed = false;
12437             };
12438
12439             var collapse = function() {
12440                 isCollapsed = true;
12441                 if (initialAnimSkip) {
12442                     initialAnimSkip = false;
12443                     fixUpHeight(scope, element, 0);
12444                 } else {
12445                     fixUpHeight(scope, element, element[0].scrollHeight + 'px');
12446                     doTransition(angular.extend({height: 0}, props.closed)).then(function() {
12447                         scope.postTransition = false;
12448                     });
12449                     element.css({overflow: 'hidden'});
12450                 }
12451             };
12452         }
12453     };
12454 }]);
12455 angular.module('b2b.att.position', [])
12456
12457 .factory('$position', ['$document', '$window', function ($document, $window) {
12458     function getStyle(el, cssprop) {
12459         if (el.currentStyle) { //IE
12460             return el.currentStyle[cssprop];
12461         } else if ($window.getComputedStyle) {
12462             return $window.getComputedStyle(el)[cssprop];
12463         }
12464         // finally try and get inline style
12465         return el.style[cssprop];
12466     }
12467
12468     /**
12469      * Checks if a given element is statically positioned
12470      * @param element - raw DOM element
12471      */
12472     function isStaticPositioned(element) {
12473         return (getStyle(element, "position") || 'static') === 'static';
12474     }
12475
12476     /**
12477      * returns the closest, non-statically positioned parentOffset of a given element
12478      * @param element
12479      */
12480     var parentOffsetEl = function (element) {
12481         var docDomEl = $document[0];
12482         var offsetParent = element.offsetParent || docDomEl;
12483         while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
12484             offsetParent = offsetParent.offsetParent;
12485         }
12486         return offsetParent || docDomEl;
12487     };
12488
12489     return {
12490         /**
12491          * Provides read-only equivalent of jQuery's position function:
12492          * http://api.jquery.com/position/
12493          */
12494         position: function (element) {
12495             var elBCR = this.offset(element);
12496             var offsetParentBCR = {
12497                 top: 0,
12498                 left: 0
12499             };
12500             var offsetParentEl = parentOffsetEl(element[0]);
12501             if (offsetParentEl !== $document[0]) {
12502                 offsetParentBCR = this.offset(angular.element(offsetParentEl));
12503                 offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
12504                 offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
12505             }
12506
12507             return {
12508                 width: element.prop('offsetWidth'),
12509                 height: element.prop('offsetHeight'),
12510                 top: elBCR.top - offsetParentBCR.top,
12511                 left: elBCR.left - offsetParentBCR.left
12512             };
12513         },
12514
12515         /**
12516          * Provides read-only equivalent of jQuery's offset function:
12517          * http://api.jquery.com/offset/
12518          */
12519         offset: function (element) {
12520             var boundingClientRect = element[0].getBoundingClientRect();
12521             return {
12522                 width: element.prop('offsetWidth'),
12523                 height: element.prop('offsetHeight'),
12524                 top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
12525                 left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
12526             };
12527         },
12528                 
12529                  /**
12530          * Provides functionality to check whether an element is in view port.
12531          */
12532         isElementInViewport: function (element) {
12533             if (element) {
12534                 var rect = element[0].getBoundingClientRect();
12535                 return (
12536                     rect.top >= 0 &&
12537                     rect.left >= 0 &&
12538                     rect.bottom <= ($window.innerHeight || $document[0].documentElement.clientHeight) &&
12539                     rect.right <= ($window.innerWidth || $document[0].documentElement.clientWidth)
12540                 );
12541             } else {
12542                 return false;
12543             }
12544         }
12545     };
12546 }])
12547
12548 .factory('$isElement', [function () {
12549     var isElement = function (currentElem, targetElem, alternateElem) {
12550         if (currentElem[0] === targetElem[0]) {
12551             return true;
12552         } else if (currentElem[0] === alternateElem[0]) {
12553             return false;
12554         } else {
12555             return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
12556         }
12557     };
12558
12559     return isElement;
12560 }])
12561
12562 .directive('attPosition', ['$position', function ($position) {
12563     return {
12564         restrict: 'A',
12565         link: function (scope, elem, attr) {
12566             scope.$watchCollection(function () {
12567                 return $position.position(elem);
12568             }, function (value) {
12569                 scope[attr.attPosition] = value;
12570             });
12571         }
12572     };
12573 }]);
12574
12575 angular.module('b2b.att.transition', [])
12576
12577 .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
12578
12579   var $transition = function(element, trigger, options) {
12580     options = options || {};
12581     var deferred = $q.defer();
12582     var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
12583
12584     var transitionEndHandler = function() {
12585       $rootScope.$apply(function() {
12586         element.unbind(endEventName, transitionEndHandler);
12587         deferred.resolve(element);
12588       });
12589     };
12590
12591     if (endEventName) {
12592       element.bind(endEventName, transitionEndHandler);
12593     }
12594
12595     // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
12596     $timeout(function() {
12597       if ( angular.isString(trigger) ) {
12598         element.addClass(trigger);
12599       } else if ( angular.isFunction(trigger) ) {
12600         trigger(element);
12601       } else if ( angular.isObject(trigger) ) {
12602         element.css(trigger);
12603       }
12604       //If browser does not support transitions, instantly resolve
12605       if ( !endEventName ) {
12606         deferred.resolve(element);
12607       }
12608     }, 100);
12609
12610     // Add our custom cancel function to the promise that is returned
12611     // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
12612     // i.e. it will therefore never raise a transitionEnd event for that transition
12613     deferred.promise.cancel = function() {
12614       if ( endEventName ) {
12615         element.unbind(endEventName, transitionEndHandler);
12616       }
12617       deferred.reject('Transition cancelled');
12618     };
12619
12620     return deferred.promise;
12621   };
12622
12623   // Work out the name of the transitionEnd event
12624   var transElement = document.createElement('trans');
12625   var transitionEndEventNames = {
12626     'WebkitTransition': 'webkitTransitionEnd',
12627     'MozTransition': 'transitionend',
12628     'OTransition': 'oTransitionEnd',
12629     'transition': 'transitionend'
12630   };
12631   var animationEndEventNames = {
12632     'WebkitTransition': 'webkitAnimationEnd',
12633     'MozTransition': 'animationend',
12634     'OTransition': 'oAnimationEnd',
12635     'transition': 'animationend'
12636   };
12637   function findEndEventName(endEventNames) {
12638     for (var name in endEventNames){
12639       if (transElement.style[name] !== undefined) {
12640         return endEventNames[name];
12641       }
12642     }
12643   }
12644   $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
12645   $transition.animationEndEventName = findEndEventName(animationEndEventNames);
12646   return $transition;
12647 }])
12648
12649 .factory('$scrollTo', ['$window', function($window) {
12650     var $scrollTo = function(offsetLeft, offsetTop, duration) {
12651         TweenMax.to($window, duration || 1, {scrollTo: {y: offsetTop, x: offsetLeft}, ease: Power4.easeOut});
12652     };
12653     return $scrollTo;
12654 }])
12655 .factory('animation', function(){
12656     return TweenMax;
12657 })
12658 .factory('$progressBar', function(){
12659
12660    //Provides a function to pass in code for closure purposes
12661    var loadingAnimationCreator = function(onUpdateCallback){
12662
12663       //Use closure to setup some resuable code
12664       var loadingAnimation = function(callback, duration){
12665           TweenMax.to({}, duration, {
12666               onUpdateParams: ["{self}"],
12667               onUpdate: onUpdateCallback,
12668               onComplete: callback
12669           });
12670       };
12671       //Returns a function that takes a callback function and a duration for the animation
12672       return (function(){
12673         return loadingAnimation;
12674       })();
12675     };
12676
12677   return loadingAnimationCreator;
12678 })
12679 .factory('$height', function(){
12680   var heightAnimation = function(element,duration,height,alpha){
12681     TweenMax.to(element,
12682       duration,
12683       {height:height, autoAlpha:alpha},
12684       0);
12685   };
12686   return heightAnimation;
12687 });
12688 angular.module('b2b.att.utilities', ['ngSanitize'])
12689 .constant('b2bUtilitiesConfig', {
12690     prev: '37',
12691     up: '38',
12692     next: '39',
12693     down: '40',
12694     type: 'list',
12695     columns: 1,
12696     enableSearch: false,
12697     searchTimer: 200,
12698     circularTraversal: false
12699 })
12700 .constant('b2bWhenScrollEndsConstants', {
12701     'threshold': 100,
12702     'width': 0,
12703     'height': 0
12704 })
12705 // All breakpoints ranges from >= min and < max
12706 .constant('b2bAwdBreakpoints', {
12707     breakpoints: {
12708         mobile: {
12709             min: 1,
12710             max: 768
12711         },
12712         tablet: {
12713             min: 768,
12714             max: 1025
12715         },
12716         desktop: {
12717             min: 1025,
12718             max: 1920
12719         }
12720     }
12721 })
12722 .filter('groupBy', function ($timeout) {
12723     //Custom GroupBy Filter for treeNav, returns key string and value.childarray as set of grouped elements
12724     return function (data, key) {
12725         if (!key) return data;
12726         var outputPropertyName = '__groupBy__' + key;
12727         if (!data[outputPropertyName]) {
12728             var result = {};
12729             for (var i = 0; i < data.length; i++) {
12730                 if (!result[data[i][key]])
12731                     result[data[i][key]] = {};
12732                 if (!result[data[i][key]].childArray) {
12733                     result[data[i][key]].childArray = [];
12734                 }
12735                 result[data[i][key]].childArray.push(data[i]);
12736                 if (data[i].activeGrp && data[i].activeGrp == true) {
12737                     result[data[i][key]].showGroup = true;
12738                 }
12739             }
12740             Object.defineProperty(result, 'length', {enumerable: false,value: Object.keys(result).length});
12741             Object.defineProperty(data, outputPropertyName, {enumerable: false,configurable: true,writable:false,value:result});
12742             $timeout(function(){delete data[outputPropertyName];},0,false);
12743         }
12744         return data[outputPropertyName];
12745     };
12746 })
12747 .filter('searchObjectPropertiesFilter', [function() {
12748     return function(items, searchText, attrs) {
12749         if(!searchText){
12750             return items;
12751         }
12752         var filtered = [];
12753         searchText = searchText.toLowerCase();
12754         angular.forEach(items, function(item) {
12755             angular.forEach(attrs, function(attr) {
12756                 if (item.hasOwnProperty(attr) && (item[attr].toLowerCase().indexOf(searchText) != -1)) {
12757                     filtered.push(item);
12758                     return;
12759                 }
12760             });
12761         });
12762         return filtered;
12763     };
12764 }])
12765 .filter('unsafe',[ '$sce', function ($sce) { 
12766     return function(val){ 
12767        return $sce.trustAsHtml(val); 
12768     }; 
12769 }])
12770 .filter('b2bHighlight', function () {
12771     function escapeRegexp(queryToEscape) {
12772         return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
12773     }
12774     return function (matchItem, query, className) {
12775         return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class=\"' + className + '\">$&</span>') : matchItem;
12776     }
12777 })
12778 /*License (MIT)
12779 Copyright Â© 2013 Matt Diamond
12780 https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js
12781 */
12782 .factory('b2bRecorder', function() {
12783
12784     var Recorder = function(source, cfg) {
12785         var WORKER_PATH = 'recorderWorker.js';
12786         var config = cfg || {};
12787         var bufferLen = config.bufferLen || 4096;
12788         this.context = source.context;
12789         if(!this.context.createScriptProcessor) {
12790             this.node = this.context.createJavacriptProcessor(bufferLen, 2, 2);
12791         } else {
12792             this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
12793         }
12794         var workerCode = 'function init(a){sampleRate=a.sampleRate}function record(a){recBuffersL.push(a[0]),recBuffersR.push(a[1]),recLength+=a[0].length}function exportWAV(a){var b=mergeBuffers(recBuffersL,recLength),c=mergeBuffers(recBuffersR,recLength),d=interleave(b,c),e=encodeWAV(d),f=new Blob([e],{type:a});this.postMessage(f)}function exportMonoWAV(a){var b=mergeBuffers(recBuffersL,recLength),c=encodeWAV(b,!0),d=new Blob([c],{type:a});this.postMessage(d)}function getBuffers(){var a=[];a.push(mergeBuffers(recBuffersL,recLength)),a.push(mergeBuffers(recBuffersR,recLength)),this.postMessage(a)}function clear(){recLength=0,recBuffersL=[],recBuffersR=[]}function mergeBuffers(a,b){for(var c=new Float32Array(b),d=0,e=0;e<a.length;e++)c.set(a[e],d),d+=a[e].length;return c}function interleave(a,b){for(var c=a.length+b.length,d=new Float32Array(c),e=0,f=0;e<c;)d[e++]=a[f],d[e++]=b[f],f++;return d}function floatTo16BitPCM(a,b,c){for(var d=0;d<c.length;d++,b+=2){var e=Math.max(-1,Math.min(1,c[d]));a.setInt16(b,e<0?32768*e:32767*e,!0)}}function writeString(a,b,c){for(var d=0;d<c.length;d++)a.setUint8(b+d,c.charCodeAt(d))}function encodeWAV(a,b){var c=new ArrayBuffer(44+2*a.length),d=new DataView(c);return writeString(d,0,"RIFF"),d.setUint32(4,32+2*a.length,!0),writeString(d,8,"WAVE"),writeString(d,12,"fmt "),d.setUint32(16,16,!0),d.setUint16(20,1,!0),d.setUint16(22,b?1:2,!0),d.setUint32(24,sampleRate,!0),d.setUint32(28,4*sampleRate,!0),d.setUint16(32,4,!0),d.setUint16(34,16,!0),writeString(d,36,"data"),d.setUint32(40,2*a.length,!0),floatTo16BitPCM(d,44,a),d}var recLength=0,recBuffersL=[],recBuffersR=[],sampleRate;this.onmessage=function(a){switch(a.data.command){case"init":init(a.data.config);break;case"record":record(a.data.buffer);break;case"exportWAV":exportWAV(a.data.type);break;case"exportMonoWAV":exportMonoWAV(a.data.type);break;case"getBuffers":getBuffers();break;case"clear":clear()}};';
12795         var blob = new Blob([workerCode]);
12796
12797         var worker = new Worker(window.URL.createObjectURL(blob)); //TODO: Use a blob instead
12798         worker.postMessage({
12799             command: 'init',
12800             config: {
12801                 sampleRate: this.context.sampleRate
12802             }
12803         });
12804         var recording = false,
12805             currCallback;
12806
12807         this.node.onaudioprocess = function(e) {
12808             if (!recording) return;
12809             worker.postMessage({
12810                 command: 'record',
12811                 buffer: [
12812                     e.inputBuffer.getChannelData(0),
12813                     e.inputBuffer.getChannelData(1)
12814                 ]
12815             });
12816         };
12817
12818         this.configure = function(cfg) {
12819             for (var prop in cfg) {//TODO: look into using angular.extend() here
12820                 if (cfg.hasOwnProperty(prop)) {
12821                     config[prop] = cfg[prop];
12822                 }
12823             }
12824         };
12825
12826         this.record = function() {
12827             recording = true;
12828         };
12829
12830         this.stop = function() {
12831             recording = false;
12832         };
12833
12834         this.clear = function() {
12835             worker.postMessage({ command: 'clear' });
12836             window.URL.revokeObjectURL(blob);
12837         };
12838
12839         this.getBuffers = function(cb) {
12840             currCallback = cb || config.callback;
12841             worker.postMessage({ command: 'getBuffers' });
12842         };
12843
12844         this.exportWAV = function(cb, type) {
12845             currCallback = cb || config.callback;
12846             type = type || config.type || 'audio/wav';
12847             if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
12848             worker.postMessage({
12849                 command: 'exportWAV',
12850                 type: type
12851             });
12852         };
12853
12854         this.exportMonoWAV = function(cb, type) {
12855             currCallback = cb || config.callback;
12856             type = type || config.type || 'audio/wav';
12857             if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
12858             worker.postMessage({
12859                 command: 'exportMonoWAV',
12860                 type: type
12861             });
12862         };
12863
12864         worker.onmessage = function(e) {
12865             var blob = e.data;
12866             currCallback(blob);
12867         };
12868
12869         source.connect(this.node);
12870         this.node.connect(this.context.destination); // if the script node is not connected to an output the "onaudioprocess" event is not triggerd in Chrome
12871
12872     };
12873
12874     return Recorder;
12875
12876 })
12877 .factory('b2bViewport', function() {
12878   /* Source: https://gist.github.com/bjankord/2399828 */
12879   var _viewportWidth = function() {
12880     var vpw;
12881     var webkit = (!(window.webkitConvertPointFromNodeToPage == null));
12882     
12883     // Webkit:
12884     if ( webkit ) {
12885       var vpwtest = document.createElement( "div" );
12886       // Sets test div to width 100%, !important overrides any other misc. box model styles that may be set in the CSS
12887       vpwtest.style.cssText = "width:100% !important; margin:0 !important; padding:0 !important; border:none !important;";
12888       document.documentElement.insertBefore( vpwtest, document.documentElement.firstChild );
12889       vpw = vpwtest.offsetWidth;
12890       document.documentElement.removeChild( vpwtest );
12891     }
12892     // IE 6-8:
12893     else if ( window.innerWidth === undefined ) { 
12894       vpw = document.documentElement.clientWidth; 
12895     }
12896     // Other:
12897     else{
12898       vpw =  window.innerWidth;
12899     }
12900
12901     return (vpw);
12902   }
12903   return {
12904     viewportWidth: _viewportWidth
12905   };
12906 })
12907 .directive('b2bWhenScrollEnds', function(b2bWhenScrollEndsConstants, $log) {
12908     return {
12909         restrict: 'A',
12910         link: function (scope, element, attrs) {
12911             /**
12912             * Exposed Attributes:
12913             *       threshold - integer - number of pixels before scrollbar hits end that callback is called
12914             *       width - integer - override for element's width (px)
12915             *       height - integer - override for element's height (px)
12916             *       axis - string - x/y for scroll bar axis
12917             */
12918             var threshold = parseInt(attrs.threshold, 10) || b2bWhenScrollEndsConstants.threshold;
12919
12920             if (!attrs.axis || attrs.axis === '') {
12921                 $log.warn('axis attribute must be defined for b2bWhenScrollEnds.');
12922                 return;
12923             }
12924
12925             if (attrs.axis === 'x') {
12926                 visibleWidth = parseInt(attrs.width, 10) || b2bWhenScrollEndsConstants.width;
12927                 if (element.css('width')) {
12928                     visibleWidth = element.css('width').split('px')[0];  
12929                 }
12930
12931                 element[0].addEventListener('scroll', function() {
12932                     var scrollableWidth = element.prop('scrollWidth');
12933                     if (scrollableWidth === undefined) {
12934                         scrollableWidth = 1;
12935                     }
12936                     var hiddenContentWidth = scrollableWidth - visibleWidth;
12937
12938                     if (hiddenContentWidth - element[0].scrollLeft <= threshold) {
12939                         /* Scroll almost at bottom, load more rows */
12940                         scope.$apply(attrs.b2bWhenScrollEnds);
12941                     }
12942                 });
12943             } else if (attrs.axis === 'y') {
12944                 visibleHeight = parseInt(attrs.height, 10) || b2bWhenScrollEndsConstants.height;
12945                 if (element.css('height')) {
12946                     visibleHeight = element.css('height').split('px')[0]; 
12947                 }
12948
12949                 element[0].addEventListener('scroll', function() {
12950                     var scrollableHeight = element.prop('scrollHeight');
12951                     if (scrollableHeight === undefined) {
12952                         scrollableHeight = 1;
12953                     }
12954                     var hiddenContentHeight = scrollableHeight - visibleHeight;
12955
12956                     if (hiddenContentHeight - element[0].scrollTop <= threshold) {
12957                         /* Scroll almost at bottom, load more rows */
12958                         scope.$apply(attrs.b2bWhenScrollEnds);
12959                     }
12960                 });
12961             }
12962         }
12963     };
12964 })
12965
12966 .factory('$windowBind', ['$window', '$timeout', function($window, $timeout) {
12967     var win = angular.element($window);
12968     var _scroll = function (flag, callbackFunc, scope) {
12969         scope.$watch(flag, function (val) {
12970             $timeout(function () {
12971                 if (val) {
12972                     win.bind('scroll', callbackFunc);
12973                 } else {
12974                     win.unbind('scroll', callbackFunc);
12975                 }
12976             });
12977         });
12978     };
12979
12980     var throttle = function(type, name, obj) {
12981         obj = obj || window;
12982         var running = false;
12983         var func = function() {
12984             if (running) { return; }
12985             running = true;
12986              requestAnimationFrame(function() {
12987                 obj.dispatchEvent(new CustomEvent(name));
12988                 running = false;
12989             });
12990         };
12991         obj.addEventListener(type, func);
12992     };
12993
12994     var _resize = function(callbackFunc, scope) {
12995         throttle("resize", "optimizedResize");
12996         window.addEventListener("optimizedResize", function(event) {
12997             callbackFunc();
12998             //win.bind(event, callbackFunc);
12999             if (!scope.$$phase) {
13000                 scope.$digest();
13001             }
13002         });
13003     };
13004
13005     var _click = function (flag, callbackFunc, scope) {
13006         scope.$watch(flag, function (val) {
13007             $timeout(function () {
13008                 if (val) {
13009                     win.bind('click', callbackFunc);
13010                 } else {
13011                     win.unbind('click', callbackFunc);
13012                 }
13013             });
13014         });
13015     };
13016
13017     var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
13018         if (timeoutFlag) {
13019             if (!(timeoutValue)) {
13020                 timeoutValue = 0;
13021             }
13022             scope.$watch(flag, function (newVal, oldVal) {
13023                 if (newVal !== oldVal) {
13024                     $timeout(function () {
13025                         if (newVal) {
13026                             win.bind(event, callbackFunc);
13027                         } else {
13028                             win.unbind(event, callbackFunc);
13029                         }
13030                     }, timeoutValue);
13031                 }
13032             });
13033         } else {
13034             scope.$watch(flag, function (newVal, oldVal) {
13035                 if (newVal !== oldVal) {
13036                     if (newVal) {
13037                         win.bind(event, callbackFunc);
13038                     } else {
13039                         win.unbind(event, callbackFunc);
13040                     }
13041                 }
13042             });
13043         }
13044     };
13045
13046     return {
13047         click: _click,
13048         scroll: _scroll,
13049         event: _event, 
13050         resize: _resize
13051     };
13052 }])
13053
13054 .factory('keymap', function () {
13055     return {
13056         KEY: {
13057             TAB: 9,
13058             ENTER: 13,
13059             ESC: 27,
13060             SPACE: 32,
13061             LEFT: 37,
13062             UP: 38,
13063             RIGHT: 39,
13064             DOWN: 40,
13065             SHIFT: 16,
13066             CTRL: 17,
13067             ALT: 18,
13068             PAGE_UP: 33,
13069             PAGE_DOWN: 34,
13070             HOME: 36,
13071             END: 35,
13072             BACKSPACE: 8,
13073             DELETE: 46,
13074             COMMAND: 91
13075         },
13076         MAP: { 91 : "COMMAND", 8 : "BACKSPACE" , 9 : "TAB" , 13 : "ENTER" , 16 : "SHIFT" , 17 : "CTRL" , 18 : "ALT" , 19 : "PAUSEBREAK" , 20 : "CAPSLOCK" , 27 : "ESC" , 32 : "SPACE" , 33 : "PAGE_UP", 34 : "PAGE_DOWN" , 35 : "END" , 36 : "HOME" , 37 : "LEFT" , 38 : "UP" , 39 : "RIGHT" , 40 : "DOWN" , 43 : "+" , 44 : "PRINTSCREEN" , 45 : "INSERT" , 46 : "DELETE", 48 : "0" , 49 : "1" , 50 : "2" , 51 : "3" , 52 : "4" , 53 : "5" , 54 : "6" , 55 : "7" , 56 : "8" , 57 : "9" , 59 : ";", 61 : "=" , 65 : "A" , 66 : "B" , 67 : "C" , 68 : "D" , 69 : "E" , 70 : "F" , 71 : "G" , 72 : "H" , 73 : "I" , 74 : "J" , 75 : "K" , 76 : "L", 77 : "M" , 78 : "N" , 79 : "O" , 80 : "P" , 81 : "Q" , 82 : "R" , 83 : "S" , 84 : "T" , 85 : "U" , 86 : "V" , 87 : "W" , 88 : "X" , 89 : "Y" , 90 : "Z", 96 : "0" , 97 : "1" , 98 : "2" , 99 : "3" , 100 : "4" , 101 : "5" , 102 : "6" , 103 : "7" , 104 : "8" , 105 : "9", 106 : "*" , 107 : "+" , 109 : "-" , 110 : "." , 111 : "/", 112 : "F1" , 113 : "F2" , 114 : "F3" , 115 : "F4" , 116 : "F5" , 117 : "F6" , 118 : "F7" , 119 : "F8" , 120 : "F9" , 121 : "F10" , 122 : "F11" , 123 : "F12", 144 : "NUMLOCK" , 145 : "SCROLLLOCK" , 186 : ";" , 187 : "=" , 188 : "," , 189 : "-" , 190 : "." , 191 : "/" , 192 : "`" , 219 : "[" , 220 : "\\" , 221 : "]" , 222 : "'"
13077         },
13078         isControl: function (e) {
13079             var k = e.keyCode;
13080             switch (k) {
13081             case this.KEY.COMMAND:
13082             case this.KEY.SHIFT:
13083             case this.KEY.CTRL:
13084             case this.KEY.ALT:
13085                 return true;
13086             default:;
13087             }
13088
13089             if (e.metaKey) {
13090                 return true;
13091             }
13092
13093             return false;
13094         },
13095         isFunctionKey: function (k) {
13096             k = k.keyCode ? k.keyCode : k;
13097             return k >= 112 && k <= 123;
13098         },
13099         isVerticalMovement: function (k) {
13100             return ~[this.KEY.UP, this.KEY.DOWN].indexOf(k);
13101         },
13102         isHorizontalMovement: function (k) {
13103             return ~[this.KEY.LEFT, this.KEY.RIGHT, this.KEY.BACKSPACE, this.KEY.DELETE].indexOf(k);
13104         },
13105         isAllowedKey: function (k) {
13106             return (~[this.KEY.SPACE, this.KEY.ESC, this.KEY.ENTER].indexOf(k)) || this.isHorizontalMovement(k) || this.isVerticalMovement(k);
13107         },
13108         isNumericKey: function (e) {
13109             var k = e.keyCode;
13110             if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105)) {
13111                 return true;
13112             } else {
13113                 return false;
13114             }
13115         },
13116         isAlphaNumericKey: function (e) {
13117             var k = e.keyCode;
13118             if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105) || (k >= 65 && k <= 90)) {
13119                 return true;
13120             } else {
13121                 return false;
13122             }
13123         }
13124     };
13125 })
13126
13127 .factory('$isElement', [function () {
13128     var isElement = function (currentElem, targetElem, alternateElem) {
13129         if (currentElem[0] === targetElem[0]) {
13130             return true;
13131         } else if (currentElem[0] === alternateElem[0]) {
13132             return false;
13133         } else {
13134             return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
13135         }
13136     };
13137
13138     return isElement;
13139 }])
13140
13141 .factory('events', function () {
13142     var _stopPropagation = function (evt) {
13143         if (evt.stopPropagation) {
13144             evt.stopPropagation();
13145         } else {
13146             evt.returnValue = false;
13147         }
13148     };
13149     var _preventDefault = function (evt) {
13150         if (evt.preventDefault) {
13151             evt.preventDefault();
13152         } else {
13153             evt.returnValue = false;
13154         }
13155     }
13156     return {
13157         stopPropagation: _stopPropagation,
13158         preventDefault: _preventDefault
13159     };
13160 })
13161
13162
13163 .factory('isHighContrast', function () {
13164     var _isHighContrast = function (idval)
13165
13166
13167      {
13168         var objDiv, objImage, strColor, strWidth, strReady;
13169         var strImageID = idval; // ID of image on the page
13170
13171         // Create a test div
13172         objDiv = document.createElement('div');
13173
13174         //Set its color style to something unusual
13175         objDiv.style.color = 'rgb(31, 41, 59)';
13176
13177         // Attach to body so we can inspect it
13178         document.body.appendChild(objDiv);
13179
13180         // Read computed color value
13181         strColor = document.defaultView ? document.defaultView.getComputedStyle(objDiv, null).color : objDiv.currentStyle.color;
13182         strColor = strColor.replace(/ /g, '');
13183
13184         // Delete the test DIV
13185         document.body.removeChild(objDiv);
13186
13187         // Check if we get the color back that we set. If not, we're in
13188         // high contrast mode.
13189         if (strColor !== 'rgb(31,41,59)') {
13190             return true;
13191         } else {
13192             return false;
13193         }
13194     };
13195
13196     return _isHighContrast;
13197 })
13198
13199 .run(['isHighContrast', '$document', function (isHighContrast, $document) {
13200     var html = $document.find('html').eq(0);
13201     if (isHighContrast()) {
13202         html.addClass('ds2-no-colors');
13203     } else {
13204         html.removeClass('ds2-no-colors');
13205     }
13206 }])
13207
13208 .factory('$documentBind', ['$document', '$timeout', function ($document, $timeout) {
13209     var _click = function (flag, callbackFunc, scope) {
13210         scope.$watch(flag, function (val) {
13211             $timeout(function () {
13212                 if (val) {
13213                     $document.bind('click', callbackFunc);
13214                 } else {
13215                     $document.unbind('click', callbackFunc);
13216                 }
13217             });
13218         });
13219     };
13220
13221     var _touch = function (flag, callbackFunc, scope) {
13222         scope.$watch(flag, function (val) {
13223             $timeout(function () {
13224                 if (val) {
13225                     $document.bind('touchstart', callbackFunc);
13226                 } else {
13227                     $document.unbind('touchstart', callbackFunc);
13228                 }
13229             });
13230         });
13231     };
13232
13233     var _scroll = function (flag, callbackFunc, scope) {
13234         scope.$watch(flag, function (val) {
13235             $timeout(function () {
13236                 if (val) {
13237                     $document.bind('scroll', callbackFunc);
13238                 } else {
13239                     $document.unbind('scroll', callbackFunc);
13240                 }
13241             });
13242         });
13243     };
13244
13245     var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
13246         if (timeoutFlag) {
13247             if (!(timeoutValue)) {
13248                 timeoutValue = 0;
13249             }
13250             scope.$watch(flag, function (newVal, oldVal) {
13251                 if (newVal !== oldVal) {
13252                     $timeout(function () {
13253                         if (newVal) {
13254                             $document.bind(event, callbackFunc);
13255                         } else {
13256                             $document.unbind(event, callbackFunc);
13257                         }
13258                     }, timeoutValue);
13259                 }
13260             });
13261         } else {
13262             scope.$watch(flag, function (newVal, oldVal) {
13263                 if (newVal !== oldVal) {
13264                     if (newVal) {
13265                         $document.bind(event, callbackFunc);
13266                     } else {
13267                         $document.unbind(event, callbackFunc);
13268                     }
13269                 }
13270             });
13271         }
13272     };
13273
13274     return {
13275         click: _click,
13276         touch: _touch,
13277         scroll: _scroll,
13278         event: _event
13279     };
13280 }])
13281
13282 .directive('b2bOnlyNums', function (keymap) {
13283     return {
13284         restrict: 'A',
13285         require: 'ngModel',
13286         link: function (scope, elm, attrs, ctrl) {
13287             var maxChars = attrs.b2bOnlyNums ? attrs.b2bOnlyNums : 4;
13288             elm.on('keydown', function (event) {
13289                 if ((event.which >= 48 && event.which <= 57) || (event.which >= 96 && event.which <= 105)) {
13290                     // check for maximum characters allowed
13291                     if (elm.val().length < maxChars){
13292                         return true;
13293                     } else {
13294                         event.preventDefault();
13295                         return false;
13296                     }
13297                 } else if ([8, 9, 13, 27, 37, 38, 39, 40].indexOf(event.which) > -1) {
13298                     // to allow backspace, tab, enter, escape, arrows
13299                     return true;
13300                 } else if (event.altKey || event.ctrlKey) {
13301                     // to allow alter, control, and shift keys
13302                     return true;
13303                 } else {
13304                     // to stop others
13305                     event.preventDefault();
13306                     return false;
13307                 }
13308             });
13309
13310             var validateString = function (value) {
13311                 if (angular.isUndefined(value) || value === null || value === '') {
13312                     return ctrl.$modelValue;
13313                 }
13314                 return value;
13315             };
13316             ctrl.$parsers.unshift(validateString);
13317         }
13318     }
13319 })
13320
13321 .directive('b2bKeyupClick', [ function () {
13322     return {
13323         restrict: 'A',
13324         link: function (scope, elem, attr) {
13325             var keyCode = [];
13326             attr.$observe('b2bKeyupClick', function (value) {
13327                 if (value) {
13328                     keyCode = value.split(',');
13329                 }
13330             });
13331             elem.bind('keydown keyup', function (ev) {
13332                 var keyCodeCondition = function () {
13333                     var flag = false;
13334                     if (!(ev.keyCode)) {
13335                         if (ev.which) {
13336                             ev.keyCode = ev.which;
13337                         } else if (ev.charCode) {
13338                             ev.keyCode = ev.charCode;
13339                         }
13340                     }
13341                     if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
13342                         flag = true;
13343                     }
13344                     return flag;
13345                 };
13346                 if (ev.type === 'keydown' && keyCodeCondition()) {
13347                     ev.preventDefault();
13348                 }
13349                 else if (ev.type === 'keyup' && keyCodeCondition()) {
13350                     elem[0].click();
13351                 }
13352             });
13353         }
13354     };
13355 }])
13356
13357 .factory('b2bDOMHelper', function() {
13358
13359     var _isTabable = function(node) {
13360         var element = angular.element(node);
13361         var tagName = element[0].tagName.toUpperCase();
13362
13363         if (isHidden(element)) {
13364             return false;
13365         }
13366         if (element.attr('tabindex') !== undefined) {
13367             return (parseInt(element.attr('tabindex'), 10) >= 0);
13368         }
13369         if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
13370             if (tagName === 'A' || tagName === 'AREA') {
13371                 // anchors/areas without href are not focusable
13372                 return (element[0].href !== '');
13373             }
13374             return !(element[0].disabled);
13375         }
13376         return false;
13377     };
13378
13379     function isValidChild(child) {
13380         return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
13381     }
13382     
13383     function isHidden(obj) {
13384         var elem = angular.element(obj);
13385         var elemStyle = undefined;
13386         if(obj instanceof HTMLElement){
13387             elemStyle = window.getComputedStyle(obj);
13388         }
13389         else {
13390             elemStyle = window.getComputedStyle(obj[0]);
13391         }
13392         return elem.hasClass('ng-hide') || elem.css('display') === 'none' || elemStyle.display === 'none' || elemStyle.visibility === 'hidden' || elem.css('visibility') === 'hidden';
13393     }
13394
13395     function hasValidParent(obj) {
13396         return (isValidChild(obj) && obj.parentElement.nodeName !== 'BODY');
13397     }
13398
13399     function traverse(obj, fromTop) {
13400         var obj = obj || document.getElementsByTagName('body')[0];
13401         if (isValidChild(obj) && _isTabable(obj)) {
13402             return obj;
13403         }
13404         // If object is hidden, skip it's children
13405         if (isValidChild(obj) && isHidden(obj)) {
13406             return undefined;
13407         } 
13408         // If object is hidden, skip it's children
13409         if (angular.element(obj).hasClass('ng-hide')) {
13410             return undefined;
13411         }  
13412         if (obj.hasChildNodes()) {
13413             var child;
13414             if (fromTop) {
13415                 child = obj.firstChild;
13416             } else {
13417                 child = obj.lastChild;
13418             }
13419             while(child) {
13420                 var res =  traverse(child, fromTop);
13421                 if(res){
13422                     return res;
13423                 }
13424                 else{
13425                     if (fromTop) {
13426                         child = child.nextSibling;
13427                     } else {
13428                         child = child.previousSibling;
13429                     }
13430                 }
13431             }
13432         }
13433         else{
13434             return undefined;
13435         }
13436     }
13437
13438     var _previousElement = function(el, isFocusable){
13439
13440         var elem = el;
13441         if (el.hasOwnProperty('length')) {
13442             elem = el[0];
13443         }
13444
13445         var parent = elem.parentElement;
13446         var previousElem = undefined;
13447
13448         if(isFocusable) {
13449             if (hasValidParent(elem)) {
13450                 var siblings = angular.element(parent).children();
13451                 if (siblings.length > 0) {
13452                     // Good practice to splice out the elem from siblings if there, saving some time.
13453                     // We allow for a quick check for jumping to parent first before removing. 
13454                     if (siblings[0] === elem) {
13455                         // If we are looking at immidiate parent and elem is first child, we need to go higher
13456                         var e = _previousElement(angular.element(elem).parent(), isFocusable);
13457                         if (_isTabable(e)) {
13458                             return e;
13459                         }
13460                     } else {
13461                         // I need to filter myself and any nodes next to me from the siblings
13462                         var indexOfElem = Array.prototype.indexOf.call(siblings, elem);
13463                         siblings = Array.prototype.filter.call(siblings, function(item, itemIndex) {
13464                             if (!angular.equals(elem, item) && itemIndex < indexOfElem) {
13465                                 return true;
13466                             }
13467                         });
13468                     }
13469                     // We need to search backwards
13470                     for (var i = 0; i <= siblings.length-1; i++) {//for (var i = siblings.length-1; i >= 0; i--) {
13471                         var ret = traverse(siblings[i], false);
13472                         if (ret !== undefined) {
13473                             return ret;
13474                         }
13475                     }
13476
13477                     var e = _previousElement(angular.element(elem).parent(), isFocusable);
13478                     if (_isTabable(e)) {
13479                         return e;
13480                     }
13481                 }
13482             }
13483         } else {
13484             var siblings = angular.element(parent).children();
13485             if (siblings.length > 1) {
13486                 // Since indexOf is on Array.prototype and parent.children is a NodeList, we have to use call()
13487                 var index = Array.prototype.indexOf.call(siblings, elem);
13488                 previousElem = siblings[index-1];
13489             }
13490         }
13491         return previousElem;
13492     };
13493
13494     var _lastTabableElement = function(el) {
13495         /* This will return the first tabable element from the parent el */
13496         var elem = el;
13497         if (el.hasOwnProperty('length')) {
13498             elem = el[0];
13499         }
13500
13501         return traverse(elem, false);
13502     };
13503
13504     var _firstTabableElement = function(el) {
13505         /* This will return the first tabable element from the parent el */
13506         var elem = el;
13507         if (el.hasOwnProperty('length')) {
13508             elem = el[0];
13509         }
13510
13511         return traverse(elem, true);
13512     };
13513
13514     var _isInDOM = function(obj) {
13515       return document.documentElement.contains(obj);
13516     }
13517
13518     return {
13519         firstTabableElement: _firstTabableElement,
13520         lastTabableElement: _lastTabableElement,
13521         previousElement: _previousElement,
13522         isInDOM: _isInDOM,
13523         isTabable: _isTabable,
13524         isHidden: isHidden
13525     };
13526 })
13527
13528 .factory('windowOrientation', ['$window', function ($window) {
13529     var _isPotrait = function () {
13530         if ($window.innerHeight > $window.innerWidth) {
13531             return true;
13532         } else {
13533             return false;
13534         }
13535     };
13536     var _isLandscape = function () {
13537         if ($window.innerHeight < $window.innerWidth) {
13538             return true;
13539         } else {
13540             return false;
13541         }
13542     };
13543
13544     return {
13545         isPotrait: _isPotrait,
13546         isLandscape: _isLandscape
13547     };
13548 }])
13549 .directive('b2bNextElement', function() {
13550   return {
13551     restrict: 'A',
13552     transclude: false,
13553     link: function (scope, elem, attr, ctrls) {
13554
13555         var keys = attr.b2bNextElement.split(',');
13556
13557         elem.bind('keydown', function (e) {
13558             var nextElement = elem.next();
13559             if(e.keyCode == 39 || e.keyCode == 40){ // if e.keyCode in keys
13560                 if(nextElement.length) {
13561                     e.preventDefault();
13562                     nextElement[0].focus();
13563                 }
13564             }
13565         });
13566     }
13567   }
13568 })
13569
13570 .directive('b2bAccessibilityClick', [function () {
13571     return {
13572         restrict: 'A',
13573         link: function (scope, elem, attr, ctrl) {
13574             var keyCode = [];
13575             attr.$observe('b2bAccessibilityClick', function (value) {
13576                 if (value) {
13577                     keyCode = value.split(',');
13578                 }
13579             });
13580             elem.bind('keydown keypress', function (ev) {
13581                 var keyCodeCondition = function () {
13582                     var flag = false;
13583                     if (!(ev.keyCode)) {
13584                         if (ev.which) {
13585                             ev.keyCode = ev.which; 
13586                         } else if (ev.charCode) {
13587                             ev.keyCode = ev.charCode;
13588                         }
13589                     }
13590                     if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
13591                         flag = true;
13592                     }
13593                     return flag;
13594                 };
13595                 if (keyCode.length > 0 && keyCodeCondition()) {
13596                     elem[0].click();
13597                     ev.preventDefault();
13598                 }
13599             });
13600         }
13601     };
13602 }])
13603
13604 .directive('b2bReset', ['$compile', function ($compile) {
13605         return {
13606             restrict: 'A',
13607             require: ['?ngModel', 'b2bReset'],
13608             controller: ['$scope', function ($scope) {
13609                 var resetButton = angular.element('<button type="button" class="reset-field" tabindex="-1" aria-label="Click to reset" aria-hidden="true" role="button"></button>');
13610
13611                 this.getResetButton = function () {
13612                     return resetButton;
13613                 };
13614             }],
13615             link: function (scope, element, attrs, ctrls) {
13616
13617                 var ngModelCtrl = ctrls[0];
13618                 var ctrl = ctrls[1];
13619
13620                 var resetButton = ctrl.getResetButton();
13621
13622
13623                 resetButton.on('click', function () {
13624                     element[0].value = '';
13625
13626                     if (ngModelCtrl) {
13627                         if (attrs.b2bReset) {
13628                             ngModelCtrl.$setViewValue(attrs.b2bReset);
13629                         } else {
13630                             ngModelCtrl.$setViewValue('');
13631                         }
13632                         element[0].value = ngModelCtrl.$viewValue;
13633                         ngModelCtrl.$render();
13634                         scope.$digest();
13635                     }
13636                     element[0].focus();
13637                     element[0].select();
13638                 });
13639
13640                 var addResetButton = function () {
13641                     element.after(resetButton);
13642                     element.unbind('focus input', addResetButton);
13643                 };
13644
13645                 element.bind('focus input', addResetButton);
13646             }
13647         };
13648     }])
13649
13650 .directive('b2bPrevElement', ['b2bDOMHelper', 'keymap', function (b2bDOMHelper, keymap) {
13651   return {
13652     restrict: 'A',
13653     transclude: false,
13654     link: function (scope, elem, attr) {
13655
13656         elem.bind('keydown', function (e) {
13657             if(e.keyCode == 37 || e.keyCode == 38){
13658                 var prev = b2bDOMHelper.previousElement(elem, false);
13659                 if(prev !== undefined) {
13660                     e.preventDefault();
13661                     prev.focus();
13662                 }
13663             }
13664         });
13665     }
13666   }
13667 }])
13668 /**
13669  * @param {integer} delay - Timeout before first and last focusable elements are found
13670  * @param {boolean} trigger - A variable on scope that will trigger refinding first/last focusable elements 
13671  */
13672 .directive('b2bTrapFocusInsideElement', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
13673     return {
13674         restrict: 'A',
13675         transclude: false,
13676         link: function (scope, elem, attr) {
13677
13678             var delay = parseInt(attr.delay, 10) || 10;
13679
13680             /* Before opening modal, find the focused element */
13681             var firstTabableElement = undefined,
13682                 lastTabableElement = undefined;
13683                 
13684             function init() {
13685                 $timeout(function () {
13686                     firstTabableElement = b2bDOMHelper.firstTabableElement(elem);
13687                     lastTabableElement = b2bDOMHelper.lastTabableElement(elem);
13688                     angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
13689                     angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
13690                 }, delay, false);
13691             }
13692
13693             if (attr.trigger !== undefined) {
13694                 scope.$watch('trigger', function() {
13695                     if (scope.trigger) {
13696                         init();
13697                     }
13698                 });
13699             }
13700
13701             var firstTabableElementKeyhandler = function(e) {
13702                 if (!e.keyCode) {
13703                     e.keyCode = e.which;
13704                 }
13705                 if (e.keyCode === keymap.KEY.TAB && e.shiftKey) {
13706                     if (attr.trapFocusInsideElement === 'true') {
13707                         var temp = b2bDOMHelper.lastTabableElement(elem);
13708                         if (lastTabableElement !== temp) {
13709                             // Unbind keydown handler on lastTabableElement
13710                             angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
13711                             lastTabableElement = temp;
13712                             angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
13713                         }
13714                     }
13715                     lastTabableElement.focus();
13716                     events.preventDefault(e);
13717                     events.stopPropagation(e);
13718                 }
13719             };
13720
13721             var lastTabableElementKeyhandler = function(e) {
13722                 if (!e.keyCode) {
13723                     e.keyCode = e.which;
13724                 }
13725                 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
13726                     if (attr.trapFocusInsideElement === 'true') {
13727                         var temp = b2bDOMHelper.firstTabableElement(elem);
13728                         if (firstTabableElement !== temp) {
13729                             // Unbind keydown handler on firstTabableElement
13730                             angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
13731                             firstTabableElement = temp;
13732                             angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
13733                         }
13734                     }
13735                     firstTabableElement.focus();
13736                     events.preventDefault(e);
13737                     events.stopPropagation(e);
13738                 }
13739             };
13740
13741             init();
13742         }
13743     };
13744 }])
13745
13746 .factory('trapFocusInElement', ['$document', '$isElement', 'b2bDOMHelper', 'keymap', '$interval', function ($document, $isElement, b2bDOMHelper, keymap, $interval) {
13747     var elementStack = [];
13748     var stackHead = undefined;
13749     var stopInterval = undefined;
13750     var intervalRequired = false;
13751     var interval = 1000;
13752     var firstTabableElement, lastTabableElement;
13753
13754     var trapKeyboardFocusInFirstElement = function (e) {
13755         if (!e.keyCode) {
13756             e.keyCode = e.which;
13757         }
13758
13759         if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
13760             lastTabableElement[0].focus();
13761             e.preventDefault(e);
13762             e.stopPropagation(e);
13763         }
13764
13765     };
13766
13767     var trapKeyboardFocusInLastElement = function (e) {
13768         if (!e.keyCode) {
13769             e.keyCode = e.which;
13770         }
13771
13772         if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
13773             firstTabableElement[0].focus();
13774             e.preventDefault(e);
13775             e.stopPropagation(e);
13776         }
13777     };
13778
13779     var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
13780         var bodyElements = $document.find('body').children();
13781
13782         firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(b2bDOMHelper.firstTabableElement(stackHead));
13783         lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(b2bDOMHelper.lastTabableElement(stackHead));
13784
13785         if (flag) {
13786             for (var i = 0; i < bodyElements.length; i++) {
13787                 if (bodyElements[i] !== stackHead[0]) {
13788                     bodyElements.eq(i).attr('aria-hidden', true);
13789                 }
13790             }
13791             firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
13792             lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
13793         } else {
13794             for (var j = 0; j < bodyElements.length; j++) {
13795                 if (bodyElements[j] !== stackHead[0]) {
13796                     bodyElements.eq(j).removeAttr('aria-hidden');
13797                 }
13798             }
13799             firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
13800             lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
13801         }
13802
13803         if (intervalRequired && flag) {
13804             stopInterval = $interval(function () {
13805                 var firstTabableElementTemp = angular.element(b2bDOMHelper.firstTabableElement(stackHead));
13806                 var lastTabableElementTemp = angular.element(b2bDOMHelper.lastTabableElement(stackHead));
13807                 if (firstTabableElementTemp[0] !== firstTabableElement[0] || lastTabableElementTemp[0] !== lastTabableElement[0]) {
13808                     $interval.cancel(stopInterval);
13809                     stopInterval = undefined;
13810                     trapFocusInElement(false, firstTabableElement, lastTabableElement);
13811                     trapFocusInElement(true, firstTabableElementTemp, lastTabableElementTemp);
13812                 }
13813             }, interval);
13814         } else {
13815             if (stopInterval) {
13816                 $interval.cancel(stopInterval);
13817                 stopInterval = undefined;
13818             }
13819         }
13820     };
13821     var toggleTrapFocusInElement = function (flag, element, intervalRequiredParam, intervalParam) {
13822         intervalRequired = intervalRequiredParam ? intervalRequiredParam : intervalRequired;
13823         interval = intervalParam ? intervalParam : interval;
13824         if (angular.isDefined(flag) && angular.isDefined(element)) {
13825             if (flag && angular.isUndefined(stackHead)) {
13826                 stackHead = element;
13827                 trapFocusInElement(flag);
13828             } else {
13829                 if (flag) {
13830                     trapFocusInElement(false);
13831                     elementStack.push(stackHead);
13832                     stackHead = element;
13833                     trapFocusInElement(true);
13834                 } else {
13835                     if (angular.isDefined(stackHead) && (stackHead[0] === element[0])) {
13836                         trapFocusInElement(false);
13837                         stackHead = elementStack.pop();
13838                         if (angular.isDefined(stackHead)) {
13839                             trapFocusInElement(true);
13840                         }
13841                     }
13842                 }
13843             }
13844         } else {
13845             if (angular.isDefined(stackHead)) {
13846                 trapFocusInElement(false, firstTabableElement, lastTabableElement);
13847                 trapFocusInElement(true);
13848             }
13849         }
13850     };
13851
13852     return toggleTrapFocusInElement;
13853 }])
13854 .factory('draggedElement', function(){
13855     var draggedElement;
13856     return {
13857         setElement: function(data){
13858             draggedElement = data;
13859         },
13860         getElement: function(){
13861             return draggedElement;
13862         }
13863     }
13864 })
13865
13866 .directive('draggable', ['draggedElement',function (draggedElement) { 
13867     return function(scope, element) { 
13868  
13869       element[0].draggable = true; 
13870  
13871       element.bind('dragstart', function(e) {
13872         draggedElement.setElement(this.parentElement.parentElement); 
13873         e.dataTransfer.effectAllowed = 'move'; 
13874         e.dataTransfer.setDragImage(this.parentElement.parentElement, 0, 0); 
13875         this.parentElement.parentElement.classList.add('b2-drag-element');
13876         return false; 
13877       }); 
13878  
13879       element.bind('dragend', function(e) { 
13880         draggedElement.setElement(e); 
13881         this.parentElement.parentElement.classList.remove('b2-drag-element'); 
13882         return false; 
13883       });
13884     }; 
13885 }]) 
13886  
13887 .directive('droppable', ['draggedElement',function (draggedElement) { 
13888     return { 
13889         restrict: 'EA', 
13890         replace: true, 
13891         scope: { 
13892           rowData: '=' 
13893         }, 
13894         link: function(scope, element, attr) {
13895             if(attr.droppable === 'true') {
13896               element.bind('dragover', function(e) { 
13897                 e.dataTransfer.dropEffect = 'move'; 
13898                 this.classList.add('b2b-drag-over') 
13899                 if (e.preventDefault) e.preventDefault(); // allows us to drop 
13900                 return false; 
13901               }); 
13902
13903               element.bind('dragstart', function(e) {
13904                 if(!e.target.parentElement.classList.contains('b2b-draggable')) {
13905                     e.preventDefault();
13906                     return false;
13907                 }
13908               }); 
13909
13910               element.bind('dragenter', function(e) { 
13911                 if(e.target.getAttribute('droppable') ==='true') {
13912                     this.click();
13913                 }
13914               });
13915
13916               element.bind('dragleave', function(e) { 
13917                 this.classList.remove('b2b-drag-over'); 
13918                 return false; 
13919               });
13920
13921               element.bind('drop', function(e) { 
13922                 var ele = draggedElement.getElement();
13923                 if (e.stopPropagation) e.stopPropagation(); 
13924                 if (e.preventDefault) e.preventDefault(); 
13925                 this.classList.remove('b2b-drag-over'); 
13926
13927                 if(ele && ele.hasAttribute('data-index')){
13928                     var element = scope.rowData[parseInt(ele.getAttribute('data-index'))]; 
13929                     if(element !== undefined) { 
13930                         scope.rowData.splice(parseInt(ele.getAttribute('data-index')), 1); 
13931                         scope.rowData.splice(parseInt(e.currentTarget.getAttribute('data-index')), 0 , element); 
13932                     }
13933                 }
13934                 scope.$apply(); 
13935      
13936                 return false; 
13937               }); 
13938           }
13939         } 
13940     } 
13941 }])
13942 .directive('b2bSetNextFocusOn', ['b2bDOMHelper', '$timeout', function(b2bDOMHelper, $timeout) {
13943     return {
13944         restrict: 'A',
13945         scope: true,
13946         link: function (scope, elem, attr) {
13947             elem.bind('click', function(){
13948                 var firstFocusableElement = undefined; 
13949                 var containerElem = undefined; 
13950                 var containerArray = [];
13951                 var timeout = parseInt(attr.setNextFocusTimeout, 0) | 100;
13952                 var index = parseInt(attr.b2bSetNextFocusIndex, 0) | 0;
13953
13954                                  /*
13955                                   *Fix for IE7 and lower 
13956                                   *polyfill src: https://github.com/HubSpot/pace/issues/102
13957                                   */
13958                                 if (!document.querySelectorAll) {
13959                                         document.querySelectorAll = function (selectors) {
13960                                                 var style = document.createElement('style'), elements = [], element;
13961                                                 document.documentElement.firstChild.appendChild(style);
13962                                                 document._qsa = [];
13963
13964                                                 style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
13965                                                 window.scrollBy(0, 0);
13966                                                 style.parentNode.removeChild(style);
13967
13968                                                 while (document._qsa.length) {
13969                                                         element = document._qsa.shift();
13970                                                         element.style.removeAttribute('x-qsa');
13971                                                         elements.push(element);
13972                                                 }
13973                                                 document._qsa = null;
13974                                                 return elements;
13975                                         };
13976                                 }
13977
13978                 if (attr.b2bSetNextFocusOn === '') {
13979                     return;
13980                 } else {
13981                     containerArray = attr.b2bSetNextFocusOn.split(' ');
13982                 }
13983                 $timeout(function(){
13984                     var i = 0;
13985                     do { // cycles thru containerArray until finds a match in DOM to set focus to
13986                         containerElem = document.querySelectorAll(containerArray[i])[index]; 
13987                         i++;
13988                     } while ( (!containerElem) && (i < containerArray.length) );
13989                     if(containerElem){
13990                         if (!angular.isDefined(firstFocusableElement)) { 
13991                             firstFocusableElement = b2bDOMHelper.firstTabableElement(containerElem); 
13992                         }
13993                         firstFocusableElement.focus(); 
13994                     }
13995                 }, timeout, false)
13996             });
13997         }
13998
13999     };
14000 }])
14001
14002 .directive('b2bInputAllow', [function() {
14003     return {
14004         restrict: 'A',
14005         require: 'ngModel',
14006         link: function (scope, elem, attr, ctrl) {
14007             var regexExpression = null;
14008             attr.$observe('b2bInputAllow', function (value) {
14009                 if (value) {
14010                     regexExpression = new RegExp(value);
14011                 }
14012             });
14013             var isValid = function(str) {
14014                 if (regexExpression !== null) {
14015                     return regexExpression.test(str);
14016                 }
14017                 return false;
14018             };
14019             elem.bind('keypress', function($event) {
14020                 var charcode = String.fromCharCode($event.which || $event.keyCode);
14021                 if (!isValid(charcode)) {
14022                     $event.preventDefault();
14023                     $event.stopPropagation();
14024                 }
14025             });
14026             elem.bind('input', function (evt) {
14027                 var inputString = ctrl.$viewValue;
14028                 if (isValid(inputString)) {
14029                     ctrl.$setViewValue(inputString);
14030                     ctrl.$render();
14031                     scope.$apply();
14032                 }
14033             });
14034         }
14035     };
14036 }])
14037
14038 .directive('b2bInputDeny', [function() {
14039     return {
14040         restrict: 'A',
14041         require: 'ngModel',
14042         link: function (scope, elem, attr, ctrl) {
14043             var regexExpression = null;
14044             attr.$observe('b2bInputDeny', function (value) {
14045                 if (value) {
14046                     regexExpression = new RegExp(value, 'g');
14047                 }
14048             });
14049             elem.bind('input', function () {
14050                 var inputString = ctrl.$viewValue && ctrl.$viewValue.replace(regexExpression, '');
14051                 if (inputString !== ctrl.$viewValue) {
14052                     ctrl.$setViewValue(inputString);
14053                     ctrl.$render();
14054                     scope.$apply();
14055                 }
14056             });
14057         }
14058     };
14059 }])
14060
14061 .directive('b2bDragonInput', [function() {
14062     return {
14063         restrict: 'A',
14064         require: 'ngModel',
14065         link: function (scope, elem, attr, ctrl) {
14066             elem.on('focus keyup', function(){
14067                 elem.triggerHandler('change');
14068             });
14069         }
14070     };
14071 }])
14072
14073 .directive('b2bKey', ['b2bUtilitiesConfig', '$timeout', 'keymap', function (b2bUtilitiesConfig, $timeout, keymap) {
14074     return {
14075         restrict: 'EA',
14076         controller: ['$scope', '$element', '$attrs', function ($scope, $element,attr) {
14077             this.childElements = [];
14078             this.disableNodes = {};
14079             this.enableSearch = attr.enableSearch !== undefined ? true : b2bUtilitiesConfig.enableSearch;
14080             this.circularTraversal = attr.circularTraversal !== undefined ? true : b2bUtilitiesConfig.circularTraversal;
14081             this.counter = -1;
14082             if (this.enableSearch) {
14083                 this.searchKeys = [];
14084             }
14085             var searchString = '';
14086
14087             var selfCtrl = this;
14088
14089             this.childElementsList = [];
14090
14091             this.b2bKeyID = "";
14092
14093             if (angular.isDefined(attr.b2bKey)) {
14094                 this.b2bKeyID = attr.b2bKey;
14095             }
14096
14097             this.calculateChildElementsList = function () {
14098                 return $element[0].querySelectorAll("[b2b-key-item='" + this.b2bKeyID + "']:not([disabled])");
14099             };
14100
14101             this.resetChildElementsList = function () {
14102                 return $timeout(function () {
14103                     selfCtrl.childElementsList = selfCtrl.calculateChildElementsList();
14104                 });
14105             };
14106
14107             this.resetChildElementsList();
14108
14109             $scope.$on('b2b-key-reset-child-elements-list', function () {
14110                 selfCtrl.resetChildElementsList();
14111             });
14112
14113
14114             this.registerElement = function (childElement, searchKey) {
14115                 this.childElements.push(childElement);
14116                 if (this.enableSearch) {
14117                     this.searchKeys.push(searchKey);
14118                 }
14119                 var count = this.childElements.length - 1;
14120                 this.maxLength = count + 1;
14121                 return count;
14122             };
14123             this.toggleDisable = function (count, state) {
14124                 this.disableNodes[count] = state;
14125             };
14126             this.searchElement = function (searchExp) {
14127                 var regex = new RegExp("\\b" + searchExp, "gi");
14128                 var position = this.searchKeys.regexIndexOf(regex, this.counter + 1, true);
14129                 if (position > -1) {
14130                     this.counter = position;
14131                     this.moveFocus(this.counter);
14132                 }
14133             };
14134             this.startTimer = function (time) {
14135                 if (searchString === '') {
14136                     $timeout(function () {
14137                         searchString = '';
14138                     }, time);
14139                 }
14140             };
14141             this.resetCounter = function (count) {
14142                 this.counter = count;
14143             };
14144             this.moveNext = function (count) {
14145                 this.counter = (this.counter + count) < this.maxLength ? this.counter + count : (this.circularTraversal ? 0 : this.counter);
14146                 if (this.disableNodes[this.counter]) {
14147                     if ((this.counter + count) < this.maxLength) {
14148                         this.moveNext(count);
14149                     }
14150                 } else {
14151                     this.moveFocus(this.counter);
14152                 }
14153             };
14154             this.movePrev = function (count) {
14155                 this.counter = (this.counter - count) > -1 ? this.counter - count : (this.circularTraversal ? this.maxLength-1 : this.counter);
14156                 if (this.disableNodes[this.counter]) {
14157                     if ((this.counter - count) > -1) {
14158                         this.movePrev(count);
14159                     }
14160                 } else {
14161                     this.moveFocus(this.counter);
14162                 }
14163             };
14164             this.moveFocus = function (index) {
14165                 this.childElements[index][0].focus();
14166             };
14167
14168             this.keyDownHandler = function (ev, count) {
14169                 if (angular.isDefined(count) && !isNaN(count) && count !== this.counter) {
14170                     this.resetCounter(count);
14171                 }
14172                 if (!ev.keyCode) {
14173                     if (ev.which) {
14174                         ev.keyCode = ev.which;
14175                     } else if (ev.charCode) {
14176                         ev.keyCode = ev.charCode;
14177                     }
14178                 }
14179                 if (ev.keyCode) {
14180                     if (this.prev && this.prev.indexOf(ev.keyCode.toString()) > -1) {
14181                         this.movePrev(1);
14182                         ev.preventDefault();
14183                         ev.stopPropagation();
14184                     } else if (this.next && this.next.indexOf(ev.keyCode.toString()) > -1) {
14185                         this.moveNext(1);
14186                         ev.preventDefault();
14187                         ev.stopPropagation();
14188                     } else if (this.up && this.up.indexOf(ev.keyCode.toString()) > -1) {
14189                         if (this.type === 'table') {
14190                             this.movePrev(this.columns);
14191                             ev.preventDefault();
14192                             ev.stopPropagation();
14193                         }
14194                     } else if (this.down && this.down.indexOf(ev.keyCode.toString()) > -1) {
14195                         if (this.type === 'table') {
14196                             this.moveNext(this.columns);
14197                             ev.preventDefault();
14198                             ev.stopPropagation();
14199                         }
14200                     } else if (ev.keyCode === keymap.KEY.HOME) {
14201                         var firstIndex = 0;
14202                         while (this.disableNodes[firstIndex] !== false) {
14203                             firstIndex++;
14204                         };
14205                         var count = this.counter - firstIndex;
14206                         this.movePrev(count);
14207                         ev.preventDefault();
14208                         ev.stopPropagation();
14209                     } else if (ev.keyCode === keymap.KEY.END) {
14210                         var lastIndex = this.childElements.length - 1;
14211                         while (this.disableNodes[lastIndex] !== false) {
14212                             lastIndex--;
14213                         };
14214                         var count = lastIndex - this.counter;
14215                         this.moveNext(count);
14216                         ev.preventDefault();
14217                         ev.stopPropagation();
14218                     } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
14219                         if (this.enableSearch) {
14220                             this.startTimer(b2bUtilitiesConfig.searchTimer);
14221                             searchString = searchString + (keymap.MAP[ev.keyCode] || '');
14222                             this.searchElement(searchString);
14223                             ev.preventDefault();
14224                             ev.stopPropagation();
14225                         }
14226                     }
14227                 }
14228             };
14229         }],
14230         link: function (scope, elem, attr, ctrl) {
14231             ctrl.prev = attr.prev ? attr.prev.split(',') : b2bUtilitiesConfig.prev.split(',');
14232             ctrl.next = attr.next ? attr.next.split(',') : b2bUtilitiesConfig.next.split(',');
14233             ctrl.type = attr.type ? attr.type : b2bUtilitiesConfig.type;
14234             if (ctrl.type === 'table') {
14235                 ctrl.up = attr.up ? attr.up.split(',') : b2bUtilitiesConfig.up.split(',');
14236                 ctrl.down = attr.down ? attr.down.split(',') : b2bUtilitiesConfig.down.split(',');
14237                 ctrl.columns = attr.columns ? parseInt(attr.columns, 10) : b2bUtilitiesConfig.columns;
14238             }
14239
14240             elem.bind('keydown', function (ev) {
14241                 ctrl.keyDownHandler(ev);
14242             });
14243         }
14244     };
14245 }])
14246
14247 .directive('b2bKeyItem', [function () {
14248     return {
14249         restrict: 'EA',
14250         link: function (scope, elem, attr, ctrl) {
14251             var parentCtrl = (elem.parent() && elem.parent().controller('b2bKey')) || undefined;
14252             if (angular.isDefined(parentCtrl)) {
14253                 var count = parentCtrl.registerElement(elem, attr.searchKey);
14254                 elem.bind('keydown', function (ev) {
14255                     parentCtrl.keyDownHandler(ev, count);
14256                 });
14257                 scope.$watch(attr.b2bKeyItem, function (value) {
14258                     value = value === undefined ? true : value;
14259                     parentCtrl.toggleDisable(count, !value); 
14260                 });
14261                 scope.$on('$destroy', function () {
14262                     parentCtrl.toggleDisable(count, true);
14263                 });
14264             }
14265         }
14266     };
14267 }])
14268
14269 .directive('b2bElementFocus', [function () {
14270     return {
14271         restrict: 'A',
14272         link: function (scope, elem, attr, ctrl) {
14273             scope.$watch(attr.b2bElementFocus, function (value) {
14274                 if (value === true) {
14275                     elem[0].focus();
14276                 }
14277             });
14278         }
14279     };
14280 }])
14281
14282
14283 .directive('b2bAppendElement', ['$compile', function ($compile) {
14284     return {
14285         restrict: 'A',
14286         link: function (scope, elem, attr, ctrl) {
14287             var parameters = attr.b2bAppendElement.split(':');
14288             if (parameters.length === 1) {
14289                 elem.append(scope.$eval(parameters[0]));
14290             } else if (parameters.length === 2) {
14291                 if (parameters[1] === 'compile') {
14292                     var element = angular.element('<span>' + scope.$eval(parameters[0]) + '</span>');
14293                     elem.append($compile(element)(scope));
14294                 }
14295             }
14296
14297         }
14298     };
14299 }])
14300
14301 .directive('b2bKeyItemRefreshInNgRepeat', [function () {
14302     return {
14303         restrict: 'EA',
14304         require: '^^b2bKey',
14305         link: function (scope, elem, attr, parentCtrl) {
14306             if (angular.isDefined(parentCtrl)) {
14307
14308                 var attrToObserve = 'attrToObserve';
14309
14310                 if (attr.b2bKeyItemRefreshInNgRepeat) {
14311                     attrToObserve = 'b2bKeyItemRefreshInNgRepeat';
14312                 }
14313
14314                 attr.$observe(attrToObserve, function (newVal, oldVal) {
14315                     if (newVal && newVal !== oldVal) {
14316                         parentCtrl.resetChildElementsList();
14317                     }
14318                 });
14319             }
14320         }
14321     };
14322 }])
14323
14324 .constant('b2bMaskConfig', {
14325     maskDefinitions: {
14326         '9': /\d/,
14327         'A': /[a-zA-Z]/,
14328         '*': /[a-zA-Z0-9]/
14329     },
14330     clearOnBlur: false,
14331     clearOnBlurPlaceholder: false,
14332     escChar: '\\',
14333     eventsToHandle: ['input', 'keyup', 'click', 'focus'],
14334     addDefaultPlaceholder: true,
14335     allowInvalidValue: true
14336 })
14337 /**
14338  * @param {boolean} modelViewValue - If this is set to true, then the model value bound with ng-model will be the same as the $viewValue meaning it will contain any static mask characters present in the mask definition. This will not set the model value to a $viewValue that is considered invalid.
14339  * @param {String} maskPlaceholder - Allows customizing the mask placeholder when a user has focused the input element and while typing in their value
14340  * @param {String} maskPlaceholderChar - Allows customizing the mask placeholder character. The default mask placeholder is _.
14341  * @param {boolean} addDefaultPlaceholder - The default placeholder is constructed from the ui-mask definition so a mask of 999-9999 would have a default placeholder of ___-____; unless you have overridden the default placeholder character.
14342  */
14343 .directive('b2bMask', ['b2bMaskConfig', function(b2bMaskConfig) {
14344     return {
14345         require: 'ngModel',
14346         restrict: 'A',
14347         link: function(scope, element, attrs, ctrl) {
14348             var maskProcessed = false, eventsBound = false,
14349                 maskCaretMap, maskPatterns, maskPlaceholder, maskComponents,
14350                 // Minimum required length of the value to be considered valid
14351                 minRequiredLength,
14352                 value, valueMasked, isValid,
14353                 // Vars for initializing/uninitializing
14354                 originalPlaceholder = attrs.placeholder,
14355                 originalMaxlength = attrs.maxlength,
14356                 // Vars used exclusively in eventHandler()
14357                 oldValue, oldValueUnmasked, oldCaretPosition, oldSelectionLength,
14358                 // Used for communicating if a backspace operation should be allowed between
14359                 // keydownHandler and eventHandler
14360                 preventBackspace;
14361
14362             var options = b2bMaskConfig;
14363
14364             function isFocused (elem) {
14365               return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
14366             }
14367
14368             var originalIsEmpty = ctrl.$isEmpty;
14369             ctrl.$isEmpty = function(value) {
14370                 if (maskProcessed) {
14371                     return originalIsEmpty(unmaskValue(value || ''));
14372                 } else {
14373                     return originalIsEmpty(value);
14374                 }
14375             };
14376
14377             function initialize(maskAttr) {
14378                 if (!angular.isDefined(maskAttr)) {
14379                     return uninitialize();
14380                 }
14381                 processRawMask(maskAttr);
14382                 if (!maskProcessed) {
14383                     return uninitialize();
14384                 }
14385                 initializeElement();
14386                 bindEventListeners();
14387                 return true;
14388             }
14389
14390             function initPlaceholder(placeholderAttr) {
14391                 if ( ! placeholderAttr) {
14392                     return;
14393                 }
14394                 maskPlaceholder = placeholderAttr;
14395                 /* If the mask is processed, then we need to update the value
14396                    but don't set the value if there is nothing entered into the element
14397                    and there is a placeholder attribute on the element because that
14398                    will only set the value as the blank maskPlaceholder
14399                    and override the placeholder on the element */
14400                 if (maskProcessed && !(element.val().length === 0 && angular.isDefined(attrs.placeholder))) {
14401                     element.val(maskValue(unmaskValue(element.val())));
14402                 }
14403             }
14404
14405             function initPlaceholderChar() {
14406                 return initialize(attrs.uiMask);
14407             }
14408
14409             var modelViewValue = false;
14410
14411             attrs.$observe('modelViewValue', function(val) {
14412                 if (val === 'true') {
14413                     modelViewValue = true;
14414                 }
14415             });
14416
14417             attrs.$observe('allowInvalidValue', function(val) {
14418                 linkOptions.allowInvalidValue = val === ''? true : !!val;
14419                 formatter(ctrl.$modelValue);
14420             });
14421
14422             function formatter(fromModelValue) {
14423                 if (!maskProcessed) {
14424                     return fromModelValue;
14425                 }
14426                 value = unmaskValue(fromModelValue || '');
14427                 isValid = validateValue(value);
14428                 ctrl.$setValidity('mask', isValid);
14429
14430                 if (!value.length) return undefined;
14431                 if (isValid || linkOptions.allowInvalidValue) {
14432                     return maskValue(value);
14433                 } else {
14434                     return undefined;
14435                 }
14436             }
14437
14438             function parser(fromViewValue) {
14439                 if (!maskProcessed) {
14440                     return fromViewValue;
14441                 }
14442                 value = unmaskValue(fromViewValue || '');
14443                 isValid = validateValue(value);
14444                 /* We have to set viewValue manually as the reformatting of the input
14445                    value performed by eventHandler() doesn't happen until after
14446                    this parser is called, which causes what the user sees in the input
14447                    to be out-of-sync with what the ctrl's $viewValue is set to. */
14448                 ctrl.$viewValue = value.length ? maskValue(value) : '';
14449                 ctrl.$setValidity('mask', isValid);
14450
14451                 if (isValid || linkOptions.allowInvalidValue) {
14452                     return modelViewValue ? ctrl.$viewValue : value;
14453                 }
14454             }
14455
14456             var linkOptions = {};
14457
14458             // to do 
14459             if (attrs.b2bMaskOptions) {
14460                 linkOptions = scope.$eval('[' + attrs.b2bMaskOptions + ']');
14461                 if (angular.isObject(linkOptions[0])) {
14462                     // we can't use angular.copy nor angular.extend, they lack the power to do a deep merge
14463                     linkOptions = (function(original, current) {
14464                         for (var i in original) {
14465                             if (Object.prototype.hasOwnProperty.call(original, i)) {
14466                                 if (current[i] === undefined) {
14467                                     current[i] = angular.copy(original[i]);
14468                                 } else {
14469                                     if (angular.isObject(current[i]) && !angular.isArray(current[i])) {
14470                                         current[i] = angular.extend({}, original[i], current[i]);
14471                                     }
14472                                 }
14473                             }
14474                         }
14475                         return current;
14476                     })(options, linkOptions[0]);
14477                 } else {
14478                     linkOptions = options;  //gotta be a better way to do this..
14479                 }
14480             } else {
14481                 linkOptions = options;
14482             }
14483
14484             attrs.$observe('b2bMask', initialize);
14485             if (angular.isDefined(attrs.maskPlaceholder)) {
14486                 attrs.$observe('maskPlaceholder', initPlaceholder);
14487             }
14488             else {
14489                 attrs.$observe('placeholder', initPlaceholder);
14490             }
14491             if (angular.isDefined(attrs.maskPlaceholderChar)) {
14492                 attrs.$observe('maskPlaceholderChar', initPlaceholderChar);
14493             }
14494
14495             ctrl.$formatters.unshift(formatter);
14496             ctrl.$parsers.unshift(parser);
14497
14498             function uninitialize() {
14499                 maskProcessed = false;
14500                 unbindEventListeners();
14501
14502                 if (angular.isDefined(originalPlaceholder)) {
14503                     element.attr('placeholder', originalPlaceholder);
14504                 } else {
14505                     element.removeAttr('placeholder');
14506                 }
14507
14508                 if (angular.isDefined(originalMaxlength)) {
14509                     element.attr('maxlength', originalMaxlength);
14510                 } else {
14511                     element.removeAttr('maxlength');
14512                 }
14513
14514                 element.val(ctrl.$modelValue);
14515                 ctrl.$viewValue = ctrl.$modelValue;
14516                 return false;
14517             }
14518
14519             function initializeElement() {
14520                 value = oldValueUnmasked = unmaskValue(ctrl.$modelValue || '');
14521                 valueMasked = oldValue = maskValue(value);
14522                 isValid = validateValue(value);
14523                 if (attrs.maxlength) { // Double maxlength to allow pasting new val at end of mask
14524                     element.attr('maxlength', maskCaretMap[maskCaretMap.length - 1] * 2);
14525                 }
14526                 if ( ! originalPlaceholder && linkOptions.addDefaultPlaceholder) {
14527                     element.attr('placeholder', maskPlaceholder);
14528                 }
14529                 var viewValue = ctrl.$modelValue;
14530                 var idx = ctrl.$formatters.length;
14531                 while(idx--) {
14532                     viewValue = ctrl.$formatters[idx](viewValue);
14533                 }
14534                 ctrl.$viewValue = viewValue || '';
14535                 ctrl.$render();
14536             }
14537
14538             function bindEventListeners() {
14539                 if (eventsBound) {
14540                     return;
14541                 }
14542                 element.bind('blur', blurHandler);
14543                 element.bind('mousedown mouseup', mouseDownUpHandler);
14544                 element.bind('keydown', keydownHandler);
14545                 element.bind(linkOptions.eventsToHandle.join(' '), eventHandler);
14546                 eventsBound = true;
14547             }
14548
14549             function unbindEventListeners() {
14550                 if (!eventsBound) {
14551                     return;
14552                 }
14553                 element.unbind('blur', blurHandler);
14554                 element.unbind('mousedown', mouseDownUpHandler);
14555                 element.unbind('mouseup', mouseDownUpHandler);
14556                 element.unbind('keydown', keydownHandler);
14557                 element.unbind('input', eventHandler);
14558                 element.unbind('keyup', eventHandler);
14559                 element.unbind('click', eventHandler);
14560                 element.unbind('focus', eventHandler);
14561                 eventsBound = false;
14562             }
14563
14564             function validateValue(value) {
14565                 // Zero-length value validity is ngRequired's determination
14566                 return value.length ? value.length >= minRequiredLength : true;
14567             }
14568
14569              function unmaskValue(value) {
14570                 var valueUnmasked = '',
14571                     input = element[0],
14572                     maskPatternsCopy = maskPatterns.slice(),
14573                     selectionStart = oldCaretPosition,
14574                     selectionEnd = selectionStart + getSelectionLength(input),
14575                     valueOffset, valueDelta, tempValue = '';
14576                 // Preprocess by stripping mask components from value
14577                 value = value.toString();
14578                 valueOffset = 0;
14579                 valueDelta = value.length - maskPlaceholder.length;
14580                 angular.forEach(maskComponents, function(component) {
14581                     var position = component.position;
14582                     //Only try and replace the component if the component position is not within the selected range
14583                     //If component was in selected range then it was removed with the user input so no need to try and remove that component
14584                     if (!(position >= selectionStart && position < selectionEnd)) {
14585                         if (position >= selectionStart) {
14586                             position += valueDelta;
14587                         }
14588                         if (value.substring(position, position + component.value.length) === component.value) {
14589                             tempValue += value.slice(valueOffset, position);// + value.slice(position + component.value.length);
14590                             valueOffset = position + component.value.length;
14591                         }
14592                     }
14593                 });
14594                 value = tempValue + value.slice(valueOffset);
14595                 angular.forEach(value.split(''), function(chr) {
14596                     if (maskPatternsCopy.length && maskPatternsCopy[0].test(chr)) {
14597                         valueUnmasked += chr;
14598                         maskPatternsCopy.shift();
14599                     }
14600                 });
14601
14602                 return valueUnmasked;
14603             }
14604
14605             function maskValue(unmaskedValue) {
14606                 var valueMasked = '',
14607                         maskCaretMapCopy = maskCaretMap.slice();
14608
14609                 angular.forEach(maskPlaceholder.split(''), function(chr, i) {
14610                     if (unmaskedValue.length && i === maskCaretMapCopy[0]) {
14611                         valueMasked += unmaskedValue.charAt(0) || '_';
14612                         unmaskedValue = unmaskedValue.substr(1);
14613                         maskCaretMapCopy.shift();
14614                     }
14615                     else {
14616                         valueMasked += chr;
14617                     }
14618                 });
14619                 return valueMasked;
14620             }
14621
14622             function getPlaceholderChar(i) {
14623                 var placeholder = angular.isDefined(attrs.uiMaskPlaceholder) ? attrs.uiMaskPlaceholder : attrs.placeholder,
14624                     defaultPlaceholderChar;
14625
14626                 if (angular.isDefined(placeholder) && placeholder[i]) {
14627                     return placeholder[i];
14628                 } else {
14629                     defaultPlaceholderChar = angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar ? attrs.uiMaskPlaceholderChar : '_';
14630                     return (defaultPlaceholderChar.toLowerCase() === 'space') ? ' ' : defaultPlaceholderChar[0];
14631                 }
14632             }
14633
14634             /* Generate array of mask components that will be stripped from a masked value
14635                before processing to prevent mask components from being added to the unmasked value.
14636                E.g., a mask pattern of '+7 9999' won't have the 7 bleed into the unmasked value. */
14637             function getMaskComponents() {
14638                 var maskPlaceholderChars = maskPlaceholder.split(''),
14639                         maskPlaceholderCopy, components;
14640
14641                 /* maskCaretMap can have bad values if the input has the ui-mask attribute implemented as an obversable property, e.g. the demo page */
14642                 if (maskCaretMap && !isNaN(maskCaretMap[0])) {
14643                     /* Instead of trying to manipulate the RegEx based on the placeholder characters
14644                        we can simply replace the placeholder characters based on the already built
14645                        maskCaretMap to underscores and leave the original working RegEx to get the proper
14646                        mask components */
14647                     angular.forEach(maskCaretMap, function(value) {
14648                         maskPlaceholderChars[value] = '_';
14649                     });
14650                 }
14651                 maskPlaceholderCopy = maskPlaceholderChars.join('');
14652                 components = maskPlaceholderCopy.replace(/[_]+/g, '_').split('_');
14653                 components = components.filter(function(s) {
14654                     return s !== '';
14655                 });
14656
14657                 /* need a string search offset in cases where the mask contains multiple identical components
14658                    E.g., a mask of 99.99.99-999.99 */
14659                 var offset = 0;
14660                 return components.map(function(c) {
14661                     var componentPosition = maskPlaceholderCopy.indexOf(c, offset);
14662                     offset = componentPosition + 1;
14663                     return {
14664                         value: c,
14665                         position: componentPosition
14666                     };
14667                 });
14668             }
14669
14670             function processRawMask(mask) {
14671                 var characterCount = 0;
14672
14673                 maskCaretMap = [];
14674                 maskPatterns = [];
14675                 maskPlaceholder = '';
14676
14677                 if (angular.isString(mask)) {
14678                     minRequiredLength = 0;
14679
14680                     var isOptional = false,
14681                             numberOfOptionalCharacters = 0,
14682                             splitMask = mask.split('');
14683
14684                     var inEscape = false;
14685                     angular.forEach(splitMask, function(chr, i) {
14686                         if (inEscape) {
14687                             inEscape = false;
14688                             maskPlaceholder += chr;
14689                             characterCount++;
14690                         }
14691                         else if (linkOptions.escChar === chr) {
14692                             inEscape = true;
14693                         }
14694                         else if (linkOptions.maskDefinitions[chr]) {
14695                             maskCaretMap.push(characterCount);
14696
14697                             maskPlaceholder += getPlaceholderChar(i - numberOfOptionalCharacters);
14698                             maskPatterns.push(linkOptions.maskDefinitions[chr]);
14699
14700                             characterCount++;
14701                             if (!isOptional) {
14702                                 minRequiredLength++;
14703                             }
14704
14705                             isOptional = false;
14706                         }
14707                         else if (chr === '?') {
14708                             isOptional = true;
14709                             numberOfOptionalCharacters++;
14710                         }
14711                         else {
14712                             maskPlaceholder += chr;
14713                             characterCount++;
14714                         }
14715                     });
14716                 }
14717                 // Caret position immediately following last position is valid.
14718                 maskCaretMap.push(maskCaretMap.slice().pop() + 1);
14719
14720                 maskComponents = getMaskComponents();
14721                 maskProcessed = maskCaretMap.length > 1 ? true : false;
14722             }
14723
14724             var prevValue = element.val();
14725             function blurHandler() {
14726                 if (linkOptions.clearOnBlur || ((linkOptions.clearOnBlurPlaceholder) && (value.length === 0) && attrs.placeholder)) {
14727                     oldCaretPosition = 0;
14728                     oldSelectionLength = 0;
14729                     if (!isValid || value.length === 0) {
14730                         valueMasked = '';
14731                         element.val('');
14732                         scope.$apply(function() {
14733                             //only $setViewValue when not $pristine to avoid changing $pristine state.
14734                             if (!ctrl.$pristine) {
14735                                 ctrl.$setViewValue('');
14736                             }
14737                         });
14738                     }
14739                 }
14740                 //Check for different value and trigger change.
14741                 if (value !== prevValue) {
14742                     var currentVal = element.val();
14743                     var isTemporarilyEmpty = value === '' && currentVal && angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar === 'space';
14744                     if(isTemporarilyEmpty) {
14745                         element.val('');
14746                     }
14747                     triggerChangeEvent(element[0]);
14748                     if(isTemporarilyEmpty) {
14749                         element.val(currentVal);
14750                     }
14751                 }
14752                 prevValue = value;
14753             }
14754
14755             function triggerChangeEvent(element) {
14756                 var change;
14757                 if (angular.isFunction(window.Event) && !element.fireEvent) {
14758                     // modern browsers and Edge
14759                     try {
14760                         change = new Event('change', {
14761                             view: window,
14762                             bubbles: true,
14763                             cancelable: false
14764                         });
14765                     } catch (ex) {
14766                         //this is for certain mobile browsers that have the Event object
14767                         //but don't support the Event constructor 
14768                         change = document.createEvent('HTMLEvents');
14769                         change.initEvent('change', false, true);
14770                     } finally {
14771                         element.dispatchEvent(change);
14772                     }
14773                 } else if ('createEvent' in document) {
14774                     // older browsers
14775                     change = document.createEvent('HTMLEvents');
14776                     change.initEvent('change', false, true);
14777                     element.dispatchEvent(change);
14778                 }
14779                 else if (element.fireEvent) {
14780                     // IE <= 11
14781                     element.fireEvent('onchange');
14782                 }
14783             }
14784
14785             function mouseDownUpHandler(e) {
14786                 if (e.type === 'mousedown') {
14787                     element.bind('mouseout', mouseoutHandler);
14788                 } else {
14789                     element.unbind('mouseout', mouseoutHandler);
14790                 }
14791             }
14792
14793             element.bind('mousedown mouseup', mouseDownUpHandler);
14794
14795             function mouseoutHandler() {
14796                 oldSelectionLength = getSelectionLength(this);
14797                 element.unbind('mouseout', mouseoutHandler);
14798             }
14799
14800             function keydownHandler(e) {
14801                 var isKeyBackspace = e.which === 8,
14802                 caretPos = getCaretPosition(this) - 1 || 0, //value in keydown is pre change so bump caret position back to simulate post change
14803                 isCtrlZ = e.which === 90 && e.ctrlKey; //ctrl+z pressed
14804
14805                 if (isKeyBackspace) {
14806                     while(caretPos >= 0) {
14807                         if (isValidCaretPosition(caretPos)) {
14808                             //re-adjust the caret position.
14809                             //Increment to account for the initial decrement to simulate post change caret position
14810                             setCaretPosition(this, caretPos + 1);
14811                             break;
14812                         }
14813                         caretPos--;
14814                     }
14815                     preventBackspace = caretPos === -1;
14816                 }
14817
14818                 if (isCtrlZ) {
14819                     // prevent IE bug - value should be returned to initial state
14820                     element.val('');
14821                     e.preventDefault();
14822                 }
14823             }
14824
14825             function eventHandler(e) {
14826                 e = e || {};
14827                 // Allows more efficient minification
14828                 var eventWhich = e.which,
14829                         eventType = e.type;
14830
14831                 // Prevent shift and ctrl from mucking with old values
14832                 if (eventWhich === 16 || eventWhich === 91) {
14833                     return; 
14834                 }
14835
14836                 var val = element.val(),
14837                         valOld = oldValue,
14838                         valMasked,
14839                         valAltered = false,
14840                         valUnmasked = unmaskValue(val),
14841                         valUnmaskedOld = oldValueUnmasked,
14842                         caretPos = getCaretPosition(this) || 0,
14843                         caretPosOld = oldCaretPosition || 0,
14844                         caretPosDelta = caretPos - caretPosOld,
14845                         caretPosMin = maskCaretMap[0],
14846                         caretPosMax = maskCaretMap[valUnmasked.length] || maskCaretMap.slice().shift(),
14847                         selectionLenOld = oldSelectionLength || 0,
14848                         isSelected = getSelectionLength(this) > 0,
14849                         wasSelected = selectionLenOld > 0,
14850                         // Case: Typing a character to overwrite a selection
14851                         isAddition = (val.length > valOld.length) || (selectionLenOld && val.length > valOld.length - selectionLenOld),
14852                         // Case: Delete and backspace behave identically on a selection
14853                         isDeletion = (val.length < valOld.length) || (selectionLenOld && val.length === valOld.length - selectionLenOld),
14854                         isSelection = (eventWhich >= 37 && eventWhich <= 40) && e.shiftKey, // Arrow key codes
14855
14856                         isKeyLeftArrow = eventWhich === 37,
14857                         // Necessary due to "input" event not providing a key code
14858                         isKeyBackspace = eventWhich === 8 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === -1)),
14859                         isKeyDelete = eventWhich === 46 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === 0) && !wasSelected),
14860                         // Handles cases where caret is moved and placed in front of invalid maskCaretMap position. Logic below
14861                         // ensures that, on click or leftward caret placement, caret is moved leftward until directly right of
14862                         // non-mask character. Also applied to click since users are (arguably) more likely to backspace
14863                         // a character when clicking within a filled input.
14864                         caretBumpBack = (isKeyLeftArrow || isKeyBackspace || eventType === 'click') && caretPos > caretPosMin;
14865
14866                 oldSelectionLength = getSelectionLength(this);
14867
14868                 // These events don't require any action
14869                 if (isSelection || (isSelected && (eventType === 'click' || eventType === 'keyup' || eventType === 'focus'))) {
14870                     return;
14871                 }
14872
14873                 if (isKeyBackspace && preventBackspace) {
14874                     element.val(maskPlaceholder);
14875                     // This shouldn't be needed but for some reason after aggressive backspacing the ctrl $viewValue is incorrect.
14876                     // This keeps the $viewValue updated and correct.
14877                     scope.$apply(function () {
14878                         ctrl.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
14879                     });
14880                     setCaretPosition(this, caretPosOld);
14881                     return;
14882                 }
14883
14884                 // User attempted to delete but raw value was unaffected--correct this grievous offense
14885                 if ((eventType === 'input') && isDeletion && !wasSelected && valUnmasked === valUnmaskedOld) {
14886                     while (isKeyBackspace && caretPos > caretPosMin && !isValidCaretPosition(caretPos)) {
14887                         caretPos--;
14888                     }
14889                     while (isKeyDelete && caretPos < caretPosMax && maskCaretMap.indexOf(caretPos) === -1) {
14890                         caretPos++;
14891                     }
14892                     var charIndex = maskCaretMap.indexOf(caretPos);
14893                     // Strip out non-mask character that user would have deleted if mask hadn't been in the way.
14894                     valUnmasked = valUnmasked.substring(0, charIndex) + valUnmasked.substring(charIndex + 1);
14895
14896                     // If value has not changed, don't want to call $setViewValue, may be caused by IE raising input event due to placeholder
14897                     if (valUnmasked !== valUnmaskedOld)
14898                         valAltered = true;
14899                 }
14900
14901                 // Update values
14902                 valMasked = maskValue(valUnmasked);
14903
14904                 oldValue = valMasked;
14905                 oldValueUnmasked = valUnmasked;
14906
14907                 //additional check to fix the problem where the viewValue is out of sync with the value of the element.
14908                 //better fix for commit 2a83b5fb8312e71d220a497545f999fc82503bd9 (I think)
14909                 if (!valAltered && val.length > valMasked.length)
14910                     valAltered = true;
14911
14912                 element.val(valMasked);
14913
14914                 //we need this check.  What could happen if you don't have it is that you'll set the model value without the user
14915                 //actually doing anything.  Meaning, things like pristine and touched will be set.
14916                 if (valAltered) {
14917                     scope.$apply(function () {
14918                         ctrl.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
14919                     });
14920                 }
14921
14922                 // Caret Repositioning
14923                 // Ensure that typing always places caret ahead of typed character in cases where the first char of
14924                 // the input is a mask char and the caret is placed at the 0 position.
14925                 if (isAddition && (caretPos <= caretPosMin)) {
14926                     caretPos = caretPosMin + 1;
14927                 }
14928
14929                 if (caretBumpBack) {
14930                     caretPos--;
14931                 }
14932
14933                 // Make sure caret is within min and max position limits
14934                 caretPos = caretPos > caretPosMax ? caretPosMax : caretPos < caretPosMin ? caretPosMin : caretPos;
14935
14936                 // Scoot the caret back or forth until it's in a non-mask position and within min/max position limits
14937                 while (!isValidCaretPosition(caretPos) && caretPos > caretPosMin && caretPos < caretPosMax) {
14938                     caretPos += caretBumpBack ? -1 : 1;
14939                 }
14940
14941                 if ((caretBumpBack && caretPos < caretPosMax) || (isAddition && !isValidCaretPosition(caretPosOld))) {
14942                     caretPos++;
14943                 }
14944                 oldCaretPosition = caretPos;
14945                 setCaretPosition(this, caretPos);
14946             }
14947
14948             function isValidCaretPosition(pos) {
14949                 return maskCaretMap.indexOf(pos) > -1;
14950             }
14951
14952             function getCaretPosition(input) {
14953                 if (!input)
14954                     return 0;
14955                 if (input.selectionStart !== undefined) {
14956                     return input.selectionStart;
14957                 } else if (document.selection) {
14958                     if (isFocused(element[0])) {
14959                         // For IE
14960                         input.focus();
14961                         var selection = document.selection.createRange();
14962                         selection.moveStart('character', input.value ? -input.value.length : 0);
14963                         return selection.text.length;
14964                     }
14965                 }
14966                 return 0;
14967             }
14968
14969             function setCaretPosition(input, pos) {
14970                 if (!input)
14971                     return 0;
14972                 if (input.offsetWidth === 0 || input.offsetHeight === 0) {
14973                     return; // Input's hidden
14974                 }
14975                 if (input.setSelectionRange) {
14976                     if (isFocused(element[0])) {
14977                         input.focus();
14978                         input.setSelectionRange(pos, pos);
14979                     }
14980                 }
14981                 else if (input.createTextRange) {
14982                     // For IE
14983                     var range = input.createTextRange();
14984                     range.collapse(true);
14985                     range.moveEnd('character', pos);
14986                     range.moveStart('character', pos);
14987                     range.select();
14988                 }
14989             }
14990
14991             function getSelectionLength(input) {
14992                 if (!input)
14993                     return 0;
14994                 if (input.selectionStart !== undefined) {
14995                     return (input.selectionEnd - input.selectionStart);
14996                 }
14997                 if (window.getSelection) {
14998                     return (window.getSelection().toString().length);
14999                 }
15000                 if (document.selection) {
15001                     return (document.selection.createRange().text.length);
15002                 }
15003                 return 0;
15004             }
15005         }
15006     };
15007 }])
15008 .filter('b2bMultiSepartorHighlight', function($sce) {
15009         return function(text, searchText, searchSeperator) {
15010             var splitText = function(string) {
15011                 if(angular.isDefined(searchSeperator)){
15012                     if (string.indexOf(searchSeperator) > -1) {
15013                         return string.split(searchSeperator);
15014                     } else {
15015                         return string
15016                     }
15017                 }else{
15018                     return string;
15019                 }
15020             }
15021             if (text) {
15022                 var newText = splitText(text);
15023                 var newPhrase = splitText(searchText);
15024                 if (angular.isArray(newPhrase)) {
15025                     for (var i = 0; i < newText.length; i++) {
15026                         if (i <= 0) {
15027                             text = newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
15028                                 '<span class="b2b-search-hightlight">$1</span>');
15029                         } else {
15030                             text = text + searchSeperator + ' ' + (newPhrase[i] ? newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
15031                                 '<span class="b2b-search-hightlight">$1</span>') : newText[i]);
15032                         }
15033                     }
15034                 } else {
15035                     text = text.replace(new RegExp('(' + searchText + ')', 'gi'),
15036                         '<span class="b2b-search-hightlight">$1</span>');
15037                 }
15038             }
15039             return $sce.trustAsHtml(text)
15040         }
15041     })
15042     
15043     .factory('b2bUserAgent', [function() {
15044         var _isMobile = function() {
15045             if(/Android/i.test(navigator.userAgent)){
15046                 return /Mobile/i.test(navigator.userAgent);
15047             }else{
15048                 return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
15049             }
15050             
15051         };
15052         var _notMobile = function() {
15053             if(/Android/i.test(navigator.userAgent)){
15054                 return !/Mobile/i.test(navigator.userAgent);
15055             }else{
15056                 return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
15057             }
15058             
15059         };
15060         var _isIE = function() {
15061             return /msie|trident/i.test(navigator.userAgent);
15062         };
15063         var _isFF = function() {
15064             return /Firefox/.test(navigator.userAgent);
15065         };
15066         var _isChrome = function() {
15067             return /Chrome/.test(navigator.userAgent);
15068         };
15069         var _isSafari = function() {
15070             return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
15071         };
15072
15073         return {
15074             isMobile: _isMobile,
15075             notMobile: _notMobile,
15076             isIE: _isIE,
15077             isFF: _isFF,
15078             isChrome: _isChrome,
15079             isSafari: _isSafari
15080         };
15081     }])
15082     .run(['$document', 'b2bUserAgent', function($document, b2bUserAgent) {
15083         var html = $document.find('html').eq(0);
15084         if (b2bUserAgent.isIE()) {
15085             html.addClass('isIE');
15086         } else {
15087             html.removeClass('isIE');
15088         }
15089     }]);
15090     
15091
15092 (function () {
15093     String.prototype.toSnakeCase = function () {
15094         return this.replace(/([A-Z])/g, function ($1) {
15095             return "-" + $1.toLowerCase();
15096         });
15097     };
15098     var concat = function (character, times) {
15099         character = character || '';
15100         times = (!isNaN(times) && times) || 0;
15101         var finalChar = '';
15102         for (var i = 0; i < times; i++) {
15103             finalChar += character;
15104         }
15105         return finalChar;
15106     };
15107
15108     // direction: true for left and false for right
15109     var pad = function (actualString, width, character, direction) {
15110         actualString = actualString || '';
15111         width = (!isNaN(width) && width) || 0;
15112         character = character || '';
15113         if (width > actualString.length) {
15114             if (direction) {
15115                 return concat(character, (width - actualString.length)) + actualString;
15116             } else {
15117                 return actualString + concat(character, (width - actualString.length));
15118             }
15119         }
15120         return actualString;
15121     };
15122
15123     String.prototype.lPad = function (width, character) {
15124         return pad(this, width, character, true);
15125     };
15126
15127     String.prototype.rPad = function (width, character) {
15128         return pad(this, width, character, false);
15129     };
15130
15131     if (!Array.prototype.indexOf) {
15132         Array.prototype.indexOf = function (val) {
15133             for (var index = 0; index < this.length; index++) {
15134                 if (this[index] === val) {
15135                     return index;
15136                 }
15137             }
15138             return -1;
15139         };
15140     }
15141
15142     if (!Array.prototype.regexIndexOf) {
15143         Object.defineProperty(Array.prototype, 'regexIndexOf', {
15144             enumerable: false,
15145             value: function (regex, startIndex, loop) {
15146                 startIndex = startIndex && startIndex > -1 ? startIndex : 0;
15147                 for (var index = startIndex; index < this.length; index++) {
15148                     if (this[index].toString().match(regex)) {
15149                         return index;
15150                     }
15151                 }
15152                 if (loop) {
15153                     for (var index = 0; index < startIndex; index++) {
15154                         if (this[index].toString().match(regex)) {
15155                             return index;
15156                         }
15157                     }
15158                 }
15159                 return -1;
15160             }
15161         })
15162     }
15163 })();
15164 angular.module("b2bTemplate/audioPlayer/audioPlayer.html", []).run(["$templateCache", function($templateCache) {
15165   $templateCache.put("b2bTemplate/audioPlayer/audioPlayer.html",
15166     "<div class=\"b2b-audio\">\n" +
15167     "   <audio preload=\"auto\">\n" +
15168     "           <source ng-src=\"{{audio.mp3 | trustedAudioUrl}}\" type=\"audio/mp3\"></source>\n" +
15169     "           <i>Your browser does not support the audio element.</i>\n" +
15170     "    </audio>\n" +
15171     "\n" +
15172     "    <div audio-play-pause-icon class=\"controls-wrapper\" ng-click=\"toggleAudio()\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" role=\"button\" aria-label=\"{{isPlayInProgress?'pause':'play'}}\">\n" +
15173     "           <i class=\"icoControls-pointer\" ng-show='!isPlayInProgress'></i>\n" +
15174     "           <i class=\"icoControls-pause\" ng-show='isPlayInProgress'></i>\n" +
15175     "    </div>\n" +
15176     "\n" +
15177     "    <div class=\"seek-bar-container-wrapper\">\n" +
15178     "           <b2b-seek-bar min=\"0\" max=\"{{audio.duration}}\" step=\"1\" skip-interval=\"{{audio.timeShiftInSeconds}}\" no-aria-label ng-model=\"audio.currentTime\" on-drag-init=\"isAudioDragging=true\" on-drag-end=\"setAudioPosition(audio.currentTime)\"></b2b-seek-bar>\n" +
15179     "           <div class=\"timing-container\">\n" +
15180     "                   <span class=\"timing-container-left\">{{timeFormatter(audio.currentTime)}}</span>\n" +
15181     "                   <span class=\"timing-container-right\">{{timeFormatter(audio.duration)}}</span>\n" +
15182     "                   <div class=\"timing-container-spacer\"></div>\n" +
15183     "           </div>\n" +
15184     "    </div>\n" +
15185     "           \n" +
15186     "    <b2b-flyout>\n" +
15187     "           <div tabindex=\"0\" b2b-accessibility-click=\"13,32\" role=\"button\" aria-label=\"Volume pop-over. Volume set at {{audio.currentVolume}}%\" class=\"controls-wrapper audio-volume-control\" b2b-flyout-toggler>\n" +
15188     "                   <i class=\"icoControls-mutespeakers\" ng-show=\"audio.currentVolume === 0\"></i>\n" +
15189     "                   <i class=\"icoControls-volumedown\" ng-show=\"audio.currentVolume > 0 && audio.currentVolume <= 50\"></i>\n" +
15190     "                   <i class=\"icoControls-volumeup\" ng-show=\"audio.currentVolume > 50\"></i>\n" +
15191     "           </div> \n" +
15192     "           \n" +
15193     "           <b2b-flyout-content horizontal-placement=\"center\" flyout-style=\"width:70px; height:190px;\" vertical-placement=\"above\">\n" +
15194     "                   <div class=\"b2b-audio-popover text-center\">\n" +
15195     "                           <span>Max</span>\n" +
15196     "                           <b2b-seek-bar min=\"0\" max=\"100\" step=\"1\" skip-interval=\"10\" vertical data-ng-model=\"audio.currentVolume\" class='volume-popover'></b2b-seek-bar>\n" +
15197     "                           <div class=\"min-label\">Min</div>\n" +
15198     "                   </div>\n" +
15199     "           </b2b-flyout-content>\n" +
15200     "   </b2b-flyout>\n" +
15201     "</div>");
15202 }]);
15203
15204 angular.module("b2bTemplate/audioRecorder/audioRecorder.html", []).run(["$templateCache", function($templateCache) {
15205   $templateCache.put("b2bTemplate/audioRecorder/audioRecorder.html",
15206     "<div class=\"b2b-audio-recorder row\">\n" +
15207     "   <div class=\"b2b-elapsed-time span11\">\n" +
15208     "           <div ng-if=\"isRecording\">\n" +
15209     "                   <span style=\"padding-right: 25px;\">{{config.whileRecordingMessage}}</span>\n" +
15210     "                   <span>{{timeFormatter(elapsedTime)}}</span>\n" +
15211     "           </div>\n" +
15212     "           <span ng-if=\"!isRecording\">{{config.startRecordingMessage}}</span>\n" +
15213     "   </div>      \n" +
15214     "   <div class=\"b2b-controls\" title=\"{{isRecording ? 'Stop' : 'REC'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"toggleRecording()\" role=\"button\">\n" +
15215     "           <i ng-if=\"isRecording\" class=\"icoControls-stop\" ></i>\n" +
15216     "           <i ng-if=\"!isRecording\" class=\"icoControls-record\"></i>\n" +
15217     "    </div>\n" +
15218     "</div>");
15219 }]);
15220
15221 angular.module("b2bTemplate/backToTop/backToTop.html", []).run(["$templateCache", function($templateCache) {
15222   $templateCache.put("b2bTemplate/backToTop/backToTop.html",
15223     "<button class=\"btn-arrow b2b-backtotop-button\" type=\"button\" aria-label=\"Back to top\">\n" +
15224     "    <div class=\"btn-secondary b2b-top-btn\">\n" +
15225     "        <i class=\"icoControls-upPRIMARY\" role=\"img\"></i>\n" +
15226     "    </div>\n" +
15227     "</button>\n" +
15228     "");
15229 }]);
15230
15231 angular.module("b2bTemplate/boardstrip/b2bAddBoard.html", []).run(["$templateCache", function($templateCache) {
15232   $templateCache.put("b2bTemplate/boardstrip/b2bAddBoard.html",
15233     "<div tabindex=\"0\" role=\"menuitem\" b2b-accessibility-click=\"13,32\" ng-click=\"addBoard()\" aria-label=\"Add Board\" class=\"boardstrip-item--add\">\n" +
15234     "    <div class=\"centered\"><i aria-hidden=\"true\" class=\"icoControls-add-maximize\"></i> Add board</div>\n" +
15235     "</div> ");
15236 }]);
15237
15238 angular.module("b2bTemplate/boardstrip/b2bBoard.html", []).run(["$templateCache", function($templateCache) {
15239   $templateCache.put("b2bTemplate/boardstrip/b2bBoard.html",
15240     "<li b2b-board-navigation tabindex=\"-1\" role=\"menuitem\" aria-label=\"{{boardLabel}} {{getCurrentIndex()===boardIndex?'selected':''}}\" b2b-accessibility-click=\"13,32\" ng-click=\"selectBoard(boardIndex)\" class=\"board-item\" ng-class=\"{'selected': getCurrentIndex()===boardIndex}\">\n" +
15241     "    <div ng-transclude></div>\n" +
15242     "    <div class=\"board-caret\" ng-if=\"getCurrentIndex()===boardIndex\">\n" +
15243     "        <div class=\"board-caret-indicator\"></div>\n" +
15244     "        <div class=\"board-caret-arrow-up\"></div>\n" +
15245     "    </div>\n" +
15246     "</li>");
15247 }]);
15248
15249 angular.module("b2bTemplate/boardstrip/b2bBoardstrip.html", []).run(["$templateCache", function($templateCache) {
15250   $templateCache.put("b2bTemplate/boardstrip/b2bBoardstrip.html",
15251     "<div class=\"b2b-boardstrip\">\n" +
15252     "   <div class=\"boardstrip-reel\" role=\"menu\">\n" +
15253     "           <div class=\"prev-items\">\n" +
15254     "                   <!-- <i tabindex=\"{{isPrevBoard() ? 0 : -1}}\" ng-class=\"{'disabled': !isPrevBoard()}\" role=\"menuitem\" aria-label=\"See previous boards\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" class=\"arrow icoControls-left\"></i> -->\n" +
15255     "                   <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" ng-disabled=\"!isPrevBoard()\">\n" +
15256     "                       <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-left\"></i>\n" +
15257     "                       </div>\n" +
15258     "                       <span class=\"offscreen-text\">Previous boards</span>\n" +
15259     "                   </button>\n" +
15260     "           </div>\n" +
15261     "           <div b2b-add-board on-add-board=\"onAddBoard()\"></div>\n" +
15262     "           <div class=\"board-viewport\"><ul role=\"menu\" class=\"boardstrip-container\" ng-transclude></ul></div>\n" +
15263     "           <div class=\"next-items\">\n" +
15264     "                   <!-- <i tabindex=\"{{isNextBoard() ? 0 : -1}}\" ng-class=\"{'disabled': !isNextBoard()}\" role=\"menuitem\" aria-label=\"See next boards\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" class=\"arrow icoControls-right\"></i> -->\n" +
15265     "                   <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" ng-disabled=\"!isNextBoard()\">\n" +
15266     "                       <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-right\"></i>\n" +
15267     "                       </div>\n" +
15268     "                       <span class=\"offscreen-text\">Next boards</span>\n" +
15269     "                   </button>\n" +
15270     "           </div>\n" +
15271     "   </div>\n" +
15272     "</div>\n" +
15273     "");
15274 }]);
15275
15276 angular.module("b2bTemplate/calendar/datepicker-popup.html", []).run(["$templateCache", function($templateCache) {
15277   $templateCache.put("b2bTemplate/calendar/datepicker-popup.html",
15278     "<div id=\"datepicker\" class=\"datepicker dropdown-menu\" ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" style=\"display: block;\" aria-hidden=\"false\" role=\"dialog\" tabindex=\"-1\" b2b-key type=\"table\" columns=\"7\">\n" +
15279     "    <div class=\"datepicker-days\" style=\"display: block;\">\n" +
15280     "        <div ng-repeat=\"header in headers\" class=\"text-left\" style=\"width: 100%;\" b2b-append-element=\"header\"></div>\n" +
15281     "        <table class=\"table-condensed\">\n" +
15282     "            <thead>\n" +
15283     "                <tr>\n" +
15284     "                    <th id=\"prev\" class=\"prev\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" aria-label=\"Previous Month\" role=\"button\" b2b-element-focus=\"!disablePrev && getFocus\" ng-style=\"{visibility: visibilityPrev}\" ng-click=\"!disablePrev && move(-1,$event)\"><i class=\"icon-primary-left\" aria-hidden=\"true\"></i></th>\n" +
15285     "                    <th id=\"month\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
15286     "                    <th id=\"next\" class=\"next\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" b2b-element-focus=\"disablePrev && getFocus\" aria-label=\"Next Month\" role=\"button\" ng-style=\"{visibility: visibilityNext}\" ng-click=\"!disableNext && move(1,$event)\"><i class=\"icon-primary-right\" aria-hidden=\"true\"></i></th>\n" +
15287     "                </tr>\n" +
15288     "                <tr ng-show=\"labels.length > 0\">\n" +
15289     "                    <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
15290     "                </tr>\n" +
15291     "            </thead>\n" +
15292     "            <tbody>\n" +
15293     "                <tr ng-repeat=\"row in rows\">\n" +
15294     "                    <td headers=\"{{dt.header}}\" b2b-key-item=\"dt.focusable\" b2b-accessibility-click=\"13\" b2b-element-focus=\"disablePrev && disableNext && getFocus && (dt.selected || dt.firstFocus || dt.fromDate || dt.dateRange)\" tabindex=\"{{(!(dt.focusable && (dt.selected || dt.firstFocus || dt.fromDate || currFocus)) && -1) || 0}}\" aria-hidden=\"{{(!dt.focusable && true) || false}}\" role=\"{{(dt.focusable && 'gridcell') || ''}}\" aria-label=\"{{(dt.focusable && dt.date | date : 'EEEE, MMMM d') || ''}}\" aria-selected=\"{{(dt.focusable && (dt.selected || dt.dateRange) && true) || false}}\" ng-repeat=\"dt in row\" class=\"day magic\" ng-class=\"{'active': dt.focusable && dt.selected && !dt.dateRange, 'start-date': dt.focusable && dt.fromDate && dt.dateRange, 'between-date': dt.focusable && !dt.fromDate && !dt.selected && dt.dateRange, 'end-date': dt.focusable && dt.selected && dt.dateRange, 'old': dt.oldMonth, 'new': dt.nextMonth, 'disabled': dt.disabled, 'due-date': dt.dueDate, 'late-fee': dt.pastDue}\" ng-focus=\"currFocus=true; trapFocus();\" ng-blur=\"currFocus=false; trapFocus();\" title=\"{{(dt.focusable && dt.pastDue && legendMessage) || ''}}\" ng-click=\"!ngDisabled && !dt.disabled && select(dt.date)\">\n" +
15295     "                        <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label}}</div>{{(dt.focusable && dt.date | date : 'EEEE, MMMM d yyyy') || ''}}{{(dt.focusable && dt.dueDate && '. Bill-due-date.. ') || ''}}{{(dt.focusable && dt.pastDue && legendMessage) || ''}}</td>\n" +
15296     "                </tr>\n" +
15297     "            </tbody>\n" +
15298     "            <tfoot>\n" +
15299     "                <tr ng-repeat=\"footer in footers\">\n" +
15300     "                    <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"footer\"></th>\n" +
15301     "                </tr>\n" +
15302     "            </tfoot>\n" +
15303     "        </table>\n" +
15304     "    </div>\n" +
15305     "</div>");
15306 }]);
15307
15308 angular.module("b2bTemplate/calendar/datepicker.html", []).run(["$templateCache", function($templateCache) {
15309   $templateCache.put("b2bTemplate/calendar/datepicker.html",
15310     "<div>\n" +
15311     "    <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
15312     "</div>");
15313 }]);
15314
15315 angular.module("b2bTemplate/coachmark/coachmark.html", []).run(["$templateCache", function($templateCache) {
15316   $templateCache.put("b2bTemplate/coachmark/coachmark.html",
15317     "<div class=\"b2b-coachmark-container\" tabindex=\"-1\" role=\"dialog\" aria-label=\"{{currentCoachmark.contentHeader}} {{currentCoachmark.content}} {{currentCoachmark.offscreenText}}\" ng-style=\"{'top':coackmarkElPos.top,'left':coackmarkElPos.left}\" aria-describeby=\"{{currentCoachmark}}\">\n" +
15318     "   <i class=\"b2b-coachmark-caret\"></i>\n" +
15319     "   <div class=\"b2b-coachmark-header\">\n" +
15320     "           <div class=\"b2b-coachmark-countlabel\"><span ng-if=\"coachmarkIndex !== 0\">{{coachmarkIndex}} of {{(coachmarks.length-1)}}<span></div>\n" +
15321     "           <div class=\"corner-button\">\n" +
15322     "                   <button type=\"button\" ng-focus=\"closeButtonFocus()\" class=\"close\" title=\"close\" aria-label=\"Close\" ng-click=\"closeCoachmark()\"></button>\n" +
15323     "           </div>\n" +
15324     "   </div>\n" +
15325     "   <div class=\"b2b-coachmark-content\">   \n" +
15326     "           <i class=\"icon-misc-dimmer\"></i>\n" +
15327     "           <div class=\"b2b-coachmark-content-header\"><span class=\"offscreen-text\">{{currentCoachmark.offscreenText}}</span>{{currentCoachmark.contentHeader}}</div>\n" +
15328     "           <div class=\"b2b-coachmark-description\">{{currentCoachmark.content}}</div>\n" +
15329     "           <div class=\"b2b-coachmark-btn-group\">\n" +
15330     "                   <a class=\"b2b-coachmark-link\" href=\"javascript:void(0)\" ng-if=\"currentCoachmark.linkLabel !== '' && currentCoachmark.linkLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.linkLabel)\">{{currentCoachmark.linkLabel}}</a>\n" +
15331     "                   <button class=\"btn btn-alt\" ng-if=\"currentCoachmark.buttonLabel !== '' && currentCoachmark.buttonLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.buttonLabel)\">{{currentCoachmark.buttonLabel}}</button>\n" +
15332     "           </div>  \n" +
15333     "   </div>  \n" +
15334     "</div>");
15335 }]);
15336
15337 angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templateCache", function($templateCache) {
15338   $templateCache.put("b2bTemplate/dropdowns/b2bDropdownDesktop.html",
15339     "<span b2b-key prev=\"38\" next=\"40\" enable-search ng-class=\"{'large': (dropdownSize === 'large'), 'disabled': (disabled), 'selectWrap': (isInputDropdown), 'selectorModule': (dropdownType === 'menu'), 'linkSelectorModule': (dropdownType === 'link-menu')}\">\n" +
15340     "    <input b2b-dropdown-toggle b2b-dropdown-validation ng-disabled=\"disabled\" type=\"text\" id=\"{{dropdownId}}\" name=\"{{dropdownName}}\" class=\"awd-select isWrapped\" ng-required=\"dropdownRequired\" ng-model=\"currentSelected.text\" role=\"combobox\" aria-owns=\"listbox{{$id}}\" aria-expanded=\"{{toggleFlag}}\" ng-click=\"toggleDropdown()\" ng-blur=\"setBlur();\" ng-class=\"{'active': toggleFlag, 'closed': !toggleFlag, 'large': (dropdownSize === 'large')}\" style=\"width:100%;\" value=\"{{currentSelected.text}}\" ng-show=\"isInputDropdown\" aria-describedby=\"{{dropdownDescribedBy}}\" readonly=\"readonly\">\n" +
15341     "    <button type=\"button\" b2b-dropdown-toggle ng-disabled=\"disabled\" class=\"selectModule\" aria-label=\"{{labelText}} {{currentSelected.label}}\" aria-expanded=\"{{toggleFlag}}\" aria-haspopup=\"true\" ng-click=\"toggleDropdown()\" ng-blur=\"setBlur()\" ng-class=\"{'active opened': toggleFlag, 'closed': !toggleFlag, 'large': (dropdownSize === 'large')}\" ng-bind-html=\"currentSelected.text\" ng-show=\"!isInputDropdown\"></button>\n" +
15342     "    <div ng-class=\"{'selectWrapper': (isInputDropdown), 'moduleWrapper': (!isInputDropdown)}\">\n" +
15343     "        <ul id=\"listbox{{$id}}\" role=\"{{isInputDropdown?'listbox':'menu'}}\" ng-class=\"{'awd-select-list': (isInputDropdown), 'awd-module-list': (!isInputDropdown)}\" tabindex=\"-1\" ng-show=\"toggleFlag\" aria-label=\"Choose options\"></ul>\n" +
15344     "        <ul class=\"module-optinalcta\" ng-if=\"toggleFlag && optionalCta\" tabindex=\"-1\" aria-hidden=\"false\">\n" +
15345     "            <li class=\"module-list-item\" tabindex=\"-1\" role=\"menuitem\" value=\"\" aria-label=\"Optinal CTA\" aria-selected=\"true\">\n" +
15346     "                <span class=\"module-data\" b2b-append-element=\"optionalCta\"></span>\n" +
15347     "            </li>\n" +
15348     "        </ul>\n" +
15349     "</div>\n" +
15350     "<i class=\"icon-primary-down\" aria-hidden=\"true\"></i>\n" +
15351     "</span>");
15352 }]);
15353
15354 angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$templateCache", function($templateCache) {
15355   $templateCache.put("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html",
15356     "<li b2b-dropdown-group-desktop class=\"optgroup-wrapper\">{{groupHeader}}\n" +
15357     "    <ul class=\"optgroup\" role=\"group\" aria-label=\"{{groupHeader}}\"></ul>\n" +
15358     "</li>");
15359 }]);
15360
15361 angular.module("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", []).run(["$templateCache", function($templateCache) {
15362   $templateCache.put("b2bTemplate/dropdowns/b2bDropdownListDesktop.html",
15363     "<li b2b-dropdown-list-desktop b2b-key-item b2b-accessibility-click=\"13\" aria-selected=\"{{currentSelected.value === dropdownListValue}}\" data-hover=\"{{dropdown.highlightedValue === dropdownListValue}}\" ng-class=\"{'awd-select-list-item': (isInputDropdown), 'module-list-item': (!isInputDropdown)}\" tabindex=\"0\" role=\"{{isInputDropdown?'option':'menuitem'}}\" ng-click=\"selectDropdownItem()\" ng-focus=\"highlightDropdown()\"></li>");
15364 }]);
15365
15366 angular.module("b2bTemplate/fileUpload/fileUpload.html", []).run(["$templateCache", function($templateCache) {
15367   $templateCache.put("b2bTemplate/fileUpload/fileUpload.html",
15368     "<label class=\"b2b-file-container\">\n" +
15369     "   <span class=\"b2b-upload-link\" ng-transclude></span>\n" +
15370     "   <input type=\"file\" b2b-file-change>\n" +
15371     "</label>");
15372 }]);
15373
15374 angular.module("b2bTemplate/flyout/flyout.html", []).run(["$templateCache", function($templateCache) {
15375   $templateCache.put("b2bTemplate/flyout/flyout.html",
15376     "<span class=\"b2b-flyout\"  b2b-flyout-trap-focus-inside>\n" +
15377     "    <span ng-transclude></span>\n" +
15378     "</span>");
15379 }]);
15380
15381 angular.module("b2bTemplate/flyout/flyoutContent.html", []).run(["$templateCache", function($templateCache) {
15382   $templateCache.put("b2bTemplate/flyout/flyoutContent.html",
15383     "<div class=\"b2b-flyout-container\" aria-live=\"polite\" aria-atomic=\"false\" ng-class=\"{'b2b-flyout-left':horizontalPlacement==='left',\n" +
15384     "                'b2b-flyout-center':horizontalPlacement==='center', \n" +
15385     "                'b2b-flyout-right':horizontalPlacement==='right',\n" +
15386     "                'b2b-flyout-centerLeft':horizontalPlacement==='centerLeft',\n" +
15387     "                'b2b-flyout-centerRight':horizontalPlacement==='centerRight',  \n" +
15388     "                'b2b-flyout-above':verticalPlacement==='above', \n" +
15389     "                'b2b-flyout-below':verticalPlacement==='below',\n" +
15390     "                'open-flyout': openFlyout,\n" +
15391     "                'b2b-close-flyout': !openFlyout}\">\n" +
15392     "    <i class=\"b2b-flyout-caret\" ng-class=\"{'open-flyout': openFlyout, \n" +
15393     "                                   'b2b-flyout-caret-above':verticalPlacement==='above',\n" +
15394     "                                   'b2b-flyout-caret-below':verticalPlacement==='below'}\"></i>\n" +
15395     "    <span ng-transclude></span>\n" +
15396     "</div>");
15397 }]);
15398
15399 angular.module("b2bTemplate/footer/footer_column_switch_tpl.html", []).run(["$templateCache", function($templateCache) {
15400   $templateCache.put("b2bTemplate/footer/footer_column_switch_tpl.html",
15401     "<div class=\"footer-columns \" ng-class=\"{'five-column':footerColumns===5, 'four-column':footerColumns===4, 'three-column':footerColumns===3}\" ng-repeat=\"item in footerLinkItems\">\n" +
15402     "    <h3 class=\"b2b-footer-header\">{{item.categoryName}}</h3>\n" +
15403     "    <ul>\n" +
15404     "        <li ng-repeat=\"i in item.values\">\n" +
15405     "            <a href=\"{{i.href}}\" title=\"{{item.categoryName}} - {{i.displayLink}}\">{{i.displayLink}}</a>  \n" +
15406     "        </li>\n" +
15407     "    </ul>\n" +
15408     "\n" +
15409     "</div>\n" +
15410     "\n" +
15411     "<div ng-transclude></div>\n" +
15412     "");
15413 }]);
15414
15415 angular.module("b2bTemplate/horizontalTable/horizontalTable.html", []).run(["$templateCache", function($templateCache) {
15416   $templateCache.put("b2bTemplate/horizontalTable/horizontalTable.html",
15417     "<div class=\"b2b-horizontal-table\">\n" +
15418     "    <div class=\"b2b-horizontal-table-arrows row span12\">\n" +
15419     "        <div class=\"span4 b2b-prev-link\">\n" +
15420     "            <a href=\"javascript:void(0)\" ng-click=\"moveViewportLeft()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableLeft\">Previous</a>\n" +
15421     "            <span ng-if=\"disableLeft\" class=\"b2b-disabled-text\">Previous</span>\n" +
15422     "        </div>\n" +
15423     "        \n" +
15424     "        <span class=\"span5 b2b-horizontal-table-column-info\" aria-live=\"polite\" tabindex=\"-1\">\n" +
15425     "            Showing {{countDisplayText}} {{getColumnSet()[0]+1}}-{{getColumnSet()[1]+1}} of {{numOfCols}} columns\n" +
15426     "        </span>\n" +
15427     "\n" +
15428     "        <div ng-if=\"legendContent\" class=\"span2 b2b-horizontal-table-legend\">\n" +
15429     "           | <b2b-flyout>\n" +
15430     "                <div tabindex=\"0\" role=\"button\" aria-haspopup=\"true\" b2b-flyout-toggler b2b-accessibility-click=\"13,32\" aria-expanded=\"{{flyoutOpened ? 'true' : 'false'}}\">\n" +
15431     "                    Legend\n" +
15432     "                    <i class=\"icoControls-down\" role=\"img\"></i>\n" +
15433     "                </div>\n" +
15434     "              <b2b-flyout-content horizontal-placement=\"center\" vertical-placement=\"below\">\n" +
15435     "                <div ng-bind-html=\"legendContent\"></div>\n" +
15436     "              </b2b-flyout-content>\n" +
15437     "            </b2b-flyout>\n" +
15438     "        </div>\n" +
15439     "        \n" +
15440     "        <div class=\"span3 text-right b2b-next-link\">\n" +
15441     "            <a href=\"javascript:void(0)\" ng-click=\"moveViewportRight()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableRight\">Next</a>\n" +
15442     "            <span ng-if=\"disableRight\" class=\"b2b-disabled-text\">Next</span>\n" +
15443     "        </div>\n" +
15444     "    </div>\n" +
15445     "    <div class=\"b2b-horizontal-table-inner-container\">\n" +
15446     "        <span ng-transclude></span>\n" +
15447     "    </div>\n" +
15448     "</div>");
15449 }]);
15450
15451 angular.module("b2bTemplate/hourPicker/b2bHourpicker.html", []).run(["$templateCache", function($templateCache) {
15452   $templateCache.put("b2bTemplate/hourPicker/b2bHourpicker.html",
15453     "<div class=\"hp-container\">\n" +
15454     "    <div class=\"hp-selected\">\n" +
15455     "        <div b2b-hourpicker-value=\"$index\" days=\"value.days\" start-time=\"value.startTime\" start-meridiem=\"value.startMeridiem\" end-time=\"value.endTime\" end-meridiem=\"value.endMeridiem\" ng-repeat=\"value in finalHourpickerValues\"></div>\n" +
15456     "    </div>\n" +
15457     "    <div b2b-hourpicker-panel></div>\n" +
15458     "</div>");
15459 }]);
15460
15461 angular.module("b2bTemplate/hourPicker/b2bHourpickerPanel.html", []).run(["$templateCache", function($templateCache) {
15462   $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerPanel.html",
15463     "<form name=\"{{'hourpickerForm' + $id}}\">\n" +
15464     "    <div class=\"hp-checkbox\" role=\"group\">\n" +
15465     "        <label class=\"checkbox\" for=\"checkbox_{{dayOption.title}}_{{$id}}\" ng-repeat=\"dayOption in hourpicker.dayOptions\">\n" +
15466     "            <input type=\"checkbox\" id=\"checkbox_{{dayOption.title}}_{{$id}}\" name=\"{{'hourpickerDays' + $id}}\" ng-model=\"hourpickerPanelValue.days[$index].value\" ng-disabled=\"dayOption.disabled\" /><i class=\"skin\"></i><span>{{dayOption.label}}</span>\n" +
15467     "        </label>\n" +
15468     "    </div>\n" +
15469     "    <div class=\"row hp-dropdowns\">\n" +
15470     "        <div class=\"span4\">\n" +
15471     "            <label for=\"{{'hourpickerStartTime' + $id}}\">From</label>\n" +
15472     "            <select id=\"{{'hourpickerStartTime' + $parent.$id}}\" b2b-dropdown type=\"\" ng-model=\"hourpickerPanelValue.startTime\">\n" +
15473     "                <option b2b-dropdown-list value=\"\">From</option>\n" +
15474     "                <option b2b-dropdown-list option-repeat=\"startTimeOption in hourpicker.startTimeOptions\" value=\"{{startTimeOption}}\">{{startTimeOption}}</option>\n" +
15475     "            </select>\n" +
15476     "        </div>\n" +
15477     "        <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
15478     "            <label class=\"radio\" for=\"hourpickerStartMeridiem_AM_{{$id}}\">\n" +
15479     "                <input type=\"radio\" id=\"hourpickerStartMeridiem_AM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
15480     "            </label>\n" +
15481     "            <label class=\"radio\" for=\"hourpickerStartMeridiem_PM_{{$id}}\">\n" +
15482     "                <input type=\"radio\" id=\"hourpickerStartMeridiem_PM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
15483     "            </label>\n" +
15484     "        </div>\n" +
15485     "    </div>\n" +
15486     "    <div class=\"row hp-dropdowns\">\n" +
15487     "        <div class=\"span4\">\n" +
15488     "            <label for=\"{{'hourpickerEndTime' + $id}}\">To</label>\n" +
15489     "            <select id=\"{{'hourpickerEndTime' + $parent.$id}}\" b2b-dropdown ng-model=\"hourpickerPanelValue.endTime\">\n" +
15490     "                <option b2b-dropdown-list value=\"\">To</option>\n" +
15491     "                <option b2b-dropdown-list option-repeat=\"endTimeOption in hourpicker.endTimeOptions\" value=\"{{endTimeOption}}\">{{endTimeOption}}</option>\n" +
15492     "            </select>\n" +
15493     "        </div>\n" +
15494     "        <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
15495     "            <label class=\"radio\" for=\"hourpickerEndMeridiem_AM_{{$id}}\">\n" +
15496     "                <input type=\"radio\" id=\"hourpickerEndMeridiem_AM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
15497     "            </label>\n" +
15498     "            <label class=\"radio\" for=\"hourpickerEndMeridiem_PM_{{$id}}\">\n" +
15499     "                <input type=\"radio\" id=\"hourpickerEndMeridiem_PM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
15500     "            </label>\n" +
15501     "        </div>\n" +
15502     "    </div>\n" +
15503     "    <div class=\"row hp-buttons\">\n" +
15504     "        <div class=\"span12\">\n" +
15505     "            <div style=\"float:right\">\n" +
15506     "                <button class=\"btn btn-secondary btn-small\" ng-click=\"resetHourpickerPanelValue()\">Clear</button>\n" +
15507     "                <button class=\"btn btn-alt btn-small\" ng-disabled = \"disableAddBtn\" ng-click=\"updateHourpickerValue()\">{{hourpicker.editMode > -1 ? 'Update' : 'Add'}}</button>\n" +
15508     "            </div>\n" +
15509     "        </div>\n" +
15510     "    </div>\n" +
15511     "</form>");
15512 }]);
15513
15514 angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$templateCache", function($templateCache) {
15515   $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerValue.html",
15516     "<div class=\"selected-days\">\n" +
15517     "    <span class=\"day\">{{hourpickerValue.days}} &nbsp; {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}</span>\n" +
15518     "    <span style=\"float:right\">\n" +
15519     "        <i class=\"icon-misc-pen\" role=\"button\" aria-label=\"Edit {{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}\" title=\"Edit\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" ng-click=\"editHourpickerValue(hourpickerValue.index)\"></i>\n" +
15520     "        <i class=\"icon-misc-trash\" role=\"button\" aria-label=\"Delete {{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}\" title=\"Delete\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" ng-click=\"deleteHourpickerValue(hourpickerValue.index)\"></i>\n" +
15521     "    </span>\n" +
15522     "    <div style=\"clear:both\"></div>\n" +
15523     "</div>");
15524 }]);
15525
15526 angular.module("b2bTemplate/leftNavigation/leftNavigation.html", []).run(["$templateCache", function($templateCache) {
15527   $templateCache.put("b2bTemplate/leftNavigation/leftNavigation.html",
15528     "<div class=\"b2b-nav-menu\">\n" +
15529     "    <div class=\"b2b-subnav-container\">\n" +
15530     "        <ul class=\"b2b-subnav-content\">\n" +
15531     "            <li ng-repeat=\"menu in menuData\" ng-click=\"toggleNav($index)\"><a ng-class=\"{'expand': idx==$index}\" aria-label=\"{{menu.name}}\" title=\" \" aria-expanded=\"{{(idx==$index)?true:false;}}\" href=\"javascript:void(0);\">{{menu.name}}<i class=\"b2b-icon-primary-plus-minus\" ng-class=\"idx==$index ? 'icon-primary-expanded' : 'icon-primary-collapsed'\"></i></a>\n" +
15532     "                <ul ng-class=\"{expand: idx==$index}\">\n" +
15533     "                    <li ng-repeat=\"menuItem in menu.menuItems\" ng-click=\"liveLink($event, $index, $parent.$index)\"><a aria-hidden=\"{{!(idx==$parent.$index)}}\" aria-label=\"{{menuItem.name}}\" title=\" \" href=\"{{menuItem.href}}\" tabindex=\"{{(idx==$parent.$index)?0:-1;}}\" ng-class=\"{active: itemIdx==$index && navIdx==$parent.$index}\">{{menuItem.name}}</a></li>\n" +
15534     "                </ul>\n" +
15535     "            </li>\n" +
15536     "        </ul>\n" +
15537     "    </div>\n" +
15538     "</div>");
15539 }]);
15540
15541 angular.module("b2bTemplate/listbox/listbox.html", []).run(["$templateCache", function($templateCache) {
15542   $templateCache.put("b2bTemplate/listbox/listbox.html",
15543     "<div class=\"b2b-list-box\" tabindex=\"0\" role=\"listbox\" ng-transclude>\n" +
15544     "</div>");
15545 }]);
15546
15547 angular.module("b2bTemplate/modalsAndAlerts/b2b-backdrop.html", []).run(["$templateCache", function($templateCache) {
15548   $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-backdrop.html",
15549     "<div class=\"b2b-modal-backdrop fade in hidden-by-modal\" aria-hidden=\"true\" tabindex=\"-1\"></div>");
15550 }]);
15551
15552 angular.module("b2bTemplate/modalsAndAlerts/b2b-window.html", []).run(["$templateCache", function($templateCache) {
15553   $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-window.html",
15554     "<div class=\"modalwrapper active {{windowClass}}\" ng-class=\"{'modal-landscape': isModalLandscape}\" role=\"{{isNotifDialog?'alertdialog':'dialog'}}\" tabindex=\"-1\" aria-labelledby=\"{{title}}\" aria-describedby=\"{{content}}\">\n" +
15555     "    <div class=\"modal fade in {{sizeClass}}\" ng-transclude></div>\n" +
15556     "</div>");
15557 }]);
15558
15559 angular.module("b2bTemplate/monthSelector/monthSelector-popup.html", []).run(["$templateCache", function($templateCache) {
15560   $templateCache.put("b2bTemplate/monthSelector/monthSelector-popup.html",
15561     "<div id=\"monthselector{{$id}}\" class=\"datepicker dropdown-menu monthselector\" \n" +
15562     "     ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" \n" +
15563     "     ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" \n" +
15564     "     style=\"display: block;\" aria-hidden=\"false\" role=\"dialog presentation\" tabindex=\"-1\">\n" +
15565     "    <div class=\"datepicker-days\" style=\"display: block;\">\n" +
15566     "        <span class=\"offscreen-text\" aria-live=\"polite\" aria-atomic=\"true\">{{title}} displaying</span>\n" +
15567     "        <table class=\"table-condensed\" role=\"grid\">\n" +
15568     "            <thead>\n" +
15569     "                <tr ng-repeat=\"header in headers\">\n" +
15570     "                    <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"header\"></th>\n" +
15571     "                </tr>\n" +
15572     "                <tr>\n" +
15573     "                    <th id=\"prev{{$id}}\" role=\"button\" tabindex=\"0\" b2b-accessibility-click=\"13\" aria-label=\"Previous Year\" ng-class=\"{'prev': !disablePrev}\" ng-style=\"{visibility: visibilityPrev}\" ng-click=\"!disablePrev && move(-1,$event)\"><i class=\"icon-primary-left\"></i></th>\n" +
15574     "                    <th id=\"year{{$id}}\" role=\"heading\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch b2b-monthSelector-label\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
15575     "                    <th id=\"next{{$id}}\" role=\"button\" tabindex=\"0\" b2b-accessibility-click=\"13\" aria-label=\"Next Year\" ng-class=\"{'next': !disableNext}\" ng-style=\"{visibility: visibilityNext}\" ng-click=\"!disableNext && move(1,$event)\"><i class=\"icon-primary-right\"></i></th>\n" +
15576     "                </tr>\n" +
15577     "                <tr ng-show=\"labels.length > 0\">\n" +
15578     "                    <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
15579     "                </tr>\n" +
15580     "            </thead>\n" +
15581     "            <tbody b2b-key type=\"table\" columns=\"4\" >\n" +
15582     "                <tr ng-repeat=\"row in rows\">\n" +
15583     "                    <td headers=\"{{dt.header}}\" b2b-key-item=\"dt.focusable\" b2b-accessibility-click=\"13,32\" tabindex=\"{{(!(dt.focusable && (dt.selected || dt.firstFocus || currFocus)) && -1) || 0}}\" aria-hidden=\"{{(!dt.focusable && true) || false}}\" role=\"{{(dt.focusable && 'gridcell') || ''}}\" aria-label=\"{{(dt.focusable && dt.date | date : 'MMMM, y') || ''}}\" aria-selected=\"{{(dt.focusable && (dt.selected || dt.dateRange) && true) || false}}\" ng-repeat=\"dt in row\" class=\"day magic\" ng-class=\"{'active': dt.focusable && dt.selected && !dt.dateRange, 'start-date': dt.focusable && dt.fromDate && dt.dateRange, 'between-date': dt.focusable && !dt.fromDate && !dt.selected && dt.dateRange, 'end-date': dt.focusable && dt.selected && dt.dateRange, 'old': dt.oldMonth, 'new': dt.nextMonth, 'disabled': dt.disabled, 'due-date': dt.dueDate, 'late-fee': dt.pastDue}\" ng-focus=\"currFocus=true\" ng-blur=\"currFocus=false\" title=\"{{(dt.focusable && dt.pastDue && legendMessage) || ''}}\" b2b-element-focus=\"inline && dt.focusable && dt.selected && !dt.dateRange\" ng-click=\"!ngDisabled && !dt.disabled && select(dt.date)\">\n" +
15584     "                        <div aria-hidden=\"true\"  tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label | limitTo: 3}}</div>\n" +
15585     "                    </td>\n" +
15586     "                </tr>\n" +
15587     "            </tbody>\n" +
15588     "            <tfoot>\n" +
15589     "                <tr ng-repeat=\"footer in footers\">\n" +
15590     "                    <th colspan=\"7\" class=\"text-left\" b2b-append-element=\"footer\"></th>\n" +
15591     "                </tr>\n" +
15592     "            </tfoot>\n" +
15593     "        </table>\n" +
15594     "    </div>\n" +
15595     "</div>");
15596 }]);
15597
15598 angular.module("b2bTemplate/monthSelector/monthSelector.html", []).run(["$templateCache", function($templateCache) {
15599   $templateCache.put("b2bTemplate/monthSelector/monthSelector.html",
15600     "<div>\n" +
15601     "    <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
15602     "</div>");
15603 }]);
15604
15605 angular.module("b2bTemplate/monthSelector/monthSelectorLink.html", []).run(["$templateCache", function($templateCache) {
15606   $templateCache.put("b2bTemplate/monthSelector/monthSelectorLink.html",
15607     "<div>\n" +
15608     "    <span class=\"span12\" ng-transclude></span>\n" +
15609     "</div>");
15610 }]);
15611
15612 angular.module("b2bTemplate/pagination/b2b-pagination.html", []).run(["$templateCache", function($templateCache) {
15613   $templateCache.put("b2bTemplate/pagination/b2b-pagination.html",
15614     "<div class=\"b2b-pager\">\n" +
15615     "    <div ng-if=\"notMobile && totalPages > 1\">\n" +
15616     "        <a  tabindex=\"{{currentPage <= 1 ? -1 : 0 }}\" class=\"b2b-pager__item--prev\" b2b-accessibility-click=\"13,32\" title=\"Previous Page\" ng-click=\"prev($event)\" ng-if=\"totalPages > 10\" ng-class=\"currentPage <= 1 ? 'b2b-pager__item--prev-disabled': '' \">\n" +
15617     "            <i class=\"icon-primary-left\"></i>\n" +
15618     "        </a>\n" +
15619     "        <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{currentPage === 1 ? -1 : 0}}\" ng-class=\"{'b2b-pager__item--noclick': currentPage === 1}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page 1{{checkSelectedPage(page) ? ' selected' : '' }}\" ng-if=\"totalPages > 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(1, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
15620     "            1<span class=\"offscreen-text\" ng-if=\"currentPage === 1\"> is selected</span>\n" +
15621     "        </a>\n" +
15622     "\n" +
15623     "        <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage > 6\">...</span>\n" +
15624     "\n" +
15625     "        <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0);\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{page}}\" b2b-element-focus=\"isFocused(page)\" ng-repeat=\"page in pages\" ng-class=\"{'b2b-pager__item--active': checkSelectedPage(page), 'b2b-pager__item--noclick': checkSelectedPage(page),'b2b-pager__item--droppable': droppableAttribute == true}\" b2b-accessibility-click=\"13,32\" ng-click=\"!checkSelectedPage(page) && selectPage(page, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
15626     "            {{page}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span>\n" +
15627     "        </a>\n" +
15628     "\n" +
15629     "        <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage <= (totalPages - boundary)\">...</span>\n" +
15630     "\n" +
15631     "        <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{currentPage === totalPages ? -1 : 0}}\" href=\"javascript:void(0);\" ng-class=\"{'b2b-pager__item--noclick': currentPage === totalPages}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{totalPages}}\" ng-if=\"totalPages > 10 && currentPage <= (totalPages - boundary)\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
15632     "          {{totalPages}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span>\n" +
15633     "        </a>\n" +
15634     "\n" +
15635     "\n" +
15636     "        <a tabindex=\"{{currentPage >= totalPages ? -1 : 0 }}\" href=\"javascript:void(0);\" class=\"b2b-pager__item--next\" b2b-accessibility-click=\"13,32\" title=\"Next Page\" ng-click=\"next($event)\" ng-if=\"totalPages > 10\" ng-class=\"currentPage >= totalPages ? 'b2b-pager__item--next-disabled' :'' \">\n" +
15637     "            <i class=\"icon-primary-right\"></i>\n" +
15638     "        </a>\n" +
15639     "        \n" +
15640     "        <div class=\"fieldLabel b2b-go-to-page\" ng-class=\"{'b2b-go-to-page-inline' : inputClass !== undefined }\" ng-show=\"totalPages > 20\">    \n" +
15641     "            <label for=\"{{inputId}}\">Go to Page:</label>\n" +
15642     "            <input id=\"{{inputId}}\" class=\"b2b-pager__item--input\" type=\"number\" ng-blur=\"onfocusOut($event)\" ng-focus=\"onfocusIn($event)\" ng-keypress=\"gotoKeyClick($event)\" b2b-only-nums=\"4\" ng-model=\"$parent.gotoPage\" />\n" +
15643     "            <button class=\"btn-arrow\" ng-click=\"gotoBtnClick()\" ng-disabled=\"$parent.gotoPage !== 0 || $parent.gotoPage !== undefined\" aria-label=\"Go to\">\n" +
15644     "                <div class=\"btn btn-small btn-secondary\">\n" +
15645     "                    <i class=\"icon-primary-right\"></i>\n" +
15646     "                </div>\n" +
15647     "            </button>\n" +
15648     "        </div>\n" +
15649     "    </div>\n" +
15650     "    <div ng-if=\"isMobile && totalPages > 1\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\" ng-class=\"isMobile ? 'b2b-mobile-view': '' \">\n" +
15651     "        <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0);\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{page}}\" b2b-element-focus=\"isFocused(page)\" ng-repeat=\"page in pages\" ng-class=\"{'b2b-pager__item--active': checkSelectedPage(page), fade1: ($index == 0 && currentPage > meanVal+1),  fade2: ($index == 1 && currentPage > meanVal+1), fadesl: ($index == pages.length-2 && currentPage < totalPages - meanVal),  fadel: ($last && currentPage < totalPages - meanVal), 'b2b-pager__item--noclick': checkSelectedPage(page)}\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(page, $event)\">{{page}}</a>\n" +
15652     "    </div>\n" +
15653     "</div>\n" +
15654     "");
15655 }]);
15656
15657 angular.module("b2bTemplate/paneSelector/paneSelector.html", []).run(["$templateCache", function($templateCache) {
15658   $templateCache.put("b2bTemplate/paneSelector/paneSelector.html",
15659     "<div class=\"panes\" ng-transclude></div>");
15660 }]);
15661
15662 angular.module("b2bTemplate/paneSelector/paneSelectorPane.html", []).run(["$templateCache", function($templateCache) {
15663   $templateCache.put("b2bTemplate/paneSelector/paneSelectorPane.html",
15664     "<div class=\"pane-block\" ng-transclude></div>");
15665 }]);
15666
15667 angular.module("b2bTemplate/profileCard/profileCard-addUser.html", []).run(["$templateCache", function($templateCache) {
15668   $templateCache.put("b2bTemplate/profileCard/profileCard-addUser.html",
15669     "<div  class=\"span3 b2b-profile-card b2b-add-user\">\n" +
15670     "    <div class=\"atcenter\">\n" +
15671     "        <div class=\"circle\"><i class=\"icon-primary-add-maximize\"></i></div>\n" +
15672     "        <div>Create new user</div>\n" +
15673     "    </div>\n" +
15674     "</div>");
15675 }]);
15676
15677 angular.module("b2bTemplate/profileCard/profileCard.html", []).run(["$templateCache", function($templateCache) {
15678   $templateCache.put("b2bTemplate/profileCard/profileCard.html",
15679     "<div class=\"span3 b2b-profile-card\">\n" +
15680     "    <div class=\"top-block\">\n" +
15681     "       <div class=\"profile-image\">\n" +
15682     "            <img ng-if=\"image\" profile-name=\"{{profile.name}}\" ng-src=\"{{profile.img}}\" alt=\"{{profile.name}}\">\n" +
15683     "            <span ng-if=\"!image\" class=\"default-img\">{{initials}}</span>\n" +
15684     "\n" +
15685     "            <h4 class=\"name\">{{profile.name}}</h4>\n" +
15686     "\n" +
15687     "            <p class=\"status\">\n" +
15688     "                <span class=\"status-icon\" ng-class=\"{'status-green':colorIcon==='green','status-red':colorIcon==='red','status-blue':colorIcon==='blue','status-yellow':colorIcon==='yellow'}\">   \n" +
15689     "                </span>\n" +
15690     "                <span>{{profile.state}}<span ng-if=\"badge\" class=\"status-badge\">Admin</span></span>\n" +
15691     "            </p>\n" +
15692     "        </div>\n" +
15693     "    </div>\n" +
15694     "    <div class=\"bottom-block\">\n" +
15695     "         <div class=\"profile-details\">\n" +
15696     "            <label>Username</label>\n" +
15697     "            <div ng-if=\"shouldClip(profile.userName)\" ng-mouseover=\"showUserNameTooltip = true;\" ng-mouseleave=\"showUserNameTooltip = false;\">\n" +
15698     "                <div ng-if=\"shouldClip(profile.userName)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showUserNameTooltip\">\n" +
15699     "                    {{profile.userName.slice(0, 25)+'...'}}\n" +
15700     "                    <div class=\"arrow\"></div>\n" +
15701     "                    <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15702     "                        <div class=\"tooltip-size-control\">\n" +
15703     "                            <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15704     "                                {{profile.userName}}\n" +
15705     "                            </div>\n" +
15706     "                        </div>\n" +
15707     "                    </div>\n" +
15708     "                </div>\n" +
15709     "            </div>\n" +
15710     "            <div ng-if=\"!shouldClip(profile.userName)\">\n" +
15711     "                {{profile.userName}}\n" +
15712     "            </div>\n" +
15713     "            <label>Email</label>\n" +
15714     "            <div ng-if=\"shouldClip(profile.email)\" ng-mouseover=\"showEmailTooltip = true;\" ng-mouseleave=\"showEmailTooltip = false;\">\n" +
15715     "                <div ng-if=\"shouldClip(profile.email)\" class=\"tooltip\" data-placement=\"bottom\" b2b-tooltip show-tooltip=\"showEmailTooltip\">\n" +
15716     "                    {{profile.email.slice(0, 25)+'...'}}\n" +
15717     "                    <div class=\"arrow\"></div>\n" +
15718     "                    <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15719     "                        <div class=\"tooltip-size-control\">\n" +
15720     "                            <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15721     "                                {{profile.email}}\n" +
15722     "                            </div>\n" +
15723     "                        </div>\n" +
15724     "                    </div>\n" +
15725     "                </div>\n" +
15726     "            </div>\n" +
15727     "            <div ng-if=\"!shouldClip(profile.email)\">\n" +
15728     "                {{profile.email}}\n" +
15729     "            </div>\n" +
15730     "            <label>Role</label>\n" +
15731     "            <div ng-if=\"shouldClip(profile.role)\" ng-mouseover=\"showRoleTooltip = true;\" ng-mouseleave=\"showRoleTooltip = false;\">\n" +
15732     "                <div ng-if=\"shouldClip(profile.role)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showRoleTooltip\">\n" +
15733     "                    {{profile.role.slice(0, 25)+'...'}}\n" +
15734     "                    <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15735     "                        <div class=\"tooltip-size-control\">\n" +
15736     "                            <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15737     "                                {{profile.role}}\n" +
15738     "                            </div>\n" +
15739     "                        </div>\n" +
15740     "                    </div>\n" +
15741     "                </div>\n" +
15742     "            </div>\n" +
15743     "            <div ng-if=\"!shouldClip(profile.role)\">\n" +
15744     "                {{profile.role}}\n" +
15745     "            </div>\n" +
15746     "            <label>Last login</label>\n" +
15747     "            <div ng-if=\"shouldClip(profile.lastLogin)\" ng-mouseover=\"showLastLoginTooltip = true;\" ng-mouseleave=\"showLastLoginTooltip = false;\">\n" +
15748     "                <div ng-if=\"shouldClip(profile.lastLogin)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showLastLoginTooltip\">\n" +
15749     "                    {{profile.lastLogin.slice(0, 25)+'...'}}\n" +
15750     "                    <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15751     "                        <div class=\"tooltip-size-control\">\n" +
15752     "                            <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15753     "                                {{profile.lastLogin}}\n" +
15754     "                            </div>\n" +
15755     "                        </div>\n" +
15756     "                    </div>\n" +
15757     "                </div>\n" +
15758     "            </div>\n" +
15759     "            <div ng-if=\"!shouldClip(profile.lastLogin)\">\n" +
15760     "                {{profile.lastLogin}}\n" +
15761     "            </div>\n" +
15762     "         </div>\n" +
15763     "    </div>\n" +
15764     "</div>");
15765 }]);
15766
15767 angular.module("b2bTemplate/searchField/searchField.html", []).run(["$templateCache", function($templateCache) {
15768   $templateCache.put("b2bTemplate/searchField/searchField.html",
15769     "<div class=\"search-bar\">\n" +
15770     "    <div class='input-container' ng-blur=\"blurInput()\">\n" +
15771     "        <input type=\"text\" class=\"innershadow b2b-search-input-field\" id=\"{{configObj.labelId}}\" b2b-search-input ng-model=\"inputModel\" ng-disabled=\"disabled\" b2b-reset ng-keydown=\"selectionIndex($event)\" placeholder=\"{{configObj.ghostText}}\" style=\"width:100%\" maxlength=\"{{configObj.maxLength}}\" title=\"{{inputModel}}\" aria-label=\"{{inputModel.length>0?inputModel:configObj.ghostText}}\" />\n" +
15772     "            <button class=\"btn-search\" ng-disabled=\"disabled\" ng-click=\"startSearch()\" b2b-accessibility-click=\"13,32\" aria-label=\"Search\" ng-focus=\"showListFlag = false\" type=\"button\"><i class=\"icoControls-magnifyingglass\"></i></button>\n" +
15773     "    </div>\n" +
15774     "    <div class=\"search-suggestion-wrapper\" ng-if=\"filterList.length >=0\" ng-show=\"showListFlag\">\n" +
15775     "        <ul class=\"search-suggestion-list\" role=\"listbox\">      \n" +
15776     "            <li class=\"no-result\" ng-if=\"filterList.length == 0 && configObj.display\">{{configObj.resultText}}</li>\n" +
15777     "            <li class=\"search-suggestion-item\" ng-if=\"filterList.length\" ng-repeat=\"item in filterList | limitTo:configObj.noOfItemsDisplay track by $index\" ng-bind-html=\"item.title | b2bMultiSepartorHighlight:inputModel:configObj.searchSeperator\" ng-class=\"{active:isActive($index,filterList.length)}\" ng-click=\"selectItem($index,item.title)\">\n" +
15778     "                {{item.title}}     \n" +
15779     "            </li>\n" +
15780     "        </ul>\n" +
15781     "    </div>\n" +
15782     "</div>");
15783 }]);
15784
15785 angular.module("b2bTemplate/seekBar/seekBar.html", []).run(["$templateCache", function($templateCache) {
15786   $templateCache.put("b2bTemplate/seekBar/seekBar.html",
15787     "<div class=\"b2b-seek-bar-container\" ng-class=\"{vertical:verticalSeekBar}\">\n" +
15788     "    <div class=\"b2b-seek-bar-track-container\">\n" +
15789     "        <div class=\"b2b-seek-bar-track\"></div>\n" +
15790     "        <div class=\"b2b-seek-bar-track-fill\"></div>\n" +
15791     "    </div>\n" +
15792     "    <div class=\"b2b-seek-bar-knob-container\" role=\"menu\"  >\n" +
15793     "        <div class=\"b2b-seek-bar-knob\" tabindex=\"0\" role=\"menuitem\"></div>\n" +
15794     "    </div>\n" +
15795     "</div>");
15796 }]);
15797
15798 angular.module("b2bTemplate/slider/slider.html", []).run(["$templateCache", function($templateCache) {
15799   $templateCache.put("b2bTemplate/slider/slider.html",
15800     "<div class=\"b2b-slider-container\" ng-class=\"{'vertical':verticalSlider}\">\n" +
15801     "    <div class=\"slider-track-container\">\n" +
15802     "        <div class=\"slider-track\"></div>\n" +
15803     "        <div class=\"slider-track-fill\" ng-style=\"{backgroundColor:trackFillColor}\"></div>\n" +
15804     "    </div>\n" +
15805     "    <div class=\"slider-knob-container\" ng-class=\"{'slider-knob-hidden':hideKnob}\">\n" +
15806     "        <div class=\"slider-knob\" role=\"slider\" aria-valuemin=\"{{min}}\" aria-valuemax=\"{{max}}\" aria-labelledby=\"{{labelId}}\" aria-valuenow=\"{{currentModelValue}}\" aria-valuetext=\"{{currentModelValue}}{{postAriaLabel}}\" aria-orientation=\"{{verticalSlider ? 'vertical' : 'horizontal'}}\"></div>\n" +
15807     "    </div>\n" +
15808     "</div>");
15809 }]);
15810
15811 angular.module("b2bTemplate/spinButton/spinButton.html", []).run(["$templateCache", function($templateCache) {
15812   $templateCache.put("b2bTemplate/spinButton/spinButton.html",
15813     "<div class=\"btn-group btn-spinbutton-toggle\" ng-class=\"{'disabled': disabledFlag}\">\n" +
15814     "    <button type=\"button\" tabindex=\"{{isMobile?'0':'-1'}}\" aria-hidden=\"{{notMobile}}\" class=\"btn btn-secondary btn-prev icon-primary-subtractminimize\" ng-click=\"minus();focusInputSpinButton($event)\" aria-label=\"Remove {{step}}\" aria-controls=\"{{spinButtonId}}\" ng-class=\"{'disabled': isMinusDisabled()}\" ng-disabled=\"isMinusDisabled()\" aria-disabled=\"{{isMinusDisabled()}}\" role=\"spinbutton\"></button>\n" +
15815     "    <input class=\"btn pull-left\" id=\"{{spinButtonId}}\" type=\"tel\" b2b-only-nums=\"3\" maxlength=\"3\" min={{min}} max={{max}} data-max-value=\"{{max}}\" ng-model=\"inputValue[inputModelKey]\" value=\"{{inputValue[inputModelKey]}}\" aria-live=\"polite\" aria-valuenow=\"{{inputValue[inputModelKey]}}\" aria-valuemin=\"{{min}}\" aria-valuemax=\"{{max}}\" ng-disabled=\"disabledFlag\">\n" +
15816     "    <button type=\"button\" tabindex=\"{{isMobile?'0':'-1'}}\" aria-hidden=\"{{notMobile}}\" class=\"btn btn-secondary btn-next icon-primary-add-maximize\" ng-click=\"plus();focusInputSpinButton($event)\" aria-label=\"Add {{step}}\" aria-controls=\"{{spinButtonId}}\" ng-class=\"{'disabled': isPlusDisabled()}\" ng-disabled=\"isPlusDisabled()\" aria-disabled=\"{{isPlusDisabled()}}\" role=\"spinbutton\"></button>\n" +
15817     "</div>");
15818 }]);
15819
15820 angular.module("b2bTemplate/statusTracker/statusTracker.html", []).run(["$templateCache", function($templateCache) {
15821   $templateCache.put("b2bTemplate/statusTracker/statusTracker.html",
15822     "<div class=\"b2b-status-tracker row\">\n" +
15823     "   <button tabindex=\"0\" ng-disabled=\"currentViewIndex === 0\" ng-if=\"statuses.length > b2bStatusTrackerConfig.maxViewItems\" class=\"btn-arrow\" type=\"button\" aria-label=\"Previous status\" ng-click=\"previousStatus()\">\n" +
15824     "       <div class=\"btn btn-small btn-secondary\">\n" +
15825     "           <i class=\"icon-primary-left\"></i>\n" +
15826     "       </div>\n" +
15827     "   </button>\n" +
15828     "   <div ng-repeat=\"status in statuses\" class=\"b2b-status-tracker-step {{ status.state }}\" ng-show=\"isInViewport($index)\">\n" +
15829     "       <p class=\"b2b-status-tracker-heading\">{{status.heading}}</p>\n" +
15830     "       <div class=\"progress\">\n" +
15831     "           <div class=\"progress-bar\">\n" +
15832     "                   <span class=\"hidden-spoken\">\n" +
15833     "                   {{ removeCamelCase(status.state) }}\n" +
15834     "                   </span>\n" +
15835     "           </div>\n" +
15836     "       </div>\n" +
15837     "       <div class=\"b2b-status-tracker-estimate {{status.state}}\">\n" +
15838     "           <i ng-show=\"status.estimate !== '' && status.estimate !== undefined\" ng-class=\"b2bStatusTrackerConfig.icons[status.state]\"></i>\n" +
15839     "           &nbsp;\n" +
15840     "           <span ng-bind-html=\"status.estimate\"></span>\n" +
15841     "       </div>\n" +
15842     "       \n" +
15843     "       <div class=\"b2b-status-tracker-description\" ng-bind-html=\"status.description\"> \n" +
15844     "       </div>\n" +
15845     "   </div>\n" +
15846     "   <button tabindex=\"0\" ng-disabled=\"currentViewIndex + b2bStatusTrackerConfig.maxViewItems === statuses.length\" ng-if=\"statuses.length > b2bStatusTrackerConfig.maxViewItems\" class=\"btn-arrow\" type=\"button\" aria-label=\"Next status\" ng-click=\"nextStatus()\">\n" +
15847     "       <div class=\"btn btn-small btn-secondary\">\n" +
15848     "           <i class=\"icon-primary-right\"></i>\n" +
15849     "       </div>\n" +
15850     "   </button>\n" +
15851     "</div>");
15852 }]);
15853
15854 angular.module("b2bTemplate/stepTracker/stepTracker.html", []).run(["$templateCache", function($templateCache) {
15855   $templateCache.put("b2bTemplate/stepTracker/stepTracker.html",
15856     "<div class=\"b2b-step-tracker\">\n" +
15857     "    <button class=\"btn-arrow b2b-left-arrow\" ng-click=\"previousStatus()\" ng-disabled=\"currentViewIndex === 0\" ng-if=\"stepsItemsObject.length > b2bStepTrackerConfig.maxViewItems\" aria-label=\"Previous step\">\n" +
15858     "           <div class=\"btn btn-left btn-small btn-secondary\"><i class=\"icon-primary-left\"></i></div>\n" +
15859     "   </button>\n" +
15860     "    <button class=\"btn-arrow b2b-right-arrow\" ng-click=\"nextStatus()\" ng-disabled=\"currentViewIndex + b2bStepTrackerConfig.maxViewItems === stepsItemsObject.length\" ng-if=\"stepsItemsObject.length > b2bStepTrackerConfig.maxViewItems\" aria-label=\"Next step\">\n" +
15861     "           <div class=\"btn btn-small btn-right btn-secondary\"><i class=\"icon-primary-right\"></i></div>\n" +
15862     "   </button>\n" +
15863     "    <ul class=\"b2b-steps\">\n" +
15864     "        <li ng-class=\"{'b2b-step-done':$index < currentIndex - 1 ,'b2b-step-on':$index == currentIndex - 1}\" \n" +
15865     "            ng-repeat=\"stepsItem in stepsItemsObject\" ng-show=\"isInViewport($index)\">\n" +
15866     "                   <p class=\"b2b-step-text\" data-sm-text=\"{{stepsItem.dataMobile}}\" data-large-text=\"{{stepsItem.dataDesktop}}\">{{stepsItem.text}}</p>\n" +
15867     "            <span class=\"hidden-spoken\">\n" +
15868     "                {{($index < currentIndex - 1)? 'Complete. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
15869     "                {{($index == currentIndex - 1)? 'In Progress. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
15870     "                {{($index > currentIndex - 1)? 'Incomplete. '+stepsItem.text+' '+stepsItem.dataMobile:''}}\n" +
15871     "            </span>\n" +
15872     "        </li>\n" +
15873     "    </ul>\n" +
15874     "</div>");
15875 }]);
15876
15877 angular.module("b2bTemplate/switches/switches-spanish.html", []).run(["$templateCache", function($templateCache) {
15878   $templateCache.put("b2bTemplate/switches/switches-spanish.html",
15879     "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
15880     "    <span class=\"btn-slider-on\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"activo\">activo</i></span>\n" +
15881     "    <span class=\"switch-handle\"></span>\n" +
15882     "    <span class=\"btn-slider-off\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"inactivo\">inactivo</i></span>\n" +
15883     "</div>");
15884 }]);
15885
15886 angular.module("b2bTemplate/switches/switches.html", []).run(["$templateCache", function($templateCache) {
15887   $templateCache.put("b2bTemplate/switches/switches.html",
15888     "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
15889     "    <span class=\"btn-slider-on\">On</span>\n" +
15890     "    <span class=\"switch-handle\"></span>\n" +
15891     "    <span class=\"btn-slider-off\">Off</span>\n" +
15892     "</div>");
15893 }]);
15894
15895 angular.module("b2bTemplate/tableMessages/tableMessage.html", []).run(["$templateCache", function($templateCache) {
15896   $templateCache.put("b2bTemplate/tableMessages/tableMessage.html",
15897     "<div class=\"b2b-table-message\">\n" +
15898     "    <div class=\"b2b-message\" ng-if=\"msgType === 'noMatchingResults'\">\n" +
15899     "        <div class=\"b2b-magnify-glass\"></div>\n" +
15900     "        <div>\n" +
15901     "            <div ng-transclude></div>\n" +
15902     "        </div>\n" +
15903     "    </div>\n" +
15904     "    <div class=\"b2b-message\" ng-if=\"msgType == 'infoCouldNotLoad'\">\n" +
15905     "        <div class=\"icon-primary-alert b2b-alert\" aria-label=\"Oops! The information could not load at this time. Please click link to refresh the page.\"></div>\n" +
15906     "        <div>Oops!</div>\n" +
15907     "        <div>The information could not load at this time.</div>\n" +
15908     "        <div>Please <a href=\"javascript:void(0)\" title=\"Refresh the page\" ng-click=\"refreshAction($event)\">refresh the page</a>\n" +
15909     "        </div>\n" +
15910     "    </div>\n" +
15911     "    <div class=\"b2b-message\" ng-if=\"msgType == 'magnifySearch'\">\n" +
15912     "        <div class=\"b2b-magnify-glass\"></div>\n" +
15913     "        <div>\n" +
15914     "            <p class=\"b2b-message-title\">Please input values to\n" +
15915     "                <br/> begin your search.</p>\n" +
15916     "        </div>\n" +
15917     "    </div>\n" +
15918     "    <div class=\"b2b-message\" ng-if=\"msgType === 'loadingTable'\">\n" +
15919     "        <div class=\"icon-primary-spinner-ddh b2b-loading-dots\"></div>\n" +
15920     "        <div ng-transclude></div>\n" +
15921     "    </div>\n" +
15922     "</div>\n" +
15923     "");
15924 }]);
15925
15926 angular.module("b2bTemplate/tableScrollbar/tableScrollbar.html", []).run(["$templateCache", function($templateCache) {
15927   $templateCache.put("b2bTemplate/tableScrollbar/tableScrollbar.html",
15928     "<div class=\"b2b-table-scrollbar\">\n" +
15929     "    <div class=\"b2b-scrollbar-arrows\">\n" +
15930     "        <button class=\"btn-arrow  b2b-scrollbar-arrow-left\" type=\"button\" ng-attr-aria-label=\"{{disableLeft ? 'Scroll Left Disabled' : 'Scroll Left'}}\" ng-click=\"scrollLeft()\" ng-disabled=\"disableLeft\"><div class=\"btn btn-alt\"><i class=\"icon-primary-left\"></i></div></button>\n" +
15931     "        <button class=\"btn-arrow b2b-scrollbar-arrow-right\" ng-attr-aria-label=\"{{disableRight ? 'Scroll Right Disabled' : 'Scroll Right'}}\" ng-click=\"scrollRight()\" ng-disabled=\"disableRight\" type=\"button\"><div class=\"btn btn-alt\"><i class=\"icon-primary-right\"></i></div></button>\n" +
15932     "    </div>\n" +
15933     "    <div class=\"b2b-table-inner-container\">\n" +
15934     "        <span ng-transclude></span>\n" +
15935     "    </div>\n" +
15936     "</div>");
15937 }]);
15938
15939 angular.module("b2bTemplate/tables/b2bTable.html", []).run(["$templateCache", function($templateCache) {
15940   $templateCache.put("b2bTemplate/tables/b2bTable.html",
15941     "<table ng-class=\"{'striped': responsive, 'complex-table': responsive && active}\" ng-transclude></table>");
15942 }]);
15943
15944 angular.module("b2bTemplate/tables/b2bTableBody.html", []).run(["$templateCache", function($templateCache) {
15945   $templateCache.put("b2bTemplate/tables/b2bTableBody.html",
15946     "<td ng-hide=\"isHidden()\" ng-transclude></td>");
15947 }]);
15948
15949 angular.module("b2bTemplate/tables/b2bTableHeaderSortable.html", []).run(["$templateCache", function($templateCache) {
15950   $templateCache.put("b2bTemplate/tables/b2bTableHeaderSortable.html",
15951     "<th scope=\"col\" role=\"columnheader\" aria-sort=\"{{sortPattern !== 'null' && 'none' || sortPattern}}\" aria-label=\"{{headerName}} {{sortable !== 'false' && ': activate to sort' || ' '}} {{sortPattern !== 'null' && '' || sortPattern}}\" tabindex=\"{{sortable !== 'false' && '0' || '-1'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"(sortable !== 'false') && sort();\" ng-hide=\"isHidden()\">\n" +
15952     "    <span ng-transclude></span>\n" +
15953     "    <i ng-class=\"{'icoArrows-sort-arrow active': sortPattern === 'ascending', 'icoArrows-sort-arrow active down': sortPattern === 'descending'}\"></i>\n" +
15954     "</th>");
15955 }]);
15956
15957 angular.module("b2bTemplate/tables/b2bTableHeaderUnsortable.html", []).run(["$templateCache", function($templateCache) {
15958   $templateCache.put("b2bTemplate/tables/b2bTableHeaderUnsortable.html",
15959     "<th scope=\"col\" ng-hide=\"isHidden()\" ng-transclude></th>");
15960 }]);
15961
15962 angular.module("b2bTemplate/tabs/b2bTab.html", []).run(["$templateCache", function($templateCache) {
15963   $templateCache.put("b2bTemplate/tabs/b2bTab.html",
15964     "<li role=\"tab\" aria-selected=\"{{isTabActive()}}\" aria-controls=\"{{tabPanelId}}\" class=\"tab\" \n" +
15965     "    ng-class=\"{'active': isTabActive()}\" ng-click=\"clickTab()\" ng-hide=\"tabItem.disabled\">\n" +
15966     "    <a href=\"javascript:void(0)\"  tabindex=\"{{(isTabActive() && tabItem.disabled)?-1:0}}\"\n" +
15967     "       ng-disabled=\"tabItem.disabled\" aria-expanded=\"{{(isTabActive() && !tabItem.disabled)}}\" \n" +
15968     "       b2b-accessibility-click=\"13,32\" ng-transclude></a>\n" +
15969     "    <span class=\"hidden-spoken\" ng-if=\"isTabActive()\">Active tab</span>\n" +
15970     "</li>");
15971 }]);
15972
15973 angular.module("b2bTemplate/tabs/b2bTabset.html", []).run(["$templateCache", function($templateCache) {
15974   $templateCache.put("b2bTemplate/tabs/b2bTabset.html",
15975     "<ul class=\"tabs promo-tabs\" role=\"tablist\" ng-transclude></ul>");
15976 }]);
15977
15978 angular.module("b2bTemplate/treeNav/groupedTree.html", []).run(["$templateCache", function($templateCache) {
15979   $templateCache.put("b2bTemplate/treeNav/groupedTree.html",
15980     "<ul role=\"group\">\n" +
15981     "    <li aria-expanded=\"{{(member.active?true:false)}}\" role=\"treeitem\" aria-label=\"{{key}}\" ng-repeat='(key,value) in collection | groupBy : \"grpChild\"' b2b-tree-link><a class=\"grp\" ng-class=\"{'active': value.showGroup == true}\" tabindex=\"-1\" href='javascript:void(0);'>{{(key)?key:''}}<span class=\"b2b-tree-node-icon\"><i ng-class=\"{'icon-primary-expanded': value.showGroup == true, 'icon-primary-collapsed': value.showGroup == undefined || value.showGroup == false }\"></i></span></a>\n" +
15982     "        <ul role=\"group\">\n" +
15983     "            <b2b-member ng-repeat='member in value.childArray' member='member' time-delay='{{timeDelay}}' group-it='groupIt'></b2b-member>\n" +
15984     "        </ul>\n" +
15985     "    </li>\n" +
15986     "</ul>");
15987 }]);
15988
15989 angular.module("b2bTemplate/treeNav/treeMember.html", []).run(["$templateCache", function($templateCache) {
15990   $templateCache.put("b2bTemplate/treeNav/treeMember.html",
15991     "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.tooltipContent ? member.tooltipContent : member.name}}\" aria-describedby=\"description_{{$id}}\" ng-class=\"{'bg':member.selected}\"  b2b-tree-link>\n" +
15992     "    <a tabindex=\"-1\" title=\"{{member.tooltipContent ? member.tooltipContent : member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active,'b2b-locked-node':member.locked}\">           <span class=\"b2b-tree-node-name\">{{member.name}}</span>\n" +
15993     "        <span class=\"{{!member.child?'end':''}} b2b-tree-node-icon\">\n" +
15994     "            <i class=\"b2b-tree-expandCollapse-icon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
15995     "        </span>\n" +
15996     "         <div id=\"description_{{$id}}\" class=\"offscreen-text\">\n" +
15997     "           {{member.descriptionText}}\n" +
15998     "        </div>\n" +
15999     "        <div class=\"b2b-tree-tooltip\" ng-if=\"member.showTooltip\">\n" +
16000     "           <span class=\"b2b-tree-arrow-left\"></span>\n" +
16001     "           <div class=\"b2b-tree-tooltip-content\">\n" +
16002     "                   {{member.tooltipContent}}\n" +
16003     "           </div>  \n" +
16004     "        </div>\n" +
16005     "    </a>\n" +
16006     "</li>");
16007 }]);
16008
16009 angular.module("b2bTemplate/treeNav/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
16010   $templateCache.put("b2bTemplate/treeNav/ungroupedTree.html",
16011     "<ul role=\"{{setRole}}\"><b2b-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-member></ul>");
16012 }]);
16013
16014 angular.module("b2bTemplate/treeNodeCheckbox/groupedTree.html", []).run(["$templateCache", function($templateCache) {
16015   $templateCache.put("b2bTemplate/treeNodeCheckbox/groupedTree.html",
16016     "<ul role=\"group\">\n" +
16017     "    <li aria-expanded=\"{{(member.active?true:false)}}\" role=\"treeitem\" aria-label=\"{{key}}\" ng-repeat='(key,value) in collection | groupBy : \"grpChild\"' b2b-tree-node-link><a class=\"grp\" ng-class=\"{'active': value.showGroup == true}\" tabindex=\"-1\" href='javascript:void(0);'>\n" +
16018     "        <span class=\"ng-hide\">\n" +
16019     "            <label class=\"checkbox\">\n" +
16020     "                <input type=\"checkbox\" tabindex=\"-1\" class=\"treeCheckBox grpTreeCheckbox\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{(key)?key:''}}</span>\n" +
16021     "            </label>\n" +
16022     "        </span>\n" +
16023     "        <span>\n" +
16024     "            {{(key)?key:''}}    \n" +
16025     "        </span>\n" +
16026     "        <span class=\"nodeIcon\"><i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded': value.showGroup == true, 'icon-primary-collapsed': value.showGroup == undefined || value.showGroup == false }\"></i></span></a>\n" +
16027     "        <ul role=\"group\">\n" +
16028     "            <b2b-tree-member ng-repeat='member in value.childArray' member='member' group-it='groupIt'></b2b-tree-member>\n" +
16029     "        </ul>\n" +
16030     "    </li>\n" +
16031     "</ul>");
16032 }]);
16033
16034 angular.module("b2bTemplate/treeNodeCheckbox/treeMember.html", []).run(["$templateCache", function($templateCache) {
16035   $templateCache.put("b2bTemplate/treeNodeCheckbox/treeMember.html",
16036     "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.name}}\" ng-class=\"{'bg':member.selected}\"  b2b-tree-node-link>\n" +
16037     "    <a tabindex=\"-1\" title=\"{{member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active}\">\n" +
16038     "           <span ng-show=\"member.displayCheckbox\">\n" +
16039     "                           <label class=\"checkbox\">\n" +
16040     "                <input type=\"checkbox\" tabindex=\"-1\" ng-model=\"member.isSelected\" ng-class=\"{'treeCheckBox': (member.displayCheckbox !== undefined)}\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{member.name}}</span>\n" +
16041     "            </label>\n" +
16042     "        </span>\n" +
16043     "           <span ng-show=\"!member.displayCheckbox\">\n" +
16044     "                   {{member.name}} \n" +
16045     "           </span>\n" +
16046     "        <span class=\"nodeIcon {{!member.child?'end':''}}\">\n" +
16047     "            <i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
16048     "        </span>\n" +
16049     "    </a>\n" +
16050     "</li>");
16051 }]);
16052
16053 angular.module("b2bTemplate/treeNodeCheckbox/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
16054   $templateCache.put("b2bTemplate/treeNodeCheckbox/ungroupedTree.html",
16055     "<ul role=\"{{setRole}}\"><b2b-tree-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-tree-member></ul>");
16056 }]);